aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.arcconfig4
-rw-r--r--CMakeLists.txt152
-rw-r--r--LICENSE.TXT4
-rw-r--r--Makefile40
-rw-r--r--SDKs/darwin/usr/include/stdio.h29
-rw-r--r--SDKs/linux/usr/include/stdio.h7
-rw-r--r--cmake/Modules/AddCompilerRT.cmake49
-rw-r--r--cmake/Modules/CompilerRTCompile.cmake16
-rw-r--r--cmake/Modules/CompilerRTLink.cmake14
-rw-r--r--cmake/Modules/CompilerRTUtils.cmake17
-rw-r--r--include/sanitizer/asan_interface.h (renamed from lib/asan/asan_interface.h)77
-rw-r--r--include/sanitizer/common_interface_defs.h (renamed from lib/sanitizer_common/sanitizer_interface_defs.h)44
-rw-r--r--include/sanitizer/msan_interface.h124
-rw-r--r--lib/CMakeLists.txt51
-rw-r--r--lib/Makefile.mk1
-rw-r--r--lib/arm/Makefile.mk2
-rw-r--r--lib/arm/divsi3.S12
-rw-r--r--lib/arm/udivsi3.S10
-rw-r--r--lib/asan/CMakeLists.txt121
-rw-r--r--lib/asan/Makefile.mk7
-rw-r--r--lib/asan/Makefile.old349
-rw-r--r--lib/asan/README.txt26
-rw-r--r--lib/asan/asan_allocator.cc536
-rw-r--r--lib/asan/asan_allocator.h147
-rw-r--r--lib/asan/asan_allocator2.cc710
-rw-r--r--lib/asan/asan_fake_stack.cc182
-rw-r--r--lib/asan/asan_flags.h33
-rw-r--r--lib/asan/asan_globals.cc151
-rw-r--r--lib/asan/asan_intercepted_functions.h260
-rw-r--r--lib/asan/asan_interceptors.cc393
-rw-r--r--lib/asan/asan_interceptors.h1
-rw-r--r--lib/asan/asan_internal.h81
-rw-r--r--lib/asan/asan_linux.cc111
-rw-r--r--lib/asan/asan_lock.h42
-rw-r--r--lib/asan/asan_mac.cc287
-rw-r--r--lib/asan/asan_mac.h8
-rw-r--r--lib/asan/asan_malloc_linux.cc42
-rw-r--r--lib/asan/asan_malloc_mac.cc195
-rw-r--r--lib/asan/asan_malloc_win.cc15
-rw-r--r--lib/asan/asan_mapping.h31
-rw-r--r--lib/asan/asan_new_delete.cc62
-rw-r--r--lib/asan/asan_poisoning.cc71
-rw-r--r--lib/asan/asan_posix.cc10
-rw-r--r--lib/asan/asan_printf.cc59
-rw-r--r--lib/asan/asan_report.cc681
-rw-r--r--lib/asan/asan_report.h57
-rw-r--r--lib/asan/asan_rtl.cc425
-rw-r--r--lib/asan/asan_stack.cc225
-rw-r--r--lib/asan/asan_stack.h90
-rw-r--r--lib/asan/asan_stats.cc36
-rw-r--r--lib/asan/asan_stats.h10
-rw-r--r--lib/asan/asan_thread.cc35
-rw-r--r--lib/asan/asan_thread.h28
-rw-r--r--lib/asan/asan_thread_registry.cc62
-rw-r--r--lib/asan/asan_thread_registry.h12
-rw-r--r--lib/asan/asan_win.cc154
-rw-r--r--lib/asan/dynamic/Makefile.mk25
-rw-r--r--lib/asan/dynamic/asan_interceptors_dynamic.cc111
-rw-r--r--lib/asan/lit_tests/CMakeLists.txt32
-rw-r--r--lib/asan/lit_tests/Helpers/blacklist-extra.cc5
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc15
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-blacklist.txt2
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-bug-extra.cc5
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc6
-rw-r--r--lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc9
-rw-r--r--lib/asan/lit_tests/Helpers/lit.local.cfg3
-rw-r--r--lib/asan/lit_tests/Linux/clone_test.cc48
-rw-r--r--lib/asan/lit_tests/Linux/initialization-bug-any-order.cc37
-rw-r--r--lib/asan/lit_tests/Linux/interception_failure_test.cc26
-rw-r--r--lib/asan/lit_tests/Linux/interception_malloc_test.cc27
-rw-r--r--lib/asan/lit_tests/Linux/interception_test.cc26
-rw-r--r--lib/asan/lit_tests/Linux/lit.local.cfg9
-rw-r--r--lib/asan/lit_tests/Linux/malloc-in-qsort.cc50
-rw-r--r--lib/asan/lit_tests/Linux/overflow-in-qsort.cc47
-rw-r--r--lib/asan/lit_tests/Linux/rlimit_mmap_test.cc16
-rw-r--r--lib/asan/lit_tests/Linux/swapcontext_test.cc66
-rw-r--r--lib/asan/lit_tests/SharedLibs/dlclose-test-so.cc (renamed from lib/asan/output_tests/dlclose-test-so.cc)0
-rw-r--r--lib/asan/lit_tests/SharedLibs/lit.local.cfg4
-rw-r--r--lib/asan/lit_tests/SharedLibs/shared-lib-test-so.cc (renamed from lib/asan/output_tests/shared-lib-test-so.cc)0
-rw-r--r--lib/asan/lit_tests/Unit/lit.cfg27
-rw-r--r--lib/asan/lit_tests/Unit/lit.site.cfg.in10
-rw-r--r--lib/asan/lit_tests/blacklist.cc44
-rw-r--r--lib/asan/lit_tests/deep_stack_uaf.cc36
-rw-r--r--lib/asan/lit_tests/deep_tail_call.cc24
-rw-r--r--lib/asan/lit_tests/deep_thread_stack.cc61
-rw-r--r--lib/asan/lit_tests/default_options.cc (renamed from lib/asan/output_tests/default_options.cc)5
-rw-r--r--lib/asan/lit_tests/dlclose-test.cc (renamed from lib/asan/output_tests/dlclose-test.cc)40
-rw-r--r--lib/asan/lit_tests/force_inline_opt0.cc14
-rw-r--r--lib/asan/lit_tests/global-overflow.cc25
-rw-r--r--lib/asan/lit_tests/heap-overflow.cc38
-rw-r--r--lib/asan/lit_tests/initialization-blacklist.cc32
-rw-r--r--lib/asan/lit_tests/initialization-bug.cc46
-rw-r--r--lib/asan/lit_tests/initialization-nobug.cc67
-rw-r--r--lib/asan/lit_tests/interface_symbols.c28
-rw-r--r--lib/asan/lit_tests/large_func_test.cc62
-rw-r--r--lib/asan/lit_tests/lit.cfg97
-rw-r--r--lib/asan/lit_tests/lit.site.cfg.in20
-rw-r--r--lib/asan/lit_tests/log-path_test.cc39
-rw-r--r--lib/asan/lit_tests/log_path_fork_test.cc22
-rw-r--r--lib/asan/lit_tests/malloc_delete_mismatch.cc26
-rw-r--r--lib/asan/lit_tests/malloc_hook.cc24
-rw-r--r--lib/asan/lit_tests/memcmp_test.cc19
-rw-r--r--lib/asan/lit_tests/null_deref.cc31
-rw-r--r--lib/asan/lit_tests/on_error_callback.cc16
-rw-r--r--lib/asan/lit_tests/sanity_check_pure_c.c19
-rw-r--r--lib/asan/lit_tests/shared-lib-test.cc54
-rw-r--r--lib/asan/lit_tests/sleep_before_dying.c10
-rw-r--r--lib/asan/lit_tests/stack-frame-demangle.cc24
-rw-r--r--lib/asan/lit_tests/stack-overflow.cc19
-rw-r--r--lib/asan/lit_tests/stack-use-after-return.cc45
-rw-r--r--lib/asan/lit_tests/strip_path_prefix.c12
-rw-r--r--lib/asan/lit_tests/strncpy-overflow.cc40
-rw-r--r--lib/asan/lit_tests/symbolize_callback.cc17
-rw-r--r--lib/asan/lit_tests/use-after-free.cc48
-rw-r--r--lib/asan/lit_tests/use-after-scope-inlined.cc29
-rw-r--r--lib/asan/output_tests/clone_test.cc34
-rw-r--r--lib/asan/output_tests/deep_tail_call.cc15
-rw-r--r--lib/asan/output_tests/global-overflow.cc16
-rw-r--r--lib/asan/output_tests/heap-overflow.cc22
-rw-r--r--lib/asan/output_tests/interception_failure_test-linux.cc17
-rw-r--r--lib/asan/output_tests/interception_malloc_test-linux.cc19
-rw-r--r--lib/asan/output_tests/interception_test-linux.cc18
-rw-r--r--lib/asan/output_tests/large_func_test.cc48
-rw-r--r--lib/asan/output_tests/memcmp_test.cc10
-rw-r--r--lib/asan/output_tests/null_deref.cc17
-rw-r--r--lib/asan/output_tests/shared-lib-test.cc42
-rw-r--r--lib/asan/output_tests/stack-overflow.cc11
-rw-r--r--lib/asan/output_tests/stack-use-after-return.cc.disabled27
-rw-r--r--lib/asan/output_tests/strncpy-overflow.cc24
-rwxr-xr-xlib/asan/output_tests/test_output.sh79
-rw-r--r--lib/asan/output_tests/use-after-free.c9
-rw-r--r--lib/asan/output_tests/use-after-free.cc31
-rwxr-xr-xlib/asan/scripts/asan_symbolize.py419
-rw-r--r--lib/asan/tests/CMakeLists.txt237
-rw-r--r--lib/asan/tests/asan_benchmarks_test.cc1
-rw-r--r--lib/asan/tests/asan_globals_test.cc2
-rw-r--r--lib/asan/tests/asan_mac_test.mm12
-rw-r--r--lib/asan/tests/asan_noinst_test.cc233
-rw-r--r--lib/asan/tests/asan_test.cc749
-rw-r--r--lib/asan/tests/asan_test.ignore1
-rw-r--r--lib/asan/tests/asan_test_config.h18
-rw-r--r--lib/asan/tests/asan_test_main.cc (renamed from lib/asan/tests/asan_break_optimization.cc)12
-rw-r--r--lib/asan/tests/asan_test_utils.h45
-rw-r--r--lib/cmpdi2.c13
-rw-r--r--lib/fixsfdi.c2
-rw-r--r--lib/int_endianness.h15
-rw-r--r--lib/interception/CMakeLists.txt44
-rw-r--r--lib/interception/interception.h110
-rw-r--r--lib/interception/interception_linux.cc7
-rw-r--r--lib/interception/interception_linux.h10
-rw-r--r--lib/interception/interception_mac.cc14
-rw-r--r--lib/interception/interception_mac.h8
-rw-r--r--lib/interception/interception_win.cc20
-rw-r--r--lib/interception/interception_win.h13
-rw-r--r--lib/interception/mach_override/mach_override.c68
-rw-r--r--lib/lit.common.cfg54
-rw-r--r--lib/lit.common.unit.cfg21
-rw-r--r--lib/msan/CMakeLists.txt35
-rw-r--r--lib/msan/msan.cc420
-rw-r--r--lib/msan/msan.h68
-rw-r--r--lib/msan/msan_allocator.cc107
-rw-r--r--lib/msan/msan_flags.h34
-rw-r--r--lib/msan/msan_interceptors.cc942
-rw-r--r--lib/msan/msan_linux.cc108
-rw-r--r--lib/msan/msan_new_delete.cc51
-rw-r--r--lib/msan/msan_platform_limits_posix.cc59
-rw-r--r--lib/msan/msan_platform_limits_posix.h36
-rw-r--r--lib/msan/msan_report.cc100
-rw-r--r--lib/msan/tests/CMakeLists.txt166
-rw-r--r--lib/msan/tests/lit.cfg29
-rw-r--r--lib/msan/tests/lit.site.cfg.in9
-rw-r--r--lib/msan/tests/msan_test.cc1675
-rw-r--r--lib/msan/tests/msandr_test_so.cc36
-rw-r--r--lib/msan/tests/msandr_test_so.h23
-rw-r--r--lib/profile/GCDAProfiling.c91
-rw-r--r--lib/sanitizer_common/CMakeLists.txt72
-rw-r--r--lib/sanitizer_common/sanitizer_allocator.cc35
-rw-r--r--lib/sanitizer_common/sanitizer_allocator.h989
-rw-r--r--lib/sanitizer_common/sanitizer_allocator64.h488
-rw-r--r--lib/sanitizer_common/sanitizer_atomic_clang.h2
-rw-r--r--lib/sanitizer_common/sanitizer_atomic_msvc.h48
-rw-r--r--lib/sanitizer_common/sanitizer_common.cc135
-rw-r--r--lib/sanitizer_common/sanitizer_common.h108
-rw-r--r--lib/sanitizer_common/sanitizer_common_interceptors.inc224
-rw-r--r--lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc142
-rw-r--r--lib/sanitizer_common/sanitizer_flags.cc63
-rw-r--r--lib/sanitizer_common/sanitizer_internal_defs.h83
-rw-r--r--lib/sanitizer_common/sanitizer_lfstack.h73
-rw-r--r--lib/sanitizer_common/sanitizer_libc.cc45
-rw-r--r--lib/sanitizer_common/sanitizer_libc.h14
-rw-r--r--lib/sanitizer_common/sanitizer_linux.cc327
-rw-r--r--lib/sanitizer_common/sanitizer_list.h4
-rw-r--r--lib/sanitizer_common/sanitizer_mac.cc91
-rw-r--r--lib/sanitizer_common/sanitizer_mutex.h31
-rw-r--r--lib/sanitizer_common/sanitizer_placement_new.h2
-rw-r--r--lib/sanitizer_common/sanitizer_platform_interceptors.h38
-rw-r--r--lib/sanitizer_common/sanitizer_posix.cc84
-rw-r--r--lib/sanitizer_common/sanitizer_printf.cc88
-rw-r--r--lib/sanitizer_common/sanitizer_procmaps.h48
-rw-r--r--lib/sanitizer_common/sanitizer_quarantine.h172
-rw-r--r--lib/sanitizer_common/sanitizer_report_decorator.h37
-rw-r--r--lib/sanitizer_common/sanitizer_stackdepot.cc204
-rw-r--r--lib/sanitizer_common/sanitizer_stackdepot.h36
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace.cc262
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace.h79
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer.cc325
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer.h60
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_itanium.cc42
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_linux.cc182
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_mac.cc31
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_win.cc37
-rw-r--r--lib/sanitizer_common/sanitizer_win.cc119
-rwxr-xr-xlib/sanitizer_common/scripts/check_lint.sh82
-rw-r--r--lib/sanitizer_common/tests/CMakeLists.txt138
-rw-r--r--lib/sanitizer_common/tests/lit.cfg29
-rw-r--r--lib/sanitizer_common/tests/lit.site.cfg.in9
-rw-r--r--lib/sanitizer_common/tests/sanitizer_allocator64_test.cc257
-rw-r--r--lib/sanitizer_common/tests/sanitizer_allocator64_testlib.cc99
-rw-r--r--lib/sanitizer_common/tests/sanitizer_allocator_test.cc484
-rw-r--r--lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc162
-rw-r--r--lib/sanitizer_common/tests/sanitizer_common_test.cc33
-rw-r--r--lib/sanitizer_common/tests/sanitizer_flags_test.cc10
-rw-r--r--lib/sanitizer_common/tests/sanitizer_libc_test.cc42
-rw-r--r--lib/sanitizer_common/tests/sanitizer_list_test.cc20
-rw-r--r--lib/sanitizer_common/tests/sanitizer_mutex_test.cc128
-rw-r--r--lib/sanitizer_common/tests/sanitizer_printf_test.cc (renamed from lib/tsan/unit_tests/tsan_printf_test.cc)47
-rw-r--r--lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc85
-rw-r--r--lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc69
-rw-r--r--lib/sanitizer_common/tests/sanitizer_test_main.cc19
-rw-r--r--lib/sanitizer_common/tests/sanitizer_test_utils.h80
-rw-r--r--lib/sanitizer_common/tests/standalone_malloc_test.cc87
-rw-r--r--lib/tsan/CMakeLists.txt24
-rw-r--r--lib/tsan/Makefile.old43
-rwxr-xr-xlib/tsan/check_cmake.sh11
-rwxr-xr-xlib/tsan/go/buildgo.sh57
-rw-r--r--lib/tsan/go/test.c2
-rw-r--r--lib/tsan/go/tsan_go.cc81
-rw-r--r--lib/tsan/lit_tests/CMakeLists.txt36
-rw-r--r--lib/tsan/lit_tests/Helpers/blacklist.txt1
-rw-r--r--lib/tsan/lit_tests/Helpers/lit.local.cfg2
-rw-r--r--lib/tsan/lit_tests/Unit/lit.cfg37
-rw-r--r--lib/tsan/lit_tests/Unit/lit.site.cfg.in18
-rw-r--r--lib/tsan/lit_tests/blacklist.cc31
-rw-r--r--lib/tsan/lit_tests/fd_close_norace.cc33
-rw-r--r--lib/tsan/lit_tests/fd_dup_norace.cc34
-rw-r--r--lib/tsan/lit_tests/fd_location.cc33
-rw-r--r--lib/tsan/lit_tests/fd_pipe_norace.cc33
-rw-r--r--lib/tsan/lit_tests/fd_pipe_race.cc37
-rw-r--r--lib/tsan/lit_tests/fd_socket_connect_norace.cc45
-rw-r--r--lib/tsan/lit_tests/fd_socket_norace.cc52
-rw-r--r--lib/tsan/lit_tests/fd_socketpair_norace.cc37
-rw-r--r--lib/tsan/lit_tests/fd_stdout_race.cc41
-rw-r--r--lib/tsan/lit_tests/free_race.c (renamed from lib/tsan/output_tests/free_race.c)8
-rw-r--r--lib/tsan/lit_tests/free_race2.c (renamed from lib/tsan/output_tests/free_race2.c)2
-rw-r--r--lib/tsan/lit_tests/global_race.cc25
-rw-r--r--lib/tsan/lit_tests/heap_race.cc (renamed from lib/tsan/output_tests/heap_race.cc)1
-rw-r--r--lib/tsan/lit_tests/ignore_race.cc31
-rw-r--r--lib/tsan/lit_tests/java.h17
-rw-r--r--lib/tsan/lit_tests/java_alloc.cc32
-rw-r--r--lib/tsan/lit_tests/java_lock.cc33
-rw-r--r--lib/tsan/lit_tests/java_lock_move.cc40
-rw-r--r--lib/tsan/lit_tests/java_race.cc23
-rw-r--r--lib/tsan/lit_tests/java_race_move.cc31
-rw-r--r--lib/tsan/lit_tests/java_rwlock.cc33
-rw-r--r--lib/tsan/lit_tests/lit.cfg93
-rw-r--r--lib/tsan/lit_tests/lit.site.cfg.in19
-rw-r--r--lib/tsan/lit_tests/memcpy_race.cc (renamed from lib/tsan/output_tests/memcpy_race.cc)8
-rw-r--r--lib/tsan/lit_tests/mop_with_offset.cc (renamed from lib/tsan/output_tests/mop_with_offset.cc)8
-rw-r--r--lib/tsan/lit_tests/mop_with_offset2.cc (renamed from lib/tsan/output_tests/mop_with_offset2.cc)8
-rw-r--r--lib/tsan/lit_tests/mutex_destroy_locked.cc21
-rw-r--r--lib/tsan/lit_tests/mutexset1.cc37
-rw-r--r--lib/tsan/lit_tests/mutexset2.cc37
-rw-r--r--lib/tsan/lit_tests/mutexset3.cc45
-rw-r--r--lib/tsan/lit_tests/mutexset4.cc45
-rw-r--r--lib/tsan/lit_tests/mutexset5.cc46
-rw-r--r--lib/tsan/lit_tests/mutexset6.cc53
-rw-r--r--lib/tsan/lit_tests/mutexset7.cc38
-rw-r--r--lib/tsan/lit_tests/race_on_barrier.c (renamed from lib/tsan/output_tests/race_on_barrier.c)4
-rw-r--r--lib/tsan/lit_tests/race_on_barrier2.c (renamed from lib/tsan/output_tests/race_on_barrier2.c)1
-rw-r--r--lib/tsan/lit_tests/race_on_heap.cc47
-rw-r--r--lib/tsan/lit_tests/race_on_mutex.c (renamed from lib/tsan/output_tests/race_on_mutex.c)13
-rw-r--r--lib/tsan/lit_tests/race_on_read.cc32
-rw-r--r--lib/tsan/lit_tests/race_with_finished_thread.cc (renamed from lib/tsan/output_tests/race_with_finished_thread.cc)10
-rw-r--r--lib/tsan/lit_tests/signal_errno.cc42
-rw-r--r--lib/tsan/lit_tests/signal_malloc.cc25
-rw-r--r--lib/tsan/lit_tests/simple_race.c (renamed from lib/tsan/output_tests/simple_race.c)1
-rw-r--r--lib/tsan/lit_tests/simple_race.cc (renamed from lib/tsan/output_tests/simple_race.cc)1
-rw-r--r--lib/tsan/lit_tests/simple_stack.c66
-rw-r--r--lib/tsan/lit_tests/simple_stack2.cc53
-rw-r--r--lib/tsan/lit_tests/sleep_sync.cc30
-rw-r--r--lib/tsan/lit_tests/sleep_sync2.cc22
-rw-r--r--lib/tsan/lit_tests/stack_race.cc20
-rw-r--r--lib/tsan/lit_tests/stack_race2.cc28
-rw-r--r--lib/tsan/lit_tests/static_init1.cc (renamed from lib/tsan/output_tests/static_init1.cc)2
-rw-r--r--lib/tsan/lit_tests/static_init2.cc (renamed from lib/tsan/output_tests/static_init2.cc)4
-rw-r--r--lib/tsan/lit_tests/static_init3.cc (renamed from lib/tsan/output_tests/static_init3.cc)1
-rw-r--r--lib/tsan/lit_tests/static_init4.cc (renamed from lib/tsan/output_tests/static_init4.cc)4
-rw-r--r--lib/tsan/lit_tests/static_init5.cc (renamed from lib/tsan/output_tests/static_init5.cc)6
-rw-r--r--lib/tsan/lit_tests/static_init6.cc42
-rw-r--r--lib/tsan/lit_tests/suppress_same_address.cc (renamed from lib/tsan/output_tests/suppress_same_address.cc)2
-rw-r--r--lib/tsan/lit_tests/suppress_same_stacks.cc (renamed from lib/tsan/output_tests/suppress_same_stacks.cc)2
-rwxr-xr-xlib/tsan/lit_tests/test_output.sh (renamed from lib/tsan/output_tests/test_output.sh)18
-rw-r--r--lib/tsan/lit_tests/thread_leak.c (renamed from lib/tsan/output_tests/thread_leak.c)4
-rw-r--r--lib/tsan/lit_tests/thread_leak2.c (renamed from lib/tsan/output_tests/thread_leak2.c)4
-rw-r--r--lib/tsan/lit_tests/thread_leak3.c (renamed from lib/tsan/output_tests/thread_leak3.c)2
-rw-r--r--lib/tsan/lit_tests/thread_name.cc34
-rw-r--r--lib/tsan/lit_tests/tiny_race.c (renamed from lib/tsan/output_tests/tiny_race.c)1
-rw-r--r--lib/tsan/lit_tests/tls_race.cc19
-rw-r--r--lib/tsan/lit_tests/tls_race2.cc28
-rw-r--r--lib/tsan/lit_tests/user_fopen.cc34
-rw-r--r--lib/tsan/lit_tests/user_malloc.cc27
-rw-r--r--lib/tsan/lit_tests/virtual_inheritance_compile_bug.cc (renamed from lib/tsan/output_tests/virtual_inheritance_compile_bug.cc)8
-rw-r--r--lib/tsan/lit_tests/vptr_benign_race.cc (renamed from lib/tsan/output_tests/vptr_benign_race.cc)1
-rw-r--r--lib/tsan/lit_tests/vptr_harmful_race.cc (renamed from lib/tsan/output_tests/vptr_harmful_race.cc)1
-rw-r--r--lib/tsan/lit_tests/write_in_reader_lock.cc35
-rw-r--r--lib/tsan/output_tests/simple_stack.c65
-rw-r--r--lib/tsan/output_tests/simple_stack2.cc46
-rw-r--r--lib/tsan/rtl/CMakeLists.txt58
-rw-r--r--lib/tsan/rtl/Makefile.mk2
-rw-r--r--lib/tsan/rtl/Makefile.old9
-rw-r--r--lib/tsan/rtl/tsan_clock.cc7
-rw-r--r--lib/tsan/rtl/tsan_clock.h2
-rw-r--r--lib/tsan/rtl/tsan_defs.h39
-rw-r--r--lib/tsan/rtl/tsan_fd.cc265
-rw-r--r--lib/tsan/rtl/tsan_fd.h65
-rw-r--r--lib/tsan/rtl/tsan_flags.cc37
-rw-r--r--lib/tsan/rtl/tsan_flags.h28
-rw-r--r--lib/tsan/rtl/tsan_interceptors.cc931
-rw-r--r--lib/tsan/rtl/tsan_interface.h44
-rw-r--r--lib/tsan/rtl/tsan_interface_ann.cc123
-rw-r--r--lib/tsan/rtl/tsan_interface_ann.h6
-rw-r--r--lib/tsan/rtl/tsan_interface_atomic.cc449
-rw-r--r--lib/tsan/rtl/tsan_interface_atomic.h178
-rw-r--r--lib/tsan/rtl/tsan_interface_inl.h8
-rw-r--r--lib/tsan/rtl/tsan_interface_java.cc305
-rw-r--r--lib/tsan/rtl/tsan_interface_java.h74
-rw-r--r--lib/tsan/rtl/tsan_mman.cc103
-rw-r--r--lib/tsan/rtl/tsan_mman.h58
-rw-r--r--lib/tsan/rtl/tsan_mutex.cc65
-rw-r--r--lib/tsan/rtl/tsan_mutex.h6
-rw-r--r--lib/tsan/rtl/tsan_mutexset.cc89
-rw-r--r--lib/tsan/rtl/tsan_mutexset.h65
-rw-r--r--lib/tsan/rtl/tsan_platform.h80
-rw-r--r--lib/tsan/rtl/tsan_platform_linux.cc130
-rw-r--r--lib/tsan/rtl/tsan_platform_mac.cc18
-rw-r--r--lib/tsan/rtl/tsan_platform_windows.cc58
-rw-r--r--lib/tsan/rtl/tsan_printf.cc39
-rw-r--r--lib/tsan/rtl/tsan_report.cc146
-rw-r--r--lib/tsan/rtl/tsan_report.h24
-rw-r--r--lib/tsan/rtl/tsan_rtl.cc173
-rw-r--r--lib/tsan/rtl/tsan_rtl.h199
-rw-r--r--lib/tsan/rtl/tsan_rtl_amd64.S95
-rw-r--r--lib/tsan/rtl/tsan_rtl_mutex.cc146
-rw-r--r--lib/tsan/rtl/tsan_rtl_report.cc342
-rw-r--r--lib/tsan/rtl/tsan_rtl_thread.cc92
-rw-r--r--lib/tsan/rtl/tsan_stat.cc51
-rw-r--r--lib/tsan/rtl/tsan_stat.h47
-rw-r--r--lib/tsan/rtl/tsan_suppressions.cc34
-rw-r--r--lib/tsan/rtl/tsan_suppressions.h4
-rw-r--r--lib/tsan/rtl/tsan_symbolize.cc43
-rw-r--r--lib/tsan/rtl/tsan_symbolize.h3
-rw-r--r--lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc40
-rw-r--r--lib/tsan/rtl/tsan_sync.cc114
-rw-r--r--lib/tsan/rtl/tsan_sync.h31
-rw-r--r--lib/tsan/rtl/tsan_trace.h16
-rw-r--r--lib/tsan/rtl/tsan_update_shadow_word_inl.h4
-rw-r--r--lib/tsan/tests/CMakeLists.txt24
-rw-r--r--lib/tsan/tests/rtl/CMakeLists.txt15
-rw-r--r--lib/tsan/tests/rtl/tsan_bench.cc (renamed from lib/tsan/rtl_tests/tsan_bench.cc)0
-rw-r--r--lib/tsan/tests/rtl/tsan_mop.cc (renamed from lib/tsan/rtl_tests/tsan_mop.cc)0
-rw-r--r--lib/tsan/tests/rtl/tsan_mutex.cc (renamed from lib/tsan/rtl_tests/tsan_mutex.cc)0
-rw-r--r--lib/tsan/tests/rtl/tsan_posix.cc (renamed from lib/tsan/rtl_tests/tsan_posix.cc)0
-rw-r--r--lib/tsan/tests/rtl/tsan_string.cc (renamed from lib/tsan/rtl_tests/tsan_string.cc)4
-rw-r--r--lib/tsan/tests/rtl/tsan_test.cc (renamed from lib/tsan/rtl_tests/tsan_test.cc)10
-rw-r--r--lib/tsan/tests/rtl/tsan_test_util.h (renamed from lib/tsan/rtl_tests/tsan_test_util.h)0
-rw-r--r--lib/tsan/tests/rtl/tsan_test_util_linux.cc (renamed from lib/tsan/rtl_tests/tsan_test_util_linux.cc)2
-rw-r--r--lib/tsan/tests/rtl/tsan_thread.cc (renamed from lib/tsan/rtl_tests/tsan_thread.cc)0
-rw-r--r--lib/tsan/tests/unit/CMakeLists.txt14
-rw-r--r--lib/tsan/tests/unit/tsan_clock_test.cc (renamed from lib/tsan/unit_tests/tsan_clock_test.cc)0
-rw-r--r--lib/tsan/tests/unit/tsan_flags_test.cc (renamed from lib/tsan/unit_tests/tsan_flags_test.cc)4
-rw-r--r--lib/tsan/tests/unit/tsan_mman_test.cc (renamed from lib/tsan/unit_tests/tsan_mman_test.cc)0
-rw-r--r--lib/tsan/tests/unit/tsan_mutex_test.cc (renamed from lib/tsan/unit_tests/tsan_mutex_test.cc)0
-rw-r--r--lib/tsan/tests/unit/tsan_mutexset_test.cc126
-rw-r--r--lib/tsan/tests/unit/tsan_platform_test.cc (renamed from lib/tsan/unit_tests/tsan_platform_test.cc)1
-rw-r--r--lib/tsan/tests/unit/tsan_shadow_test.cc (renamed from lib/tsan/unit_tests/tsan_shadow_test.cc)31
-rw-r--r--lib/tsan/tests/unit/tsan_stack_test.cc80
-rw-r--r--lib/tsan/tests/unit/tsan_suppressions_test.cc (renamed from lib/tsan/unit_tests/tsan_suppressions_test.cc)0
-rw-r--r--lib/tsan/tests/unit/tsan_sync_test.cc (renamed from lib/tsan/unit_tests/tsan_sync_test.cc)2
-rw-r--r--lib/tsan/tests/unit/tsan_vector_test.cc (renamed from lib/tsan/unit_tests/tsan_vector_test.cc)0
-rw-r--r--lib/ubsan/CMakeLists.txt49
-rw-r--r--lib/ubsan/Makefile.mk23
-rw-r--r--lib/ubsan/lit_tests/CMakeLists.txt22
-rw-r--r--lib/ubsan/lit_tests/Float/cast-overflow.cpp98
-rw-r--r--lib/ubsan/lit_tests/Integer/add-overflow.cpp32
-rw-r--r--lib/ubsan/lit_tests/Integer/div-overflow.cpp10
-rw-r--r--lib/ubsan/lit_tests/Integer/div-zero.cpp15
-rw-r--r--lib/ubsan/lit_tests/Integer/incdec-overflow.cpp16
-rw-r--r--lib/ubsan/lit_tests/Integer/mul-overflow.cpp14
-rw-r--r--lib/ubsan/lit_tests/Integer/negate-overflow.cpp12
-rw-r--r--lib/ubsan/lit_tests/Integer/no-recover.cpp22
-rw-r--r--lib/ubsan/lit_tests/Integer/shift.cpp37
-rw-r--r--lib/ubsan/lit_tests/Integer/sub-overflow.cpp31
-rw-r--r--lib/ubsan/lit_tests/Integer/uadd-overflow.cpp32
-rw-r--r--lib/ubsan/lit_tests/Integer/uincdec-overflow.cpp16
-rw-r--r--lib/ubsan/lit_tests/Integer/umul-overflow.cpp19
-rw-r--r--lib/ubsan/lit_tests/Integer/usub-overflow.cpp31
-rw-r--r--lib/ubsan/lit_tests/Misc/bool.cpp11
-rw-r--r--lib/ubsan/lit_tests/Misc/deduplication.cpp25
-rw-r--r--lib/ubsan/lit_tests/Misc/enum.cpp17
-rw-r--r--lib/ubsan/lit_tests/Misc/missing_return.cpp9
-rw-r--r--lib/ubsan/lit_tests/Misc/unreachable.cpp6
-rw-r--r--lib/ubsan/lit_tests/Misc/vla.c11
-rw-r--r--lib/ubsan/lit_tests/TypeCheck/misaligned.cpp73
-rw-r--r--lib/ubsan/lit_tests/TypeCheck/null.cpp38
-rw-r--r--lib/ubsan/lit_tests/TypeCheck/vptr.cpp106
-rw-r--r--lib/ubsan/lit_tests/lit.cfg65
-rw-r--r--lib/ubsan/lit_tests/lit.site.cfg.in19
-rw-r--r--lib/ubsan/ubsan_diag.cc271
-rw-r--r--lib/ubsan/ubsan_diag.h202
-rw-r--r--lib/ubsan/ubsan_handlers.cc244
-rw-r--r--lib/ubsan/ubsan_handlers.h108
-rw-r--r--lib/ubsan/ubsan_handlers_cxx.cc75
-rw-r--r--lib/ubsan/ubsan_handlers_cxx.h40
-rw-r--r--lib/ubsan/ubsan_type_hash.cc248
-rw-r--r--lib/ubsan/ubsan_type_hash.h63
-rw-r--r--lib/ubsan/ubsan_value.cc81
-rw-r--r--lib/ubsan/ubsan_value.h195
-rw-r--r--lib/ucmpdi2.c13
-rw-r--r--make/AppleBI.mk3
-rw-r--r--make/config.mk3
-rw-r--r--make/lib_info.mk7
-rw-r--r--make/options.mk12
-rw-r--r--make/platform/clang_darwin.mk143
-rw-r--r--make/platform/clang_linux.mk82
-rw-r--r--make/platform/clang_linux_test_input.c4
-rw-r--r--make/platform/darwin_bni.mk4
-rw-r--r--test/Unit/endianness.h15
437 files changed, 25435 insertions, 6095 deletions
diff --git a/.arcconfig b/.arcconfig
new file mode 100644
index 000000000000..413b70b05f7b
--- /dev/null
+++ b/.arcconfig
@@ -0,0 +1,4 @@
+{
+ "project_id" : "compiler-rt",
+ "conduit_uri" : "http://llvm-reviews.chandlerc.com/"
+}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 97835a1e945e..04d6e9763bf8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,49 +15,157 @@ include(LLVMParseArguments)
# runtime libraries.
cmake_minimum_required(VERSION 2.8.8)
-# FIXME: Below we assume that the target build of LLVM/Clang is x86, which is
-# not at all valid. Much of this can be fixed just by switching to use
-# a just-built-clang binary for the compiles.
+# Add path for custom modules
+set(CMAKE_MODULE_PATH
+ ${CMAKE_MODULE_PATH}
+ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules"
+ )
+include(AddCompilerRT)
+
+set(COMPILER_RT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
# Detect whether the current target platform is 32-bit or 64-bit, and setup
# the correct commandline flags needed to attempt to target 32-bit and 64-bit.
-if(CMAKE_SIZEOF_VOID_P EQUAL 4)
- set(TARGET_X86_64_CFLAGS "-m64")
- set(TARGET_I386_CFLAGS "")
+if(CMAKE_SIZEOF_VOID_P EQUAL 4 OR LLVM_BUILD_32_BITS)
+ set(TARGET_64_BIT_CFLAGS "-m64")
+ set(TARGET_32_BIT_CFLAGS "")
else()
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
message(FATAL_ERROR "Please use a sane architecture with 4 or 8 byte pointers.")
endif()
- set(TARGET_X86_64_CFLAGS "")
- set(TARGET_I386_CFLAGS "-m32")
+ set(TARGET_64_BIT_CFLAGS "")
+ set(TARGET_32_BIT_CFLAGS "-m32")
endif()
+# FIXME: Below we assume that the target build of LLVM/Clang is x86, which is
+# not at all valid. Much of this can be fixed just by switching to use
+# a just-built-clang binary for the compiles.
+
+set(TARGET_x86_64_CFLAGS ${TARGET_64_BIT_CFLAGS})
+set(TARGET_i386_CFLAGS ${TARGET_32_BIT_CFLAGS})
+
+set(COMPILER_RT_SUPPORTED_ARCH
+ x86_64 i386)
+
+function(get_target_flags_for_arch arch out_var)
+ list(FIND COMPILER_RT_SUPPORTED_ARCH ${arch} ARCH_INDEX)
+ if(ARCH_INDEX EQUAL -1)
+ message(FATAL_ERROR "Unsupported architecture: ${arch}")
+ else()
+ set(${out_var} ${TARGET_${arch}_CFLAGS} PARENT_SCOPE)
+ endif()
+endfunction()
+
# Try to compile a very simple source file to ensure we can target the given
# platform. We use the results of these tests to build only the various target
# runtime libraries supported by our current compilers cross-compiling
# abilities.
set(SIMPLE_SOURCE64 ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/simple64.c)
file(WRITE ${SIMPLE_SOURCE64} "#include <stdlib.h>\nint main() {}")
-try_compile(CAN_TARGET_X86_64 ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE64}
- COMPILE_DEFINITIONS "${TARGET_X86_64_CFLAGS}"
- CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${TARGET_X86_64_CFLAGS}")
+try_compile(CAN_TARGET_x86_64 ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE64}
+ COMPILE_DEFINITIONS "${TARGET_x86_64_CFLAGS}"
+ CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${TARGET_x86_64_CFLAGS}")
set(SIMPLE_SOURCE32 ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/simple32.c)
file(WRITE ${SIMPLE_SOURCE32} "#include <stdlib.h>\nint main() {}")
-try_compile(CAN_TARGET_I386 ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE32}
- COMPILE_DEFINITIONS "${TARGET_I386_CFLAGS}"
- CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${TARGET_I386_CFLAGS}")
-
-# Because compiler-rt spends a lot of time setting up custom compile flags,
-# define a handy helper function for it. The compile flags setting in CMake
-# has serious issues that make its syntax challenging at best.
-function(set_target_compile_flags target)
- foreach(arg ${ARGN})
- set(argstring "${argstring} ${arg}")
+try_compile(CAN_TARGET_i386 ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE32}
+ COMPILE_DEFINITIONS "${TARGET_i386_CFLAGS}"
+ CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${TARGET_i386_CFLAGS}")
+
+# We only support running instrumented tests when we're not cross compiling
+# and target a unix-like system. On Android we define the rules for building
+# unit tests, but don't execute them.
+if("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND UNIX AND NOT ANDROID)
+ set(COMPILER_RT_CAN_EXECUTE_TESTS TRUE)
+else()
+ set(COMPILER_RT_CAN_EXECUTE_TESTS FALSE)
+endif()
+
+function(filter_available_targets out_var)
+ set(archs)
+ foreach(arch ${ARGN})
+ list(FIND COMPILER_RT_SUPPORTED_ARCH ${arch} ARCH_INDEX)
+ if(NOT (ARCH_INDEX EQUAL -1) AND CAN_TARGET_${arch})
+ list(APPEND archs ${arch})
+ endif()
endforeach()
- set_property(TARGET ${target} PROPERTY COMPILE_FLAGS "${argstring}")
+ set(${out_var} ${archs} PARENT_SCOPE)
endfunction()
+# Provide some common commmandline flags for Sanitizer runtimes.
+set(SANITIZER_COMMON_CFLAGS
+ -fPIC
+ -fno-builtin
+ -fno-exceptions
+ -fomit-frame-pointer
+ -funwind-tables
+ -O3
+ )
+if(NOT WIN32)
+ list(APPEND SANITIZER_COMMON_CFLAGS -fvisibility=hidden)
+endif()
+# Build sanitizer runtimes with debug info.
+check_cxx_compiler_flag(-gline-tables-only SUPPORTS_GLINE_TABLES_ONLY_FLAG)
+if(SUPPORTS_GLINE_TABLES_ONLY_FLAG)
+ list(APPEND SANITIZER_COMMON_CFLAGS -gline-tables-only)
+else()
+ list(APPEND SANITIZER_COMMON_CFLAGS -g)
+endif()
+# Warnings suppressions.
+check_cxx_compiler_flag(-Wno-variadic-macros SUPPORTS_NO_VARIADIC_MACROS_FLAG)
+if(SUPPORTS_NO_VARIADIC_MACROS_FLAG)
+ list(APPEND SANITIZER_COMMON_CFLAGS -Wno-variadic-macros)
+endif()
+check_cxx_compiler_flag(-Wno-c99-extensions SUPPORTS_NO_C99_EXTENSIONS_FLAG)
+if(SUPPORTS_NO_C99_EXTENSIONS_FLAG)
+ list(APPEND SANITIZER_COMMON_CFLAGS -Wno-c99-extensions)
+endif()
+if(APPLE)
+ list(APPEND SANITIZER_COMMON_CFLAGS -mmacosx-version-min=10.5)
+endif()
+
+# Architectures supported by Sanitizer runtimes. Specific sanitizers may
+# support only subset of these (e.g. TSan works on x86_64 only).
+filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH
+ x86_64 i386)
+
+# Compute the Clang version from the LLVM version.
+# FIXME: We should be able to reuse CLANG_VERSION variable calculated
+# in Clang cmake files, instead of copying the rules here.
+string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" CLANG_VERSION
+ ${PACKAGE_VERSION})
+# Setup the paths where compiler-rt runtimes and headers should be stored.
+set(LIBCLANG_INSTALL_PATH lib${LLVM_LIBDIR_SUFFIX}/clang/${CLANG_VERSION})
+string(TOLOWER ${CMAKE_SYSTEM_NAME} LIBCLANG_OS_DIR)
+
+# Install compiler-rt headers.
+install(DIRECTORY include/
+ DESTINATION ${LIBCLANG_INSTALL_PATH}/include
+ FILES_MATCHING
+ PATTERN "*.h"
+ PATTERN ".svn" EXCLUDE
+ )
+
+# Call add_clang_compiler_rt_libraries to make sure that targets are built
+# and installed in the directories where Clang driver expects to find them.
+macro(add_clang_compiler_rt_libraries)
+ # Setup output directories so that clang in build tree works.
+ set_target_properties(${ARGN} PROPERTIES
+ ARCHIVE_OUTPUT_DIRECTORY
+ ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/lib/${LIBCLANG_OS_DIR}
+ LIBRARY_OUTPUT_DIRECTORY
+ ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/lib/${LIBCLANG_OS_DIR}
+ )
+ # Add installation command.
+ install(TARGETS ${ARGN}
+ ARCHIVE DESTINATION ${LIBCLANG_INSTALL_PATH}/lib/${LIBCLANG_OS_DIR}
+ LIBRARY DESTINATION ${LIBCLANG_INSTALL_PATH}/lib/${LIBCLANG_OS_DIR}
+ )
+endmacro(add_clang_compiler_rt_libraries)
+
+# Add the public header's directory to the includes for all of compiler-rt.
+include_directories(include)
+
add_subdirectory(lib)
if(LLVM_INCLUDE_TESTS)
diff --git a/LICENSE.TXT b/LICENSE.TXT
index f7179425605e..6aab1f694cc2 100644
--- a/LICENSE.TXT
+++ b/LICENSE.TXT
@@ -14,7 +14,7 @@ Full text of the relevant licenses is included below.
University of Illinois/NCSA
Open Source License
-Copyright (c) 2009-2012 by the contributors listed in CREDITS.TXT
+Copyright (c) 2009-2013 by the contributors listed in CREDITS.TXT
All rights reserved.
@@ -55,7 +55,7 @@ SOFTWARE.
==============================================================================
-Copyright (c) 2009-2012 by the contributors listed in CREDITS.TXT
+Copyright (c) 2009-2013 by the contributors listed in CREDITS.TXT
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Makefile b/Makefile
index 642b654325c8..63b05367b125 100644
--- a/Makefile
+++ b/Makefile
@@ -92,7 +92,7 @@ test:
%/.dir:
$(Summary) " MKDIR: $*"
$(Verb) $(MKDIR) $* > /dev/null
- $(Verb) $(DATE) > $@
+ $(Verb) echo 'Created.' > $@
# Remove directories
%/.remove:
@@ -117,7 +117,7 @@ $(call Set,Tmp.Configs,$($(Tmp.Key).Configs))
$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name))
# Top-Level Platform Target
-$(Tmp.Name):: $(Tmp.Configs:%=$(Tmp.ObjPath)/%/libcompiler_rt.a)
+$(Tmp.Name):: $(Tmp.Configs:%=$(Tmp.Name)-%)
.PHONY: $(Tmp.Name)
clean::
@@ -131,6 +131,15 @@ endef
define PerPlatformConfig_template
$(call Set,Tmp.Config,$(1))
$(call Set,Tmp.ObjPath,$(ProjObjRoot)/$(Tmp.Name)/$(Tmp.Config))
+$(call Set,Tmp.SHARED_LIBRARY,$(strip \
+ $(call GetCNAVar,SHARED_LIBRARY,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
+$(call Set,Tmp.SHARED_LIBRARY_SUFFIX,$(strip \
+ $(call GetCNAVar,SHARED_LIBRARY_SUFFIX,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
+
+# Compute the library suffix.
+$(if $(call streq,1,$(Tmp.SHARED_LIBRARY)),
+ $(call Set,Tmp.LibrarySuffix,$(Tmp.SHARED_LIBRARY_SUFFIX)),
+ $(call Set,Tmp.LibrarySuffix,a))
# Compute the archs to build, depending on whether this is a universal build or
# not.
@@ -142,8 +151,8 @@ $(call Set,Tmp.ArchsToBuild,\
$(call VarOrDefault,$(Tmp.Key).Arch.$(Tmp.Config),$($(Tmp.Key).Arch))))
# Copy or lipo to create the per-config library.
-$(call Set,Tmp.Inputs,$(Tmp.ArchsToBuild:%=$(Tmp.ObjPath)/%/libcompiler_rt.a))
-$(Tmp.ObjPath)/libcompiler_rt.a: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
+$(call Set,Tmp.Inputs,$(Tmp.ArchsToBuild:%=$(Tmp.ObjPath)/%/libcompiler_rt.$(Tmp.LibrarySuffix)))
+$(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix): $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
$(Summary) " FINAL-ARCHIVE: $(Tmp.Name)/$(Tmp.Config): $$@"
-$(Verb) $(RM) $$@
$(if $(call streq,1,$(words $(Tmp.ArchsToBuild))), \
@@ -152,7 +161,7 @@ $(Tmp.ObjPath)/libcompiler_rt.a: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
.PRECIOUS: $(Tmp.ObjPath)/.dir
# Per-Config Targets
-$(Tmp.Name)-$(Tmp.Config):: $(Tmp.ObjPath)/libcompiler_rt.a
+$(Tmp.Name)-$(Tmp.Config):: $(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix)
.PHONY: $(Tmp.Name)-$(Tmp.Config)
# Per-Config-Arch Libraries
@@ -172,10 +181,21 @@ $(call Set,Tmp.AR,$(strip \
$(call GetCNAVar,AR,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.ARFLAGS,$(strip \
$(call GetCNAVar,ARFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
+$(call Set,Tmp.CC,$(strip \
+ $(call GetCNAVar,CC,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
+$(call Set,Tmp.LDFLAGS,$(strip \
+ $(call GetCNAVar,LDFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.RANLIB,$(strip \
$(call GetCNAVar,RANLIB,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
$(call Set,Tmp.RANLIBFLAGS,$(strip \
$(call GetCNAVar,RANLIBFLAGS,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
+$(call Set,Tmp.SHARED_LIBRARY,$(strip \
+ $(call GetCNAVar,SHARED_LIBRARY,$(Tmp.Key),$(Tmp.Config),$(Tmp.Arch))))
+
+# Compute the library suffix.
+$(if $(call streq,1,$(Tmp.SHARED_LIBRARY)),
+ $(call Set,Tmp.LibrarySuffix,$(Tmp.SHARED_LIBRARY_SUFFIX)),
+ $(call Set,Tmp.LibrarySuffix,a))
# Compute the object inputs for this library.
$(call Set,Tmp.Inputs,\
@@ -188,10 +208,18 @@ $(Tmp.ObjPath)/libcompiler_rt.a: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
-$(Verb) $(RM) $$@
$(Verb) $(Tmp.AR) $(Tmp.ARFLAGS) $$@ $(Tmp.Inputs)
$(Verb) $(Tmp.RANLIB) $(Tmp.RANLIBFLAGS) $$@
+$(Tmp.ObjPath)/libcompiler_rt.dylib: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
+ $(Summary) " DYLIB: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$@"
+ $(Verb) $(Tmp.CC) -arch $(Tmp.Arch) -dynamiclib -o $$@ \
+ $(Tmp.Inputs) $(Tmp.LDFLAGS)
+$(Tmp.ObjPath)/libcompiler_rt.so: $(Tmp.Inputs) $(Tmp.ObjPath)/.dir
+ $(Summary) " SO: $(Tmp.Name)/$(Tmp.Config)/$(Tmp.Arch): $$@"
+ $(Verb) $(Tmp.CC) -shared -o $$@ \
+ $(Tmp.Inputs) $(Tmp.LDFLAGS)
.PRECIOUS: $(Tmp.ObjPath)/.dir
# Per-Config-Arch Targets
-$(Tmp.Name)-$(Tmp.Config)-$(Tmp.Arch):: $(Tmp.ObjPath)/libcompiler_rt.a
+$(Tmp.Name)-$(Tmp.Config)-$(Tmp.Arch):: $(Tmp.ObjPath)/libcompiler_rt.$(Tmp.LibrarySuffix)
.PHONY: $(Tmp.Name)-$(Tmp.Config)-$(Tmp.Arch)
# Per-Config-Arch-SubDir Objects
diff --git a/SDKs/darwin/usr/include/stdio.h b/SDKs/darwin/usr/include/stdio.h
index 3b560369f326..63b10a86b632 100644
--- a/SDKs/darwin/usr/include/stdio.h
+++ b/SDKs/darwin/usr/include/stdio.h
@@ -17,6 +17,10 @@
#ifndef __STDIO_H__
#define __STDIO_H__
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
typedef struct __sFILE FILE;
typedef __SIZE_TYPE__ size_t;
@@ -51,11 +55,30 @@ typedef __SIZE_TYPE__ size_t;
# define stderr __stderrp
extern FILE *__stderrp;
+#ifndef SEEK_SET
+#define SEEK_SET 0 /* set file offset to offset */
+#endif
+#ifndef SEEK_CUR
+#define SEEK_CUR 1 /* set file offset to current plus offset */
+#endif
+#ifndef SEEK_END
+#define SEEK_END 2 /* set file offset to EOF plus offset */
+#endif
+
int fclose(FILE *);
int fflush(FILE *);
-FILE *fopen(const char * restrict, const char * restrict) __asm(__FOPEN_NAME);
-int fprintf(FILE * restrict, const char * restrict, ...);
-size_t fwrite(const void * restrict, size_t, size_t, FILE * restrict)
+FILE *fopen(const char * __restrict, const char * __restrict) __asm(__FOPEN_NAME);
+int fprintf(FILE * __restrict, const char * __restrict, ...);
+size_t fwrite(const void * __restrict, size_t, size_t, FILE * __restrict)
__asm(__FWRITE_NAME);
+size_t fread(void * __restrict, size_t, size_t, FILE * __restrict);
+long ftell(FILE *);
+int fseek(FILE *, long, int);
+
+int snprintf(char * __restrict, size_t, const char * __restrict, ...);
+
+#if defined(__cplusplus)
+}
+#endif
#endif /* __STDIO_H__ */
diff --git a/SDKs/linux/usr/include/stdio.h b/SDKs/linux/usr/include/stdio.h
index ddfe75548793..7c258d2aca17 100644
--- a/SDKs/linux/usr/include/stdio.h
+++ b/SDKs/linux/usr/include/stdio.h
@@ -26,10 +26,17 @@ extern struct _IO_FILE *stdin;
extern struct _IO_FILE *stdout;
extern struct _IO_FILE *stderr;
+#define SEEK_SET 0 /* set file offset to offset */
+#define SEEK_CUR 1 /* set file offset to current plus offset */
+#define SEEK_END 2 /* set file offset to EOF plus offset */
+
extern int fclose(FILE *);
extern int fflush(FILE *);
extern FILE *fopen(const char * restrict, const char * restrict);
extern int fprintf(FILE * restrict, const char * restrict, ...);
extern size_t fwrite(const void * restrict, size_t, size_t, FILE * restrict);
+extern size_t fread(void * restrict, size_t, size_t, FILE * restrict);
+extern long ftell(FILE *);
+extern int fseek(FILE *, long, int);
#endif /* __STDIO_H__ */
diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake
new file mode 100644
index 000000000000..e90253fdfa1e
--- /dev/null
+++ b/cmake/Modules/AddCompilerRT.cmake
@@ -0,0 +1,49 @@
+include(AddLLVM)
+include(LLVMParseArguments)
+include(CompilerRTUtils)
+
+# Tries to add "object library" target for a given architecture
+# with name "<name>.<arch>" if architecture can be targeted.
+# add_compiler_rt_object_library(<name> <arch>
+# SOURCES <source files>
+# CFLAGS <compile flags>)
+macro(add_compiler_rt_object_library name arch)
+ if(CAN_TARGET_${arch})
+ parse_arguments(LIB "SOURCES;CFLAGS" "" ${ARGN})
+ add_library(${name}.${arch} OBJECT ${LIB_SOURCES})
+ set_target_compile_flags(${name}.${arch}
+ ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS})
+ else()
+ message(FATAL_ERROR "Archtecture ${arch} can't be targeted")
+ endif()
+endmacro()
+
+# Unittests support.
+set(COMPILER_RT_GTEST_PATH ${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest)
+set(COMPILER_RT_GTEST_SOURCE ${COMPILER_RT_GTEST_PATH}/gtest-all.cc)
+set(COMPILER_RT_GTEST_INCLUDE_CFLAGS
+ -DGTEST_NO_LLVM_RAW_OSTREAM=1
+ -I${COMPILER_RT_GTEST_PATH}/include
+)
+
+# Use Clang to link objects into a single executable with just-built
+# Clang, using specific link flags. Make executable a part of provided
+# test_suite.
+# add_compiler_rt_test(<test_suite> <test_name>
+# OBJECTS <object files>
+# DEPS <deps (e.g. runtime libs)>
+# LINK_FLAGS <link flags>)
+macro(add_compiler_rt_test test_suite test_name)
+ parse_arguments(TEST "OBJECTS;DEPS;LINK_FLAGS" "" ${ARGN})
+ get_unittest_directory(OUTPUT_DIR)
+ file(MAKE_DIRECTORY ${OUTPUT_DIR})
+ set(output_bin "${OUTPUT_DIR}/${test_name}")
+ add_custom_command(
+ OUTPUT ${output_bin}
+ COMMAND clang ${TEST_OBJECTS} -o "${output_bin}"
+ ${TEST_LINK_FLAGS}
+ DEPENDS clang ${TEST_DEPS})
+ add_custom_target(${test_name} DEPENDS ${output_bin})
+ # Make the test suite depend on the binary.
+ add_dependencies(${test_suite} ${test_name})
+endmacro()
diff --git a/cmake/Modules/CompilerRTCompile.cmake b/cmake/Modules/CompilerRTCompile.cmake
new file mode 100644
index 000000000000..2794cabe59c5
--- /dev/null
+++ b/cmake/Modules/CompilerRTCompile.cmake
@@ -0,0 +1,16 @@
+include(LLVMParseArguments)
+
+# Compile a source into an object file with just-built Clang using
+# a provided compile flags and dependenices.
+# clang_compile(<object> <source>
+# CFLAGS <list of compile flags>
+# DEPS <list of dependencies>)
+macro(clang_compile object_file source)
+ parse_arguments(SOURCE "CFLAGS;DEPS" "" ${ARGN})
+ get_filename_component(source_rpath ${source} REALPATH)
+ add_custom_command(
+ OUTPUT ${object_file}
+ COMMAND clang ${SOURCE_CFLAGS} -c -o "${object_file}" ${source_rpath}
+ MAIN_DEPENDENCY ${source}
+ DEPENDS clang ${SOURCE_DEPS})
+endmacro()
diff --git a/cmake/Modules/CompilerRTLink.cmake b/cmake/Modules/CompilerRTLink.cmake
new file mode 100644
index 000000000000..85030a725e19
--- /dev/null
+++ b/cmake/Modules/CompilerRTLink.cmake
@@ -0,0 +1,14 @@
+include(LLVMParseArguments)
+
+# Link a shared library with just-built Clang.
+# clang_link_shared(<output.so>
+# OBJECTS <list of input objects>
+# LINKFLAGS <list of link flags>
+# DEPS <list of dependencies>)
+macro(clang_link_shared so_file)
+ parse_arguments(SOURCE "OBJECTS;LINKFLAGS;DEPS" "" ${ARGN})
+ add_custom_command(
+ OUTPUT ${so_file}
+ COMMAND clang -o "${so_file}" -shared ${SOURCE_LINKFLAGS} ${SOURCE_OBJECTS}
+ DEPENDS clang ${SOURCE_DEPS})
+endmacro()
diff --git a/cmake/Modules/CompilerRTUtils.cmake b/cmake/Modules/CompilerRTUtils.cmake
new file mode 100644
index 000000000000..50f068091e64
--- /dev/null
+++ b/cmake/Modules/CompilerRTUtils.cmake
@@ -0,0 +1,17 @@
+# Because compiler-rt spends a lot of time setting up custom compile flags,
+# define a handy helper function for it. The compile flags setting in CMake
+# has serious issues that make its syntax challenging at best.
+function(set_target_compile_flags target)
+ foreach(arg ${ARGN})
+ set(argstring "${argstring} ${arg}")
+ endforeach()
+ set_property(TARGET ${target} PROPERTY COMPILE_FLAGS "${argstring}")
+endfunction()
+
+function(set_target_link_flags target)
+ foreach(arg ${ARGN})
+ set(argstring "${argstring} ${arg}")
+ endforeach()
+ set_property(TARGET ${target} PROPERTY LINK_FLAGS "${argstring}")
+endfunction()
+
diff --git a/lib/asan/asan_interface.h b/include/sanitizer/asan_interface.h
index c625a6217c0c..6afc3800f4e7 100644
--- a/lib/asan/asan_interface.h
+++ b/include/sanitizer/asan_interface.h
@@ -1,4 +1,4 @@
-//===-- asan_interface.h ----------------------------------------*- C++ -*-===//
+//===-- sanitizer/asan_interface.h ------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -12,10 +12,11 @@
// This header can be included by the instrumented program to fetch
// data (mostly allocator statistics) from ASan runtime library.
//===----------------------------------------------------------------------===//
-#ifndef ASAN_INTERFACE_H
-#define ASAN_INTERFACE_H
+#ifndef SANITIZER_ASAN_INTERFACE_H
+#define SANITIZER_ASAN_INTERFACE_H
+
+#include <sanitizer/common_interface_defs.h>
-#include "sanitizer_common/sanitizer_interface_defs.h"
// ----------- ATTENTION -------------
// This header should NOT include any other headers from ASan runtime.
// All functions in this header are extern "C" and start with __asan_.
@@ -27,17 +28,13 @@ extern "C" {
// before any instrumented code is executed and before any call to malloc.
void __asan_init() SANITIZER_INTERFACE_ATTRIBUTE;
- // This function should be called by the instrumented code.
- // 'addr' is the address of a global variable called 'name' of 'size' bytes.
- void __asan_register_global(uptr addr, uptr size, const char *name)
- SANITIZER_INTERFACE_ATTRIBUTE;
-
// This structure describes an instrumented global variable.
struct __asan_global {
uptr beg; // The address of the global.
uptr size; // The original size of the global.
uptr size_with_redzone; // The size with the redzone.
- const char *name; // Name as a C string.
+ const char *name; // Name as a C string.
+ uptr has_dynamic_init; // Non-zero if the global has dynamic initializer.
};
// These two functions should be called by the instrumented code.
@@ -47,6 +44,14 @@ extern "C" {
void __asan_unregister_globals(__asan_global *globals, uptr n)
SANITIZER_INTERFACE_ATTRIBUTE;
+ // These two functions should be called before and after dynamic initializers
+ // run, respectively. They should be called with parameters describing all
+ // dynamically initialized globals defined in the calling TU.
+ void __asan_before_dynamic_init(uptr first_addr, uptr last_addr)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ void __asan_after_dynamic_init()
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
// These two functions are used by the instrumented code in the
// use-after-return mode. __asan_stack_malloc allocates size bytes of
// fake stack and __asan_stack_free poisons it. real_stack is a pointer to
@@ -56,6 +61,15 @@ extern "C" {
void __asan_stack_free(uptr ptr, uptr size, uptr real_stack)
SANITIZER_INTERFACE_ATTRIBUTE;
+ // These two functions are used by instrumented code in the
+ // use-after-scope mode. They mark memory for local variables as
+ // unaddressable when they leave scope and addressable before the
+ // function exits.
+ void __asan_poison_stack_memory(uptr addr, uptr size)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+ void __asan_unpoison_stack_memory(uptr addr, uptr size)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
// Marks memory region [addr, addr+size) as unaddressable.
// This memory must be previously allocated by the user program. Accessing
// addresses in this region from instrumented code is forbidden until
@@ -98,6 +112,15 @@ extern "C" {
bool __asan_address_is_poisoned(void const volatile *addr)
SANITIZER_INTERFACE_ATTRIBUTE;
+ // If at least on byte in [beg, beg+size) is poisoned, return the address
+ // of the first such byte. Otherwise return 0.
+ uptr __asan_region_is_poisoned(uptr beg, uptr size)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ // Print the description of addr (useful when debugging in gdb).
+ void __asan_describe_address(uptr addr)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
// This is an internal function that is called to report an error.
// However it is still a part of the interface because users may want to
// set a breakpoint on this function in a debugger.
@@ -118,6 +141,21 @@ extern "C" {
void __asan_set_error_report_callback(void (*callback)(const char*))
SANITIZER_INTERFACE_ATTRIBUTE;
+ // User may provide function that would be called right when ASan detects
+ // an error. This can be used to notice cases when ASan detects an error, but
+ // the program crashes before ASan report is printed.
+ /* OPTIONAL */ void __asan_on_error()
+ SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
+
+ // User may provide its own implementation for symbolization function.
+ // It should print the description of instruction at address "pc" to
+ // "out_buffer". Description should be at most "out_size" bytes long.
+ // User-specified function should return true if symbolization was
+ // successful.
+ /* OPTIONAL */ bool __asan_symbolize(const void *pc, char *out_buffer,
+ int out_size)
+ SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
+
// Returns the estimated number of bytes that will be reserved by allocator
// for request of "size" bytes. If ASan allocator can't allocate that much
// memory, returns the maximal possible allocation size, otherwise returns
@@ -154,6 +192,21 @@ extern "C" {
// Prints accumulated stats to stderr. Used for debugging.
void __asan_print_accumulated_stats()
SANITIZER_INTERFACE_ATTRIBUTE;
-} // namespace
-#endif // ASAN_INTERFACE_H
+ // This function may be optionally provided by user and should return
+ // a string containing ASan runtime options. See asan_flags.h for details.
+ /* OPTIONAL */ const char* __asan_default_options()
+ SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
+
+ // Malloc hooks that may be optionally provided by user.
+ // __asan_malloc_hook(ptr, size) is called immediately after
+ // allocation of "size" bytes, which returned "ptr".
+ // __asan_free_hook(ptr) is called immediately before
+ // deallocation of "ptr".
+ /* OPTIONAL */ void __asan_malloc_hook(void *ptr, uptr size)
+ SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
+ /* OPTIONAL */ void __asan_free_hook(void *ptr)
+ SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
+} // extern "C"
+
+#endif // SANITIZER_ASAN_INTERFACE_H
diff --git a/lib/sanitizer_common/sanitizer_interface_defs.h b/include/sanitizer/common_interface_defs.h
index 2395ea505657..9d8fa5582b67 100644
--- a/lib/sanitizer_common/sanitizer_interface_defs.h
+++ b/include/sanitizer/common_interface_defs.h
@@ -1,4 +1,4 @@
-//===-- sanitizer_interface_defs.h -----------------------------*- C++ -*-===//
+//===-- sanitizer/common_interface_defs.h -----------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -12,8 +12,8 @@
// NOTE: This file may be included into user code.
//===----------------------------------------------------------------------===//
-#ifndef SANITIZER_INTERFACE_DEFS_H
-#define SANITIZER_INTERFACE_DEFS_H
+#ifndef SANITIZER_COMMON_INTERFACE_DEFS_H
+#define SANITIZER_COMMON_INTERFACE_DEFS_H
// ----------- ATTENTION -------------
// This header should NOT include any other headers to avoid portability issues.
@@ -30,6 +30,12 @@
# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak))
#endif
+#ifdef __linux__
+# define SANITIZER_SUPPORTS_WEAK_HOOKS 1
+#else
+# define SANITIZER_SUPPORTS_WEAK_HOOKS 0
+#endif
+
// __has_feature
#if !defined(__has_feature)
# define __has_feature(x) 0
@@ -40,8 +46,21 @@
// in a portable way by the language itself.
namespace __sanitizer {
+#if defined(_WIN64)
+// 64-bit Windows uses LLP64 data model.
+typedef unsigned long long uptr; // NOLINT
+typedef signed long long sptr; // NOLINT
+#else
typedef unsigned long uptr; // NOLINT
typedef signed long sptr; // NOLINT
+#endif // defined(_WIN64)
+#if defined(__x86_64__)
+// Since x32 uses ILP32 data model in 64-bit hardware mode, we must use
+// 64-bit pointer to unwind stack frame.
+typedef unsigned long long uhwptr; // NOLINT
+#else
+typedef uptr uhwptr; // NOLINT
+#endif
typedef unsigned char u8;
typedef unsigned short u16; // NOLINT
typedef unsigned int u32;
@@ -53,4 +72,21 @@ typedef signed long long s64; // NOLINT
} // namespace __sanitizer
-#endif // SANITIZER_INTERFACE_DEFS_H
+extern "C" {
+ // Tell the tools to write their reports to "path.<pid>" instead of stderr.
+ void __sanitizer_set_report_path(const char *path)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ // Tell the tools to write their reports to given file descriptor instead of
+ // stderr.
+ void __sanitizer_set_report_fd(int fd)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+ // Notify the tools that the sandbox is going to be turned on. The reserved
+ // parameter will be used in the future to hold a structure with functions
+ // that the tools may call to bypass the sandbox.
+ void __sanitizer_sandbox_on_notify(void *reserved)
+ SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE;
+} // extern "C"
+
+#endif // SANITIZER_COMMON_INTERFACE_DEFS_H
diff --git a/include/sanitizer/msan_interface.h b/include/sanitizer/msan_interface.h
new file mode 100644
index 000000000000..1a76dd60599f
--- /dev/null
+++ b/include/sanitizer/msan_interface.h
@@ -0,0 +1,124 @@
+//===-- msan_interface.h --------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// Public interface header.
+//===----------------------------------------------------------------------===//
+#ifndef MSAN_INTERFACE_H
+#define MSAN_INTERFACE_H
+
+#include <sanitizer/common_interface_defs.h>
+
+using __sanitizer::uptr;
+using __sanitizer::sptr;
+using __sanitizer::u32;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// FIXME: document all interface functions.
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __msan_get_track_origins();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_init();
+
+// Print a warning and maybe return.
+// This function can die based on flags()->exit_code.
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_warning();
+
+// Print a warning and die.
+// Intrumentation inserts calls to this function when building in "fast" mode
+// (i.e. -mllvm -msan-keep-going)
+SANITIZER_INTERFACE_ATTRIBUTE __attribute__((noreturn))
+void __msan_warning_noreturn();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_unpoison(void *a, uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_clear_and_unpoison(void *a, uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE
+void* __msan_memcpy(void *dst, const void *src, uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE
+void* __msan_memset(void *s, int c, uptr n);
+SANITIZER_INTERFACE_ATTRIBUTE
+void* __msan_memmove(void* dest, const void* src, uptr n);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_copy_poison(void *dst, const void *src, uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_copy_origin(void *dst, const void *src, uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_move_poison(void *dst, const void *src, uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_poison(void *a, uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_poison_stack(void *a, uptr size);
+
+// Copy size bytes from src to dst and unpoison the result.
+// Useful to implement unsafe loads.
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_load_unpoisoned(void *src, uptr size, void *dst);
+
+// Returns the offset of the first (at least partially) poisoned byte,
+// or -1 if the whole range is good.
+SANITIZER_INTERFACE_ATTRIBUTE
+sptr __msan_test_shadow(const void *x, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_set_origin(void *a, uptr size, u32 origin);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_set_alloca_origin(void *a, uptr size, const char *descr);
+SANITIZER_INTERFACE_ATTRIBUTE
+u32 __msan_get_origin(void *a);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_clear_on_return();
+
+// Default: -1 (don't exit on error).
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_set_exit_code(int exit_code);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __msan_set_poison_in_malloc(int do_poison);
+
+// For testing.
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_set_expect_umr(int expect_umr);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_break_optimization(void *x);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_print_shadow(const void *x, uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_print_param_shadow();
+SANITIZER_INTERFACE_ATTRIBUTE
+int __msan_has_dynamic_component();
+
+// Returns x such that %fs:x is the first byte of __msan_retval_tls.
+SANITIZER_INTERFACE_ATTRIBUTE
+int __msan_get_retval_tls_offset();
+SANITIZER_INTERFACE_ATTRIBUTE
+int __msan_get_param_tls_offset();
+
+// For testing.
+SANITIZER_INTERFACE_ATTRIBUTE
+u32 __msan_get_origin_tls();
+SANITIZER_INTERFACE_ATTRIBUTE
+const char *__msan_get_origin_descr_if_stack(u32 id);
+SANITIZER_INTERFACE_ATTRIBUTE
+void __msan_partial_poison(void* data, void* shadow, uptr size);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 67019655332a..fa6d8abc65e6 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -1,34 +1,23 @@
-# Compute the Clang version from the LLVM version.
-# FIXME: We should be able to reuse CLANG_VERSION variable calculated
-# in Clang cmake files, instead of copying the rules here.
-string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" CLANG_VERSION
- ${PACKAGE_VERSION})
-
-# Call add_clang_runtime_static_library(<target_library>) to make
-# sure that static <target_library> is built in the directory
-# where Clang driver expects to find it.
-if (APPLE)
- set(CLANG_RUNTIME_LIB_DIR
- ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/lib/darwin)
-elseif (UNIX)
- # Assume Linux.
- set(CLANG_RUNTIME_LIB_DIR
- ${LLVM_BINARY_DIR}/lib/clang/${CLANG_VERSION}/lib/linux)
-endif()
-function(add_clang_runtime_static_library target_name)
- set_target_properties(${target_name} PROPERTIES
- ARCHIVE_OUTPUT_DIRECTORY ${CLANG_RUNTIME_LIB_DIR})
-endfunction()
-
# First, add the subdirectories which contain feature-based runtime libraries
# and several convenience helper libraries.
-add_subdirectory(asan)
-add_subdirectory(interception)
-add_subdirectory(sanitizer_common)
+if(CMAKE_SYSTEM_NAME MATCHES "Darwin|Linux")
+ # AddressSanitizer is supported on Linux and Mac OS X.
+ # Windows support is work in progress.
+ add_subdirectory(asan)
+ add_subdirectory(interception)
+ add_subdirectory(sanitizer_common)
+ if(NOT ANDROID)
+ add_subdirectory(ubsan)
+ endif()
+endif()
+if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND NOT ANDROID)
+ # ThreadSanitizer and MemorySanitizer are supported on Linux only.
+ add_subdirectory(tsan)
+ add_subdirectory(msan)
+endif()
# FIXME: Add support for the profile library.
-
# The top-level lib directory contains a large amount of C code which provides
# generic implementations of the core runtime library along with optimized
# architecture-specific code in various subdirectories.
@@ -163,7 +152,7 @@ set(GENERIC_SOURCES
umodti3.c
)
-if(CAN_TARGET_X86_64)
+if(CAN_TARGET_x86_64)
add_library(clang_rt.x86_64 STATIC
x86_64/floatdidf.c
x86_64/floatdisf.c
@@ -173,9 +162,10 @@ if(CAN_TARGET_X86_64)
x86_64/floatundixf.S
${GENERIC_SOURCES}
)
- set_target_properties(clang_rt.x86_64 PROPERTIES COMPILE_FLAGS "-std=c99 ${TARGET_X86_64_CFLAGS}")
+ set_target_properties(clang_rt.x86_64 PROPERTIES COMPILE_FLAGS "-std=c99 ${TARGET_x86_64_CFLAGS}")
+ add_clang_compiler_rt_libraries(clang_rt.x86_64)
endif()
-if(CAN_TARGET_I386)
+if(CAN_TARGET_i386)
add_library(clang_rt.i386 STATIC
i386/ashldi3.S
i386/ashrdi3.S
@@ -193,5 +183,6 @@ if(CAN_TARGET_I386)
i386/umoddi3.S
${GENERIC_SOURCES}
)
- set_target_properties(clang_rt.i386 PROPERTIES COMPILE_FLAGS "-std=c99 ${TARGET_I386_CFLAGS}")
+ set_target_properties(clang_rt.i386 PROPERTIES COMPILE_FLAGS "-std=c99 ${TARGET_i386_CFLAGS}")
+ add_clang_compiler_rt_libraries(clang_rt.i386)
endif()
diff --git a/lib/Makefile.mk b/lib/Makefile.mk
index 791921a80068..ea471e01b1e6 100644
--- a/lib/Makefile.mk
+++ b/lib/Makefile.mk
@@ -19,6 +19,7 @@ SubDirs += interception
SubDirs += profile
SubDirs += sanitizer_common
SubDirs += tsan
+SubDirs += ubsan
# FIXME: We don't currently support building an atomic library, and as it must
# be a separate library from the runtime library, we need to remove its source
diff --git a/lib/arm/Makefile.mk b/lib/arm/Makefile.mk
index e7bbd7b615d5..04dec88a9714 100644
--- a/lib/arm/Makefile.mk
+++ b/lib/arm/Makefile.mk
@@ -9,7 +9,7 @@
ModuleName := builtins
SubDirs :=
-OnlyArchs := armv5 armv6 armv7
+OnlyArchs := armv5 armv6 armv7 armv7f armv7k armv7s
AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
Sources := $(foreach file,$(wildcard $(Dir)/*.c),$(notdir $(file)))
diff --git a/lib/arm/divsi3.S b/lib/arm/divsi3.S
index 00e61815ab4f..e76fe31bb782 100644
--- a/lib/arm/divsi3.S
+++ b/lib/arm/divsi3.S
@@ -25,7 +25,16 @@
// Ok, APCS and AAPCS agree on 32 bit args, so it's safe to use the same routine.
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_idiv, __divsi3)
DEFINE_COMPILERRT_FUNCTION(__divsi3)
- ESTABLISH_FRAME
+#if __ARM_ARCH_7S__
+ tst r1,r1
+ beq LOCAL_LABEL(divzero)
+ sdiv r0, r0, r1
+ bx lr
+LOCAL_LABEL(divzero):
+ mov r0,#0
+ bx lr
+#else
+ESTABLISH_FRAME
// Set aside the sign of the quotient.
eor r4, r0, r1
// Take absolute value of a and b via abs(x) = (x^(x >> 31)) - (x >> 31).
@@ -39,3 +48,4 @@ DEFINE_COMPILERRT_FUNCTION(__divsi3)
eor r0, r0, r4, asr #31
sub r0, r0, r4, asr #31
CLEAR_FRAME_AND_RETURN
+#endif
diff --git a/lib/arm/udivsi3.S b/lib/arm/udivsi3.S
index 6d8966539c7f..28979fee4bdc 100644
--- a/lib/arm/udivsi3.S
+++ b/lib/arm/udivsi3.S
@@ -33,6 +33,15 @@
// Ok, APCS and AAPCS agree on 32 bit args, so it's safe to use the same routine.
DEFINE_AEABI_FUNCTION_ALIAS(__aeabi_uidiv, __udivsi3)
DEFINE_COMPILERRT_FUNCTION(__udivsi3)
+#if __ARM_ARCH_7S__
+ tst r1,r1
+ beq LOCAL_LABEL(divzero)
+ udiv r0, r0, r1
+ bx lr
+ LOCAL_LABEL(divzero):
+ mov r0,#0
+ bx lr
+#else
// We use a simple digit by digit algorithm; before we get into the actual
// divide loop, we must calculate the left-shift amount necessary to align
// the MSB of the divisor with that of the dividend (If this shift is
@@ -78,3 +87,4 @@ LOCAL_LABEL(return):
// Move the quotient to r0 and return.
mov r0, q
CLEAR_FRAME_AND_RETURN
+#endif
diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt
index ce985f528172..92cba6dee622 100644
--- a/lib/asan/CMakeLists.txt
+++ b/lib/asan/CMakeLists.txt
@@ -2,6 +2,8 @@
set(ASAN_SOURCES
asan_allocator.cc
+ asan_allocator2.cc
+ asan_fake_stack.cc
asan_globals.cc
asan_interceptors.cc
asan_linux.cc
@@ -12,7 +14,7 @@ set(ASAN_SOURCES
asan_new_delete.cc
asan_poisoning.cc
asan_posix.cc
- asan_printf.cc
+ asan_report.cc
asan_rtl.cc
asan_stack.cc
asan_stats.cc
@@ -21,62 +23,99 @@ set(ASAN_SOURCES
asan_win.cc
)
+set(ASAN_DYLIB_SOURCES
+ ${ASAN_SOURCES}
+ dynamic/asan_interceptors_dynamic.cc
+ )
+
include_directories(..)
-set(ASAN_CFLAGS
- -fPIC
- -fno-exceptions
- -funwind-tables
- -fvisibility=hidden
- -fno-builtin
- -fomit-frame-pointer
- -O3
- )
-if (SUPPORTS_NO_VARIADIC_MACROS_FLAG)
- list(APPEND ASAN_CFLAGS -Wno-variadic-macros)
-endif ()
+set(ASAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
-if (APPLE)
- list(APPEND ASAN_CFLAGS -mmacosx-version-min=10.5)
+if(ANDROID)
+ set(ASAN_COMMON_DEFINITIONS
+ ASAN_HAS_EXCEPTIONS=1
+ ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
+ ASAN_NEEDS_SEGV=0
+ ASAN_LOW_MEMORY=1
+ )
+else()
+ set(ASAN_COMMON_DEFINITIONS
+ ASAN_HAS_EXCEPTIONS=1
+ ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
+ ASAN_NEEDS_SEGV=1
+ )
endif()
-set(ASAN_COMMON_DEFINITIONS
- ASAN_HAS_EXCEPTIONS=1
- ASAN_NEEDS_SEGV=1
+set(ASAN_DYLIB_DEFINITIONS
+ ${ASAN_COMMON_DEFINITIONS}
+ MAC_INTERPOSE_FUNCTIONS=1
)
-# FIXME: We need to build universal binaries on OS X instead of
-# two arch-specific binaries.
+# Architectures supported by ASan.
+filter_available_targets(ASAN_SUPPORTED_ARCH
+ x86_64 i386)
-if(CAN_TARGET_X86_64)
- add_library(clang_rt.asan-x86_64 STATIC
+set(ASAN_RUNTIME_LIBRARIES)
+if(APPLE)
+ # Build universal binary on APPLE.
+ add_library(clang_rt.asan_osx STATIC
${ASAN_SOURCES}
- $<TARGET_OBJECTS:RTInterception.x86_64>
- $<TARGET_OBJECTS:RTSanitizerCommon.x86_64>
- )
- set_target_compile_flags(clang_rt.asan-x86_64
- ${ASAN_CFLAGS}
- ${TARGET_X86_64_CFLAGS}
+ $<TARGET_OBJECTS:RTInterception.osx>
+ $<TARGET_OBJECTS:RTSanitizerCommon.osx>
)
- set_property(TARGET clang_rt.asan-x86_64 APPEND PROPERTY COMPILE_DEFINITIONS
- ${ASAN_COMMON_DEFINITIONS})
- add_clang_runtime_static_library(clang_rt.asan-x86_64)
-endif()
-if(CAN_TARGET_I386)
- add_library(clang_rt.asan-i386 STATIC
+ set_target_compile_flags(clang_rt.asan_osx ${ASAN_CFLAGS})
+ set_target_properties(clang_rt.asan_osx PROPERTIES
+ OSX_ARCHITECTURES "${ASAN_SUPPORTED_ARCH}")
+ list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan_osx)
+elseif(ANDROID)
+ add_library(clang_rt.asan-arm-android SHARED
${ASAN_SOURCES}
- $<TARGET_OBJECTS:RTInterception.i386>
- $<TARGET_OBJECTS:RTSanitizerCommon.i386>
+ $<TARGET_OBJECTS:RTInterception.arm.android>
+ $<TARGET_OBJECTS:RTSanitizerCommon.arm.android>
)
- set_target_compile_flags(clang_rt.asan-i386
+ set_target_compile_flags(clang_rt.asan-arm-android
${ASAN_CFLAGS}
- ${TARGET_I386_CFLAGS}
)
- set_property(TARGET clang_rt.asan-i386 APPEND PROPERTY COMPILE_DEFINITIONS
- ${ASAN_COMMON_DEFINITIONS})
- add_clang_runtime_static_library(clang_rt.asan-i386)
+ target_link_libraries(clang_rt.asan-arm-android dl)
+ list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-arm-android)
+else()
+ # Otherwise, build separate libraries for each target.
+ foreach(arch ${ASAN_SUPPORTED_ARCH})
+ add_library(clang_rt.asan-${arch} STATIC
+ ${ASAN_SOURCES}
+ $<TARGET_OBJECTS:RTInterception.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>)
+ set_target_compile_flags(clang_rt.asan-${arch}
+ ${ASAN_CFLAGS} ${TARGET_${arch}_CFLAGS})
+ list(APPEND ASAN_RUNTIME_LIBRARIES clang_rt.asan-${arch})
+ endforeach()
endif()
+set_property(TARGET ${ASAN_RUNTIME_LIBRARIES} APPEND PROPERTY
+ COMPILE_DEFINITIONS ${ASAN_COMMON_DEFINITIONS})
+add_clang_compiler_rt_libraries(${ASAN_RUNTIME_LIBRARIES})
+
+set(ASAN_DYNAMIC_RUNTIME_LIBRARIES)
+if(APPLE)
+ # Build universal binary on APPLE.
+ add_library(clang_rt.asan_osx_dynamic SHARED
+ ${ASAN_DYLIB_SOURCES}
+ $<TARGET_OBJECTS:RTInterception.osx>
+ $<TARGET_OBJECTS:RTSanitizerCommon.osx>
+ )
+ set_target_compile_flags(clang_rt.asan_osx_dynamic ${ASAN_CFLAGS})
+ set_target_properties(clang_rt.asan_osx_dynamic PROPERTIES
+ COMPILE_DEFINITIONS "${ASAN_DYLIB_DEFINITIONS}"
+ OSX_ARCHITECTURES "${ASAN_SUPPORTED_ARCH}"
+ LINK_FLAGS "-framework Foundation")
+ list(APPEND ASAN_DYNAMIC_RUNTIME_LIBRARIES clang_rt.asan_osx_dynamic)
+endif()
+add_clang_compiler_rt_libraries(${ASAN_DYNAMIC_RUNTIME_LIBRARIES})
+
+
if(LLVM_INCLUDE_TESTS)
add_subdirectory(tests)
endif()
+
+add_subdirectory(lit_tests)
diff --git a/lib/asan/Makefile.mk b/lib/asan/Makefile.mk
index 9d1a2e8a9a28..af9602e8b242 100644
--- a/lib/asan/Makefile.mk
+++ b/lib/asan/Makefile.mk
@@ -8,7 +8,7 @@
#===------------------------------------------------------------------------===#
ModuleName := asan
-SubDirs :=
+SubDirs := dynamic
Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
ObjNames := $(Sources:%.cc=%.o)
@@ -17,8 +17,9 @@ Implementation := Generic
# FIXME: use automatic dependencies?
Dependencies := $(wildcard $(Dir)/*.h)
-Dependencies += $(wildcard $(Dir)/interception/*.h)
-Dependencies += $(wildcard $(Dir)/interception/mach_override/*.h)
+Dependencies += $(wildcard $(Dir)/../interception/*.h)
+Dependencies += $(wildcard $(Dir)/../interception/mach_override/*.h)
+Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h)
# Define a convenience variable for all the asan functions.
AsanFunctions := $(Sources:%.cc=%)
diff --git a/lib/asan/Makefile.old b/lib/asan/Makefile.old
deleted file mode 100644
index 4ab80e20bcb1..000000000000
--- a/lib/asan/Makefile.old
+++ /dev/null
@@ -1,349 +0,0 @@
-#===- lib/asan/Makefile.old --------------------------------*- Makefile -*--===#
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-#===------------------------------------------------------------------------===#
-
-OS=$(shell uname | tr '[A-Z]' '[a-z]')
-ROOT=$(shell pwd)
-MAKEFILE=Makefile.old # this file.
-
-ifeq ($(ARCH), android)
- ANDROID_CFLAGS= \
- -DANDROID \
- -D__WORDSIZE=32 \
- -I$(ANDROID_BUILD_TOP)/external/stlport/stlport \
- -I$(ANDROID_BUILD_TOP)/bionic \
- -I$(ANDROID_BUILD_TOP)/bionic/libstdc++/include \
- -I$(ANDROID_BUILD_TOP)/bionic/libc/arch-arm/include \
- -I$(ANDROID_BUILD_TOP)/bionic/libc/include \
- -I$(ANDROID_BUILD_TOP)/bionic/libc/kernel/common \
- -I$(ANDROID_BUILD_TOP)/bionic/libc/kernel/arch-arm \
- -I$(ANDROID_BUILD_TOP)/bionic/libm/include \
- -I$(ANDROID_BUILD_TOP)/bionic/libm/include/arm \
- -I$(ANDROID_BUILD_TOP)/bionic/libthread_db/include \
- -L$(ANDROID_PRODUCT_OUT)/obj/lib
- CLANG_FLAGS= \
- -ccc-host-triple arm-linux-androideabi \
- -D__compiler_offsetof=__builtin_offsetof \
- -D__ELF__=1 \
- -ccc-gcc-name arm-linux-androideabi-g++ \
- $(ANDROID_CFLAGS)
- CC=$(ANDROID_EABI_TOOLCHAIN)/arm-linux-androideabi-gcc $(ANDROID_CFLAGS)
- CXX=$(ANDROID_EABI_TOOLCHAIN)/arm-linux-androideabi-g++ $(ANDROID_CFLAGS)
-endif
-
-ifeq ($(ARCH), arm)
- # Example make command line:
- # CROSSTOOL=$HOME/x-tools/arm-unknown-linux-gnueabi/ PATH=$CROSSTOOL/bin:$PATH make ARCH=arm asan_test
- CLANG_FLAGS= \
- -ccc-host-triple arm-unknown-linux-gnueabi \
- -march=armv7-a -mfloat-abi=softfp -mfp=neon \
- -ccc-gcc-name arm-unknown-linux-gnueabi-g++ \
- -B$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4 \
- -B$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/lib \
- -I$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4/include \
- -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/include/c++/4.4.4 \
- -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/include/c++/4.4.4/arm-unknown-linux-gnueabi \
- -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/include \
- -I$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/include \
- -L$(CROSSTOOL)/lib/gcc/arm-unknown-linux-gnueabi/4.4.4 \
- -L$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/lib \
- -L$(CROSSTOOL)/arm-unknown-linux-gnueabi/sys-root/usr/lib
- CC=$(CROSSTOOL)/bin/arm-unknown-linux-gnueabi-gcc
- CXX=$(CROSSTOOL)/bin/arm-unknown-linux-gnueabi-g++
-endif
-
-CLANG_FLAGS=
-CLANG_VERSION=3.2
-CLANG_BUILD=$(ROOT)/../../../../build/Release+Asserts
-CLANG_CC=$(CLANG_BUILD)/bin/clang $(CLANG_FLAGS)
-CLANG_CXX=$(CLANG_BUILD)/bin/clang++ $(CLANG_FLAGS)
-FILE_CHECK=$(CLANG_BUILD)/bin/FileCheck
-
-CC=$(CLANG_CC)
-CXX=$(CLANG_CXX)
-
-CFLAGS:=-Wall -fvisibility=hidden
-
-CLEANROOM_CXX=$(CXX) -Wall
-
-INSTALL_DIR=../asan_clang_$(OS)
-BIN=bin_$(OS)
-
-LIBS=#-lpthread -ldl
-ARCH=x86_64
-
-ASAN_STACK=1
-ASAN_GLOBALS=1
-ASAN_SCALE=0 # default will be used
-ASAN_OFFSET=-1 #default will be used
-ASAN_UAR=0
-ASAN_HAS_EXCEPTIONS=1
-ASAN_FLEXIBLE_MAPPING_AND_OFFSET=0
-ASAN_HAS_BLACKLIST=1
-ASAN_NEEDS_SEGV=1
-ASAN_PIE=0
-
-ifeq ($(ARCH), i386)
-BITS=32
-SUFF=$(BITS)
-CFLAGS:=$(CFLAGS) -m$(BITS)
-endif
-
-ifeq ($(ARCH), x86_64)
-BITS=64
-SUFF=$(BITS)
-CFLAGS:=$(CFLAGS) -m$(BITS)
-endif
-
-ifeq ($(ARCH), arm)
-BITS=32
-SUFF=_arm
-CFLAGS:=$(CFLAGS) -march=armv7-a
-ASAN_HAS_EXCEPTIONS=0
-endif
-
-ifeq ($(ARCH), android)
-BITS=32
-SUFF=_android
-CFLAGS:=$(CFLAGS)
-ASAN_HAS_EXCEPTIONS=0
-endif
-
-PIE=
-ifeq ($(ASAN_PIE), 1)
- PIE=-fPIE -pie
-endif
-
-# This will build libasan on linux for both x86_64 and i386 in the
-# desired location. The Mac library is already build by the clang's make.
-# $(CLANG_BUILD)/lib/clang/$(CLANG_VERSION)/lib/$(OS)/libclang_rt.asan-$(ARCH).a
-LIBASAN_INST_DIR=$(CLANG_BUILD)/lib/clang/$(CLANG_VERSION)/lib/$(OS)
-LIBASAN_A=$(LIBASAN_INST_DIR)/libclang_rt.asan-$(ARCH).a
-
-BLACKLIST=
-ifeq ($(ASAN_HAS_BLACKLIST), 1)
- BLACKLIST=-mllvm -asan-blacklist=$(ROOT)/tests/asan_test.ignore
-endif
-
-COMMON_ASAN_DEFINES=\
- -DASAN_UAR=$(ASAN_UAR) \
- -DASAN_HAS_EXCEPTIONS=$(ASAN_HAS_EXCEPTIONS) \
- -DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV) \
- -DASAN_HAS_BLACKLIST=$(ASAN_HAS_BLACKLIST)
-
-CLANG_ASAN_CXX=$(CLANG_CXX) \
- -faddress-sanitizer \
- $(BLACKLIST) \
- -mllvm -asan-stack=$(ASAN_STACK) \
- -mllvm -asan-globals=$(ASAN_GLOBALS) \
- -mllvm -asan-mapping-scale=$(ASAN_SCALE) \
- -mllvm -asan-mapping-offset-log=$(ASAN_OFFSET) \
- -mllvm -asan-use-after-return=$(ASAN_UAR) \
- $(COMMON_ASAN_DEFINES)
-
-CLANG_ASAN_LD=$(CLANG_CXX) -faddress-sanitizer
-
-GCC_ASAN_PATH=SET_FROM_COMMAND_LINE
-GCC_ASAN_CXX=$(GCC_ASAN_PATH)/g++ \
- -faddress-sanitizer \
- $(COMMON_ASAN_DEFINES)
-
-GCC_ASAN_LD=$(GCC_ASAN_PATH)/g++ -ldl -lpthread
-
-ASAN_COMPILER=clang
-
-ifeq ($(ASAN_COMPILER), clang)
- ASAN_CXX=$(CLANG_ASAN_CXX)
- ASAN_LD=$(CLANG_ASAN_LD)
- ASAN_LD_TAIL=
-endif
-
-ifeq ($(ASAN_COMPILER), gcc)
- ASAN_CXX=$(GCC_ASAN_CXX)
- ASAN_LD=$(GCC_ASAN_LD)
- ASAN_LD_TAIL=$(LIBASAN_A)
-endif
-
-INTERCEPTION=../interception
-MACH_OVERRIDE=$(INTERCEPTION)/mach_override
-COMMON=../sanitizer_common
-
-RTL_HDR=$(wildcard *.h) \
- $(wildcard $(INTERCEPTION)/*.h) \
- $(wildcard $(MACH_OVERRIDE)/*.h) \
- $(wildcard $(COMMON)/*.h)
-
-LIBTSAN_SRC=$(wildcard *.cc)
-INTERCEPTION_SRC=$(wildcard $(INTERCEPTION)/*.cc)
-MACH_OVERRIDE_SRC=$(wildcard $(MACH_OVERRIDE)/*.c)
-COMMON_SRC=$(wildcard $(COMMON)/*.cc)
-
-
-LIBASAN_OBJ=$(patsubst %.cc,$(BIN)/%$(SUFF).o,$(LIBTSAN_SRC)) \
- $(patsubst $(INTERCEPTION)/%.cc,$(BIN)/%$(SUFF).o,$(INTERCEPTION_SRC)) \
- $(patsubst $(COMMON)/%.cc,$(BIN)/%$(SUFF).o,$(COMMON_SRC)) \
- $(patsubst $(MACH_OVERRIDE)/%.c,$(BIN)/%$(SUFF).o,$(MACH_OVERRIDE_SRC))
-
-GTEST_ROOT=third_party/googletest
-GTEST_INCLUDE=-I$(GTEST_ROOT)/include
-GTEST_MAKE_DIR=$(GTEST_ROOT)/make-$(OS)$(SUFF)
-GTEST_LIB=$(GTEST_MAKE_DIR)/gtest-all.o
-
-all: b64 b32
-
-test: t64 t32 output_tests lint
- @echo "ALL TESTS PASSED"
-
-output_tests: b32 b64
- cd output_tests && ./test_output.sh $(CLANG_CXX) $(CLANG_CC) $(FILE_CHECK)
-
-t64: b64
- $(BIN)/asan_test64
-t32: b32
- $(BIN)/asan_test32
-
-b64: | mk_bin_dir
- $(MAKE) -f $(MAKEFILE) ARCH=x86_64 asan_test asan_benchmarks
-b32: | mk_bin_dir
- $(MAKE) -f $(MAKEFILE) ARCH=i386 asan_test asan_benchmarks
-
-lib64:
- $(MAKE) -f $(MAKEFILE) ARCH=x86_64 lib
-lib32:
- $(MAKE) -f $(MAKEFILE) ARCH=i386 lib
-
-mk_bin_dir:
- mkdir -p $(BIN)
-
-clang:
- cd ../ && llvm/rebuild_clang_and_asan.sh > /dev/null
-
-install: install_clang
-
-$(INSTALL_DIR):
- mkdir -p $(INSTALL_DIR) $(INSTALL_DIR)/bin $(INSTALL_DIR)/lib
-
-install_clang: | $(INSTALL_DIR)
- cp -v $(CLANG_BUILD)/bin/clang $(INSTALL_DIR)/bin
- cp -rv $(CLANG_BUILD)/lib/clang $(INSTALL_DIR)/lib
- (cd $(INSTALL_DIR)/bin; ln -sf clang clang++)
-
-#install_lib: | $(INSTALL_DIR)
-# cp -v $(CLANG_BUILD)/lib/libasan*.a $(INSTALL_DIR)/lib
-
-$(BIN)/asan_noinst_test$(SUFF).o: tests/asan_noinst_test.cc $(RTL_HDR) $(MAKEFILE)
- $(CLEANROOM_CXX) $(PIE) $(CFLAGS) $(GTEST_INCLUDE) -I. -I.. -g -c $< -O2 -o $@
-
-$(BIN)/asan_break_optimization$(SUFF).o: tests/asan_break_optimization.cc $(MAKEFILE)
- $(CLEANROOM_CXX) $(PIE) $(CFLAGS) -c $< -O0 -o $@
-
-$(BIN)/%_test$(SUFF).o: tests/%_test.cc $(RTL_HDR) $(MAKEFILE)
- $(ASAN_CXX) $(GTEST_INCLUDE) -I. -I.. -g -c $< -O2 -o $@ $(PIE) $(CFLAGS)
-
-$(BIN)/%_test$(SUFF).o: tests/%_test.mm $(RTL_HDR) $(MAKEFILE)
- $(ASAN_CXX) $(GTEST_INCLUDE) -I. -I.. -g -c $< -O2 -o $@ -ObjC $(PIE) $(CFLAGS)
-
-RTL_COMMON_FLAGS=$(PIE) $(CFLAGS) -fPIC -c -O2 -fno-exceptions -funwind-tables \
- -Ithird_party -I.. $(ASAN_FLAGS)
-
-$(BIN)/%$(SUFF).o: $(INTERCEPTION)/%.cc $(RTL_HDR) $(MAKEFILE)
- $(CXX) $(RTL_COMMON_FLAGS) -o $@ -g $<
-
-$(BIN)/%$(SUFF).o: $(COMMON)/%.cc $(RTL_HDR) $(MAKEFILE)
- $(CXX) $(RTL_COMMON_FLAGS) -o $@ -g $<
-
-$(BIN)/%$(SUFF).o: $(MACH_OVERRIDE)/%.c $(RTL_HDR) $(MAKEFILE)
- $(CC) $(RTL_COMMON_FLAGS) -o $@ -g $<
-
-$(BIN)/%$(SUFF).o: %.cc $(RTL_HDR) $(MAKEFILE)
- $(CXX) $(RTL_COMMON_FLAGS) -o $@ -g $< \
- -DASAN_NEEDS_SEGV=$(ASAN_NEEDS_SEGV) \
- -DASAN_HAS_EXCEPTIONS=$(ASAN_HAS_EXCEPTIONS) \
- -DASAN_FLEXIBLE_MAPPING_AND_OFFSET=$(ASAN_FLEXIBLE_MAPPING_AND_OFFSET)
-
-$(BIN)/%$(SUFF).o: %.c $(RTL_HDR) $(MAKEFILE)
- $(CC) $(PIE) $(CFLAGS) -fPIC -c -O2 -o $@ -g $< -Ithird_party \
- $(ASAN_FLAGS)
-
-ifeq ($(OS),darwin)
-LD_FLAGS=-framework Foundation
-else
-LD_FLAGS=
-endif
-
-lib: $(LIBASAN_A)
-
-$(LIBASAN_A): mk_bin_dir $(LIBASAN_OBJ) $(MAKEFILE)
- mkdir -p $(LIBASAN_INST_DIR)
- ar ru $@ $(LIBASAN_OBJ)
- $(CXX) -shared $(CFLAGS) $(LIBASAN_OBJ) $(LD_FLAGS) -o $(BIN)/libasan$(SUFF).so
-
-TEST_OBJECTS_COMMON=\
- $(BIN)/asan_globals_test$(SUFF).o \
- $(BIN)/asan_break_optimization$(SUFF).o \
- $(BIN)/asan_noinst_test$(SUFF).o \
- $(BIN)/asan_test$(SUFF).o
-
-BENCHMARK_OBJECTS=\
- $(BIN)/asan_benchmarks_test$(SUFF).o \
- $(BIN)/asan_break_optimization$(SUFF).o
-
-ifeq ($(OS),darwin)
-TEST_OBJECTS=$(TEST_OBJECTS_COMMON) \
- $(BIN)/asan_mac_test$(SUFF).o
-else
-TEST_OBJECTS=$(TEST_OBJECTS_COMMON)
-endif
-
-$(BIN)/asan_test$(SUFF): $(TEST_OBJECTS) $(LIBASAN_A) $(MAKEFILE) tests/asan_test.ignore $(GTEST_LIB)
- $(ASAN_LD) $(PIE) $(CFLAGS) -g -O3 $(TEST_OBJECTS) \
- $(LD_FLAGS) -o $@ $(LIBS) $(GTEST_LIB) $(ASAN_LD_TAIL)
-
-$(BIN)/asan_benchmarks$(SUFF): $(BENCHMARK_OBJECTS) $(LIBASAN_A) $(MAKEFILE) $(GTEST_LIB)
- $(ASAN_LD) $(PIE) $(CFLAGS) -g -O3 $(BENCHMARK_OBJECTS) \
- $(LD_FLAGS) -o $@ $(LIBS) $(GTEST_LIB) $(ASAN_LD_TAIL)
-
-asan_test: $(BIN)/asan_test$(SUFF)
-
-asan_benchmarks: $(BIN)/asan_benchmarks$(SUFF)
-
-# for now, build gtest with clang/asan even if we use a different compiler.
-$(GTEST_LIB):
- mkdir -p $(GTEST_MAKE_DIR) && \
- cd $(GTEST_MAKE_DIR) && \
- $(MAKE) -f ../make/Makefile CXXFLAGS="$(PIE) $(CFLAGS) -g -w" \
- CXX="$(CLANG_CXX)"
-
-RTL_LINT_FILTER=-readability/casting,-readability/check,-build/include,-build/header_guard,-build/class,-legal/copyright,-build/namespaces
-# TODO(kcc): remove these filters one by one
-TEST_LINT_FILTER=-readability/casting,-build/include,-legal/copyright,-whitespace/newline,-runtime/sizeof,-runtime/int,-runtime/printf
-
-LLVM_LINT_FILTER=-,+whitespace
-
-ADDRESS_SANITIZER_CPP=../../../../lib/Transforms/Instrumentation/AddressSanitizer.cpp
-
-lint:
- third_party/cpplint/cpplint.py --filter=$(LLVM_LINT_FILTER) $(ADDRESS_SANITIZER_CPP)
- third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FILTER) asan_*.cc asan_*.h
- third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FILTER) $(INTERCEPTION)/interception*.h $(INTERCEPTION)/interception*.cc
- third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FILTER) $(COMMON)/sanitizer_*.h $(COMMON)/sanitizer_*.cc
- third_party/cpplint/cpplint.py --filter=$(TEST_LINT_FILTER) tests/*.cc output_tests/*.cc
-
-get_third_party:
- rm -rf third_party
- mkdir third_party
- (cd third_party && \
- svn co -r375 http://googletest.googlecode.com/svn/trunk googletest && \
- svn co -r69 http://google-styleguide.googlecode.com/svn/trunk/cpplint cpplint \
- )
-
-clean:
- rm -f *.o *.ll *.S *.a *.log asan_test64* asan_test32* a.out perf.data log
- rm -f $(LIBASAN_INST_DIR)/libclang_rt.asan-*.a
- rm -rf $(BIN)
- rm -rf $(GTEST_ROOT)/make-*
diff --git a/lib/asan/README.txt b/lib/asan/README.txt
index 5e6600489a69..e4f4961c5d4f 100644
--- a/lib/asan/README.txt
+++ b/lib/asan/README.txt
@@ -4,22 +4,26 @@ This directory contains sources of the AddressSanitizer (asan) run-time library.
We are in the process of integrating AddressSanitizer with LLVM, stay tuned.
Directory structre:
-
README.txt : This file.
-Makefile.mk : Currently a stub for a proper makefile. not usable.
-Makefile.old : Old out-of-tree makefile, the only usable one so far.
+Makefile.mk : File for make-based build.
+CMakeLists.txt : File for cmake-based build.
asan_*.{cc,h} : Sources of the asan run-time lirbary.
-mach_override/* : Utility to override functions on Darwin (MIT License).
scripts/* : Helper scripts.
+tests/* : ASan unit tests.
+lit_tests/* : ASan output tests.
-Temporary build instructions (verified on linux):
+Also ASan runtime needs the following libraries:
+lib/interception/ : Machinery used to intercept function calls.
+lib/sanitizer_common/ : Code shared between ASan and TSan.
-cd lib/asan
-make -f Makefile.old get_third_party # gets googletest and cpplint
-make -f Makefile.old test -j 8 CLANG_BUILD=/path/to/Release+Asserts
-# Optional:
-# make -f Makefile.old install # installs clang and rt to lib/asan_clang_linux
+Currently ASan runtime can be built by both make and cmake build systems.
+(see compiler-rt/make and files Makefile.mk for make-based build and
+files CMakeLists.txt for cmake-based build).
-For more info see http://code.google.com/p/address-sanitizer/
+ASan unit and output tests work only with cmake. You may run this
+command from the root of your cmake build tree:
+make check-asan
+For more instructions see:
+http://code.google.com/p/address-sanitizer/wiki/HowToBuild
diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc
index 352cce00fbee..30dd4ceddd88 100644
--- a/lib/asan/asan_allocator.cc
+++ b/lib/asan/asan_allocator.cc
@@ -24,21 +24,19 @@
// Once freed, the body of the chunk contains the stack trace of the free call.
//
//===----------------------------------------------------------------------===//
-
#include "asan_allocator.h"
+
+#if ASAN_ALLOCATOR_VERSION == 1
#include "asan_interceptors.h"
-#include "asan_interface.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_mapping.h"
#include "asan_stats.h"
+#include "asan_report.h"
#include "asan_thread.h"
#include "asan_thread_registry.h"
+#include "sanitizer/asan_interface.h"
#include "sanitizer_common/sanitizer_atomic.h"
-
-#if defined(_WIN32) && !defined(__clang__)
-#include <intrin.h>
-#endif
+#include "sanitizer_common/sanitizer_mutex.h"
namespace __asan {
@@ -57,43 +55,7 @@ static const uptr kMallocSizeClassStepLog = 26;
static const uptr kMallocSizeClassStep = 1UL << kMallocSizeClassStepLog;
static const uptr kMaxAllowedMallocSize =
- (__WORDSIZE == 32) ? 3UL << 30 : 8UL << 30;
-
-static inline bool IsAligned(uptr a, uptr alignment) {
- return (a & (alignment - 1)) == 0;
-}
-
-static inline uptr Log2(uptr x) {
- CHECK(IsPowerOfTwo(x));
-#if !defined(_WIN32) || defined(__clang__)
- return __builtin_ctzl(x);
-#elif defined(_WIN64)
- unsigned long ret; // NOLINT
- _BitScanForward64(&ret, x);
- return ret;
-#else
- unsigned long ret; // NOLINT
- _BitScanForward(&ret, x);
- return ret;
-#endif
-}
-
-static inline uptr RoundUpToPowerOfTwo(uptr size) {
- CHECK(size);
- if (IsPowerOfTwo(size)) return size;
-
- unsigned long up; // NOLINT
-#if !defined(_WIN32) || defined(__clang__)
- up = __WORDSIZE - 1 - __builtin_clzl(size);
-#elif defined(_WIN64)
- _BitScanReverse64(&up, size);
-#else
- _BitScanReverse(&up, size);
-#endif
- CHECK(size < (1ULL << (up + 1)));
- CHECK(size > (1ULL << up));
- return 1UL << (up + 1);
-}
+ (SANITIZER_WORDSIZE == 32) ? 3UL << 30 : 8UL << 30;
static inline uptr SizeClassToSize(u8 size_class) {
CHECK(size_class < kNumberOfSizeClasses);
@@ -131,7 +93,7 @@ static void PoisonHeapPartialRightRedzone(uptr mem, uptr size) {
}
static u8 *MmapNewPagesAndPoisonShadow(uptr size) {
- CHECK(IsAligned(size, kPageSize));
+ CHECK(IsAligned(size, GetPageSizeCached()));
u8 *res = (u8*)MmapOrDie(size, __FUNCTION__);
PoisonShadow((uptr)res, size, kAsanHeapLeftRedzoneMagic);
if (flags()->debug) {
@@ -166,7 +128,8 @@ struct ChunkBase {
// Second 8 bytes.
uptr alignment_log : 8;
- uptr used_size : FIRST_32_SECOND_64(32, 56); // Size requested by the user.
+ uptr alloc_type : 2;
+ uptr used_size : FIRST_32_SECOND_64(32, 54); // Size requested by the user.
// This field may overlap with the user area and thus should not
// be used while the chunk is in CHUNK_ALLOCATED state.
@@ -198,50 +161,23 @@ struct AsanChunk: public ChunkBase {
if (REDZONE < sizeof(ChunkBase)) return 0;
return (REDZONE) / sizeof(u32);
}
+};
- bool AddrIsInside(uptr addr, uptr access_size, uptr *offset) {
- if (addr >= Beg() && (addr + access_size) <= (Beg() + used_size)) {
- *offset = addr - Beg();
- return true;
- }
- return false;
- }
-
- bool AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset) {
- if (addr < Beg()) {
- *offset = Beg() - addr;
- return true;
- }
- return false;
- }
+uptr AsanChunkView::Beg() { return chunk_->Beg(); }
+uptr AsanChunkView::End() { return Beg() + UsedSize(); }
+uptr AsanChunkView::UsedSize() { return chunk_->used_size; }
+uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
+uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
- bool AddrIsAtRight(uptr addr, uptr access_size, uptr *offset) {
- if (addr + access_size >= Beg() + used_size) {
- if (addr <= Beg() + used_size)
- *offset = 0;
- else
- *offset = addr - (Beg() + used_size);
- return true;
- }
- return false;
- }
+void AsanChunkView::GetAllocStack(StackTrace *stack) {
+ StackTrace::UncompressStack(stack, chunk_->compressed_alloc_stack(),
+ chunk_->compressed_alloc_stack_size());
+}
- void DescribeAddress(uptr addr, uptr access_size) {
- uptr offset;
- AsanPrintf("%p is located ", (void*)addr);
- if (AddrIsInside(addr, access_size, &offset)) {
- AsanPrintf("%zu bytes inside of", offset);
- } else if (AddrIsAtLeft(addr, access_size, &offset)) {
- AsanPrintf("%zu bytes to the left of", offset);
- } else if (AddrIsAtRight(addr, access_size, &offset)) {
- AsanPrintf("%zu bytes to the right of", offset);
- } else {
- AsanPrintf(" somewhere around (this is AddressSanitizer bug!)");
- }
- AsanPrintf(" %zu-byte region [%p,%p)\n",
- used_size, (void*)Beg(), (void*)(Beg() + used_size));
- }
-};
+void AsanChunkView::GetFreeStack(StackTrace *stack) {
+ StackTrace::UncompressStack(stack, chunk_->compressed_free_stack(),
+ chunk_->compressed_free_stack_size());
+}
static AsanChunk *PtrToChunk(uptr ptr) {
AsanChunk *m = (AsanChunk*)(ptr - REDZONE);
@@ -251,37 +187,15 @@ static AsanChunk *PtrToChunk(uptr ptr) {
return m;
}
-
void AsanChunkFifoList::PushList(AsanChunkFifoList *q) {
CHECK(q->size() > 0);
- if (last_) {
- CHECK(first_);
- CHECK(!last_->next);
- last_->next = q->first_;
- last_ = q->last_;
- } else {
- CHECK(!first_);
- last_ = q->last_;
- first_ = q->first_;
- CHECK(first_);
- }
- CHECK(last_);
- CHECK(!last_->next);
size_ += q->size();
+ append_back(q);
q->clear();
}
void AsanChunkFifoList::Push(AsanChunk *n) {
- CHECK(n->next == 0);
- if (last_) {
- CHECK(first_);
- CHECK(!last_->next);
- last_->next = n;
- last_ = n;
- } else {
- CHECK(!first_);
- last_ = first_ = n;
- }
+ push_back(n);
size_ += n->Size();
}
@@ -290,15 +204,9 @@ void AsanChunkFifoList::Push(AsanChunk *n) {
// ago. Not sure if we can or want to do anything with this.
AsanChunk *AsanChunkFifoList::Pop() {
CHECK(first_);
- AsanChunk *res = first_;
- first_ = first_->next;
- if (first_ == 0)
- last_ = 0;
- CHECK(size_ >= res->Size());
+ AsanChunk *res = front();
size_ -= res->Size();
- if (last_) {
- CHECK(!last_->next);
- }
+ pop_front();
return res;
}
@@ -315,14 +223,13 @@ struct PageGroup {
class MallocInfo {
public:
-
explicit MallocInfo(LinkerInitialized x) : mu_(x) { }
AsanChunk *AllocateChunks(u8 size_class, uptr n_chunks) {
AsanChunk *m = 0;
AsanChunk **fl = &free_lists_[size_class];
{
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
for (uptr i = 0; i < n_chunks; i++) {
if (!(*fl)) {
*fl = GetNewChunks(size_class);
@@ -340,7 +247,7 @@ class MallocInfo {
void SwallowThreadLocalMallocStorage(AsanThreadLocalMallocStorage *x,
bool eat_free_lists) {
CHECK(flags()->quarantine_size > 0);
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
AsanChunkFifoList *q = &x->quarantine_;
if (q->size() > 0) {
quarantine_.PushList(q);
@@ -364,23 +271,24 @@ class MallocInfo {
}
void BypassThreadLocalQuarantine(AsanChunk *chunk) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
quarantine_.Push(chunk);
}
- AsanChunk *FindMallocedOrFreed(uptr addr, uptr access_size) {
- ScopedLock lock(&mu_);
- return FindChunkByAddr(addr);
+ AsanChunk *FindChunkByAddr(uptr addr) {
+ BlockingMutexLock lock(&mu_);
+ return FindChunkByAddrUnlocked(addr);
}
uptr AllocationSize(uptr ptr) {
if (!ptr) return 0;
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
+
+ // Make sure this is our chunk and |ptr| actually points to the beginning
+ // of the allocated memory.
+ AsanChunk *m = FindChunkByAddrUnlocked(ptr);
+ if (!m || m->Beg() != ptr) return 0;
- // first, check if this is our memory
- PageGroup *g = FindPageGroupUnlocked(ptr);
- if (!g) return 0;
- AsanChunk *m = PtrToChunk(ptr);
if (m->chunk_state == CHUNK_ALLOCATED) {
return m->used_size;
} else {
@@ -397,7 +305,7 @@ class MallocInfo {
}
void PrintStatus() {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
uptr malloced = 0;
Printf(" MallocInfo: in quarantine: %zu malloced: %zu; ",
@@ -415,7 +323,7 @@ class MallocInfo {
}
PageGroup *FindPageGroup(uptr addr) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
return FindPageGroupUnlocked(addr);
}
@@ -462,14 +370,14 @@ class MallocInfo {
return left_chunk;
// Choose based on offset.
uptr l_offset = 0, r_offset = 0;
- CHECK(left_chunk->AddrIsAtRight(addr, 1, &l_offset));
- CHECK(right_chunk->AddrIsAtLeft(addr, 1, &r_offset));
+ CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset));
+ CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset));
if (l_offset < r_offset)
return left_chunk;
return right_chunk;
}
- AsanChunk *FindChunkByAddr(uptr addr) {
+ AsanChunk *FindChunkByAddrUnlocked(uptr addr) {
PageGroup *g = FindPageGroupUnlocked(addr);
if (!g) return 0;
CHECK(g->size_of_chunk);
@@ -482,17 +390,18 @@ class MallocInfo {
m->chunk_state == CHUNK_AVAILABLE ||
m->chunk_state == CHUNK_QUARANTINE);
uptr offset = 0;
- if (m->AddrIsInside(addr, 1, &offset))
+ AsanChunkView m_view(m);
+ if (m_view.AddrIsInside(addr, 1, &offset))
return m;
- if (m->AddrIsAtRight(addr, 1, &offset)) {
+ if (m_view.AddrIsAtRight(addr, 1, &offset)) {
if (this_chunk_addr == g->last_chunk) // rightmost chunk
return m;
uptr right_chunk_addr = this_chunk_addr + g->size_of_chunk;
CHECK(g->InRange(right_chunk_addr));
return ChooseChunk(addr, m, (AsanChunk*)right_chunk_addr);
} else {
- CHECK(m->AddrIsAtLeft(addr, 1, &offset));
+ CHECK(m_view.AddrIsAtLeft(addr, 1, &offset));
if (this_chunk_addr == g->beg) // leftmost chunk
return m;
uptr left_chunk_addr = this_chunk_addr - g->size_of_chunk;
@@ -533,12 +442,13 @@ class MallocInfo {
uptr mmap_size = Max(size, kMinMmapSize);
uptr n_chunks = mmap_size / size;
CHECK(n_chunks * size == mmap_size);
- if (size < kPageSize) {
+ uptr PageSize = GetPageSizeCached();
+ if (size < PageSize) {
// Size is small, just poison the last chunk.
n_chunks--;
} else {
// Size is large, allocate an extra page at right and poison it.
- mmap_size += kPageSize;
+ mmap_size += PageSize;
}
CHECK(n_chunks > 0);
u8 *mem = MmapNewPagesAndPoisonShadow(mmap_size);
@@ -564,14 +474,14 @@ class MallocInfo {
pg->size_of_chunk = size;
pg->last_chunk = (uptr)(mem + size * (n_chunks - 1));
int idx = atomic_fetch_add(&n_page_groups_, 1, memory_order_relaxed);
- CHECK(idx < (int)ASAN_ARRAY_SIZE(page_groups_));
+ CHECK(idx < (int)ARRAY_SIZE(page_groups_));
page_groups_[idx] = pg;
return res;
}
AsanChunk *free_lists_[kNumberOfSizeClasses];
AsanChunkFifoList quarantine_;
- AsanLock mu_;
+ BlockingMutex mu_;
PageGroup *page_groups_[kMaxAvailableRam / kMinMmapSize];
atomic_uint32_t n_page_groups_;
@@ -584,42 +494,12 @@ void AsanThreadLocalMallocStorage::CommitBack() {
malloc_info.SwallowThreadLocalMallocStorage(this, true);
}
-static void Describe(uptr addr, uptr access_size) {
- AsanChunk *m = malloc_info.FindMallocedOrFreed(addr, access_size);
- if (!m) return;
- m->DescribeAddress(addr, access_size);
- CHECK(m->alloc_tid >= 0);
- AsanThreadSummary *alloc_thread =
- asanThreadRegistry().FindByTid(m->alloc_tid);
- AsanStackTrace alloc_stack;
- AsanStackTrace::UncompressStack(&alloc_stack, m->compressed_alloc_stack(),
- m->compressed_alloc_stack_size());
- AsanThread *t = asanThreadRegistry().GetCurrent();
- CHECK(t);
- if (m->free_tid != kInvalidTid) {
- AsanThreadSummary *free_thread =
- asanThreadRegistry().FindByTid(m->free_tid);
- AsanPrintf("freed by thread T%d here:\n", free_thread->tid());
- AsanStackTrace free_stack;
- AsanStackTrace::UncompressStack(&free_stack, m->compressed_free_stack(),
- m->compressed_free_stack_size());
- free_stack.PrintStack();
- AsanPrintf("previously allocated by thread T%d here:\n",
- alloc_thread->tid());
-
- alloc_stack.PrintStack();
- t->summary()->Announce();
- free_thread->Announce();
- alloc_thread->Announce();
- } else {
- AsanPrintf("allocated by thread T%d here:\n", alloc_thread->tid());
- alloc_stack.PrintStack();
- t->summary()->Announce();
- alloc_thread->Announce();
- }
+AsanChunkView FindHeapChunkByAddress(uptr address) {
+ return AsanChunkView(malloc_info.FindChunkByAddr(address));
}
-static u8 *Allocate(uptr alignment, uptr size, AsanStackTrace *stack) {
+static u8 *Allocate(uptr alignment, uptr size, StackTrace *stack,
+ AllocType alloc_type) {
__asan_init();
CHECK(stack);
if (size == 0) {
@@ -676,6 +556,7 @@ static u8 *Allocate(uptr alignment, uptr size, AsanStackTrace *stack) {
CHECK(m);
CHECK(m->chunk_state == CHUNK_AVAILABLE);
m->chunk_state = CHUNK_ALLOCATED;
+ m->alloc_type = alloc_type;
m->next = 0;
CHECK(m->Size() == size_to_allocate);
uptr addr = (uptr)m + REDZONE;
@@ -697,7 +578,7 @@ static u8 *Allocate(uptr alignment, uptr size, AsanStackTrace *stack) {
CHECK(m->Beg() == addr);
m->alloc_tid = t ? t->tid() : 0;
m->free_tid = kInvalidTid;
- AsanStackTrace::CompressStack(stack, m->compressed_alloc_stack(),
+ StackTrace::CompressStack(stack, m->compressed_alloc_stack(),
m->compressed_alloc_stack_size());
PoisonShadow(addr, rounded_size, 0);
if (size < rounded_size) {
@@ -710,7 +591,7 @@ static u8 *Allocate(uptr alignment, uptr size, AsanStackTrace *stack) {
return (u8*)addr;
}
-static void Deallocate(u8 *ptr, AsanStackTrace *stack) {
+static void Deallocate(u8 *ptr, StackTrace *stack, AllocType alloc_type) {
if (!ptr) return;
CHECK(stack);
@@ -726,24 +607,21 @@ static void Deallocate(u8 *ptr, AsanStackTrace *stack) {
memory_order_acq_rel);
if (old_chunk_state == CHUNK_QUARANTINE) {
- AsanReport("ERROR: AddressSanitizer attempting double-free on %p:\n", ptr);
- stack->PrintStack();
- Describe((uptr)ptr, 1);
- ShowStatsAndAbort();
+ ReportDoubleFree((uptr)ptr, stack);
} else if (old_chunk_state != CHUNK_ALLOCATED) {
- AsanReport("ERROR: AddressSanitizer attempting free on address "
- "which was not malloc()-ed: %p\n", ptr);
- stack->PrintStack();
- ShowStatsAndAbort();
+ ReportFreeNotMalloced((uptr)ptr, stack);
}
CHECK(old_chunk_state == CHUNK_ALLOCATED);
+ if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch)
+ ReportAllocTypeMismatch((uptr)ptr, stack,
+ (AllocType)m->alloc_type, (AllocType)alloc_type);
// With REDZONE==16 m->next is in the user area, otherwise it should be 0.
CHECK(REDZONE <= 16 || !m->next);
CHECK(m->free_tid == kInvalidTid);
CHECK(m->alloc_tid >= 0);
AsanThread *t = asanThreadRegistry().GetCurrent();
m->free_tid = t ? t->tid() : 0;
- AsanStackTrace::CompressStack(stack, m->compressed_free_stack(),
+ StackTrace::CompressStack(stack, m->compressed_free_stack(),
m->compressed_free_stack_size());
uptr rounded_size = RoundUpTo(m->used_size, REDZONE);
PoisonShadow((uptr)ptr, rounded_size, kAsanHeapFreeMagic);
@@ -769,7 +647,7 @@ static void Deallocate(u8 *ptr, AsanStackTrace *stack) {
}
static u8 *Reallocate(u8 *old_ptr, uptr new_size,
- AsanStackTrace *stack) {
+ StackTrace *stack) {
CHECK(old_ptr && new_size);
// Statistics.
@@ -781,114 +659,112 @@ static u8 *Reallocate(u8 *old_ptr, uptr new_size,
CHECK(m->chunk_state == CHUNK_ALLOCATED);
uptr old_size = m->used_size;
uptr memcpy_size = Min(new_size, old_size);
- u8 *new_ptr = Allocate(0, new_size, stack);
+ u8 *new_ptr = Allocate(0, new_size, stack, FROM_MALLOC);
if (new_ptr) {
CHECK(REAL(memcpy) != 0);
REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
- Deallocate(old_ptr, stack);
+ Deallocate(old_ptr, stack, FROM_MALLOC);
}
return new_ptr;
}
} // namespace __asan
-// Malloc hooks declaration.
-// ASAN_NEW_HOOK(ptr, size) is called immediately after
-// allocation of "size" bytes, which returned "ptr".
-// ASAN_DELETE_HOOK(ptr) is called immediately before
-// deallocation of "ptr".
-// If ASAN_NEW_HOOK or ASAN_DELETE_HOOK is defined, user
-// program must provide implementation of this hook.
-// If macro is undefined, the hook is no-op.
-#ifdef ASAN_NEW_HOOK
-extern "C" void ASAN_NEW_HOOK(void *ptr, uptr size);
-#else
-static inline void ASAN_NEW_HOOK(void *ptr, uptr size) { }
-#endif
-
-#ifdef ASAN_DELETE_HOOK
-extern "C" void ASAN_DELETE_HOOK(void *ptr);
-#else
-static inline void ASAN_DELETE_HOOK(void *ptr) { }
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+// Provide default (no-op) implementation of malloc hooks.
+extern "C" {
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_malloc_hook(void *ptr, uptr size) {
+ (void)ptr;
+ (void)size;
+}
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_free_hook(void *ptr) {
+ (void)ptr;
+}
+} // extern "C"
#endif
namespace __asan {
-void *asan_memalign(uptr alignment, uptr size, AsanStackTrace *stack) {
- void *ptr = (void*)Allocate(alignment, size, stack);
- ASAN_NEW_HOOK(ptr, size);
+void PrintInternalAllocatorStats() {
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
+ AllocType alloc_type) {
+ void *ptr = (void*)Allocate(alignment, size, stack, alloc_type);
+ ASAN_MALLOC_HOOK(ptr, size);
return ptr;
}
-void asan_free(void *ptr, AsanStackTrace *stack) {
- ASAN_DELETE_HOOK(ptr);
- Deallocate((u8*)ptr, stack);
+SANITIZER_INTERFACE_ATTRIBUTE
+void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
+ ASAN_FREE_HOOK(ptr);
+ Deallocate((u8*)ptr, stack, alloc_type);
}
-void *asan_malloc(uptr size, AsanStackTrace *stack) {
- void *ptr = (void*)Allocate(0, size, stack);
- ASAN_NEW_HOOK(ptr, size);
+SANITIZER_INTERFACE_ATTRIBUTE
+void *asan_malloc(uptr size, StackTrace *stack) {
+ void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC);
+ ASAN_MALLOC_HOOK(ptr, size);
return ptr;
}
-void *asan_calloc(uptr nmemb, uptr size, AsanStackTrace *stack) {
- void *ptr = (void*)Allocate(0, nmemb * size, stack);
+void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
+ void *ptr = (void*)Allocate(0, nmemb * size, stack, FROM_MALLOC);
if (ptr)
REAL(memset)(ptr, 0, nmemb * size);
- ASAN_NEW_HOOK(ptr, nmemb * size);
+ ASAN_MALLOC_HOOK(ptr, size);
return ptr;
}
-void *asan_realloc(void *p, uptr size, AsanStackTrace *stack) {
+void *asan_realloc(void *p, uptr size, StackTrace *stack) {
if (p == 0) {
- void *ptr = (void*)Allocate(0, size, stack);
- ASAN_NEW_HOOK(ptr, size);
+ void *ptr = (void*)Allocate(0, size, stack, FROM_MALLOC);
+ ASAN_MALLOC_HOOK(ptr, size);
return ptr;
} else if (size == 0) {
- ASAN_DELETE_HOOK(p);
- Deallocate((u8*)p, stack);
+ ASAN_FREE_HOOK(p);
+ Deallocate((u8*)p, stack, FROM_MALLOC);
return 0;
}
return Reallocate((u8*)p, size, stack);
}
-void *asan_valloc(uptr size, AsanStackTrace *stack) {
- void *ptr = (void*)Allocate(kPageSize, size, stack);
- ASAN_NEW_HOOK(ptr, size);
+void *asan_valloc(uptr size, StackTrace *stack) {
+ void *ptr = (void*)Allocate(GetPageSizeCached(), size, stack, FROM_MALLOC);
+ ASAN_MALLOC_HOOK(ptr, size);
return ptr;
}
-void *asan_pvalloc(uptr size, AsanStackTrace *stack) {
- size = RoundUpTo(size, kPageSize);
+void *asan_pvalloc(uptr size, StackTrace *stack) {
+ uptr PageSize = GetPageSizeCached();
+ size = RoundUpTo(size, PageSize);
if (size == 0) {
// pvalloc(0) should allocate one page.
- size = kPageSize;
+ size = PageSize;
}
- void *ptr = (void*)Allocate(kPageSize, size, stack);
- ASAN_NEW_HOOK(ptr, size);
+ void *ptr = (void*)Allocate(PageSize, size, stack, FROM_MALLOC);
+ ASAN_MALLOC_HOOK(ptr, size);
return ptr;
}
int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
- AsanStackTrace *stack) {
- void *ptr = Allocate(alignment, size, stack);
+ StackTrace *stack) {
+ void *ptr = Allocate(alignment, size, stack, FROM_MALLOC);
CHECK(IsAligned((uptr)ptr, alignment));
- ASAN_NEW_HOOK(ptr, size);
+ ASAN_MALLOC_HOOK(ptr, size);
*memptr = ptr;
return 0;
}
-uptr asan_malloc_usable_size(void *ptr, AsanStackTrace *stack) {
+uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) {
CHECK(stack);
if (ptr == 0) return 0;
uptr usable_size = malloc_info.AllocationSize((uptr)ptr);
if (flags()->check_malloc_usable_size && (usable_size == 0)) {
- AsanReport("ERROR: AddressSanitizer attempting to call "
- "malloc_usable_size() for pointer which is "
- "not owned: %p\n", ptr);
- stack->PrintStack();
- Describe((uptr)ptr, 1);
- ShowStatsAndAbort();
+ ReportMallocUsableSizeNotOwned((uptr)ptr, stack);
}
return usable_size;
}
@@ -897,10 +773,6 @@ uptr asan_mz_size(const void *ptr) {
return malloc_info.AllocationSize((uptr)ptr);
}
-void DescribeHeapAddress(uptr addr, uptr access_size) {
- Describe(addr, access_size);
-}
-
void asan_mz_force_lock() {
malloc_info.ForceLock();
}
@@ -909,170 +781,11 @@ void asan_mz_force_unlock() {
malloc_info.ForceUnlock();
}
-// ---------------------- Fake stack-------------------- {{{1
-FakeStack::FakeStack() {
- CHECK(REAL(memset) != 0);
- REAL(memset)(this, 0, sizeof(*this));
-}
-
-bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) {
- uptr mem = allocated_size_classes_[size_class];
- uptr size = ClassMmapSize(size_class);
- bool res = mem && addr >= mem && addr < mem + size;
- return res;
-}
-
-uptr FakeStack::AddrIsInFakeStack(uptr addr) {
- for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
- if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i];
- }
- return 0;
-}
-
-// We may want to compute this during compilation.
-inline uptr FakeStack::ComputeSizeClass(uptr alloc_size) {
- uptr rounded_size = RoundUpToPowerOfTwo(alloc_size);
- uptr log = Log2(rounded_size);
- CHECK(alloc_size <= (1UL << log));
- if (!(alloc_size > (1UL << (log-1)))) {
- Printf("alloc_size %zu log %zu\n", alloc_size, log);
- }
- CHECK(alloc_size > (1UL << (log-1)));
- uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog;
- CHECK(res < kNumberOfSizeClasses);
- CHECK(ClassSize(res) >= rounded_size);
- return res;
-}
-
-void FakeFrameFifo::FifoPush(FakeFrame *node) {
- CHECK(node);
- node->next = 0;
- if (first_ == 0 && last_ == 0) {
- first_ = last_ = node;
- } else {
- CHECK(first_);
- CHECK(last_);
- last_->next = node;
- last_ = node;
- }
-}
-
-FakeFrame *FakeFrameFifo::FifoPop() {
- CHECK(first_ && last_ && "Exhausted fake stack");
- FakeFrame *res = 0;
- if (first_ == last_) {
- res = first_;
- first_ = last_ = 0;
- } else {
- res = first_;
- first_ = first_->next;
- }
- return res;
-}
-
-void FakeStack::Init(uptr stack_size) {
- stack_size_ = stack_size;
- alive_ = true;
-}
-
-void FakeStack::Cleanup() {
- alive_ = false;
- for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
- uptr mem = allocated_size_classes_[i];
- if (mem) {
- PoisonShadow(mem, ClassMmapSize(i), 0);
- allocated_size_classes_[i] = 0;
- UnmapOrDie((void*)mem, ClassMmapSize(i));
- }
- }
-}
-
-uptr FakeStack::ClassMmapSize(uptr size_class) {
- return RoundUpToPowerOfTwo(stack_size_);
-}
-
-void FakeStack::AllocateOneSizeClass(uptr size_class) {
- CHECK(ClassMmapSize(size_class) >= kPageSize);
- uptr new_mem = (uptr)MmapOrDie(
- ClassMmapSize(size_class), __FUNCTION__);
- // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n",
- // asanThreadRegistry().GetCurrent()->tid(),
- // size_class, new_mem, new_mem + ClassMmapSize(size_class),
- // ClassMmapSize(size_class));
- uptr i;
- for (i = 0; i < ClassMmapSize(size_class);
- i += ClassSize(size_class)) {
- size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i));
- }
- CHECK(i == ClassMmapSize(size_class));
- allocated_size_classes_[size_class] = new_mem;
-}
-
-uptr FakeStack::AllocateStack(uptr size, uptr real_stack) {
- if (!alive_) return real_stack;
- CHECK(size <= kMaxStackMallocSize && size > 1);
- uptr size_class = ComputeSizeClass(size);
- if (!allocated_size_classes_[size_class]) {
- AllocateOneSizeClass(size_class);
- }
- FakeFrame *fake_frame = size_classes_[size_class].FifoPop();
- CHECK(fake_frame);
- fake_frame->size_minus_one = size - 1;
- fake_frame->real_stack = real_stack;
- while (FakeFrame *top = call_stack_.top()) {
- if (top->real_stack > real_stack) break;
- call_stack_.LifoPop();
- DeallocateFrame(top);
- }
- call_stack_.LifoPush(fake_frame);
- uptr ptr = (uptr)fake_frame;
- PoisonShadow(ptr, size, 0);
- return ptr;
-}
-
-void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
- CHECK(alive_);
- uptr size = fake_frame->size_minus_one + 1;
- uptr size_class = ComputeSizeClass(size);
- CHECK(allocated_size_classes_[size_class]);
- uptr ptr = (uptr)fake_frame;
- CHECK(AddrIsInSizeClass(ptr, size_class));
- CHECK(AddrIsInSizeClass(ptr + size - 1, size_class));
- size_classes_[size_class].FifoPush(fake_frame);
-}
-
-void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) {
- FakeFrame *fake_frame = (FakeFrame*)ptr;
- CHECK(fake_frame->magic = kRetiredStackFrameMagic);
- CHECK(fake_frame->descr != 0);
- CHECK(fake_frame->size_minus_one == size - 1);
- PoisonShadow(ptr, size, kAsanStackAfterReturnMagic);
-}
-
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
-uptr __asan_stack_malloc(uptr size, uptr real_stack) {
- if (!flags()->use_fake_stack) return real_stack;
- AsanThread *t = asanThreadRegistry().GetCurrent();
- if (!t) {
- // TSD is gone, use the real stack.
- return real_stack;
- }
- uptr ptr = t->fake_stack().AllocateStack(size, real_stack);
- // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack);
- return ptr;
-}
-
-void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) {
- if (!flags()->use_fake_stack) return;
- if (ptr != real_stack) {
- FakeStack::OnFree(ptr, size, real_stack);
- }
-}
-
// ASan allocator doesn't reserve extra bytes, so normally we would
// just return "size".
uptr __asan_get_estimated_allocated_size(uptr size) {
@@ -1089,12 +802,9 @@ uptr __asan_get_allocated_size(const void *p) {
uptr allocated_size = malloc_info.AllocationSize((uptr)p);
// Die if p is not malloced or if it is already freed.
if (allocated_size == 0) {
- AsanReport("ERROR: AddressSanitizer attempting to call "
- "__asan_get_allocated_size() for pointer which is "
- "not owned: %p\n", p);
- PRINT_CURRENT_STACK();
- Describe((uptr)p, 1);
- ShowStatsAndAbort();
+ GET_STACK_TRACE_FATAL_HERE;
+ ReportAsanGetAllocatedSizeNotOwned((uptr)p, &stack);
}
return allocated_size;
}
+#endif // ASAN_ALLOCATOR_VERSION
diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h
index 2aed59853868..cca24edad81f 100644
--- a/lib/asan/asan_allocator.h
+++ b/lib/asan/asan_allocator.h
@@ -17,13 +17,76 @@
#include "asan_internal.h"
#include "asan_interceptors.h"
+#include "sanitizer_common/sanitizer_list.h"
+
+// We are in the process of transitioning from the old allocator (version 1)
+// to a new one (version 2). The change is quite intrusive so both allocators
+// will co-exist in the source base for a while. The actual allocator is chosen
+// at build time by redefining this macro.
+#ifndef ASAN_ALLOCATOR_VERSION
+# if ASAN_LINUX && !ASAN_ANDROID
+# define ASAN_ALLOCATOR_VERSION 2
+# else
+# define ASAN_ALLOCATOR_VERSION 1
+# endif
+#endif // ASAN_ALLOCATOR_VERSION
namespace __asan {
+enum AllocType {
+ FROM_MALLOC = 1, // Memory block came from malloc, calloc, realloc, etc.
+ FROM_NEW = 2, // Memory block came from operator new.
+ FROM_NEW_BR = 3 // Memory block came from operator new [ ]
+};
+
static const uptr kNumberOfSizeClasses = 255;
struct AsanChunk;
-class AsanChunkFifoList {
+class AsanChunkView {
+ public:
+ explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {}
+ bool IsValid() { return chunk_ != 0; }
+ uptr Beg(); // first byte of user memory.
+ uptr End(); // last byte of user memory.
+ uptr UsedSize(); // size requested by the user.
+ uptr AllocTid();
+ uptr FreeTid();
+ void GetAllocStack(StackTrace *stack);
+ void GetFreeStack(StackTrace *stack);
+ bool AddrIsInside(uptr addr, uptr access_size, uptr *offset) {
+ if (addr >= Beg() && (addr + access_size) <= End()) {
+ *offset = addr - Beg();
+ return true;
+ }
+ return false;
+ }
+ bool AddrIsAtLeft(uptr addr, uptr access_size, uptr *offset) {
+ (void)access_size;
+ if (addr < Beg()) {
+ *offset = Beg() - addr;
+ return true;
+ }
+ return false;
+ }
+ bool AddrIsAtRight(uptr addr, uptr access_size, uptr *offset) {
+ if (addr + access_size >= End()) {
+ if (addr <= End())
+ *offset = 0;
+ else
+ *offset = addr - End();
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ AsanChunk *const chunk_;
+};
+
+AsanChunkView FindHeapChunkByAddress(uptr address);
+
+// List of AsanChunks with total size.
+class AsanChunkFifoList: public IntrusiveList<AsanChunk> {
public:
explicit AsanChunkFifoList(LinkerInitialized) { }
AsanChunkFifoList() { clear(); }
@@ -32,25 +95,31 @@ class AsanChunkFifoList {
AsanChunk *Pop();
uptr size() { return size_; }
void clear() {
- first_ = last_ = 0;
+ IntrusiveList<AsanChunk>::clear();
size_ = 0;
}
private:
- AsanChunk *first_;
- AsanChunk *last_;
uptr size_;
};
struct AsanThreadLocalMallocStorage {
explicit AsanThreadLocalMallocStorage(LinkerInitialized x)
- : quarantine_(x) { }
+#if ASAN_ALLOCATOR_VERSION == 1
+ : quarantine_(x)
+#endif
+ { }
AsanThreadLocalMallocStorage() {
CHECK(REAL(memset));
REAL(memset)(this, 0, sizeof(AsanThreadLocalMallocStorage));
}
+#if ASAN_ALLOCATOR_VERSION == 1
AsanChunkFifoList quarantine_;
AsanChunk *free_lists_[kNumberOfSizeClasses];
+#else
+ uptr quarantine_cache[16];
+ uptr allocator2_cache[96 * (512 * 8 + 16)]; // Opaque.
+#endif
void CommitBack();
};
@@ -108,6 +177,7 @@ class FakeStack {
// Return the bottom of the maped region.
uptr AddrIsInFakeStack(uptr addr);
bool StackSize() { return stack_size_; }
+
private:
static const uptr kMinStackFrameSizeLog = 9; // Min frame is 512B.
static const uptr kMaxStackFrameSizeLog = 16; // Max stack frame is 64K.
@@ -137,23 +207,70 @@ class FakeStack {
FakeFrameLifo call_stack_;
};
-void *asan_memalign(uptr alignment, uptr size, AsanStackTrace *stack);
-void asan_free(void *ptr, AsanStackTrace *stack);
+void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
+ AllocType alloc_type);
+void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type);
-void *asan_malloc(uptr size, AsanStackTrace *stack);
-void *asan_calloc(uptr nmemb, uptr size, AsanStackTrace *stack);
-void *asan_realloc(void *p, uptr size, AsanStackTrace *stack);
-void *asan_valloc(uptr size, AsanStackTrace *stack);
-void *asan_pvalloc(uptr size, AsanStackTrace *stack);
+void *asan_malloc(uptr size, StackTrace *stack);
+void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack);
+void *asan_realloc(void *p, uptr size, StackTrace *stack);
+void *asan_valloc(uptr size, StackTrace *stack);
+void *asan_pvalloc(uptr size, StackTrace *stack);
int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
- AsanStackTrace *stack);
-uptr asan_malloc_usable_size(void *ptr, AsanStackTrace *stack);
+ StackTrace *stack);
+uptr asan_malloc_usable_size(void *ptr, StackTrace *stack);
uptr asan_mz_size(const void *ptr);
void asan_mz_force_lock();
void asan_mz_force_unlock();
-void DescribeHeapAddress(uptr addr, uptr access_size);
+
+void PrintInternalAllocatorStats();
+
+// Log2 and RoundUpToPowerOfTwo should be inlined for performance.
+#if defined(_WIN32) && !defined(__clang__)
+extern "C" {
+unsigned char _BitScanForward(unsigned long *index, unsigned long mask); // NOLINT
+unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); // NOLINT
+#if defined(_WIN64)
+unsigned char _BitScanForward64(unsigned long *index, unsigned __int64 mask); // NOLINT
+unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask); // NOLINT
+#endif
+}
+#endif
+
+static inline uptr Log2(uptr x) {
+ CHECK(IsPowerOfTwo(x));
+#if !defined(_WIN32) || defined(__clang__)
+ return __builtin_ctzl(x);
+#elif defined(_WIN64)
+ unsigned long ret; // NOLINT
+ _BitScanForward64(&ret, x);
+ return ret;
+#else
+ unsigned long ret; // NOLINT
+ _BitScanForward(&ret, x);
+ return ret;
+#endif
+}
+
+static inline uptr RoundUpToPowerOfTwo(uptr size) {
+ CHECK(size);
+ if (IsPowerOfTwo(size)) return size;
+
+ unsigned long up; // NOLINT
+#if !defined(_WIN32) || defined(__clang__)
+ up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(size);
+#elif defined(_WIN64)
+ _BitScanReverse64(&up, size);
+#else
+ _BitScanReverse(&up, size);
+#endif
+ CHECK(size < (1ULL << (up + 1)));
+ CHECK(size > (1ULL << up));
+ return 1UL << (up + 1);
+}
+
} // namespace __asan
#endif // ASAN_ALLOCATOR_H
diff --git a/lib/asan/asan_allocator2.cc b/lib/asan/asan_allocator2.cc
new file mode 100644
index 000000000000..42d8b29afd6b
--- /dev/null
+++ b/lib/asan/asan_allocator2.cc
@@ -0,0 +1,710 @@
+//===-- asan_allocator2.cc ------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Implementation of ASan's memory allocator, 2-nd version.
+// This variant uses the allocator from sanitizer_common, i.e. the one shared
+// with ThreadSanitizer and MemorySanitizer.
+//
+// Status: under development, not enabled by default yet.
+//===----------------------------------------------------------------------===//
+#include "asan_allocator.h"
+#if ASAN_ALLOCATOR_VERSION == 2
+
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_thread.h"
+#include "asan_thread_registry.h"
+#include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_list.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_quarantine.h"
+
+namespace __asan {
+
+struct AsanMapUnmapCallback {
+ void OnMap(uptr p, uptr size) const {
+ PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic);
+ // Statistics.
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.mmaps++;
+ thread_stats.mmaped += size;
+ }
+ void OnUnmap(uptr p, uptr size) const {
+ PoisonShadow(p, size, 0);
+ // We are about to unmap a chunk of user memory.
+ // Mark the corresponding shadow memory as not needed.
+ // Since asan's mapping is compacting, the shadow chunk may be
+ // not page-aligned, so we only flush the page-aligned portion.
+ uptr page_size = GetPageSizeCached();
+ uptr shadow_beg = RoundUpTo(MemToShadow(p), page_size);
+ uptr shadow_end = RoundDownTo(MemToShadow(p + size), page_size);
+ FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg);
+ // Statistics.
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.munmaps++;
+ thread_stats.munmaped += size;
+ }
+};
+
+#if SANITIZER_WORDSIZE == 64
+const uptr kAllocatorSpace = 0x600000000000ULL;
+const uptr kAllocatorSize = 0x10000000000ULL; // 1T.
+typedef DefaultSizeClassMap SizeClassMap;
+typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0 /*metadata*/,
+ SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
+#elif SANITIZER_WORDSIZE == 32
+static const u64 kAddressSpaceSize = 1ULL << 32;
+typedef CompactSizeClassMap SizeClassMap;
+typedef SizeClassAllocator32<0, kAddressSpaceSize, 16,
+ SizeClassMap, AsanMapUnmapCallback> PrimaryAllocator;
+#endif
+
+typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
+typedef LargeMmapAllocator<AsanMapUnmapCallback> SecondaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
+ SecondaryAllocator> Allocator;
+
+// We can not use THREADLOCAL because it is not supported on some of the
+// platforms we care about (OSX 10.6, Android).
+// static THREADLOCAL AllocatorCache cache;
+AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) {
+ CHECK(ms);
+ CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator2_cache));
+ return reinterpret_cast<AllocatorCache *>(ms->allocator2_cache);
+}
+
+static Allocator allocator;
+
+static const uptr kMaxAllowedMallocSize =
+ FIRST_32_SECOND_64(3UL << 30, 8UL << 30);
+
+static const uptr kMaxThreadLocalQuarantine =
+ FIRST_32_SECOND_64(1 << 18, 1 << 20);
+
+static const uptr kReturnOnZeroMalloc = 2048; // Zero page is protected.
+
+// Every chunk of memory allocated by this allocator can be in one of 3 states:
+// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated.
+// CHUNK_ALLOCATED: the chunk is allocated and not yet freed.
+// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone.
+enum {
+ CHUNK_AVAILABLE = 0, // 0 is the default value even if we didn't set it.
+ CHUNK_ALLOCATED = 2,
+ CHUNK_QUARANTINE = 3
+};
+
+// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits.
+// We use adaptive redzones: for larger allocation larger redzones are used.
+static u32 RZLog2Size(u32 rz_log) {
+ CHECK_LT(rz_log, 8);
+ return 16 << rz_log;
+}
+
+static u32 RZSize2Log(u32 rz_size) {
+ CHECK_GE(rz_size, 16);
+ CHECK_LE(rz_size, 2048);
+ CHECK(IsPowerOfTwo(rz_size));
+ u32 res = __builtin_ctz(rz_size) - 4;
+ CHECK_EQ(rz_size, RZLog2Size(res));
+ return res;
+}
+
+static uptr ComputeRZLog(uptr user_requested_size) {
+ u32 rz_log =
+ user_requested_size <= 64 - 16 ? 0 :
+ user_requested_size <= 128 - 32 ? 1 :
+ user_requested_size <= 512 - 64 ? 2 :
+ user_requested_size <= 4096 - 128 ? 3 :
+ user_requested_size <= (1 << 14) - 256 ? 4 :
+ user_requested_size <= (1 << 15) - 512 ? 5 :
+ user_requested_size <= (1 << 16) - 1024 ? 6 : 7;
+ return Max(rz_log, RZSize2Log(flags()->redzone));
+}
+
+// The memory chunk allocated from the underlying allocator looks like this:
+// L L L L L L H H U U U U U U R R
+// L -- left redzone words (0 or more bytes)
+// H -- ChunkHeader (16 bytes), which is also a part of the left redzone.
+// U -- user memory.
+// R -- right redzone (0 or more bytes)
+// ChunkBase consists of ChunkHeader and other bytes that overlap with user
+// memory.
+
+// If a memory chunk is allocated by memalign and we had to increase the
+// allocation size to achieve the proper alignment, then we store this magic
+// value in the first uptr word of the memory block and store the address of
+// ChunkBase in the next uptr.
+// M B ? ? ? L L L L L L H H U U U U U U
+// M -- magic value kMemalignMagic
+// B -- address of ChunkHeader pointing to the first 'H'
+static const uptr kMemalignMagic = 0xCC6E96B9;
+
+struct ChunkHeader {
+ // 1-st 8 bytes.
+ u32 chunk_state : 8; // Must be first.
+ u32 alloc_tid : 24;
+
+ u32 free_tid : 24;
+ u32 from_memalign : 1;
+ u32 alloc_type : 2;
+ u32 rz_log : 3;
+ // 2-nd 8 bytes
+ // This field is used for small sizes. For large sizes it is equal to
+ // SizeClassMap::kMaxSize and the actual size is stored in the
+ // SecondaryAllocator's metadata.
+ u32 user_requested_size;
+ u32 alloc_context_id;
+};
+
+struct ChunkBase : ChunkHeader {
+ // Header2, intersects with user memory.
+ AsanChunk *next;
+ u32 free_context_id;
+};
+
+static const uptr kChunkHeaderSize = sizeof(ChunkHeader);
+static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize;
+COMPILER_CHECK(kChunkHeaderSize == 16);
+COMPILER_CHECK(kChunkHeader2Size <= 16);
+
+struct AsanChunk: ChunkBase {
+ uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; }
+ uptr UsedSize() {
+ if (user_requested_size != SizeClassMap::kMaxSize)
+ return user_requested_size;
+ return *reinterpret_cast<uptr *>(allocator.GetMetaData(AllocBeg()));
+ }
+ void *AllocBeg() {
+ if (from_memalign)
+ return allocator.GetBlockBegin(reinterpret_cast<void *>(this));
+ return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log));
+ }
+ // We store the alloc/free stack traces in the chunk itself.
+ u32 *AllocStackBeg() {
+ return (u32*)(Beg() - RZLog2Size(rz_log));
+ }
+ uptr AllocStackSize() {
+ CHECK_LE(RZLog2Size(rz_log), kChunkHeaderSize);
+ return (RZLog2Size(rz_log) - kChunkHeaderSize) / sizeof(u32);
+ }
+ u32 *FreeStackBeg() {
+ return (u32*)(Beg() + kChunkHeader2Size);
+ }
+ uptr FreeStackSize() {
+ if (user_requested_size < kChunkHeader2Size) return 0;
+ uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY);
+ return (available - kChunkHeader2Size) / sizeof(u32);
+ }
+};
+
+uptr AsanChunkView::Beg() { return chunk_->Beg(); }
+uptr AsanChunkView::End() { return Beg() + UsedSize(); }
+uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); }
+uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
+uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
+
+static void GetStackTraceFromId(u32 id, StackTrace *stack) {
+ CHECK(id);
+ uptr size = 0;
+ const uptr *trace = StackDepotGet(id, &size);
+ CHECK_LT(size, kStackTraceMax);
+ internal_memcpy(stack->trace, trace, sizeof(uptr) * size);
+ stack->size = size;
+}
+
+void AsanChunkView::GetAllocStack(StackTrace *stack) {
+ if (flags()->use_stack_depot)
+ GetStackTraceFromId(chunk_->alloc_context_id, stack);
+ else
+ StackTrace::UncompressStack(stack, chunk_->AllocStackBeg(),
+ chunk_->AllocStackSize());
+}
+
+void AsanChunkView::GetFreeStack(StackTrace *stack) {
+ if (flags()->use_stack_depot)
+ GetStackTraceFromId(chunk_->free_context_id, stack);
+ else
+ StackTrace::UncompressStack(stack, chunk_->FreeStackBeg(),
+ chunk_->FreeStackSize());
+}
+
+struct QuarantineCallback;
+typedef Quarantine<QuarantineCallback, AsanChunk> AsanQuarantine;
+typedef AsanQuarantine::Cache QuarantineCache;
+static AsanQuarantine quarantine(LINKER_INITIALIZED);
+static QuarantineCache fallback_quarantine_cache(LINKER_INITIALIZED);
+static AllocatorCache fallback_allocator_cache;
+static SpinMutex fallback_mutex;
+
+QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) {
+ CHECK(ms);
+ CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache));
+ return reinterpret_cast<QuarantineCache *>(ms->quarantine_cache);
+}
+
+struct QuarantineCallback {
+ explicit QuarantineCallback(AllocatorCache *cache)
+ : cache_(cache) {
+ }
+
+ void Recycle(AsanChunk *m) {
+ CHECK(m->chunk_state == CHUNK_QUARANTINE);
+ m->chunk_state = CHUNK_AVAILABLE;
+ CHECK_NE(m->alloc_tid, kInvalidTid);
+ CHECK_NE(m->free_tid, kInvalidTid);
+ PoisonShadow(m->Beg(),
+ RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
+ kAsanHeapLeftRedzoneMagic);
+ void *p = reinterpret_cast<void *>(m->AllocBeg());
+ if (m->from_memalign) {
+ uptr *memalign_magic = reinterpret_cast<uptr *>(p);
+ CHECK_EQ(memalign_magic[0], kMemalignMagic);
+ CHECK_EQ(memalign_magic[1], reinterpret_cast<uptr>(m));
+ }
+
+ // Statistics.
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.real_frees++;
+ thread_stats.really_freed += m->UsedSize();
+
+ allocator.Deallocate(cache_, p);
+ }
+
+ void *Allocate(uptr size) {
+ return allocator.Allocate(cache_, size, 1, false);
+ }
+
+ void Deallocate(void *p) {
+ allocator.Deallocate(cache_, p);
+ }
+
+ AllocatorCache *cache_;
+};
+
+static void Init() {
+ static int inited = 0;
+ if (inited) return;
+ __asan_init();
+ inited = true; // this must happen before any threads are created.
+ allocator.Init();
+ quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine);
+}
+
+static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
+ AllocType alloc_type) {
+ Init();
+ CHECK(stack);
+ const uptr min_alignment = SHADOW_GRANULARITY;
+ if (alignment < min_alignment)
+ alignment = min_alignment;
+ if (size == 0) {
+ if (alignment <= kReturnOnZeroMalloc)
+ return reinterpret_cast<void *>(kReturnOnZeroMalloc);
+ else
+ return 0; // 0 bytes with large alignment requested. Just return 0.
+ }
+ CHECK(IsPowerOfTwo(alignment));
+ uptr rz_log = ComputeRZLog(size);
+ uptr rz_size = RZLog2Size(rz_log);
+ uptr rounded_size = RoundUpTo(size, alignment);
+ if (rounded_size < kChunkHeader2Size)
+ rounded_size = kChunkHeader2Size;
+ uptr needed_size = rounded_size + rz_size;
+ if (alignment > min_alignment)
+ needed_size += alignment;
+ bool using_primary_allocator = true;
+ // If we are allocating from the secondary allocator, there will be no
+ // automatic right redzone, so add the right redzone manually.
+ if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) {
+ needed_size += rz_size;
+ using_primary_allocator = false;
+ }
+ CHECK(IsAligned(needed_size, min_alignment));
+ if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
+ Report("WARNING: AddressSanitizer failed to allocate %p bytes\n",
+ (void*)size);
+ return 0;
+ }
+
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ void *allocated;
+ if (t) {
+ AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
+ allocated = allocator.Allocate(cache, needed_size, 8, false);
+ } else {
+ SpinMutexLock l(&fallback_mutex);
+ AllocatorCache *cache = &fallback_allocator_cache;
+ allocated = allocator.Allocate(cache, needed_size, 8, false);
+ }
+ uptr alloc_beg = reinterpret_cast<uptr>(allocated);
+ // Clear the first allocated word (an old kMemalignMagic may still be there).
+ reinterpret_cast<uptr *>(alloc_beg)[0] = 0;
+ uptr alloc_end = alloc_beg + needed_size;
+ uptr beg_plus_redzone = alloc_beg + rz_size;
+ uptr user_beg = beg_plus_redzone;
+ if (!IsAligned(user_beg, alignment))
+ user_beg = RoundUpTo(user_beg, alignment);
+ uptr user_end = user_beg + size;
+ CHECK_LE(user_end, alloc_end);
+ uptr chunk_beg = user_beg - kChunkHeaderSize;
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+ m->chunk_state = CHUNK_ALLOCATED;
+ m->alloc_type = alloc_type;
+ m->rz_log = rz_log;
+ u32 alloc_tid = t ? t->tid() : 0;
+ m->alloc_tid = alloc_tid;
+ CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield?
+ m->free_tid = kInvalidTid;
+ m->from_memalign = user_beg != beg_plus_redzone;
+ if (m->from_memalign) {
+ CHECK_LE(beg_plus_redzone + 2 * sizeof(uptr), user_beg);
+ uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg);
+ memalign_magic[0] = kMemalignMagic;
+ memalign_magic[1] = chunk_beg;
+ }
+ if (using_primary_allocator) {
+ CHECK(size);
+ m->user_requested_size = size;
+ CHECK(allocator.FromPrimary(allocated));
+ } else {
+ CHECK(!allocator.FromPrimary(allocated));
+ m->user_requested_size = SizeClassMap::kMaxSize;
+ uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(allocated));
+ meta[0] = size;
+ meta[1] = chunk_beg;
+ }
+
+ if (flags()->use_stack_depot) {
+ m->alloc_context_id = StackDepotPut(stack->trace, stack->size);
+ } else {
+ m->alloc_context_id = 0;
+ StackTrace::CompressStack(stack, m->AllocStackBeg(), m->AllocStackSize());
+ }
+
+ uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY);
+ // Unpoison the bulk of the memory region.
+ if (size_rounded_down_to_granularity)
+ PoisonShadow(user_beg, size_rounded_down_to_granularity, 0);
+ // Deal with the end of the region if size is not aligned to granularity.
+ if (size != size_rounded_down_to_granularity && flags()->poison_heap) {
+ u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity);
+ *shadow = size & (SHADOW_GRANULARITY - 1);
+ }
+
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.mallocs++;
+ thread_stats.malloced += size;
+ thread_stats.malloced_redzones += needed_size - size;
+ uptr class_id = Min(kNumberOfSizeClasses, SizeClassMap::ClassID(needed_size));
+ thread_stats.malloced_by_size[class_id]++;
+ if (needed_size > SizeClassMap::kMaxSize)
+ thread_stats.malloc_large++;
+
+ void *res = reinterpret_cast<void *>(user_beg);
+ ASAN_MALLOC_HOOK(res, size);
+ return res;
+}
+
+static void Deallocate(void *ptr, StackTrace *stack, AllocType alloc_type) {
+ uptr p = reinterpret_cast<uptr>(ptr);
+ if (p == 0 || p == kReturnOnZeroMalloc) return;
+ uptr chunk_beg = p - kChunkHeaderSize;
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+
+ // Flip the chunk_state atomically to avoid race on double-free.
+ u8 old_chunk_state = atomic_exchange((atomic_uint8_t*)m, CHUNK_QUARANTINE,
+ memory_order_relaxed);
+
+ if (old_chunk_state == CHUNK_QUARANTINE)
+ ReportDoubleFree((uptr)ptr, stack);
+ else if (old_chunk_state != CHUNK_ALLOCATED)
+ ReportFreeNotMalloced((uptr)ptr, stack);
+ CHECK(old_chunk_state == CHUNK_ALLOCATED);
+ if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch)
+ ReportAllocTypeMismatch((uptr)ptr, stack,
+ (AllocType)m->alloc_type, (AllocType)alloc_type);
+
+ CHECK_GE(m->alloc_tid, 0);
+ if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area.
+ CHECK_EQ(m->free_tid, kInvalidTid);
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ m->free_tid = t ? t->tid() : 0;
+ if (flags()->use_stack_depot) {
+ m->free_context_id = StackDepotPut(stack->trace, stack->size);
+ } else {
+ m->free_context_id = 0;
+ StackTrace::CompressStack(stack, m->FreeStackBeg(), m->FreeStackSize());
+ }
+ CHECK(m->chunk_state == CHUNK_QUARANTINE);
+ // Poison the region.
+ PoisonShadow(m->Beg(),
+ RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
+ kAsanHeapFreeMagic);
+
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.frees++;
+ thread_stats.freed += m->UsedSize();
+
+ // Push into quarantine.
+ if (t) {
+ AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
+ AllocatorCache *ac = GetAllocatorCache(ms);
+ quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac),
+ m, m->UsedSize());
+ } else {
+ SpinMutexLock l(&fallback_mutex);
+ AllocatorCache *ac = &fallback_allocator_cache;
+ quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac),
+ m, m->UsedSize());
+ }
+
+ ASAN_FREE_HOOK(ptr);
+}
+
+static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) {
+ CHECK(old_ptr && new_size);
+ uptr p = reinterpret_cast<uptr>(old_ptr);
+ uptr chunk_beg = p - kChunkHeaderSize;
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+
+ AsanStats &thread_stats = asanThreadRegistry().GetCurrentThreadStats();
+ thread_stats.reallocs++;
+ thread_stats.realloced += new_size;
+
+ CHECK(m->chunk_state == CHUNK_ALLOCATED);
+ uptr old_size = m->UsedSize();
+ uptr memcpy_size = Min(new_size, old_size);
+ void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC);
+ if (new_ptr) {
+ CHECK(REAL(memcpy) != 0);
+ REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
+ Deallocate(old_ptr, stack, FROM_MALLOC);
+ }
+ return new_ptr;
+}
+
+static AsanChunk *GetAsanChunkByAddr(uptr p) {
+ void *ptr = reinterpret_cast<void *>(p);
+ uptr alloc_beg = reinterpret_cast<uptr>(allocator.GetBlockBegin(ptr));
+ if (!alloc_beg) return 0;
+ uptr *memalign_magic = reinterpret_cast<uptr *>(alloc_beg);
+ if (memalign_magic[0] == kMemalignMagic) {
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(memalign_magic[1]);
+ CHECK(m->from_memalign);
+ return m;
+ }
+ if (!allocator.FromPrimary(ptr)) {
+ uptr *meta = reinterpret_cast<uptr *>(
+ allocator.GetMetaData(reinterpret_cast<void *>(alloc_beg)));
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(meta[1]);
+ return m;
+ }
+ uptr actual_size = allocator.GetActuallyAllocatedSize(ptr);
+ CHECK_LE(actual_size, SizeClassMap::kMaxSize);
+ // We know the actually allocted size, but we don't know the redzone size.
+ // Just try all possible redzone sizes.
+ for (u32 rz_log = 0; rz_log < 8; rz_log++) {
+ u32 rz_size = RZLog2Size(rz_log);
+ uptr max_possible_size = actual_size - rz_size;
+ if (ComputeRZLog(max_possible_size) != rz_log)
+ continue;
+ return reinterpret_cast<AsanChunk *>(
+ alloc_beg + rz_size - kChunkHeaderSize);
+ }
+ return 0;
+}
+
+static uptr AllocationSize(uptr p) {
+ AsanChunk *m = GetAsanChunkByAddr(p);
+ if (!m) return 0;
+ if (m->chunk_state != CHUNK_ALLOCATED) return 0;
+ if (m->Beg() != p) return 0;
+ return m->UsedSize();
+}
+
+// We have an address between two chunks, and we want to report just one.
+AsanChunk *ChooseChunk(uptr addr,
+ AsanChunk *left_chunk, AsanChunk *right_chunk) {
+ // Prefer an allocated chunk over freed chunk and freed chunk
+ // over available chunk.
+ if (left_chunk->chunk_state != right_chunk->chunk_state) {
+ if (left_chunk->chunk_state == CHUNK_ALLOCATED)
+ return left_chunk;
+ if (right_chunk->chunk_state == CHUNK_ALLOCATED)
+ return right_chunk;
+ if (left_chunk->chunk_state == CHUNK_QUARANTINE)
+ return left_chunk;
+ if (right_chunk->chunk_state == CHUNK_QUARANTINE)
+ return right_chunk;
+ }
+ // Same chunk_state: choose based on offset.
+ uptr l_offset = 0, r_offset = 0;
+ CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset));
+ CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset));
+ if (l_offset < r_offset)
+ return left_chunk;
+ return right_chunk;
+}
+
+AsanChunkView FindHeapChunkByAddress(uptr addr) {
+ AsanChunk *m1 = GetAsanChunkByAddr(addr);
+ if (!m1) return AsanChunkView(m1);
+ uptr offset = 0;
+ if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) {
+ // The address is in the chunk's left redzone, so maybe it is actually
+ // a right buffer overflow from the other chunk to the left.
+ // Search a bit to the left to see if there is another chunk.
+ AsanChunk *m2 = 0;
+ for (uptr l = 1; l < GetPageSizeCached(); l++) {
+ m2 = GetAsanChunkByAddr(addr - l);
+ if (m2 == m1) continue; // Still the same chunk.
+ break;
+ }
+ if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset))
+ m1 = ChooseChunk(addr, m2, m1);
+ }
+ return AsanChunkView(m1);
+}
+
+void AsanThreadLocalMallocStorage::CommitBack() {
+ AllocatorCache *ac = GetAllocatorCache(this);
+ quarantine.Drain(GetQuarantineCache(this), QuarantineCallback(ac));
+ allocator.SwallowCache(GetAllocatorCache(this));
+}
+
+void PrintInternalAllocatorStats() {
+ allocator.PrintStats();
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
+ AllocType alloc_type) {
+ return Allocate(size, alignment, stack, alloc_type);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
+ Deallocate(ptr, stack, alloc_type);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *asan_malloc(uptr size, StackTrace *stack) {
+ return Allocate(size, 8, stack, FROM_MALLOC);
+}
+
+void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
+ void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC);
+ if (ptr)
+ REAL(memset)(ptr, 0, nmemb * size);
+ return ptr;
+}
+
+void *asan_realloc(void *p, uptr size, StackTrace *stack) {
+ if (p == 0)
+ return Allocate(size, 8, stack, FROM_MALLOC);
+ if (size == 0) {
+ Deallocate(p, stack, FROM_MALLOC);
+ return 0;
+ }
+ return Reallocate(p, size, stack);
+}
+
+void *asan_valloc(uptr size, StackTrace *stack) {
+ return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC);
+}
+
+void *asan_pvalloc(uptr size, StackTrace *stack) {
+ uptr PageSize = GetPageSizeCached();
+ size = RoundUpTo(size, PageSize);
+ if (size == 0) {
+ // pvalloc(0) should allocate one page.
+ size = PageSize;
+ }
+ return Allocate(size, PageSize, stack, FROM_MALLOC);
+}
+
+int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
+ StackTrace *stack) {
+ void *ptr = Allocate(size, alignment, stack, FROM_MALLOC);
+ CHECK(IsAligned((uptr)ptr, alignment));
+ *memptr = ptr;
+ return 0;
+}
+
+uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) {
+ CHECK(stack);
+ if (ptr == 0) return 0;
+ uptr usable_size = AllocationSize(reinterpret_cast<uptr>(ptr));
+ if (flags()->check_malloc_usable_size && (usable_size == 0))
+ ReportMallocUsableSizeNotOwned((uptr)ptr, stack);
+ return usable_size;
+}
+
+uptr asan_mz_size(const void *ptr) {
+ UNIMPLEMENTED();
+ return 0;
+}
+
+void asan_mz_force_lock() {
+ UNIMPLEMENTED();
+}
+
+void asan_mz_force_unlock() {
+ UNIMPLEMENTED();
+}
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+// ASan allocator doesn't reserve extra bytes, so normally we would
+// just return "size". We don't want to expose our redzone sizes, etc here.
+uptr __asan_get_estimated_allocated_size(uptr size) {
+ return size;
+}
+
+bool __asan_get_ownership(const void *p) {
+ uptr ptr = reinterpret_cast<uptr>(p);
+ return (ptr == kReturnOnZeroMalloc) || (AllocationSize(ptr) > 0);
+}
+
+uptr __asan_get_allocated_size(const void *p) {
+ if (p == 0) return 0;
+ uptr ptr = reinterpret_cast<uptr>(p);
+ uptr allocated_size = AllocationSize(ptr);
+ // Die if p is not malloced or if it is already freed.
+ if (allocated_size == 0 && ptr != kReturnOnZeroMalloc) {
+ GET_STACK_TRACE_FATAL_HERE;
+ ReportAsanGetAllocatedSizeNotOwned(ptr, &stack);
+ }
+ return allocated_size;
+}
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+// Provide default (no-op) implementation of malloc hooks.
+extern "C" {
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_malloc_hook(void *ptr, uptr size) {
+ (void)ptr;
+ (void)size;
+}
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_free_hook(void *ptr) {
+ (void)ptr;
+}
+} // extern "C"
+#endif
+
+
+#endif // ASAN_ALLOCATOR_VERSION
diff --git a/lib/asan/asan_fake_stack.cc b/lib/asan/asan_fake_stack.cc
new file mode 100644
index 000000000000..7c5a16312d46
--- /dev/null
+++ b/lib/asan/asan_fake_stack.cc
@@ -0,0 +1,182 @@
+//===-- asan_fake_stack.cc ------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// FakeStack is used to detect use-after-return bugs.
+//===----------------------------------------------------------------------===//
+#include "asan_allocator.h"
+#include "asan_thread.h"
+#include "asan_thread_registry.h"
+#include "sanitizer/asan_interface.h"
+
+namespace __asan {
+
+FakeStack::FakeStack() {
+ CHECK(REAL(memset) != 0);
+ REAL(memset)(this, 0, sizeof(*this));
+}
+
+bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) {
+ uptr mem = allocated_size_classes_[size_class];
+ uptr size = ClassMmapSize(size_class);
+ bool res = mem && addr >= mem && addr < mem + size;
+ return res;
+}
+
+uptr FakeStack::AddrIsInFakeStack(uptr addr) {
+ for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
+ if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i];
+ }
+ return 0;
+}
+
+// We may want to compute this during compilation.
+inline uptr FakeStack::ComputeSizeClass(uptr alloc_size) {
+ uptr rounded_size = RoundUpToPowerOfTwo(alloc_size);
+ uptr log = Log2(rounded_size);
+ CHECK(alloc_size <= (1UL << log));
+ if (!(alloc_size > (1UL << (log-1)))) {
+ Printf("alloc_size %zu log %zu\n", alloc_size, log);
+ }
+ CHECK(alloc_size > (1UL << (log-1)));
+ uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog;
+ CHECK(res < kNumberOfSizeClasses);
+ CHECK(ClassSize(res) >= rounded_size);
+ return res;
+}
+
+void FakeFrameFifo::FifoPush(FakeFrame *node) {
+ CHECK(node);
+ node->next = 0;
+ if (first_ == 0 && last_ == 0) {
+ first_ = last_ = node;
+ } else {
+ CHECK(first_);
+ CHECK(last_);
+ last_->next = node;
+ last_ = node;
+ }
+}
+
+FakeFrame *FakeFrameFifo::FifoPop() {
+ CHECK(first_ && last_ && "Exhausted fake stack");
+ FakeFrame *res = 0;
+ if (first_ == last_) {
+ res = first_;
+ first_ = last_ = 0;
+ } else {
+ res = first_;
+ first_ = first_->next;
+ }
+ return res;
+}
+
+void FakeStack::Init(uptr stack_size) {
+ stack_size_ = stack_size;
+ alive_ = true;
+}
+
+void FakeStack::Cleanup() {
+ alive_ = false;
+ for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
+ uptr mem = allocated_size_classes_[i];
+ if (mem) {
+ PoisonShadow(mem, ClassMmapSize(i), 0);
+ allocated_size_classes_[i] = 0;
+ UnmapOrDie((void*)mem, ClassMmapSize(i));
+ }
+ }
+}
+
+uptr FakeStack::ClassMmapSize(uptr size_class) {
+ return RoundUpToPowerOfTwo(stack_size_);
+}
+
+void FakeStack::AllocateOneSizeClass(uptr size_class) {
+ CHECK(ClassMmapSize(size_class) >= GetPageSizeCached());
+ uptr new_mem = (uptr)MmapOrDie(
+ ClassMmapSize(size_class), __FUNCTION__);
+ // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n",
+ // asanThreadRegistry().GetCurrent()->tid(),
+ // size_class, new_mem, new_mem + ClassMmapSize(size_class),
+ // ClassMmapSize(size_class));
+ uptr i;
+ for (i = 0; i < ClassMmapSize(size_class);
+ i += ClassSize(size_class)) {
+ size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i));
+ }
+ CHECK(i == ClassMmapSize(size_class));
+ allocated_size_classes_[size_class] = new_mem;
+}
+
+uptr FakeStack::AllocateStack(uptr size, uptr real_stack) {
+ if (!alive_) return real_stack;
+ CHECK(size <= kMaxStackMallocSize && size > 1);
+ uptr size_class = ComputeSizeClass(size);
+ if (!allocated_size_classes_[size_class]) {
+ AllocateOneSizeClass(size_class);
+ }
+ FakeFrame *fake_frame = size_classes_[size_class].FifoPop();
+ CHECK(fake_frame);
+ fake_frame->size_minus_one = size - 1;
+ fake_frame->real_stack = real_stack;
+ while (FakeFrame *top = call_stack_.top()) {
+ if (top->real_stack > real_stack) break;
+ call_stack_.LifoPop();
+ DeallocateFrame(top);
+ }
+ call_stack_.LifoPush(fake_frame);
+ uptr ptr = (uptr)fake_frame;
+ PoisonShadow(ptr, size, 0);
+ return ptr;
+}
+
+void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
+ CHECK(alive_);
+ uptr size = fake_frame->size_minus_one + 1;
+ uptr size_class = ComputeSizeClass(size);
+ CHECK(allocated_size_classes_[size_class]);
+ uptr ptr = (uptr)fake_frame;
+ CHECK(AddrIsInSizeClass(ptr, size_class));
+ CHECK(AddrIsInSizeClass(ptr + size - 1, size_class));
+ size_classes_[size_class].FifoPush(fake_frame);
+}
+
+void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) {
+ FakeFrame *fake_frame = (FakeFrame*)ptr;
+ CHECK(fake_frame->magic = kRetiredStackFrameMagic);
+ CHECK(fake_frame->descr != 0);
+ CHECK(fake_frame->size_minus_one == size - 1);
+ PoisonShadow(ptr, size, kAsanStackAfterReturnMagic);
+}
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+uptr __asan_stack_malloc(uptr size, uptr real_stack) {
+ if (!flags()->use_fake_stack) return real_stack;
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ if (!t) {
+ // TSD is gone, use the real stack.
+ return real_stack;
+ }
+ uptr ptr = t->fake_stack().AllocateStack(size, real_stack);
+ // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack);
+ return ptr;
+}
+
+void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) {
+ if (!flags()->use_fake_stack) return;
+ if (ptr != real_stack) {
+ FakeStack::OnFree(ptr, size, real_stack);
+ }
+}
diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h
index ca9cf84ba6c5..d7b21ea4a45f 100644
--- a/lib/asan/asan_flags.h
+++ b/lib/asan/asan_flags.h
@@ -15,7 +15,7 @@
#ifndef ASAN_FLAGS_H
#define ASAN_FLAGS_H
-#include "sanitizer_common/sanitizer_interface_defs.h"
+#include "sanitizer/common_interface_defs.h"
// ASan flag values can be defined in three ways:
// 1) initialized with default values at startup.
@@ -23,11 +23,6 @@
// __asan_default_options().
// 3) overriden from env variable ASAN_OPTIONS.
-extern "C" {
-// Can be overriden by user.
-const char *__asan_default_options() SANITIZER_WEAK_ATTRIBUTE;
-} // extern "C"
-
namespace __asan {
struct Flags {
@@ -48,7 +43,9 @@ struct Flags {
// on globals, 1 - detect buffer overflow, 2 - print data about registered
// globals).
int report_globals;
- // Max number of stack frames kept for each allocation.
+ // If set, attempts to catch initialization order issues.
+ bool check_initialization_order;
+ // Max number of stack frames kept for each allocation/deallocation.
int malloc_context_size;
// If set, uses custom wrappers and replacements for libc string functions
// to find more errors.
@@ -87,6 +84,28 @@ struct Flags {
// By default, disable core dumper on 64-bit - it makes little sense
// to dump 16T+ core.
bool disable_core;
+ // Allow the tool to re-exec the program. This may interfere badly with the
+ // debugger.
+ bool allow_reexec;
+ // Strips this prefix from file paths in error reports.
+ const char *strip_path_prefix;
+ // If set, prints not only thread creation stacks for threads in error report,
+ // but also thread creation stacks for threads that created those threads,
+ // etc. up to main thread.
+ bool print_full_thread_history;
+ // ASan will write logs to "log_path.pid" instead of stderr.
+ const char *log_path;
+ // Use fast (frame-pointer-based) unwinder on fatal errors (if available).
+ bool fast_unwind_on_fatal;
+ // Use fast (frame-pointer-based) unwinder on malloc/free (if available).
+ bool fast_unwind_on_malloc;
+ // Poison (or not) the heap memory on [de]allocation. Zero value is useful
+ // for benchmarking the allocator or instrumentator.
+ bool poison_heap;
+ // Report errors on malloc/delete, new/free, new/delete[], etc.
+ bool alloc_dealloc_mismatch;
+ // Use stack depot instead of storing stacks in the redzones.
+ bool use_stack_depot;
};
Flags *flags();
diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc
index f8c4040b8e86..4e18bb8e2355 100644
--- a/lib/asan/asan_globals.cc
+++ b/lib/asan/asan_globals.cc
@@ -12,15 +12,14 @@
// Handle globals.
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
-#include "asan_interface.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_mapping.h"
+#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"
-
-#include <ctype.h>
+#include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_mutex.h"
namespace __asan {
@@ -31,9 +30,10 @@ struct ListOfGlobals {
ListOfGlobals *next;
};
-static AsanLock mu_for_globals(LINKER_INITIALIZED);
-static ListOfGlobals *list_of_globals;
-static LowLevelAllocator allocator_for_globals(LINKER_INITIALIZED);
+static BlockingMutex mu_for_globals(LINKER_INITIALIZED);
+static LowLevelAllocator allocator_for_globals;
+static ListOfGlobals *list_of_all_globals;
+static ListOfGlobals *list_of_dynamic_init_globals;
void PoisonRedZones(const Global &g) {
uptr shadow_rz_size = kGlobalAndStackRedzone >> SHADOW_SCALE;
@@ -55,48 +55,16 @@ void PoisonRedZones(const Global &g) {
}
}
-static uptr GetAlignedSize(uptr size) {
- return ((size + kGlobalAndStackRedzone - 1) / kGlobalAndStackRedzone)
- * kGlobalAndStackRedzone;
-}
-
- // Check if the global is a zero-terminated ASCII string. If so, print it.
-void PrintIfASCII(const Global &g) {
- for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
- if (!isascii(*(char*)p)) return;
- }
- if (*(char*)(g.beg + g.size - 1) != 0) return;
- AsanPrintf(" '%s' is ascii string '%s'\n", g.name, (char*)g.beg);
-}
-
-bool DescribeAddrIfMyRedZone(const Global &g, uptr addr) {
- if (addr < g.beg - kGlobalAndStackRedzone) return false;
- if (addr >= g.beg + g.size_with_redzone) return false;
- AsanPrintf("%p is located ", (void*)addr);
- if (addr < g.beg) {
- AsanPrintf("%zd bytes to the left", g.beg - addr);
- } else if (addr >= g.beg + g.size) {
- AsanPrintf("%zd bytes to the right", addr - (g.beg + g.size));
- } else {
- AsanPrintf("%zd bytes inside", addr - g.beg); // Can it happen?
- }
- AsanPrintf(" of global variable '%s' (0x%zx) of size %zu\n",
- g.name, g.beg, g.size);
- PrintIfASCII(g);
- return true;
-}
-
-
-bool DescribeAddrIfGlobal(uptr addr) {
+bool DescribeAddressIfGlobal(uptr addr) {
if (!flags()->report_globals) return false;
- ScopedLock lock(&mu_for_globals);
+ BlockingMutexLock lock(&mu_for_globals);
bool res = false;
- for (ListOfGlobals *l = list_of_globals; l; l = l->next) {
+ for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
const Global &g = *l->g;
if (flags()->report_globals >= 2)
- AsanPrintf("Search Global: beg=%p size=%zu name=%s\n",
- (void*)g.beg, g.size, (char*)g.name);
- res |= DescribeAddrIfMyRedZone(g, addr);
+ Report("Search Global: beg=%p size=%zu name=%s\n",
+ (void*)g.beg, g.size, (char*)g.name);
+ res |= DescribeAddressRelativeToGlobal(addr, g);
}
return res;
}
@@ -106,6 +74,10 @@ bool DescribeAddrIfGlobal(uptr addr) {
// so we store the globals in a map.
static void RegisterGlobal(const Global *g) {
CHECK(asan_inited);
+ if (flags()->report_globals >= 2)
+ Report("Added Global: beg=%p size=%zu/%zu name=%s dyn.init=%zu\n",
+ (void*)g->beg, g->size, g->size_with_redzone, g->name,
+ g->has_dynamic_init);
CHECK(flags()->report_globals);
CHECK(AddrIsInMem(g->beg));
CHECK(AddrIsAlignedByGranularity(g->beg));
@@ -114,11 +86,14 @@ static void RegisterGlobal(const Global *g) {
ListOfGlobals *l =
(ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals));
l->g = g;
- l->next = list_of_globals;
- list_of_globals = l;
- if (flags()->report_globals >= 2)
- Report("Added Global: beg=%p size=%zu name=%s\n",
- (void*)g->beg, g->size, g->name);
+ l->next = list_of_all_globals;
+ list_of_all_globals = l;
+ if (g->has_dynamic_init) {
+ l = (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals));
+ l->g = g;
+ l->next = list_of_dynamic_init_globals;
+ list_of_dynamic_init_globals = l;
+ }
}
static void UnregisterGlobal(const Global *g) {
@@ -133,39 +108,83 @@ static void UnregisterGlobal(const Global *g) {
// implementation. It might not be worth doing anyway.
}
+// Poison all shadow memory for a single global.
+static void PoisonGlobalAndRedzones(const Global *g) {
+ CHECK(asan_inited);
+ CHECK(flags()->check_initialization_order);
+ CHECK(AddrIsInMem(g->beg));
+ CHECK(AddrIsAlignedByGranularity(g->beg));
+ CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
+ if (flags()->report_globals >= 3)
+ Printf("DynInitPoison : %s\n", g->name);
+ PoisonShadow(g->beg, g->size_with_redzone, kAsanInitializationOrderMagic);
+}
+
+static void UnpoisonGlobal(const Global *g) {
+ CHECK(asan_inited);
+ CHECK(flags()->check_initialization_order);
+ CHECK(AddrIsInMem(g->beg));
+ CHECK(AddrIsAlignedByGranularity(g->beg));
+ CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
+ if (flags()->report_globals >= 3)
+ Printf("DynInitUnpoison: %s\n", g->name);
+ PoisonShadow(g->beg, g->size_with_redzone, 0);
+ PoisonRedZones(*g);
+}
+
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
-// Register one global with a default redzone.
-void __asan_register_global(uptr addr, uptr size,
- const char *name) {
- if (!flags()->report_globals) return;
- ScopedLock lock(&mu_for_globals);
- Global *g = (Global *)allocator_for_globals.Allocate(sizeof(Global));
- g->beg = addr;
- g->size = size;
- g->size_with_redzone = GetAlignedSize(size) + kGlobalAndStackRedzone;
- g->name = name;
- RegisterGlobal(g);
-}
-
// Register an array of globals.
void __asan_register_globals(__asan_global *globals, uptr n) {
if (!flags()->report_globals) return;
- ScopedLock lock(&mu_for_globals);
+ BlockingMutexLock lock(&mu_for_globals);
for (uptr i = 0; i < n; i++) {
RegisterGlobal(&globals[i]);
}
}
// Unregister an array of globals.
-// We must do it when a shared objects gets dlclosed.
+// We must do this when a shared objects gets dlclosed.
void __asan_unregister_globals(__asan_global *globals, uptr n) {
if (!flags()->report_globals) return;
- ScopedLock lock(&mu_for_globals);
+ BlockingMutexLock lock(&mu_for_globals);
for (uptr i = 0; i < n; i++) {
UnregisterGlobal(&globals[i]);
}
}
+
+// This method runs immediately prior to dynamic initialization in each TU,
+// when all dynamically initialized globals are unpoisoned. This method
+// poisons all global variables not defined in this TU, so that a dynamic
+// initializer can only touch global variables in the same TU.
+void __asan_before_dynamic_init(uptr first_addr, uptr last_addr) {
+ if (!flags()->check_initialization_order) return;
+ CHECK(list_of_dynamic_init_globals);
+ BlockingMutexLock lock(&mu_for_globals);
+ bool from_current_tu = false;
+ // The list looks like:
+ // a => ... => b => last_addr => ... => first_addr => c => ...
+ // The globals of the current TU reside between last_addr and first_addr.
+ for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next) {
+ if (l->g->beg == last_addr)
+ from_current_tu = true;
+ if (!from_current_tu)
+ PoisonGlobalAndRedzones(l->g);
+ if (l->g->beg == first_addr)
+ from_current_tu = false;
+ }
+ CHECK(!from_current_tu);
+}
+
+// This method runs immediately after dynamic initialization in each TU, when
+// all dynamically initialized globals except for those defined in the current
+// TU are poisoned. It simply unpoisons all dynamically initialized globals.
+void __asan_after_dynamic_init() {
+ if (!flags()->check_initialization_order) return;
+ BlockingMutexLock lock(&mu_for_globals);
+ for (ListOfGlobals *l = list_of_dynamic_init_globals; l; l = l->next)
+ UnpoisonGlobal(l->g);
+}
diff --git a/lib/asan/asan_intercepted_functions.h b/lib/asan/asan_intercepted_functions.h
new file mode 100644
index 000000000000..a1faf713c130
--- /dev/null
+++ b/lib/asan/asan_intercepted_functions.h
@@ -0,0 +1,260 @@
+//===-- asan_intercepted_functions.h ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan-private header containing prototypes for wrapper functions and wrappers
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_INTERCEPTED_FUNCTIONS_H
+#define ASAN_INTERCEPTED_FUNCTIONS_H
+
+#include "asan_internal.h"
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_platform_interceptors.h"
+
+#include <stdarg.h>
+
+using __sanitizer::uptr;
+
+// Use macro to describe if specific function should be
+// intercepted on a given platform.
+#if !defined(_WIN32)
+# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1
+# define ASAN_INTERCEPT__LONGJMP 1
+# define ASAN_INTERCEPT_STRDUP 1
+# define ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP 1
+# define ASAN_INTERCEPT_INDEX 1
+# define ASAN_INTERCEPT_PTHREAD_CREATE 1
+# define ASAN_INTERCEPT_MLOCKX 1
+#else
+# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0
+# define ASAN_INTERCEPT__LONGJMP 0
+# define ASAN_INTERCEPT_STRDUP 0
+# define ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP 0
+# define ASAN_INTERCEPT_INDEX 0
+# define ASAN_INTERCEPT_PTHREAD_CREATE 0
+# define ASAN_INTERCEPT_MLOCKX 0
+#endif
+
+#if defined(__linux__)
+# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 1
+#else
+# define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0
+#endif
+
+#if !defined(__APPLE__)
+# define ASAN_INTERCEPT_STRNLEN 1
+#else
+# define ASAN_INTERCEPT_STRNLEN 0
+#endif
+
+#if defined(__linux__) && !defined(ANDROID)
+# define ASAN_INTERCEPT_SWAPCONTEXT 1
+#else
+# define ASAN_INTERCEPT_SWAPCONTEXT 0
+#endif
+
+#if !defined(ANDROID) && !defined(_WIN32)
+# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1
+#else
+# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0
+#endif
+
+// On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it
+// there.
+#if !defined(_WIN32) && (!defined(__APPLE__) || MAC_INTERPOSE_FUNCTIONS)
+# define ASAN_INTERCEPT_SIGLONGJMP 1
+#else
+# define ASAN_INTERCEPT_SIGLONGJMP 0
+#endif
+
+#if ASAN_HAS_EXCEPTIONS && !defined(_WIN32)
+# define ASAN_INTERCEPT___CXA_THROW 1
+#else
+# define ASAN_INTERCEPT___CXA_THROW 0
+#endif
+
+#define DECLARE_FUNCTION_AND_WRAPPER(ret_type, func, ...) \
+ ret_type func(__VA_ARGS__); \
+ ret_type WRAP(func)(__VA_ARGS__)
+
+// Use extern declarations of intercepted functions on Mac and Windows
+// to avoid including system headers.
+#if defined(__APPLE__) || (defined(_WIN32) && !defined(_DLL))
+extern "C" {
+// signal.h
+# if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
+struct sigaction;
+DECLARE_FUNCTION_AND_WRAPPER(int, sigaction, int sig,
+ const struct sigaction *act,
+ struct sigaction *oldact);
+DECLARE_FUNCTION_AND_WRAPPER(void*, signal, int signum, void *handler);
+# endif
+
+// setjmp.h
+DECLARE_FUNCTION_AND_WRAPPER(void, longjmp, void *env, int value);
+# if ASAN_INTERCEPT__LONGJMP
+DECLARE_FUNCTION_AND_WRAPPER(void, _longjmp, void *env, int value);
+# endif
+# if ASAN_INTERCEPT_SIGLONGJMP
+DECLARE_FUNCTION_AND_WRAPPER(void, siglongjmp, void *env, int value);
+# endif
+# if ASAN_INTERCEPT___CXA_THROW
+DECLARE_FUNCTION_AND_WRAPPER(void, __cxa_throw, void *a, void *b, void *c);
+#endif
+
+// string.h / strings.h
+DECLARE_FUNCTION_AND_WRAPPER(int, memcmp,
+ const void *a1, const void *a2, uptr size);
+DECLARE_FUNCTION_AND_WRAPPER(void*, memmove,
+ void *to, const void *from, uptr size);
+DECLARE_FUNCTION_AND_WRAPPER(void*, memcpy,
+ void *to, const void *from, uptr size);
+DECLARE_FUNCTION_AND_WRAPPER(void*, memset, void *block, int c, uptr size);
+DECLARE_FUNCTION_AND_WRAPPER(char*, strchr, const char *str, int c);
+DECLARE_FUNCTION_AND_WRAPPER(char*, strcat, /* NOLINT */
+ char *to, const char* from);
+DECLARE_FUNCTION_AND_WRAPPER(char*, strncat,
+ char *to, const char* from, uptr size);
+DECLARE_FUNCTION_AND_WRAPPER(char*, strcpy, /* NOLINT */
+ char *to, const char* from);
+DECLARE_FUNCTION_AND_WRAPPER(char*, strncpy,
+ char *to, const char* from, uptr size);
+DECLARE_FUNCTION_AND_WRAPPER(int, strcmp, const char *s1, const char* s2);
+DECLARE_FUNCTION_AND_WRAPPER(int, strncmp,
+ const char *s1, const char* s2, uptr size);
+DECLARE_FUNCTION_AND_WRAPPER(uptr, strlen, const char *s);
+# if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP
+DECLARE_FUNCTION_AND_WRAPPER(int, strcasecmp, const char *s1, const char *s2);
+DECLARE_FUNCTION_AND_WRAPPER(int, strncasecmp,
+ const char *s1, const char *s2, uptr n);
+# endif
+# if ASAN_INTERCEPT_STRDUP
+DECLARE_FUNCTION_AND_WRAPPER(char*, strdup, const char *s);
+# endif
+# if ASAN_INTERCEPT_STRNLEN
+DECLARE_FUNCTION_AND_WRAPPER(uptr, strnlen, const char *s, uptr maxlen);
+# endif
+#if ASAN_INTERCEPT_INDEX
+DECLARE_FUNCTION_AND_WRAPPER(char*, index, const char *string, int c);
+#endif
+
+// stdlib.h
+DECLARE_FUNCTION_AND_WRAPPER(int, atoi, const char *nptr);
+DECLARE_FUNCTION_AND_WRAPPER(long, atol, const char *nptr); // NOLINT
+DECLARE_FUNCTION_AND_WRAPPER(long, strtol, const char *nptr, char **endptr, int base); // NOLINT
+# if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
+DECLARE_FUNCTION_AND_WRAPPER(long long, atoll, const char *nptr); // NOLINT
+DECLARE_FUNCTION_AND_WRAPPER(long long, strtoll, const char *nptr, char **endptr, int base); // NOLINT
+# endif
+
+// unistd.h
+# if SANITIZER_INTERCEPT_READ
+DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, read, int fd, void *buf, SIZE_T count);
+# endif
+# if SANITIZER_INTERCEPT_PREAD
+DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread, int fd, void *buf,
+ SIZE_T count, OFF_T offset);
+# endif
+# if SANITIZER_INTERCEPT_PREAD64
+DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pread64, int fd, void *buf,
+ SIZE_T count, OFF64_T offset);
+# endif
+
+#if SANITIZER_INTERCEPT_WRITE
+DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, write, int fd, void *ptr, SIZE_T count);
+#endif
+#if SANITIZER_INTERCEPT_PWRITE
+DECLARE_FUNCTION_AND_WRAPPER(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count);
+#endif
+
+# if ASAN_INTERCEPT_MLOCKX
+// mlock/munlock
+DECLARE_FUNCTION_AND_WRAPPER(int, mlock, const void *addr, SIZE_T len);
+DECLARE_FUNCTION_AND_WRAPPER(int, munlock, const void *addr, SIZE_T len);
+DECLARE_FUNCTION_AND_WRAPPER(int, mlockall, int flags);
+DECLARE_FUNCTION_AND_WRAPPER(int, munlockall, void);
+# endif
+
+// Windows threads.
+# if defined(_WIN32)
+__declspec(dllimport)
+void* __stdcall CreateThread(void *sec, uptr st, void* start,
+ void *arg, DWORD fl, DWORD *id);
+# endif
+// Posix threads.
+# if ASAN_INTERCEPT_PTHREAD_CREATE
+DECLARE_FUNCTION_AND_WRAPPER(int, pthread_create,
+ void *thread, void *attr,
+ void *(*start_routine)(void*), void *arg);
+# endif
+
+#if defined(__APPLE__)
+typedef void* pthread_workqueue_t;
+typedef void* pthread_workitem_handle_t;
+
+typedef void* dispatch_group_t;
+typedef void* dispatch_queue_t;
+typedef void* dispatch_source_t;
+typedef u64 dispatch_time_t;
+typedef void (*dispatch_function_t)(void *block);
+typedef void* (*worker_t)(void *block);
+typedef void* CFStringRef;
+typedef void* CFAllocatorRef;
+
+DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async_f,
+ dispatch_queue_t dq,
+ void *ctxt, dispatch_function_t func);
+DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_sync_f,
+ dispatch_queue_t dq,
+ void *ctxt, dispatch_function_t func);
+DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after_f,
+ dispatch_time_t when, dispatch_queue_t dq,
+ void *ctxt, dispatch_function_t func);
+DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_barrier_async_f,
+ dispatch_queue_t dq,
+ void *ctxt, dispatch_function_t func);
+DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async_f,
+ dispatch_group_t group, dispatch_queue_t dq,
+ void *ctxt, dispatch_function_t func);
+
+DECLARE_FUNCTION_AND_WRAPPER(void, __CFInitialize, void);
+DECLARE_FUNCTION_AND_WRAPPER(CFStringRef, CFStringCreateCopy,
+ CFAllocatorRef alloc, CFStringRef str);
+DECLARE_FUNCTION_AND_WRAPPER(void, free, void* ptr);
+
+DECLARE_FUNCTION_AND_WRAPPER(int, vscanf, const char *format, va_list ap);
+DECLARE_FUNCTION_AND_WRAPPER(int, vsscanf, const char *str, const char *format,
+ va_list ap);
+DECLARE_FUNCTION_AND_WRAPPER(int, vfscanf, void *stream, const char *format,
+ va_list ap);
+DECLARE_FUNCTION_AND_WRAPPER(int, scanf, const char *format, ...);
+DECLARE_FUNCTION_AND_WRAPPER(int, fscanf,
+ void* stream, const char *format, ...);
+DECLARE_FUNCTION_AND_WRAPPER(int, sscanf, // NOLINT
+ const char *str, const char *format, ...);
+
+#if MAC_INTERPOSE_FUNCTIONS && !defined(MISSING_BLOCKS_SUPPORT)
+DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_group_async,
+ dispatch_group_t dg,
+ dispatch_queue_t dq, void (^work)(void));
+DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_async,
+ dispatch_queue_t dq, void (^work)(void));
+DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_after,
+ dispatch_queue_t dq, void (^work)(void));
+DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_event_handler,
+ dispatch_source_t ds, void (^work)(void));
+DECLARE_FUNCTION_AND_WRAPPER(void, dispatch_source_set_cancel_handler,
+ dispatch_source_t ds, void (^work)(void));
+#endif // MAC_INTERPOSE_FUNCTIONS
+#endif // __APPLE__
+} // extern "C"
+#endif
+
+#endif // ASAN_INTERCEPTED_FUNCTIONS_H
diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc
index 2ce5826e1495..6170974d6f5e 100644
--- a/lib/asan/asan_interceptors.cc
+++ b/lib/asan/asan_interceptors.cc
@@ -14,136 +14,33 @@
#include "asan_interceptors.h"
#include "asan_allocator.h"
-#include "asan_interface.h"
+#include "asan_intercepted_functions.h"
#include "asan_internal.h"
#include "asan_mapping.h"
+#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread_registry.h"
#include "interception/interception.h"
+#include "sanitizer/asan_interface.h"
#include "sanitizer_common/sanitizer_libc.h"
-// Use macro to describe if specific function should be
-// intercepted on a given platform.
-#if !defined(_WIN32)
-# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 1
-#else
-# define ASAN_INTERCEPT_ATOLL_AND_STRTOLL 0
-#endif
-
-#if !defined(__APPLE__)
-# define ASAN_INTERCEPT_STRNLEN 1
-#else
-# define ASAN_INTERCEPT_STRNLEN 0
-#endif
-
-#if defined(ANDROID) || defined(_WIN32)
-# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0
-#else
-# define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1
-#endif
-
-// Use extern declarations of intercepted functions on Mac and Windows
-// to avoid including system headers.
-#if defined(__APPLE__) || (defined(_WIN32) && !defined(_DLL))
-extern "C" {
-// signal.h
-# if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
-struct sigaction;
-int sigaction(int sig, const struct sigaction *act,
- struct sigaction *oldact);
-void *signal(int signum, void *handler);
-# endif
-
-// setjmp.h
-void longjmp(void* env, int value);
-# if !defined(_WIN32)
-void _longjmp(void *env, int value);
-# endif
-
-// string.h / strings.h
-int memcmp(const void *a1, const void *a2, uptr size);
-void* memmove(void *to, const void *from, uptr size);
-void* memcpy(void *to, const void *from, uptr size);
-void* memset(void *block, int c, uptr size);
-char* strchr(const char *str, int c);
-# if defined(__APPLE__)
-char* index(const char *string, int c);
-# endif
-char* strcat(char *to, const char* from); // NOLINT
-char *strncat(char *to, const char* from, uptr size);
-char* strcpy(char *to, const char* from); // NOLINT
-char* strncpy(char *to, const char* from, uptr size);
-int strcmp(const char *s1, const char* s2);
-int strncmp(const char *s1, const char* s2, uptr size);
-# if !defined(_WIN32)
-int strcasecmp(const char *s1, const char *s2);
-int strncasecmp(const char *s1, const char *s2, uptr n);
-char* strdup(const char *s);
-# endif
-uptr strlen(const char *s);
-# if ASAN_INTERCEPT_STRNLEN
-uptr strnlen(const char *s, uptr maxlen);
-# endif
-
-// stdlib.h
-int atoi(const char *nptr);
-long atol(const char *nptr); // NOLINT
-long strtol(const char *nptr, char **endptr, int base); // NOLINT
-# if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
-long long atoll(const char *nptr); // NOLINT
-long long strtoll(const char *nptr, char **endptr, int base); // NOLINT
-# endif
-
-// Windows threads.
-# if defined(_WIN32)
-__declspec(dllimport)
-void* __stdcall CreateThread(void *sec, uptr st, void* start,
- void *arg, DWORD fl, DWORD *id);
-# endif
-
-// Posix threads.
-# if !defined(_WIN32)
-int pthread_create(void *thread, void *attr, void *(*start_routine)(void*),
- void *arg);
-# endif
-} // extern "C"
-#endif
-
namespace __asan {
-// Instruments read/write access to a single byte in memory.
-// On error calls __asan_report_error, which aborts the program.
-#define ACCESS_ADDRESS(address, isWrite) do { \
- if (!AddrIsInMem(address) || AddressIsPoisoned(address)) { \
- GET_CURRENT_PC_BP_SP; \
- __asan_report_error(pc, bp, sp, address, isWrite, /* access_size */ 1); \
- } \
-} while (0)
-
// We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE,
// and ASAN_WRITE_RANGE as macro instead of function so
// that no extra frames are created, and stack trace contains
// relevant information only.
-
-// Instruments read/write access to a memory range.
-// More complex implementation is possible, for now just
-// checking the first and the last byte of a range.
-#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \
- if (size > 0) { \
- uptr ptr = (uptr)(offset); \
- ACCESS_ADDRESS(ptr, isWrite); \
- ACCESS_ADDRESS(ptr + (size) - 1, isWrite); \
- } \
+// We check all shadow bytes.
+#define ACCESS_MEMORY_RANGE(offset, size, isWrite) do { \
+ if (uptr __ptr = __asan_region_is_poisoned((uptr)(offset), size)) { \
+ GET_CURRENT_PC_BP_SP; \
+ __asan_report_error(pc, bp, sp, __ptr, isWrite, /* access_size */1); \
+ } \
} while (0)
-#define ASAN_READ_RANGE(offset, size) do { \
- ACCESS_MEMORY_RANGE(offset, size, false); \
-} while (0)
-
-#define ASAN_WRITE_RANGE(offset, size) do { \
- ACCESS_MEMORY_RANGE(offset, size, true); \
-} while (0)
+#define ASAN_READ_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, false)
+#define ASAN_WRITE_RANGE(offset, size) ACCESS_MEMORY_RANGE(offset, size, true);
// Behavior of functions like "memcpy" or "strcpy" is undefined
// if memory intervals overlap. We report error in this case.
@@ -156,11 +53,9 @@ static inline bool RangesOverlap(const char *offset1, uptr length1,
const char *offset1 = (const char*)_offset1; \
const char *offset2 = (const char*)_offset2; \
if (RangesOverlap(offset1, length1, offset2, length2)) { \
- AsanReport("ERROR: AddressSanitizer %s-param-overlap: " \
- "memory ranges [%p,%p) and [%p, %p) overlap\n", \
- name, offset1, offset1 + length1, offset2, offset2 + length2); \
- PRINT_CURRENT_STACK(); \
- ShowStatsAndAbort(); \
+ GET_STACK_TRACE_FATAL_HERE; \
+ ReportStringFunctionMemoryRangesOverlap(name, offset1, length1, \
+ offset2, length2, &stack); \
} \
} while (0)
@@ -180,27 +75,47 @@ static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
return internal_strnlen(s, maxlen);
}
+void SetThreadName(const char *name) {
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ if (t)
+ t->summary()->set_name(name);
+}
+
} // namespace __asan
// ---------------------- Wrappers ---------------- {{{1
using namespace __asan; // NOLINT
+#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
+ ASAN_WRITE_RANGE(ptr, size)
+#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) ASAN_READ_RANGE(ptr, size)
+#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
+ do { \
+ ctx = 0; \
+ (void)ctx; \
+ ENSURE_ASAN_INITED(); \
+ } while (false)
+#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) do { } while (false)
+#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) do { } while (false)
+#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name)
+#include "sanitizer_common/sanitizer_common_interceptors.inc"
+
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
AsanThread *t = (AsanThread*)arg;
asanThreadRegistry().SetCurrent(t);
return t->ThreadStart();
}
-#ifndef _WIN32
+#if ASAN_INTERCEPT_PTHREAD_CREATE
INTERCEPTOR(int, pthread_create, void *thread,
void *attr, void *(*start_routine)(void*), void *arg) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
+ GET_STACK_TRACE_THREAD;
u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack);
asanThreadRegistry().RegisterThread(t);
return REAL(pthread_create)(thread, attr, asan_thread_start, t);
}
-#endif // !_WIN32
+#endif // ASAN_INTERCEPT_PTHREAD_CREATE
#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
INTERCEPTOR(void*, signal, int signum, void *handler) {
@@ -223,28 +138,62 @@ DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act,
struct sigaction *oldact);
#endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
+#if ASAN_INTERCEPT_SWAPCONTEXT
+static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) {
+ // Align to page size.
+ uptr PageSize = GetPageSizeCached();
+ uptr bottom = stack & ~(PageSize - 1);
+ ssize += stack - bottom;
+ ssize = RoundUpTo(ssize, PageSize);
+ static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb
+ if (ssize && ssize <= kMaxSaneContextStackSize) {
+ PoisonShadow(bottom, ssize, 0);
+ }
+}
+
+INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp,
+ struct ucontext_t *ucp) {
+ static bool reported_warning = false;
+ if (!reported_warning) {
+ Report("WARNING: ASan doesn't fully support makecontext/swapcontext "
+ "functions and may produce false positives in some cases!\n");
+ reported_warning = true;
+ }
+ // Clear shadow memory for new context (it may share stack
+ // with current context).
+ uptr stack, ssize;
+ ReadContextStack(ucp, &stack, &ssize);
+ ClearShadowMemoryForContextStack(stack, ssize);
+ int res = REAL(swapcontext)(oucp, ucp);
+ // swapcontext technically does not return, but program may swap context to
+ // "oucp" later, that would look as if swapcontext() returned 0.
+ // We need to clear shadow for ucp once again, as it may be in arbitrary
+ // state.
+ ClearShadowMemoryForContextStack(stack, ssize);
+ return res;
+}
+#endif // ASAN_INTERCEPT_SWAPCONTEXT
+
INTERCEPTOR(void, longjmp, void *env, int val) {
__asan_handle_no_return();
REAL(longjmp)(env, val);
}
-#if !defined(_WIN32)
+#if ASAN_INTERCEPT__LONGJMP
INTERCEPTOR(void, _longjmp, void *env, int val) {
__asan_handle_no_return();
REAL(_longjmp)(env, val);
}
+#endif
+#if ASAN_INTERCEPT_SIGLONGJMP
INTERCEPTOR(void, siglongjmp, void *env, int val) {
__asan_handle_no_return();
REAL(siglongjmp)(env, val);
}
#endif
-#if ASAN_HAS_EXCEPTIONS == 1
-#ifdef __APPLE__
-extern "C" void __cxa_throw(void *a, void *b, void *c);
-#endif // __APPLE__
-
+#if ASAN_INTERCEPT___CXA_THROW
INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
CHECK(REAL(__cxa_throw));
__asan_handle_no_return();
@@ -263,26 +212,22 @@ static void MlockIsUnsupported() {
}
extern "C" {
-INTERCEPTOR_ATTRIBUTE
-int mlock(const void *addr, uptr len) {
+INTERCEPTOR(int, mlock, const void *addr, uptr len) {
MlockIsUnsupported();
return 0;
}
-INTERCEPTOR_ATTRIBUTE
-int munlock(const void *addr, uptr len) {
+INTERCEPTOR(int, munlock, const void *addr, uptr len) {
MlockIsUnsupported();
return 0;
}
-INTERCEPTOR_ATTRIBUTE
-int mlockall(int flags) {
+INTERCEPTOR(int, mlockall, int flags) {
MlockIsUnsupported();
return 0;
}
-INTERCEPTOR_ATTRIBUTE
-int munlockall(void) {
+INTERCEPTOR(int, munlockall, void) {
MlockIsUnsupported();
return 0;
}
@@ -299,6 +244,7 @@ static inline int CharCaseCmp(unsigned char c1, unsigned char c2) {
}
INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
+ if (!asan_inited) return internal_memcmp(a1, a2, size);
ENSURE_ASAN_INITED();
unsigned char c1 = 0, c2 = 0;
const unsigned char *s1 = (const unsigned char*)a1;
@@ -315,6 +261,7 @@ INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
}
INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
+ if (!asan_inited) return internal_memcpy(to, from, size);
// memcpy is called during __asan_init() from the internals
// of printf(...).
if (asan_init_is_running) {
@@ -327,25 +274,39 @@ INTERCEPTOR(void*, memcpy, void *to, const void *from, uptr size) {
// See http://llvm.org/bugs/show_bug.cgi?id=11763.
CHECK_RANGES_OVERLAP("memcpy", to, size, from, size);
}
- ASAN_WRITE_RANGE(from, size);
- ASAN_READ_RANGE(to, size);
+ ASAN_READ_RANGE(from, size);
+ ASAN_WRITE_RANGE(to, size);
}
+#if MAC_INTERPOSE_FUNCTIONS
+ // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8.
+ // See also http://code.google.com/p/address-sanitizer/issues/detail?id=116.
+ return internal_memcpy(to, from, size);
+#else
return REAL(memcpy)(to, from, size);
+#endif
}
INTERCEPTOR(void*, memmove, void *to, const void *from, uptr size) {
+ if (!asan_inited) return internal_memmove(to, from, size);
if (asan_init_is_running) {
return REAL(memmove)(to, from, size);
}
ENSURE_ASAN_INITED();
if (flags()->replace_intrin) {
- ASAN_WRITE_RANGE(from, size);
- ASAN_READ_RANGE(to, size);
+ ASAN_READ_RANGE(from, size);
+ ASAN_WRITE_RANGE(to, size);
}
+#if MAC_INTERPOSE_FUNCTIONS
+ // Interposing of resolver functions is broken on Mac OS 10.7 and 10.8.
+ // See also http://code.google.com/p/address-sanitizer/issues/detail?id=116.
+ return internal_memmove(to, from, size);
+#else
return REAL(memmove)(to, from, size);
+#endif
}
INTERCEPTOR(void*, memset, void *block, int c, uptr size) {
+ if (!asan_inited) return internal_memset(block, c, size);
// memset is called inside Printf.
if (asan_init_is_running) {
return REAL(memset)(block, c, size);
@@ -358,6 +319,12 @@ INTERCEPTOR(void*, memset, void *block, int c, uptr size) {
}
INTERCEPTOR(char*, strchr, const char *str, int c) {
+ if (!asan_inited) return internal_strchr(str, c);
+ // strchr is called inside create_purgeable_zone() when MallocGuardEdges=1 is
+ // used.
+ if (asan_init_is_running) {
+ return REAL(strchr)(str, c);
+ }
ENSURE_ASAN_INITED();
char *result = REAL(strchr)(str, c);
if (flags()->replace_str) {
@@ -367,37 +334,31 @@ INTERCEPTOR(char*, strchr, const char *str, int c) {
return result;
}
-#ifdef __linux__
+#if ASAN_INTERCEPT_INDEX
+# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
INTERCEPTOR(char*, index, const char *string, int c)
ALIAS(WRAPPER_NAME(strchr));
-#else
+# else
DEFINE_REAL(char*, index, const char *string, int c)
-#endif
-
-INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) {
- ENSURE_ASAN_INITED();
- unsigned char c1, c2;
- uptr i;
- for (i = 0; ; i++) {
- c1 = (unsigned char)s1[i];
- c2 = (unsigned char)s2[i];
- if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
- }
- ASAN_READ_RANGE(s1, i + 1);
- ASAN_READ_RANGE(s2, i + 1);
- return CharCaseCmp(c1, c2);
-}
+# endif
+#endif // ASAN_INTERCEPT_INDEX
+// For both strcat() and strncat() we need to check the validity of |to|
+// argument irrespective of the |from| length.
INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT
ENSURE_ASAN_INITED();
if (flags()->replace_str) {
uptr from_length = REAL(strlen)(from);
ASAN_READ_RANGE(from, from_length + 1);
+ uptr to_length = REAL(strlen)(to);
+ ASAN_READ_RANGE(to, to_length);
+ ASAN_WRITE_RANGE(to + to_length, from_length + 1);
+ // If the copying actually happens, the |from| string should not overlap
+ // with the resulting string starting at |to|, which has a length of
+ // to_length + from_length + 1.
if (from_length > 0) {
- uptr to_length = REAL(strlen)(to);
- ASAN_READ_RANGE(to, to_length);
- ASAN_WRITE_RANGE(to + to_length, from_length + 1);
- CHECK_RANGES_OVERLAP("strcat", to, to_length + 1, from, from_length + 1);
+ CHECK_RANGES_OVERLAP("strcat", to, from_length + to_length + 1,
+ from, from_length + 1);
}
}
return REAL(strcat)(to, from); // NOLINT
@@ -405,23 +366,25 @@ INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT
INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) {
ENSURE_ASAN_INITED();
- if (flags()->replace_str && size > 0) {
+ if (flags()->replace_str) {
uptr from_length = MaybeRealStrnlen(from, size);
- ASAN_READ_RANGE(from, Min(size, from_length + 1));
+ uptr copy_length = Min(size, from_length + 1);
+ ASAN_READ_RANGE(from, copy_length);
uptr to_length = REAL(strlen)(to);
ASAN_READ_RANGE(to, to_length);
ASAN_WRITE_RANGE(to + to_length, from_length + 1);
if (from_length > 0) {
- CHECK_RANGES_OVERLAP("strncat", to, to_length + 1,
- from, Min(size, from_length + 1));
+ CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1,
+ from, copy_length);
}
}
return REAL(strncat)(to, from, size);
}
INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
- if (!asan_inited) {
- return internal_strcmp(s1, s2);
+ if (!asan_inited) return internal_strcmp(s1, s2);
+ if (asan_init_is_running) {
+ return REAL(strcmp)(s1, s2);
}
ENSURE_ASAN_INITED();
unsigned char c1, c2;
@@ -437,6 +400,9 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
}
INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
+#if MAC_INTERPOSE_FUNCTIONS
+ if (!asan_inited) return REAL(strcpy)(to, from); // NOLINT
+#endif
// strcpy is called from malloc_default_purgeable_zone()
// in __asan::ReplaceSystemAlloc() on Mac.
if (asan_init_is_running) {
@@ -452,7 +418,17 @@ INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
return REAL(strcpy)(to, from); // NOLINT
}
+#if ASAN_INTERCEPT_STRDUP
INTERCEPTOR(char*, strdup, const char *s) {
+#if MAC_INTERPOSE_FUNCTIONS
+ // FIXME: because internal_strdup() uses InternalAlloc(), which currently
+ // just calls malloc() on Mac, we can't use internal_strdup() with the
+ // dynamic runtime. We can remove the call to REAL(strdup) once InternalAlloc
+ // starts using mmap() instead.
+ // See also http://code.google.com/p/address-sanitizer/issues/detail?id=123.
+ if (!asan_inited) return REAL(strdup)(s);
+#endif
+ if (!asan_inited) return internal_strdup(s);
ENSURE_ASAN_INITED();
if (flags()->replace_str) {
uptr length = REAL(strlen)(s);
@@ -460,8 +436,10 @@ INTERCEPTOR(char*, strdup, const char *s) {
}
return REAL(strdup)(s);
}
+#endif
INTERCEPTOR(uptr, strlen, const char *s) {
+ if (!asan_inited) return internal_strlen(s);
// strlen is called from malloc_default_purgeable_zone()
// in __asan::ReplaceSystemAlloc() on Mac.
if (asan_init_is_running) {
@@ -475,6 +453,21 @@ INTERCEPTOR(uptr, strlen, const char *s) {
return length;
}
+#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP
+INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) {
+ ENSURE_ASAN_INITED();
+ unsigned char c1, c2;
+ uptr i;
+ for (i = 0; ; i++) {
+ c1 = (unsigned char)s1[i];
+ c2 = (unsigned char)s2[i];
+ if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
+ }
+ ASAN_READ_RANGE(s1, i + 1);
+ ASAN_READ_RANGE(s2, i + 1);
+ return CharCaseCmp(c1, c2);
+}
+
INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, uptr n) {
ENSURE_ASAN_INITED();
unsigned char c1 = 0, c2 = 0;
@@ -488,8 +481,10 @@ INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, uptr n) {
ASAN_READ_RANGE(s2, Min(i + 1, n));
return CharCaseCmp(c1, c2);
}
+#endif // ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP
INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) {
+ if (!asan_inited) return internal_strncmp(s1, s2, size);
// strncmp is called from malloc_default_purgeable_zone()
// in __asan::ReplaceSystemAlloc() on Mac.
if (asan_init_is_running) {
@@ -566,6 +561,9 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
}
INTERCEPTOR(int, atoi, const char *nptr) {
+#if MAC_INTERPOSE_FUNCTIONS
+ if (!asan_inited) return REAL(atoi)(nptr);
+#endif
ENSURE_ASAN_INITED();
if (!flags()->replace_str) {
return REAL(atoi)(nptr);
@@ -582,6 +580,9 @@ INTERCEPTOR(int, atoi, const char *nptr) {
}
INTERCEPTOR(long, atol, const char *nptr) { // NOLINT
+#if MAC_INTERPOSE_FUNCTIONS
+ if (!asan_inited) return REAL(atol)(nptr);
+#endif
ENSURE_ASAN_INITED();
if (!flags()->replace_str) {
return REAL(atol)(nptr);
@@ -638,7 +639,7 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread,
void* security, uptr stack_size,
DWORD (__stdcall *start_routine)(void*), void* arg,
DWORD flags, void* tid) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
+ GET_STACK_TRACE_THREAD;
u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
AsanThread *t = AsanThread::Create(current_tid, start_routine, arg, &stack);
asanThreadRegistry().RegisterThread(t);
@@ -660,6 +661,12 @@ void InitializeAsanInterceptors() {
static bool was_called_once;
CHECK(was_called_once == false);
was_called_once = true;
+#if MAC_INTERPOSE_FUNCTIONS
+ return;
+#endif
+
+ SANITIZER_COMMON_INTERCEPTORS_INIT;
+
// Intercept mem* functions.
ASAN_INTERCEPT_FUNC(memcmp);
ASAN_INTERCEPT_FUNC(memmove);
@@ -667,7 +674,11 @@ void InitializeAsanInterceptors() {
if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) {
ASAN_INTERCEPT_FUNC(memcpy);
} else {
- REAL(memcpy) = REAL(memmove);
+#if !MAC_INTERPOSE_FUNCTIONS
+ // If we're using dynamic interceptors on Mac, these two are just plain
+ // functions.
+ internal_memcpy(&REAL(memcpy), &REAL(memmove), sizeof(REAL(memmove)));
+#endif
}
// Intercept str* functions.
@@ -679,19 +690,23 @@ void InitializeAsanInterceptors() {
ASAN_INTERCEPT_FUNC(strncat);
ASAN_INTERCEPT_FUNC(strncmp);
ASAN_INTERCEPT_FUNC(strncpy);
-#if !defined(_WIN32)
+#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP
ASAN_INTERCEPT_FUNC(strcasecmp);
- ASAN_INTERCEPT_FUNC(strdup);
ASAN_INTERCEPT_FUNC(strncasecmp);
-# ifndef __APPLE__
+#endif
+#if ASAN_INTERCEPT_STRDUP
+ ASAN_INTERCEPT_FUNC(strdup);
+#endif
+#if ASAN_INTERCEPT_STRNLEN
+ ASAN_INTERCEPT_FUNC(strnlen);
+#endif
+#if ASAN_INTERCEPT_INDEX
+# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
ASAN_INTERCEPT_FUNC(index);
# else
CHECK(OVERRIDE_FUNCTION(index, WRAP(strchr)));
# endif
#endif
-#if ASAN_INTERCEPT_STRNLEN
- ASAN_INTERCEPT_FUNC(strnlen);
-#endif
ASAN_INTERCEPT_FUNC(atoi);
ASAN_INTERCEPT_FUNC(atol);
@@ -701,25 +716,37 @@ void InitializeAsanInterceptors() {
ASAN_INTERCEPT_FUNC(strtoll);
#endif
+#if ASAN_INTERCEPT_MLOCKX
+ // Intercept mlock/munlock.
+ ASAN_INTERCEPT_FUNC(mlock);
+ ASAN_INTERCEPT_FUNC(munlock);
+ ASAN_INTERCEPT_FUNC(mlockall);
+ ASAN_INTERCEPT_FUNC(munlockall);
+#endif
+
// Intecept signal- and jump-related functions.
ASAN_INTERCEPT_FUNC(longjmp);
#if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION
ASAN_INTERCEPT_FUNC(sigaction);
ASAN_INTERCEPT_FUNC(signal);
#endif
-
-#if !defined(_WIN32)
+#if ASAN_INTERCEPT_SWAPCONTEXT
+ ASAN_INTERCEPT_FUNC(swapcontext);
+#endif
+#if ASAN_INTERCEPT__LONGJMP
ASAN_INTERCEPT_FUNC(_longjmp);
- INTERCEPT_FUNCTION(__cxa_throw);
-# if !defined(__APPLE__)
- // On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it
- // there.
+#endif
+#if ASAN_INTERCEPT_SIGLONGJMP
ASAN_INTERCEPT_FUNC(siglongjmp);
-# endif
+#endif
+
+ // Intercept exception handling functions.
+#if ASAN_INTERCEPT___CXA_THROW
+ INTERCEPT_FUNCTION(__cxa_throw);
#endif
// Intercept threading-related functions
-#if !defined(_WIN32)
+#if ASAN_INTERCEPT_PTHREAD_CREATE
ASAN_INTERCEPT_FUNC(pthread_create);
#endif
diff --git a/lib/asan/asan_interceptors.h b/lib/asan/asan_interceptors.h
index 32816920f7a3..3b3e90ef93ff 100644
--- a/lib/asan/asan_interceptors.h
+++ b/lib/asan/asan_interceptors.h
@@ -24,6 +24,7 @@ DECLARE_REAL(char*, strchr, const char *str, int c)
DECLARE_REAL(uptr, strlen, const char *s)
DECLARE_REAL(char*, strncpy, char *to, const char *from, uptr size)
DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen)
+DECLARE_REAL(char*, strstr, const char *s1, const char *s2)
struct sigaction;
DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
struct sigaction *oldact)
diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h
index 8c1f32028f2c..5d3bffa814da 100644
--- a/lib/asan/asan_internal.h
+++ b/lib/asan/asan_internal.h
@@ -17,17 +17,13 @@
#include "asan_flags.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_libc.h"
#if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32)
# error "This operating system is not supported by AddressSanitizer"
#endif
-#if defined(_WIN32)
-extern "C" void* _ReturnAddress(void);
-# pragma intrinsic(_ReturnAddress)
-#endif // defined(_WIN32)
-
#define ASAN_DEFAULT_FAILURE_EXITCODE 1
#if defined(__linux__)
@@ -48,6 +44,13 @@ extern "C" void* _ReturnAddress(void);
# define ASAN_WINDOWS 0
#endif
+#if defined(__ANDROID__) || defined(ANDROID)
+# define ASAN_ANDROID 1
+#else
+# define ASAN_ANDROID 0
+#endif
+
+
#define ASAN_POSIX (ASAN_LINUX || ASAN_MAC)
#if __has_feature(address_sanitizer)
@@ -59,7 +62,11 @@ extern "C" void* _ReturnAddress(void);
// If set, asan will install its own SEGV signal handler.
#ifndef ASAN_NEEDS_SEGV
-# define ASAN_NEEDS_SEGV 1
+# if ASAN_ANDROID == 1
+# define ASAN_NEEDS_SEGV 0
+# else
+# define ASAN_NEEDS_SEGV 1
+# endif
#endif
// If set, asan will intercept C++ exception api call(s).
@@ -76,7 +83,11 @@ extern "C" void* _ReturnAddress(void);
// If set, values like allocator chunk size, as well as defaults for some flags
// will be changed towards less memory overhead.
#ifndef ASAN_LOW_MEMORY
-# define ASAN_LOW_MEMORY 0
+#if SANITIZER_WORDSIZE == 32
+# define ASAN_LOW_MEMORY 1
+#else
+# define ASAN_LOW_MEMORY 0
+# endif
#endif
// All internal functions in asan reside inside the __asan namespace
@@ -86,14 +97,11 @@ extern "C" void* _ReturnAddress(void);
namespace __asan {
class AsanThread;
-struct AsanStackTrace;
+using __sanitizer::StackTrace;
// asan_rtl.cc
void NORETURN ShowStatsAndAbort();
-// asan_globals.cc
-bool DescribeAddrIfGlobal(uptr addr);
-
void ReplaceOperatorsNewAndDelete();
// asan_malloc_linux.cc / asan_malloc_mac.cc
void ReplaceSystemMalloc();
@@ -103,10 +111,12 @@ void *AsanDoesNotSupportStaticLinkage();
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
+void MaybeReexec();
bool AsanInterceptsSignal(int signum);
void SetAlternateSignalStack();
void UnsetAlternateSignalStack();
void InstallSignalHandlers();
+void ReadContextStack(void *context, uptr *stack, uptr *ssize);
void AsanPlatformThreadInit();
// Wrapper for TLS/TSD.
@@ -115,9 +125,6 @@ void *AsanTSDGet();
void AsanTSDSet(void *tsd);
void AppendToErrorMessageBuffer(const char *buffer);
-// asan_printf.cc
-void AsanPrintf(const char *format, ...);
-void AsanReport(const char *format, ...);
// asan_poisoning.cc
// Poisons the shadow memory for "size" bytes starting from "addr".
@@ -138,33 +145,20 @@ bool PlatformHasDifferentMemcpyAndMemmove();
# define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true
#endif // __APPLE__
+// Add convenient macro for interface functions that may be represented as
+// weak hooks.
+#define ASAN_MALLOC_HOOK(ptr, size) \
+ if (&__asan_malloc_hook) __asan_malloc_hook(ptr, size)
+#define ASAN_FREE_HOOK(ptr) \
+ if (&__asan_free_hook) __asan_free_hook(ptr)
+#define ASAN_ON_ERROR() \
+ if (&__asan_on_error) __asan_on_error()
+
extern int asan_inited;
// Used to avoid infinite recursion in __asan_init().
extern bool asan_init_is_running;
extern void (*death_callback)(void);
-enum LinkerInitialized { LINKER_INITIALIZED = 0 };
-
-#define ASAN_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
-
-#if !defined(_WIN32) || defined(__clang__)
-# define GET_CALLER_PC() (uptr)__builtin_return_address(0)
-# define GET_CURRENT_FRAME() (uptr)__builtin_frame_address(0)
-#else
-# define GET_CALLER_PC() (uptr)_ReturnAddress()
-// CaptureStackBackTrace doesn't need to know BP on Windows.
-// FIXME: This macro is still used when printing error reports though it's not
-// clear if the BP value is needed in the ASan reports on Windows.
-# define GET_CURRENT_FRAME() (uptr)0xDEADBEEF
-#endif
-
-#ifdef _WIN32
-# ifndef ASAN_USE_EXTERNAL_SYMBOLIZER
-# define ASAN_USE_EXTERNAL_SYMBOLIZER __asan_WinSymbolize
-bool __asan_WinSymbolize(const void *addr, char *out_buffer, int buffer_size);
-# endif
-#endif // _WIN32
-
// These magic values are written to shadow for better error reporting.
const int kAsanHeapLeftRedzoneMagic = 0xfa;
const int kAsanHeapRightRedzoneMagic = 0xfb;
@@ -174,26 +168,15 @@ const int kAsanStackMidRedzoneMagic = 0xf2;
const int kAsanStackRightRedzoneMagic = 0xf3;
const int kAsanStackPartialRedzoneMagic = 0xf4;
const int kAsanStackAfterReturnMagic = 0xf5;
+const int kAsanInitializationOrderMagic = 0xf6;
const int kAsanUserPoisonedMemoryMagic = 0xf7;
+const int kAsanStackUseAfterScopeMagic = 0xf8;
const int kAsanGlobalRedzoneMagic = 0xf9;
const int kAsanInternalHeapMagic = 0xfe;
static const uptr kCurrentStackFrameMagic = 0x41B58AB3;
static const uptr kRetiredStackFrameMagic = 0x45E0360E;
-// -------------------------- LowLevelAllocator ----- {{{1
-// A simple low-level memory allocator for internal use.
-class LowLevelAllocator {
- public:
- explicit LowLevelAllocator(LinkerInitialized) {}
- // 'size' must be a power of two.
- // Requires an external lock.
- void *Allocate(uptr size);
- private:
- char *allocated_end_;
- char *allocated_current_;
-};
-
} // namespace __asan
#endif // ASAN_INTERNAL_H
diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc
index 9a3d6bdb15a6..845493de0956 100644
--- a/lib/asan/asan_linux.cc
+++ b/lib/asan/asan_linux.cc
@@ -15,8 +15,8 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_thread.h"
+#include "asan_thread_registry.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h"
@@ -31,7 +31,7 @@
#include <unistd.h>
#include <unwind.h>
-#ifndef ANDROID
+#if !ASAN_ANDROID
// FIXME: where to get ucontext on Android?
#include <sys/ucontext.h>
#endif
@@ -40,13 +40,17 @@ extern "C" void* _DYNAMIC;
namespace __asan {
+void MaybeReexec() {
+ // No need to re-exec on Linux.
+}
+
void *AsanDoesNotSupportStaticLinkage() {
// This will fail to link with -static.
return &_DYNAMIC; // defined in link.h
}
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
-#ifdef ANDROID
+#if ASAN_ANDROID
*pc = *sp = *bp = 0;
#elif defined(__arm__)
ucontext_t *ucontext = (ucontext_t*)context;
@@ -63,6 +67,27 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
*pc = ucontext->uc_mcontext.gregs[REG_EIP];
*bp = ucontext->uc_mcontext.gregs[REG_EBP];
*sp = ucontext->uc_mcontext.gregs[REG_ESP];
+# elif defined(__powerpc__) || defined(__powerpc64__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.regs->nip;
+ *sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
+ // The powerpc{,64}-linux ABIs do not specify r31 as the frame
+ // pointer, but GCC always uses r31 when we need a frame pointer.
+ *bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
+# elif defined(__sparc__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ uptr *stk_ptr;
+# if defined (__arch64__)
+ *pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
+ *sp = ucontext->uc_mcontext.mc_gregs[MC_O6];
+ stk_ptr = (uptr *) (*sp + 2047);
+ *bp = stk_ptr[15];
+# else
+ *pc = ucontext->uc_mcontext.gregs[REG_PC];
+ *sp = ucontext->uc_mcontext.gregs[REG_O6];
+ stk_ptr = (uptr *) *sp;
+ *bp = stk_ptr[15];
+# endif
#else
# error "Unsupported arch"
#endif
@@ -76,69 +101,35 @@ void AsanPlatformThreadInit() {
// Nothing here for now.
}
-AsanLock::AsanLock(LinkerInitialized) {
- // We assume that pthread_mutex_t initialized to all zeroes is a valid
- // unlocked mutex. We can not use PTHREAD_MUTEX_INITIALIZER as it triggers
- // a gcc warning:
- // extended initializer lists only available with -std=c++0x or -std=gnu++0x
-}
-
-void AsanLock::Lock() {
- CHECK(sizeof(pthread_mutex_t) <= sizeof(opaque_storage_));
- pthread_mutex_lock((pthread_mutex_t*)&opaque_storage_);
- CHECK(!owner_);
- owner_ = (uptr)pthread_self();
-}
-
-void AsanLock::Unlock() {
- CHECK(owner_ == (uptr)pthread_self());
- owner_ = 0;
- pthread_mutex_unlock((pthread_mutex_t*)&opaque_storage_);
-}
-
-#ifdef __arm__
-#define UNWIND_STOP _URC_END_OF_STACK
-#define UNWIND_CONTINUE _URC_NO_REASON
-#else
-#define UNWIND_STOP _URC_NORMAL_STOP
-#define UNWIND_CONTINUE _URC_NO_REASON
-#endif
-
-uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
-#ifdef __arm__
- uptr val;
- _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
- 15 /* r15 = PC */, _UVRSD_UINT32, &val);
- CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
- // Clear the Thumb bit.
- return val & ~(uptr)1;
-#else
- return _Unwind_GetIP(ctx);
+void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
+#if defined(__arm__) || \
+ defined(__powerpc__) || defined(__powerpc64__) || \
+ defined(__sparc__)
+ fast = false;
#endif
+ if (!fast)
+ return stack->SlowUnwindStack(pc, max_s);
+ stack->size = 0;
+ stack->trace[0] = pc;
+ if (max_s > 1) {
+ stack->max_size = max_s;
+ if (!asan_inited) return;
+ if (AsanThread *t = asanThreadRegistry().GetCurrent())
+ stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom());
+ }
}
-_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx,
- void *param) {
- AsanStackTrace *b = (AsanStackTrace*)param;
- CHECK(b->size < b->max_size);
- uptr pc = Unwind_GetIP(ctx);
- b->trace[b->size++] = pc;
- if (b->size == b->max_size) return UNWIND_STOP;
- return UNWIND_CONTINUE;
+#if !ASAN_ANDROID
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
+ ucontext_t *ucp = (ucontext_t*)context;
+ *stack = (uptr)ucp->uc_stack.ss_sp;
+ *ssize = ucp->uc_stack.ss_size;
}
-
-void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) {
- size = 0;
- trace[0] = pc;
- if ((max_s) > 1) {
- max_size = max_s;
-#ifdef __arm__
- _Unwind_Backtrace(Unwind_Trace, this);
#else
- FastUnwindStack(pc, bp);
-#endif
- }
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
+ UNIMPLEMENTED();
}
+#endif
} // namespace __asan
diff --git a/lib/asan/asan_lock.h b/lib/asan/asan_lock.h
index edee49adf6a7..e69de29bb2d1 100644
--- a/lib/asan/asan_lock.h
+++ b/lib/asan/asan_lock.h
@@ -1,42 +0,0 @@
-//===-- asan_lock.h ---------------------------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// A wrapper for a simple lock.
-//===----------------------------------------------------------------------===//
-#ifndef ASAN_LOCK_H
-#define ASAN_LOCK_H
-
-#include "sanitizer_common/sanitizer_mutex.h"
-#include "asan_internal.h"
-
-// The locks in ASan are global objects and they are never destroyed to avoid
-// at-exit races (that is, a lock is being used by other threads while the main
-// thread is doing atexit destructors).
-// We define the class using opaque storage to avoid including system headers.
-
-namespace __asan {
-
-class AsanLock {
- public:
- explicit AsanLock(LinkerInitialized);
- void Lock();
- void Unlock();
- bool IsLocked() { return owner_ != 0; }
- private:
- uptr opaque_storage_[10];
- uptr owner_; // for debugging and for malloc_introspection_t interface
-};
-
-typedef GenericScopedLock<AsanLock> ScopedLock;
-
-} // namespace __asan
-
-#endif // ASAN_LOCK_H
diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc
index a3d39e7ae05c..3ed9e06eeb6f 100644
--- a/lib/asan/asan_mac.cc
+++ b/lib/asan/asan_mac.cc
@@ -23,7 +23,8 @@
#include "asan_thread_registry.h"
#include "sanitizer_common/sanitizer_libc.h"
-#include <crt_externs.h> // for _NSGetEnviron
+#include <crt_externs.h> // for _NSGetArgv
+#include <dlfcn.h> // for dladdr()
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <sys/mman.h>
@@ -41,7 +42,7 @@ namespace __asan {
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
ucontext_t *ucontext = (ucontext_t*)context;
-# if __WORDSIZE == 64
+# if SANITIZER_WORDSIZE == 64
*pc = ucontext->uc_mcontext->__ss.__rip;
*bp = ucontext->uc_mcontext->__ss.__rbp;
*sp = ucontext->uc_mcontext->__ss.__rsp;
@@ -49,7 +50,7 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
*pc = ucontext->uc_mcontext->__ss.__eip;
*bp = ucontext->uc_mcontext->__ss.__ebp;
*sp = ucontext->uc_mcontext->__ss.__esp;
-# endif // __WORDSIZE
+# endif // SANITIZER_WORDSIZE
}
int GetMacosVersion() {
@@ -67,6 +68,7 @@ int GetMacosVersion() {
switch (version[1]) {
case '0': return MACOS_VERSION_SNOW_LEOPARD;
case '1': return MACOS_VERSION_LION;
+ case '2': return MACOS_VERSION_MOUNTAIN_LION;
default: return MACOS_VERSION_UNKNOWN;
}
}
@@ -83,6 +85,42 @@ bool PlatformHasDifferentMemcpyAndMemmove() {
return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD;
}
+extern "C"
+void __asan_init();
+
+static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
+
+void MaybeReexec() {
+ if (!flags()->allow_reexec) return;
+#if MAC_INTERPOSE_FUNCTIONS
+ // If the program is linked with the dynamic ASan runtime library, make sure
+ // the library is preloaded so that the wrappers work. If it is not, set
+ // DYLD_INSERT_LIBRARIES and re-exec ourselves.
+ Dl_info info;
+ CHECK(dladdr((void*)((uptr)__asan_init), &info));
+ const char *dyld_insert_libraries = GetEnv(kDyldInsertLibraries);
+ if (!dyld_insert_libraries ||
+ !REAL(strstr)(dyld_insert_libraries, info.dli_fname)) {
+ // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
+ // library.
+ char program_name[1024];
+ uint32_t buf_size = sizeof(program_name);
+ _NSGetExecutablePath(program_name, &buf_size);
+ // Ok to use setenv() since the wrappers don't depend on the value of
+ // asan_inited.
+ setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
+ if (flags()->verbosity >= 1) {
+ Report("exec()-ing the program with\n");
+ Report("%s=%s\n", kDyldInsertLibraries, info.dli_fname);
+ Report("to enable ASan wrappers.\n");
+ Report("Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n");
+ }
+ execv(program_name, *_NSGetArgv());
+ }
+#endif // MAC_INTERPOSE_FUNCTIONS
+ // If we're not using the dynamic runtime, do nothing.
+}
+
// No-op. Mac does not support static linkage anyway.
void *AsanDoesNotSupportStaticLinkage() {
return 0;
@@ -93,37 +131,32 @@ bool AsanInterceptsSignal(int signum) {
}
void AsanPlatformThreadInit() {
- ReplaceCFAllocator();
-}
-
-AsanLock::AsanLock(LinkerInitialized) {
- // We assume that OS_SPINLOCK_INIT is zero
-}
-
-void AsanLock::Lock() {
- CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_));
- CHECK(OS_SPINLOCK_INIT == 0);
- CHECK(owner_ != (uptr)pthread_self());
- OSSpinLockLock((OSSpinLock*)&opaque_storage_);
- CHECK(!owner_);
- owner_ = (uptr)pthread_self();
-}
-
-void AsanLock::Unlock() {
- CHECK(owner_ == (uptr)pthread_self());
- owner_ = 0;
- OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
+ // For the first program thread, we can't replace the allocator before
+ // __CFInitialize() has been called. If it hasn't, we'll call
+ // MaybeReplaceCFAllocator() later on this thread.
+ // For other threads __CFInitialize() has been called before their creation.
+ // See also asan_malloc_mac.cc.
+ if (((CFRuntimeBase*)kCFAllocatorSystemDefault)->_cfisa) {
+ MaybeReplaceCFAllocator();
+ }
}
-void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) {
- size = 0;
- trace[0] = pc;
+void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
+ (void)fast;
+ stack->size = 0;
+ stack->trace[0] = pc;
if ((max_s) > 1) {
- max_size = max_s;
- FastUnwindStack(pc, bp);
+ stack->max_size = max_s;
+ if (!asan_inited) return;
+ if (AsanThread *t = asanThreadRegistry().GetCurrent())
+ stack->FastUnwindStack(pc, bp, t->stack_top(), t->stack_bottom());
}
}
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
+ UNIMPLEMENTED();
+}
+
// The range of pages to be used for escape islands.
// TODO(glider): instead of mapping a fixed range we must find a range of
// unmapped pages in vmmap and take them.
@@ -132,12 +165,12 @@ void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) {
// kHighMemBeg or kHighMemEnd.
static void *island_allocator_pos = 0;
-#if __WORDSIZE == 32
-# define kIslandEnd (0xffdf0000 - kPageSize)
-# define kIslandBeg (kIslandEnd - 256 * kPageSize)
+#if SANITIZER_WORDSIZE == 32
+# define kIslandEnd (0xffdf0000 - GetPageSizeCached())
+# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached())
#else
-# define kIslandEnd (0x7fffffdf0000 - kPageSize)
-# define kIslandBeg (kIslandEnd - 256 * kPageSize)
+# define kIslandEnd (0x7fffffdf0000 - GetPageSizeCached())
+# define kIslandBeg (kIslandEnd - 256 * GetPageSizeCached())
#endif
extern "C"
@@ -161,7 +194,7 @@ mach_error_t __interception_allocate_island(void **ptr,
internal_memset(island_allocator_pos, 0xCC, kIslandEnd - kIslandBeg);
};
*ptr = island_allocator_pos;
- island_allocator_pos = (char*)island_allocator_pos + kPageSize;
+ island_allocator_pos = (char*)island_allocator_pos + GetPageSizeCached();
if (flags()->verbosity) {
Report("Branch island allocated at %p\n", *ptr);
}
@@ -209,6 +242,7 @@ typedef void* pthread_workitem_handle_t;
typedef void* dispatch_group_t;
typedef void* dispatch_queue_t;
+typedef void* dispatch_source_t;
typedef u64 dispatch_time_t;
typedef void (*dispatch_function_t)(void *block);
typedef void* (*worker_t)(void *block);
@@ -236,30 +270,34 @@ void dispatch_barrier_async_f(dispatch_queue_t dq, void *ctxt,
dispatch_function_t func);
void dispatch_group_async_f(dispatch_group_t group, dispatch_queue_t dq,
void *ctxt, dispatch_function_t func);
-int pthread_workqueue_additem_np(pthread_workqueue_t workq,
- void *(*workitem_func)(void *), void * workitem_arg,
- pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp);
} // extern "C"
+static ALWAYS_INLINE
+void asan_register_worker_thread(int parent_tid, StackTrace *stack) {
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ if (!t) {
+ t = AsanThread::Create(parent_tid, 0, 0, stack);
+ asanThreadRegistry().RegisterThread(t);
+ t->Init();
+ asanThreadRegistry().SetCurrent(t);
+ }
+}
+
+// For use by only those functions that allocated the context via
+// alloc_asan_context().
extern "C"
void asan_dispatch_call_block_and_release(void *block) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
+ GET_STACK_TRACE_THREAD;
asan_block_context_t *context = (asan_block_context_t*)block;
if (flags()->verbosity >= 2) {
Report("asan_dispatch_call_block_and_release(): "
"context: %p, pthread_self: %p\n",
block, pthread_self());
}
- AsanThread *t = asanThreadRegistry().GetCurrent();
- if (!t) {
- t = AsanThread::Create(context->parent_tid, 0, 0, &stack);
- asanThreadRegistry().RegisterThread(t);
- t->Init();
- asanThreadRegistry().SetCurrent(t);
- }
+ asan_register_worker_thread(context->parent_tid, &stack);
// Call the original dispatcher for the block.
context->func(context->block);
- asan_free(context, &stack);
+ asan_free(context, &stack, FROM_MALLOC);
}
} // namespace __asan
@@ -270,7 +308,7 @@ using namespace __asan; // NOLINT
// The caller retains control of the allocated context.
extern "C"
asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
- AsanStackTrace *stack) {
+ StackTrace *stack) {
asan_block_context_t *asan_ctxt =
(asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
asan_ctxt->block = ctxt;
@@ -279,37 +317,30 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
return asan_ctxt;
}
-// TODO(glider): can we reduce code duplication by introducing a macro?
-INTERCEPTOR(void, dispatch_async_f, dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
- asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
- if (flags()->verbosity >= 2) {
- Report("dispatch_async_f(): context: %p, pthread_self: %p\n",
- asan_ctxt, pthread_self());
- PRINT_CURRENT_STACK();
+// Define interceptor for dispatch_*_f function with the three most common
+// parameters: dispatch_queue_t, context, dispatch_function_t.
+#define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \
+ INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \
+ dispatch_function_t func) { \
+ GET_STACK_TRACE_THREAD; \
+ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \
+ if (flags()->verbosity >= 2) { \
+ Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \
+ asan_ctxt, pthread_self()); \
+ PRINT_CURRENT_STACK(); \
+ } \
+ return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \
+ asan_dispatch_call_block_and_release); \
}
- return REAL(dispatch_async_f)(dq, (void*)asan_ctxt,
- asan_dispatch_call_block_and_release);
-}
-INTERCEPTOR(void, dispatch_sync_f, dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
- asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
- if (flags()->verbosity >= 2) {
- Report("dispatch_sync_f(): context: %p, pthread_self: %p\n",
- asan_ctxt, pthread_self());
- PRINT_CURRENT_STACK();
- }
- return REAL(dispatch_sync_f)(dq, (void*)asan_ctxt,
- asan_dispatch_call_block_and_release);
-}
+INTERCEPT_DISPATCH_X_F_3(dispatch_async_f)
+INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f)
+INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f)
INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
dispatch_queue_t dq, void *ctxt,
dispatch_function_t func) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
+ GET_STACK_TRACE_THREAD;
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
if (flags()->verbosity >= 2) {
Report("dispatch_after_f: %p\n", asan_ctxt);
@@ -319,23 +350,10 @@ INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
asan_dispatch_call_block_and_release);
}
-INTERCEPTOR(void, dispatch_barrier_async_f, dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
- asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
- if (flags()->verbosity >= 2) {
- Report("dispatch_barrier_async_f(): context: %p, pthread_self: %p\n",
- asan_ctxt, pthread_self());
- PRINT_CURRENT_STACK();
- }
- REAL(dispatch_barrier_async_f)(dq, (void*)asan_ctxt,
- asan_dispatch_call_block_and_release);
-}
-
INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
dispatch_queue_t dq, void *ctxt,
dispatch_function_t func) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
+ GET_STACK_TRACE_THREAD;
asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
if (flags()->verbosity >= 2) {
Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
@@ -346,43 +364,66 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
asan_dispatch_call_block_and_release);
}
-// The following stuff has been extremely helpful while looking for the
-// unhandled functions that spawned jobs on Chromium shutdown. If the verbosity
-// level is 2 or greater, we wrap pthread_workqueue_additem_np() in order to
-// find the points of worker thread creation (each of such threads may be used
-// to run several tasks, that's why this is not enough to support the whole
-// libdispatch API.
-extern "C"
-void *wrap_workitem_func(void *arg) {
- if (flags()->verbosity >= 2) {
- Report("wrap_workitem_func: %p, pthread_self: %p\n", arg, pthread_self());
- }
- asan_block_context_t *ctxt = (asan_block_context_t*)arg;
- worker_t fn = (worker_t)(ctxt->func);
- void *result = fn(ctxt->block);
- GET_STACK_TRACE_HERE(kStackTraceMax);
- asan_free(arg, &stack);
- return result;
+#if MAC_INTERPOSE_FUNCTIONS && !defined(MISSING_BLOCKS_SUPPORT)
+// dispatch_async, dispatch_group_async and others tailcall the corresponding
+// dispatch_*_f functions. When wrapping functions with mach_override, those
+// dispatch_*_f are intercepted automatically. But with dylib interposition
+// this does not work, because the calls within the same library are not
+// interposed.
+// Therefore we need to re-implement dispatch_async and friends.
+
+extern "C" {
+// FIXME: consolidate these declarations with asan_intercepted_functions.h.
+void dispatch_async(dispatch_queue_t dq, void(^work)(void));
+void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
+ void(^work)(void));
+void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
+ void(^work)(void));
+void dispatch_source_set_cancel_handler(dispatch_source_t ds,
+ void(^work)(void));
+void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
}
-INTERCEPTOR(int, pthread_workqueue_additem_np, pthread_workqueue_t workq,
- void *(*workitem_func)(void *), void * workitem_arg,
- pthread_workitem_handle_t * itemhandlep, unsigned int *gencountp) {
- GET_STACK_TRACE_HERE(kStackTraceMax);
- asan_block_context_t *asan_ctxt =
- (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), &stack);
- asan_ctxt->block = workitem_arg;
- asan_ctxt->func = (dispatch_function_t)workitem_func;
- asan_ctxt->parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
- if (flags()->verbosity >= 2) {
- Report("pthread_workqueue_additem_np: %p\n", asan_ctxt);
- PRINT_CURRENT_STACK();
+#define GET_ASAN_BLOCK(work) \
+ void (^asan_block)(void); \
+ int parent_tid = asanThreadRegistry().GetCurrentTidOrInvalid(); \
+ asan_block = ^(void) { \
+ GET_STACK_TRACE_THREAD; \
+ asan_register_worker_thread(parent_tid, &stack); \
+ work(); \
}
- return REAL(pthread_workqueue_additem_np)(workq, wrap_workitem_func,
- asan_ctxt, itemhandlep,
- gencountp);
+
+INTERCEPTOR(void, dispatch_async,
+ dispatch_queue_t dq, void(^work)(void)) {
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_async)(dq, asan_block);
+}
+
+INTERCEPTOR(void, dispatch_group_async,
+ dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) {
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_group_async)(dg, dq, asan_block);
+}
+
+INTERCEPTOR(void, dispatch_after,
+ dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) {
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_after)(when, queue, asan_block);
}
+INTERCEPTOR(void, dispatch_source_set_cancel_handler,
+ dispatch_source_t ds, void(^work)(void)) {
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_source_set_cancel_handler)(ds, asan_block);
+}
+
+INTERCEPTOR(void, dispatch_source_set_event_handler,
+ dispatch_source_t ds, void(^work)(void)) {
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_source_set_event_handler)(ds, asan_block);
+}
+#endif
+
// See http://opensource.apple.com/source/CF/CF-635.15/CFString.c
int __CFStrIsConstant(CFStringRef str) {
CFRuntimeBase *base = (CFRuntimeBase*)str;
@@ -404,9 +445,7 @@ INTERCEPTOR(CFStringRef, CFStringCreateCopy, CFAllocatorRef alloc,
DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
-extern "C"
-void __CFInitialize();
-DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize)
+DECLARE_REAL_AND_INTERCEPTOR(void, __CFInitialize, void)
namespace __asan {
@@ -416,12 +455,6 @@ void InitializeMacInterceptors() {
CHECK(INTERCEPT_FUNCTION(dispatch_after_f));
CHECK(INTERCEPT_FUNCTION(dispatch_barrier_async_f));
CHECK(INTERCEPT_FUNCTION(dispatch_group_async_f));
- // We don't need to intercept pthread_workqueue_additem_np() to support the
- // libdispatch API, but it helps us to debug the unsupported functions. Let's
- // intercept it only during verbose runs.
- if (flags()->verbosity >= 2) {
- CHECK(INTERCEPT_FUNCTION(pthread_workqueue_additem_np));
- }
// Normally CFStringCreateCopy should not copy constant CF strings.
// Replacing the default CFAllocator causes constant strings to be copied
// rather than just returned, which leads to bugs in big applications like
diff --git a/lib/asan/asan_mac.h b/lib/asan/asan_mac.h
index 6c65765a804c..be913865c440 100644
--- a/lib/asan/asan_mac.h
+++ b/lib/asan/asan_mac.h
@@ -40,13 +40,17 @@ enum {
MACOS_VERSION_UNKNOWN = 0,
MACOS_VERSION_LEOPARD,
MACOS_VERSION_SNOW_LEOPARD,
- MACOS_VERSION_LION
+ MACOS_VERSION_LION,
+ MACOS_VERSION_MOUNTAIN_LION
};
+// Used by asan_malloc_mac.cc and asan_mac.cc
+extern "C" void __CFInitialize();
+
namespace __asan {
int GetMacosVersion();
-void ReplaceCFAllocator();
+void MaybeReplaceCFAllocator();
} // namespace __asan
diff --git a/lib/asan/asan_malloc_linux.cc b/lib/asan/asan_malloc_linux.cc
index 1046f4c843a8..b95cfe3149b7 100644
--- a/lib/asan/asan_malloc_linux.cc
+++ b/lib/asan/asan_malloc_linux.cc
@@ -19,8 +19,16 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stack.h"
+#include "asan_thread_registry.h"
+#include "sanitizer/asan_interface.h"
+
+#if ASAN_ANDROID
+DECLARE_REAL_AND_INTERCEPTOR(void*, malloc, uptr size)
+DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
+DECLARE_REAL_AND_INTERCEPTOR(void*, calloc, uptr nmemb, uptr size)
+DECLARE_REAL_AND_INTERCEPTOR(void*, realloc, void *ptr, uptr size)
+DECLARE_REAL_AND_INTERCEPTOR(void*, memalign, uptr boundary, uptr size)
-#ifdef ANDROID
struct MallocDebug {
void* (*malloc)(uptr bytes);
void (*free)(void* mem);
@@ -30,7 +38,7 @@ struct MallocDebug {
};
const MallocDebug asan_malloc_dispatch ALIGNED(32) = {
- malloc, free, calloc, realloc, memalign
+ WRAP(malloc), WRAP(free), WRAP(calloc), WRAP(realloc), WRAP(memalign)
};
extern "C" const MallocDebug* __libc_malloc_dispatch;
@@ -53,17 +61,17 @@ void ReplaceSystemMalloc() {
using namespace __asan; // NOLINT
INTERCEPTOR(void, free, void *ptr) {
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- asan_free(ptr, &stack);
+ GET_STACK_TRACE_FREE;
+ asan_free(ptr, &stack, FROM_MALLOC);
}
INTERCEPTOR(void, cfree, void *ptr) {
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- asan_free(ptr, &stack);
+ GET_STACK_TRACE_FREE;
+ asan_free(ptr, &stack, FROM_MALLOC);
}
INTERCEPTOR(void*, malloc, uptr size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
}
@@ -79,25 +87,25 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
CHECK(allocated < kCallocPoolSize);
return mem;
}
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_calloc(nmemb, size, &stack);
}
INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_realloc(ptr, size, &stack);
}
INTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
- return asan_memalign(boundary, size, &stack);
+ GET_STACK_TRACE_MALLOC;
+ return asan_memalign(boundary, size, &stack, FROM_MALLOC);
}
INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s)
ALIAS("memalign");
INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc_usable_size(ptr, &stack);
}
@@ -120,19 +128,23 @@ INTERCEPTOR(int, mallopt, int cmd, int value) {
}
INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
// Printf("posix_memalign: %zx %zu\n", alignment, size);
return asan_posix_memalign(memptr, alignment, size, &stack);
}
INTERCEPTOR(void*, valloc, uptr size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_valloc(size, &stack);
}
INTERCEPTOR(void*, pvalloc, uptr size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_pvalloc(size, &stack);
}
+INTERCEPTOR(void, malloc_stats, void) {
+ __asan_print_accumulated_stats();
+}
+
#endif // __linux__
diff --git a/lib/asan/asan_malloc_mac.cc b/lib/asan/asan_malloc_mac.cc
index 1a6c84052a0a..545ede2debe7 100644
--- a/lib/asan/asan_malloc_mac.cc
+++ b/lib/asan/asan_malloc_mac.cc
@@ -1,4 +1,4 @@
-//===-- asan_rtl.cc -------------------------------------------------------===//
+//===-- asan_malloc_mac.cc ------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -18,13 +18,15 @@
#include <CoreFoundation/CFBase.h>
#include <dlfcn.h>
#include <malloc/malloc.h>
-#include <setjmp.h>
#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_mac.h"
+#include "asan_report.h"
#include "asan_stack.h"
+#include "asan_stats.h"
+#include "asan_thread_registry.h"
// Similar code is used in Google Perftools,
// http://code.google.com/p/google-perftools.
@@ -38,6 +40,30 @@ static malloc_zone_t *system_purgeable_zone = 0;
static malloc_zone_t asan_zone;
CFAllocatorRef cf_asan = 0;
+// _CFRuntimeCreateInstance() checks whether the supplied allocator is
+// kCFAllocatorSystemDefault and, if it is not, stores the allocator reference
+// at the beginning of the allocated memory and returns the pointer to the
+// allocated memory plus sizeof(CFAllocatorRef). See
+// http://www.opensource.apple.com/source/CF/CF-635.21/CFRuntime.c
+// Pointers returned by _CFRuntimeCreateInstance() can then be passed directly
+// to free() or CFAllocatorDeallocate(), which leads to false invalid free
+// reports.
+// The corresponding rdar bug is http://openradar.appspot.com/radar?id=1796404.
+void* ALWAYS_INLINE get_saved_cfallocator_ref(void *ptr) {
+ if (flags()->replace_cfallocator) {
+ // Make sure we're not hitting the previous page. This may be incorrect
+ // if ASan's malloc returns an address ending with 0xFF8, which will be
+ // then padded to a page boundary with a CFAllocatorRef.
+ uptr arith_ptr = (uptr)ptr;
+ if ((arith_ptr & 0xFFF) > sizeof(CFAllocatorRef)) {
+ CFAllocatorRef *saved =
+ (CFAllocatorRef*)(arith_ptr - sizeof(CFAllocatorRef));
+ if ((*saved == cf_asan) && asan_mz_size(saved)) ptr = (void*)saved;
+ }
+ }
+ return ptr;
+}
+
// The free() implementation provided by OS X calls malloc_zone_from_ptr()
// to find the owner of |ptr|. If the result is 0, an invalid free() is
// reported. Our implementation falls back to asan_free() in this case
@@ -65,26 +91,12 @@ INTERCEPTOR(void, free, void *ptr) {
malloc_zone_free(zone, ptr);
#endif
} else {
- if (flags()->replace_cfallocator) {
- // Make sure we're not hitting the previous page. This may be incorrect
- // if ASan's malloc returns an address ending with 0xFF8, which will be
- // then padded to a page boundary with a CFAllocatorRef.
- uptr arith_ptr = (uptr)ptr;
- if ((arith_ptr & 0xFFF) > sizeof(CFAllocatorRef)) {
- CFAllocatorRef *saved =
- (CFAllocatorRef*)(arith_ptr - sizeof(CFAllocatorRef));
- if ((*saved == cf_asan) && asan_mz_size(saved)) ptr = (void*)saved;
- }
- }
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- asan_free(ptr, &stack);
+ if (!asan_mz_size(ptr)) ptr = get_saved_cfallocator_ref(ptr);
+ GET_STACK_TRACE_FREE;
+ asan_free(ptr, &stack, FROM_MALLOC);
}
}
-namespace __asan {
- void ReplaceCFAllocator();
-}
-
// We can't always replace the default CFAllocator with cf_asan right in
// ReplaceSystemMalloc(), because it is sometimes called before
// __CFInitialize(), when the default allocator is invalid and replacing it may
@@ -94,11 +106,15 @@ namespace __asan {
//
// See http://code.google.com/p/address-sanitizer/issues/detail?id=87
// and http://opensource.apple.com/source/CF/CF-550.43/CFRuntime.c
-INTERCEPTOR(void, __CFInitialize) {
+INTERCEPTOR(void, __CFInitialize, void) {
+ // If the runtime is built as dynamic library, __CFInitialize wrapper may be
+ // called before __asan_init.
+#if !MAC_INTERPOSE_FUNCTIONS
CHECK(flags()->replace_cfallocator);
CHECK(asan_inited);
+#endif
REAL(__CFInitialize)();
- if (!cf_asan) ReplaceCFAllocator();
+ if (!cf_asan && asan_inited) MaybeReplaceCFAllocator();
}
namespace {
@@ -114,7 +130,7 @@ void *mz_malloc(malloc_zone_t *zone, size_t size) {
CHECK(system_malloc_zone);
return malloc_zone_malloc(system_malloc_zone, size);
}
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
}
@@ -123,7 +139,7 @@ void *cf_malloc(CFIndex size, CFOptionFlags hint, void *info) {
CHECK(system_malloc_zone);
return malloc_zone_malloc(system_malloc_zone, size);
}
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
}
@@ -139,7 +155,7 @@ void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
CHECK(allocated < kCallocPoolSize);
return mem;
}
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_calloc(nmemb, size, &stack);
}
@@ -148,39 +164,41 @@ void *mz_valloc(malloc_zone_t *zone, size_t size) {
CHECK(system_malloc_zone);
return malloc_zone_valloc(system_malloc_zone, size);
}
- GET_STACK_TRACE_HERE_FOR_MALLOC;
- return asan_memalign(kPageSize, size, &stack);
+ GET_STACK_TRACE_MALLOC;
+ return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
}
-void print_zone_for_ptr(void *ptr) {
- malloc_zone_t *orig_zone = malloc_zone_from_ptr(ptr);
- if (orig_zone) {
- if (orig_zone->zone_name) {
- AsanPrintf("malloc_zone_from_ptr(%p) = %p, which is %s\n",
- ptr, orig_zone, orig_zone->zone_name);
- } else {
- AsanPrintf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n",
- ptr, orig_zone);
- }
- } else {
- AsanPrintf("malloc_zone_from_ptr(%p) = 0\n", ptr);
- }
-}
+#define GET_ZONE_FOR_PTR(ptr) \
+ malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
+ const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
void ALWAYS_INLINE free_common(void *context, void *ptr) {
if (!ptr) return;
- if (!flags()->mac_ignore_invalid_free || asan_mz_size(ptr)) {
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- asan_free(ptr, &stack);
+ if (asan_mz_size(ptr)) {
+ GET_STACK_TRACE_FREE;
+ asan_free(ptr, &stack, FROM_MALLOC);
} else {
- // Let us just leak this memory for now.
- AsanPrintf("free_common(%p) -- attempting to free unallocated memory.\n"
- "AddressSanitizer is ignoring this error on Mac OS now.\n",
- ptr);
- print_zone_for_ptr(ptr);
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- stack.PrintStack();
- return;
+ // If the pointer does not belong to any of the zones, use one of the
+ // fallback methods to free memory.
+ malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr);
+ if (zone_ptr == system_purgeable_zone) {
+ // allocations from malloc_default_purgeable_zone() done before
+ // __asan_init() may be occasionally freed via free_common().
+ // see http://code.google.com/p/address-sanitizer/issues/detail?id=99.
+ malloc_zone_free(zone_ptr, ptr);
+ } else {
+ // If the memory chunk pointer was moved to store additional
+ // CFAllocatorRef, fix it back.
+ ptr = get_saved_cfallocator_ref(ptr);
+ GET_STACK_TRACE_FREE;
+ if (!flags()->mac_ignore_invalid_free) {
+ asan_free(ptr, &stack, FROM_MALLOC);
+ } else {
+ GET_ZONE_FOR_PTR(ptr);
+ WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
+ return;
+ }
+ }
}
}
@@ -195,55 +213,45 @@ void cf_free(void *ptr, void *info) {
void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
if (!ptr) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
} else {
if (asan_mz_size(ptr)) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_realloc(ptr, size, &stack);
} else {
// We can't recover from reallocating an unknown address, because
// this would require reading at most |size| bytes from
// potentially unaccessible memory.
- AsanPrintf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n"
- "This is an unrecoverable problem, exiting now.\n",
- ptr);
- print_zone_for_ptr(ptr);
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- stack.PrintStack();
- ShowStatsAndAbort();
- return 0; // unreachable
+ GET_STACK_TRACE_FREE;
+ GET_ZONE_FOR_PTR(ptr);
+ ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
}
}
}
void *cf_realloc(void *ptr, CFIndex size, CFOptionFlags hint, void *info) {
if (!ptr) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
} else {
if (asan_mz_size(ptr)) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_realloc(ptr, size, &stack);
} else {
// We can't recover from reallocating an unknown address, because
// this would require reading at most |size| bytes from
// potentially unaccessible memory.
- AsanPrintf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n"
- "This is an unrecoverable problem, exiting now.\n",
- ptr);
- print_zone_for_ptr(ptr);
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- stack.PrintStack();
- ShowStatsAndAbort();
- return 0; // unreachable
+ GET_STACK_TRACE_FREE;
+ GET_ZONE_FOR_PTR(ptr);
+ ReportMacCfReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
}
}
}
void mz_destroy(malloc_zone_t* zone) {
// A no-op -- we will not be destroyed!
- AsanPrintf("mz_destroy() called -- ignoring\n");
+ Printf("mz_destroy() called -- ignoring\n");
}
// from AvailabilityMacros.h
#if defined(MAC_OS_X_VERSION_10_6) && \
@@ -253,8 +261,8 @@ void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
CHECK(system_malloc_zone);
return malloc_zone_memalign(system_malloc_zone, align, size);
}
- GET_STACK_TRACE_HERE_FOR_MALLOC;
- return asan_memalign(align, size, &stack);
+ GET_STACK_TRACE_MALLOC;
+ return asan_memalign(align, size, &stack, FROM_MALLOC);
}
// This function is currently unused, and we build with -Werror.
@@ -266,7 +274,6 @@ void mz_free_definite_size(malloc_zone_t* zone, void *ptr, size_t size) {
#endif
#endif
-// malloc_introspection callbacks. I'm not clear on what all of these do.
kern_return_t mi_enumerator(task_t task, void *,
unsigned type_mask, vm_address_t zone_address,
memory_reader_t reader,
@@ -282,12 +289,10 @@ size_t mi_good_size(malloc_zone_t *zone, size_t size) {
boolean_t mi_check(malloc_zone_t *zone) {
UNIMPLEMENTED();
- return true;
}
void mi_print(malloc_zone_t *zone, boolean_t verbose) {
UNIMPLEMENTED();
- return;
}
void mi_log(malloc_zone_t *zone, void *address) {
@@ -302,17 +307,12 @@ void mi_force_unlock(malloc_zone_t *zone) {
asan_mz_force_unlock();
}
-// This function is currently unused, and we build with -Werror.
-#if 0
void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
- // TODO(csilvers): figure out how to fill these out
- // TODO(glider): port this from tcmalloc when ready.
- stats->blocks_in_use = 0;
- stats->size_in_use = 0;
- stats->max_size_in_use = 0;
- stats->size_allocated = 0;
+ AsanMallocStats malloc_stats;
+ asanThreadRegistry().FillMallocStatistics(&malloc_stats);
+ CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats));
+ internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t));
}
-#endif
#if defined(MAC_OS_X_VERSION_10_6) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
@@ -327,7 +327,7 @@ boolean_t mi_zone_locked(malloc_zone_t *zone) {
extern int __CFRuntimeClassTableSize;
namespace __asan {
-void ReplaceCFAllocator() {
+void MaybeReplaceCFAllocator() {
static CFAllocatorContext asan_context = {
/*version*/ 0, /*info*/ &asan_zone,
/*retain*/ 0, /*release*/ 0,
@@ -338,7 +338,7 @@ void ReplaceCFAllocator() {
/*preferredSize*/ 0 };
if (!cf_asan)
cf_asan = CFAllocatorCreate(kCFAllocatorUseContext, &asan_context);
- if (CFAllocatorGetDefault() != cf_asan)
+ if (flags()->replace_cfallocator && CFAllocatorGetDefault() != cf_asan)
CFAllocatorSetDefault(cf_asan);
}
@@ -354,6 +354,7 @@ void ReplaceSystemMalloc() {
asan_introspection.log = &mi_log;
asan_introspection.force_lock = &mi_force_lock;
asan_introspection.force_unlock = &mi_force_unlock;
+ asan_introspection.statistics = &mi_statistics;
internal_memset(&asan_zone, 0, sizeof(malloc_zone_t));
@@ -405,16 +406,14 @@ void ReplaceSystemMalloc() {
// Make sure the default allocator was replaced.
CHECK(malloc_default_zone() == &asan_zone);
- if (flags()->replace_cfallocator) {
- // If __CFInitialize() hasn't been called yet, cf_asan will be created and
- // installed as the default allocator after __CFInitialize() finishes (see
- // the interceptor for __CFInitialize() above). Otherwise install cf_asan
- // right now. On both Snow Leopard and Lion __CFInitialize() calls
- // __CFAllocatorInitialize(), which initializes the _base._cfisa field of
- // the default allocators we check here.
- if (((CFRuntimeBase*)kCFAllocatorSystemDefault)->_cfisa) {
- ReplaceCFAllocator();
- }
+ // If __CFInitialize() hasn't been called yet, cf_asan will be created and
+ // installed as the default allocator after __CFInitialize() finishes (see
+ // the interceptor for __CFInitialize() above). Otherwise install cf_asan
+ // right now. On both Snow Leopard and Lion __CFInitialize() calls
+ // __CFAllocatorInitialize(), which initializes the _base._cfisa field of
+ // the default allocators we check here.
+ if (((CFRuntimeBase*)kCFAllocatorSystemDefault)->_cfisa) {
+ MaybeReplaceCFAllocator();
}
}
} // namespace __asan
diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc
index 6c00e77caae8..9fcfea56384f 100644
--- a/lib/asan/asan_malloc_win.cc
+++ b/lib/asan/asan_malloc_win.cc
@@ -17,9 +17,10 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stack.h"
-
#include "interception/interception.h"
+#include <stddef.h>
+
// ---------------------- Replacement functions ---------------- {{{1
using namespace __asan; // NOLINT
@@ -30,8 +31,8 @@ using namespace __asan; // NOLINT
extern "C" {
void free(void *ptr) {
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);
- return asan_free(ptr, &stack);
+ GET_STACK_TRACE_FREE;
+ return asan_free(ptr, &stack, FROM_MALLOC);
}
void _free_dbg(void* ptr, int) {
@@ -43,7 +44,7 @@ void cfree(void *ptr) {
}
void *malloc(size_t size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc(size, &stack);
}
@@ -52,7 +53,7 @@ void* _malloc_dbg(size_t size, int , const char*, int) {
}
void *calloc(size_t nmemb, size_t size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_calloc(nmemb, size, &stack);
}
@@ -65,7 +66,7 @@ void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
}
void *realloc(void *ptr, size_t size) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_realloc(ptr, size, &stack);
}
@@ -84,7 +85,7 @@ void* _recalloc(void* p, size_t n, size_t elem_size) {
}
size_t _msize(void *ptr) {
- GET_STACK_TRACE_HERE_FOR_MALLOC;
+ GET_STACK_TRACE_MALLOC;
return asan_malloc_usable_size(ptr, &stack);
}
diff --git a/lib/asan/asan_mapping.h b/lib/asan/asan_mapping.h
index 8e0c6ec5db20..5e3067031f4a 100644
--- a/lib/asan/asan_mapping.h
+++ b/lib/asan/asan_mapping.h
@@ -20,20 +20,24 @@
// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm
#if ASAN_FLEXIBLE_MAPPING_AND_OFFSET == 1
-extern __attribute__((visibility("default"))) uptr __asan_mapping_scale;
-extern __attribute__((visibility("default"))) uptr __asan_mapping_offset;
+extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_scale;
+extern SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_mapping_offset;
# define SHADOW_SCALE (__asan_mapping_scale)
# define SHADOW_OFFSET (__asan_mapping_offset)
#else
-# ifdef ANDROID
+# if ASAN_ANDROID
# define SHADOW_SCALE (3)
# define SHADOW_OFFSET (0)
# else
# define SHADOW_SCALE (3)
-# if __WORDSIZE == 32
+# if SANITIZER_WORDSIZE == 32
# define SHADOW_OFFSET (1 << 29)
# else
-# define SHADOW_OFFSET (1ULL << 44)
+# if defined(__powerpc64__)
+# define SHADOW_OFFSET (1ULL << 41)
+# else
+# define SHADOW_OFFSET (1ULL << 44)
+# endif
# endif
# endif
#endif // ASAN_FLEXIBLE_MAPPING_AND_OFFSET
@@ -42,11 +46,15 @@ extern __attribute__((visibility("default"))) uptr __asan_mapping_offset;
#define MEM_TO_SHADOW(mem) (((mem) >> SHADOW_SCALE) | (SHADOW_OFFSET))
#define SHADOW_TO_MEM(shadow) (((shadow) - SHADOW_OFFSET) << SHADOW_SCALE)
-#if __WORDSIZE == 64
+#if SANITIZER_WORDSIZE == 64
+# if defined(__powerpc64__)
+ static const uptr kHighMemEnd = 0x00000fffffffffffUL;
+# else
static const uptr kHighMemEnd = 0x00007fffffffffffUL;
-#else // __WORDSIZE == 32
+# endif
+#else // SANITIZER_WORDSIZE == 32
static const uptr kHighMemEnd = 0xffffffff;
-#endif // __WORDSIZE
+#endif // SANITIZER_WORDSIZE
#define kLowMemBeg 0
@@ -60,7 +68,12 @@ extern __attribute__((visibility("default"))) uptr __asan_mapping_offset;
#define kHighShadowBeg MEM_TO_SHADOW(kHighMemBeg)
#define kHighShadowEnd MEM_TO_SHADOW(kHighMemEnd)
-#define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 : 16 * kPageSize)
+// With the zero shadow base we can not actually map pages starting from 0.
+// This constant is somewhat arbitrary.
+#define kZeroBaseShadowStart (1 << 18)
+
+#define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 \
+ : kZeroBaseShadowStart)
#define kShadowGapEnd (kHighShadowBeg - 1)
#define kGlobalAndStackRedzone \
diff --git a/lib/asan/asan_new_delete.cc b/lib/asan/asan_new_delete.cc
index 4a7275842f08..5d1f23c542e6 100644
--- a/lib/asan/asan_new_delete.cc
+++ b/lib/asan/asan_new_delete.cc
@@ -17,7 +17,6 @@
#include "asan_stack.h"
#include <stddef.h>
-#include <new>
namespace __asan {
// This function is a no-op. We need it to make sure that object file
@@ -28,29 +27,42 @@ void ReplaceOperatorsNewAndDelete() { }
using namespace __asan; // NOLINT
-#define OPERATOR_NEW_BODY \
- GET_STACK_TRACE_HERE_FOR_MALLOC;\
- return asan_memalign(0, size, &stack);
-
-#ifdef ANDROID
-void *operator new(size_t size) { OPERATOR_NEW_BODY; }
-void *operator new[](size_t size) { OPERATOR_NEW_BODY; }
-#else
-void *operator new(size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; }
-void *operator new[](size_t size) throw(std::bad_alloc) { OPERATOR_NEW_BODY; }
-void *operator new(size_t size, std::nothrow_t const&) throw()
-{ OPERATOR_NEW_BODY; }
-void *operator new[](size_t size, std::nothrow_t const&) throw()
-{ OPERATOR_NEW_BODY; }
-#endif
+// On Android new() goes through malloc interceptors.
+#if !ASAN_ANDROID
+
+// Fake std::nothrow_t to avoid including <new>.
+namespace std {
+struct nothrow_t {};
+} // namespace std
+
+#define OPERATOR_NEW_BODY(type) \
+ GET_STACK_TRACE_MALLOC;\
+ return asan_memalign(0, size, &stack, type);
-#define OPERATOR_DELETE_BODY \
- GET_STACK_TRACE_HERE_FOR_FREE(ptr);\
- asan_free(ptr, &stack);
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size) { OPERATOR_NEW_BODY(FROM_NEW_BR); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY(FROM_NEW); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY(FROM_NEW_BR); }
-void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; }
-void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; }
-void operator delete(void *ptr, std::nothrow_t const&) throw()
-{ OPERATOR_DELETE_BODY; }
-void operator delete[](void *ptr, std::nothrow_t const&) throw()
-{ OPERATOR_DELETE_BODY; }
+#define OPERATOR_DELETE_BODY(type) \
+ GET_STACK_TRACE_FREE;\
+ asan_free(ptr, &stack, type);
+
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW); }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW_BR); }
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY(FROM_NEW); }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY(FROM_NEW_BR); }
+
+#endif
diff --git a/lib/asan/asan_poisoning.cc b/lib/asan/asan_poisoning.cc
index 3b9d9f6795ca..dc5749243569 100644
--- a/lib/asan/asan_poisoning.cc
+++ b/lib/asan/asan_poisoning.cc
@@ -13,17 +13,19 @@
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
-#include "asan_interface.h"
#include "asan_internal.h"
#include "asan_mapping.h"
+#include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_libc.h"
namespace __asan {
void PoisonShadow(uptr addr, uptr size, u8 value) {
+ if (!flags()->poison_heap) return;
CHECK(AddrIsAlignedByGranularity(addr));
CHECK(AddrIsAlignedByGranularity(addr + size));
uptr shadow_beg = MemToShadow(addr);
- uptr shadow_end = MemToShadow(addr + size);
+ uptr shadow_end = MemToShadow(addr + size - SHADOW_GRANULARITY) + 1;
CHECK(REAL(memset) != 0);
REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg);
}
@@ -32,6 +34,7 @@ void PoisonShadowPartialRightRedzone(uptr addr,
uptr size,
uptr redzone_size,
u8 value) {
+ if (!flags()->poison_heap) return;
CHECK(AddrIsAlignedByGranularity(addr));
u8 *shadow = (u8*)MemToShadow(addr);
for (uptr i = 0; i < redzone_size;
@@ -151,3 +154,67 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
bool __asan_address_is_poisoned(void const volatile *addr) {
return __asan::AddressIsPoisoned((uptr)addr);
}
+
+uptr __asan_region_is_poisoned(uptr beg, uptr size) {
+ if (!size) return 0;
+ uptr end = beg + size;
+ if (!AddrIsInMem(beg)) return beg;
+ if (!AddrIsInMem(end)) return end;
+ uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY);
+ uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY);
+ uptr shadow_beg = MemToShadow(aligned_b);
+ uptr shadow_end = MemToShadow(aligned_e);
+ // First check the first and the last application bytes,
+ // then check the SHADOW_GRANULARITY-aligned region by calling
+ // mem_is_zero on the corresponding shadow.
+ if (!__asan::AddressIsPoisoned(beg) &&
+ !__asan::AddressIsPoisoned(end - 1) &&
+ (shadow_end <= shadow_beg ||
+ __sanitizer::mem_is_zero((const char *)shadow_beg,
+ shadow_end - shadow_beg)))
+ return 0;
+ // The fast check failed, so we have a poisoned byte somewhere.
+ // Find it slowly.
+ for (; beg < end; beg++)
+ if (__asan::AddressIsPoisoned(beg))
+ return beg;
+ UNREACHABLE("mem_is_zero returned false, but poisoned byte was not found");
+ return 0;
+}
+
+// This is a simplified version of __asan_(un)poison_memory_region, which
+// assumes that left border of region to be poisoned is properly aligned.
+static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
+ if (size == 0) return;
+ uptr aligned_size = size & ~(SHADOW_GRANULARITY - 1);
+ PoisonShadow(addr, aligned_size,
+ do_poison ? kAsanStackUseAfterScopeMagic : 0);
+ if (size == aligned_size)
+ return;
+ s8 end_offset = (s8)(size - aligned_size);
+ s8* shadow_end = (s8*)MemToShadow(addr + aligned_size);
+ s8 end_value = *shadow_end;
+ if (do_poison) {
+ // If possible, mark all the bytes mapping to last shadow byte as
+ // unaddressable.
+ if (end_value > 0 && end_value <= end_offset)
+ *shadow_end = (s8)kAsanStackUseAfterScopeMagic;
+ } else {
+ // If necessary, mark few first bytes mapping to last shadow byte
+ // as addressable
+ if (end_value != 0)
+ *shadow_end = Max(end_value, end_offset);
+ }
+}
+
+void __asan_poison_stack_memory(uptr addr, uptr size) {
+ if (flags()->verbosity > 0)
+ Report("poisoning: %p %zx\n", (void*)addr, size);
+ PoisonAlignedStackMemory(addr, size, true);
+}
+
+void __asan_unpoison_stack_memory(uptr addr, uptr size) {
+ if (flags()->verbosity > 0)
+ Report("unpoisoning: %p %zx\n", (void*)addr, size);
+ PoisonAlignedStackMemory(addr, size, false);
+}
diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc
index 061bb193034d..ceaf120fc803 100644
--- a/lib/asan/asan_posix.cc
+++ b/lib/asan/asan_posix.cc
@@ -16,6 +16,7 @@
#include "asan_internal.h"
#include "asan_interceptors.h"
#include "asan_mapping.h"
+#include "asan_report.h"
#include "asan_stack.h"
#include "asan_thread_registry.h"
#include "sanitizer_common/sanitizer_libc.h"
@@ -53,14 +54,7 @@ static void ASAN_OnSIGSEGV(int, siginfo_t *siginfo, void *context) {
if (13 != internal_write(2, "ASAN:SIGSEGV\n", 13)) Die();
uptr pc, sp, bp;
GetPcSpBp(context, &pc, &sp, &bp);
- AsanReport("ERROR: AddressSanitizer crashed on unknown address %p"
- " (pc %p sp %p bp %p T%d)\n",
- (void*)addr, (void*)pc, (void*)sp, (void*)bp,
- asanThreadRegistry().GetCurrentTidOrInvalid());
- AsanPrintf("AddressSanitizer can not provide additional info. ABORTING\n");
- GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp);
- stack.PrintStack();
- ShowStatsAndAbort();
+ ReportSIGSEGV(pc, sp, bp, addr);
}
void SetAlternateSignalStack() {
diff --git a/lib/asan/asan_printf.cc b/lib/asan/asan_printf.cc
deleted file mode 100644
index e1304f0fbe3f..000000000000
--- a/lib/asan/asan_printf.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-//===-- asan_printf.cc ----------------------------------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Internal printf function, used inside ASan run-time library.
-// We can't use libc printf because we intercept some of the functions used
-// inside it.
-//===----------------------------------------------------------------------===//
-
-#include "asan_internal.h"
-#include "asan_interceptors.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_common.h"
-
-#include <stdarg.h>
-#include <stdio.h>
-
-namespace __sanitizer {
-int VSNPrintf(char *buff, int buff_length, const char *format, va_list args);
-} // namespace __sanitizer
-
-namespace __asan {
-
-void AsanPrintf(const char *format, ...) {
- const int kLen = 1024 * 4;
- char buffer[kLen];
- va_list args;
- va_start(args, format);
- int needed_length = VSNPrintf(buffer, kLen, format, args);
- va_end(args);
- RAW_CHECK_MSG(needed_length < kLen, "Buffer in Printf is too short!\n");
- RawWrite(buffer);
- AppendToErrorMessageBuffer(buffer);
-}
-
-// Like AsanPrintf, but prints the current PID before the output string.
-void AsanReport(const char *format, ...) {
- const int kLen = 1024 * 4;
- char buffer[kLen];
- int needed_length = internal_snprintf(buffer, kLen, "==%d== ", GetPid());
- RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
- va_list args;
- va_start(args, format);
- needed_length += VSNPrintf(buffer + needed_length, kLen - needed_length,
- format, args);
- va_end(args);
- RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
- RawWrite(buffer);
- AppendToErrorMessageBuffer(buffer);
-}
-
-} // namespace __asan
diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc
new file mode 100644
index 000000000000..35ab9cabde67
--- /dev/null
+++ b/lib/asan/asan_report.cc
@@ -0,0 +1,681 @@
+//===-- asan_report.cc ----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This file contains error reporting code.
+//===----------------------------------------------------------------------===//
+#include "asan_flags.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "asan_thread.h"
+#include "asan_thread_registry.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_report_decorator.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+namespace __asan {
+
+// -------------------- User-specified callbacks ----------------- {{{1
+static void (*error_report_callback)(const char*);
+static char *error_message_buffer = 0;
+static uptr error_message_buffer_pos = 0;
+static uptr error_message_buffer_size = 0;
+
+void AppendToErrorMessageBuffer(const char *buffer) {
+ if (error_message_buffer) {
+ uptr length = internal_strlen(buffer);
+ CHECK_GE(error_message_buffer_size, error_message_buffer_pos);
+ uptr remaining = error_message_buffer_size - error_message_buffer_pos;
+ internal_strncpy(error_message_buffer + error_message_buffer_pos,
+ buffer, remaining);
+ error_message_buffer[error_message_buffer_size - 1] = '\0';
+ // FIXME: reallocate the buffer instead of truncating the message.
+ error_message_buffer_pos += remaining > length ? length : remaining;
+ }
+}
+
+// ---------------------- Decorator ------------------------------ {{{1
+bool PrintsToTtyCached() {
+ static int cached = 0;
+ static bool prints_to_tty;
+ if (!cached) { // Ok wrt threads since we are printing only from one thread.
+ prints_to_tty = PrintsToTty();
+ cached = 1;
+ }
+ return prints_to_tty;
+}
+class Decorator: private __sanitizer::AnsiColorDecorator {
+ public:
+ Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { }
+ const char *Warning() { return Red(); }
+ const char *EndWarning() { return Default(); }
+ const char *Access() { return Blue(); }
+ const char *EndAccess() { return Default(); }
+ const char *Location() { return Green(); }
+ const char *EndLocation() { return Default(); }
+ const char *Allocation() { return Magenta(); }
+ const char *EndAllocation() { return Default(); }
+
+ const char *ShadowByte(u8 byte) {
+ switch (byte) {
+ case kAsanHeapLeftRedzoneMagic:
+ case kAsanHeapRightRedzoneMagic:
+ return Red();
+ case kAsanHeapFreeMagic:
+ return Magenta();
+ case kAsanStackLeftRedzoneMagic:
+ case kAsanStackMidRedzoneMagic:
+ case kAsanStackRightRedzoneMagic:
+ case kAsanStackPartialRedzoneMagic:
+ return Red();
+ case kAsanStackAfterReturnMagic:
+ return Magenta();
+ case kAsanInitializationOrderMagic:
+ return Cyan();
+ case kAsanUserPoisonedMemoryMagic:
+ return Blue();
+ case kAsanStackUseAfterScopeMagic:
+ return Magenta();
+ case kAsanGlobalRedzoneMagic:
+ return Red();
+ case kAsanInternalHeapMagic:
+ return Yellow();
+ default:
+ return Default();
+ }
+ }
+ const char *EndShadowByte() { return Default(); }
+};
+
+// ---------------------- Helper functions ----------------------- {{{1
+
+static void PrintShadowByte(const char *before, u8 byte,
+ const char *after = "\n") {
+ Decorator d;
+ Printf("%s%s%x%x%s%s", before,
+ d.ShadowByte(byte), byte >> 4, byte & 15, d.EndShadowByte(), after);
+}
+
+static void PrintShadowBytes(const char *before, u8 *bytes,
+ u8 *guilty, uptr n) {
+ Decorator d;
+ if (before)
+ Printf("%s%p:", before, bytes);
+ for (uptr i = 0; i < n; i++) {
+ u8 *p = bytes + i;
+ const char *before = p == guilty ? "[" :
+ p - 1 == guilty ? "" : " ";
+ const char *after = p == guilty ? "]" : "";
+ PrintShadowByte(before, *p, after);
+ }
+ Printf("\n");
+}
+
+static void PrintShadowMemoryForAddress(uptr addr) {
+ if (!AddrIsInMem(addr))
+ return;
+ uptr shadow_addr = MemToShadow(addr);
+ const uptr n_bytes_per_row = 16;
+ uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
+ Printf("Shadow bytes around the buggy address:\n");
+ for (int i = -5; i <= 5; i++) {
+ const char *prefix = (i == 0) ? "=>" : " ";
+ PrintShadowBytes(prefix,
+ (u8*)(aligned_shadow + i * n_bytes_per_row),
+ (u8*)shadow_addr, n_bytes_per_row);
+ }
+ Printf("Shadow byte legend (one shadow byte represents %d "
+ "application bytes):\n", (int)SHADOW_GRANULARITY);
+ PrintShadowByte(" Addressable: ", 0);
+ Printf(" Partially addressable: ");
+ for (uptr i = 1; i < SHADOW_GRANULARITY; i++)
+ PrintShadowByte("", i, " ");
+ Printf("\n");
+ PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic);
+ PrintShadowByte(" Heap righ redzone: ", kAsanHeapRightRedzoneMagic);
+ PrintShadowByte(" Freed Heap region: ", kAsanHeapFreeMagic);
+ PrintShadowByte(" Stack left redzone: ", kAsanStackLeftRedzoneMagic);
+ PrintShadowByte(" Stack mid redzone: ", kAsanStackMidRedzoneMagic);
+ PrintShadowByte(" Stack right redzone: ", kAsanStackRightRedzoneMagic);
+ PrintShadowByte(" Stack partial redzone: ", kAsanStackPartialRedzoneMagic);
+ PrintShadowByte(" Stack after return: ", kAsanStackAfterReturnMagic);
+ PrintShadowByte(" Stack use after scope: ", kAsanStackUseAfterScopeMagic);
+ PrintShadowByte(" Global redzone: ", kAsanGlobalRedzoneMagic);
+ PrintShadowByte(" Global init order: ", kAsanInitializationOrderMagic);
+ PrintShadowByte(" Poisoned by user: ", kAsanUserPoisonedMemoryMagic);
+ PrintShadowByte(" ASan internal: ", kAsanInternalHeapMagic);
+}
+
+static void PrintZoneForPointer(uptr ptr, uptr zone_ptr,
+ const char *zone_name) {
+ if (zone_ptr) {
+ if (zone_name) {
+ Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n",
+ ptr, zone_ptr, zone_name);
+ } else {
+ Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n",
+ ptr, zone_ptr);
+ }
+ } else {
+ Printf("malloc_zone_from_ptr(%p) = 0\n", ptr);
+ }
+}
+
+// ---------------------- Address Descriptions ------------------- {{{1
+
+static bool IsASCII(unsigned char c) {
+ return /*0x00 <= c &&*/ c <= 0x7F;
+}
+
+// Check if the global is a zero-terminated ASCII string. If so, print it.
+static void PrintGlobalNameIfASCII(const __asan_global &g) {
+ for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
+ if (!IsASCII(*(unsigned char*)p)) return;
+ }
+ if (*(char*)(g.beg + g.size - 1) != 0) return;
+ Printf(" '%s' is ascii string '%s'\n", g.name, (char*)g.beg);
+}
+
+bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g) {
+ if (addr < g.beg - kGlobalAndStackRedzone) return false;
+ if (addr >= g.beg + g.size_with_redzone) return false;
+ Decorator d;
+ Printf("%s", d.Location());
+ Printf("%p is located ", (void*)addr);
+ if (addr < g.beg) {
+ Printf("%zd bytes to the left", g.beg - addr);
+ } else if (addr >= g.beg + g.size) {
+ Printf("%zd bytes to the right", addr - (g.beg + g.size));
+ } else {
+ Printf("%zd bytes inside", addr - g.beg); // Can it happen?
+ }
+ Printf(" of global variable '%s' (0x%zx) of size %zu\n",
+ g.name, g.beg, g.size);
+ Printf("%s", d.EndLocation());
+ PrintGlobalNameIfASCII(g);
+ return true;
+}
+
+bool DescribeAddressIfShadow(uptr addr) {
+ if (AddrIsInMem(addr))
+ return false;
+ static const char kAddrInShadowReport[] =
+ "Address %p is located in the %s.\n";
+ if (AddrIsInShadowGap(addr)) {
+ Printf(kAddrInShadowReport, addr, "shadow gap area");
+ return true;
+ }
+ if (AddrIsInHighShadow(addr)) {
+ Printf(kAddrInShadowReport, addr, "high shadow area");
+ return true;
+ }
+ if (AddrIsInLowShadow(addr)) {
+ Printf(kAddrInShadowReport, addr, "low shadow area");
+ return true;
+ }
+ CHECK(0 && "Address is not in memory and not in shadow?");
+ return false;
+}
+
+bool DescribeAddressIfStack(uptr addr, uptr access_size) {
+ AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr);
+ if (!t) return false;
+ const sptr kBufSize = 4095;
+ char buf[kBufSize];
+ uptr offset = 0;
+ const char *frame_descr = t->GetFrameNameByAddr(addr, &offset);
+ // This string is created by the compiler and has the following form:
+ // "FunctioName n alloc_1 alloc_2 ... alloc_n"
+ // where alloc_i looks like "offset size len ObjectName ".
+ CHECK(frame_descr);
+ // Report the function name and the offset.
+ const char *name_end = internal_strchr(frame_descr, ' ');
+ CHECK(name_end);
+ buf[0] = 0;
+ internal_strncat(buf, frame_descr,
+ Min(kBufSize,
+ static_cast<sptr>(name_end - frame_descr)));
+ Decorator d;
+ Printf("%s", d.Location());
+ Printf("Address %p is located at offset %zu "
+ "in frame <%s> of T%d's stack:\n",
+ (void*)addr, offset, Demangle(buf), t->tid());
+ Printf("%s", d.EndLocation());
+ // Report the number of stack objects.
+ char *p;
+ uptr n_objects = internal_simple_strtoll(name_end, &p, 10);
+ CHECK(n_objects > 0);
+ Printf(" This frame has %zu object(s):\n", n_objects);
+ // Report all objects in this frame.
+ for (uptr i = 0; i < n_objects; i++) {
+ uptr beg, size;
+ sptr len;
+ beg = internal_simple_strtoll(p, &p, 10);
+ size = internal_simple_strtoll(p, &p, 10);
+ len = internal_simple_strtoll(p, &p, 10);
+ if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') {
+ Printf("AddressSanitizer can't parse the stack frame "
+ "descriptor: |%s|\n", frame_descr);
+ break;
+ }
+ p++;
+ buf[0] = 0;
+ internal_strncat(buf, p, Min(kBufSize, len));
+ p += len;
+ Printf(" [%zu, %zu) '%s'\n", beg, beg + size, buf);
+ }
+ Printf("HINT: this may be a false positive if your program uses "
+ "some custom stack unwind mechanism or swapcontext\n"
+ " (longjmp and C++ exceptions *are* supported)\n");
+ DescribeThread(t->summary());
+ return true;
+}
+
+static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr,
+ uptr access_size) {
+ uptr offset;
+ Decorator d;
+ Printf("%s", d.Location());
+ Printf("%p is located ", (void*)addr);
+ if (chunk.AddrIsInside(addr, access_size, &offset)) {
+ Printf("%zu bytes inside of", offset);
+ } else if (chunk.AddrIsAtLeft(addr, access_size, &offset)) {
+ Printf("%zu bytes to the left of", offset);
+ } else if (chunk.AddrIsAtRight(addr, access_size, &offset)) {
+ Printf("%zu bytes to the right of", offset);
+ } else {
+ Printf(" somewhere around (this is AddressSanitizer bug!)");
+ }
+ Printf(" %zu-byte region [%p,%p)\n", chunk.UsedSize(),
+ (void*)(chunk.Beg()), (void*)(chunk.End()));
+ Printf("%s", d.EndLocation());
+}
+
+// Return " (thread_name) " or an empty string if the name is empty.
+const char *ThreadNameWithParenthesis(AsanThreadSummary *t, char buff[],
+ uptr buff_len) {
+ const char *name = t->name();
+ if (*name == 0) return "";
+ buff[0] = 0;
+ internal_strncat(buff, " (", 3);
+ internal_strncat(buff, name, buff_len - 4);
+ internal_strncat(buff, ")", 2);
+ return buff;
+}
+
+const char *ThreadNameWithParenthesis(u32 tid, char buff[],
+ uptr buff_len) {
+ if (tid == kInvalidTid) return "";
+ AsanThreadSummary *t = asanThreadRegistry().FindByTid(tid);
+ return ThreadNameWithParenthesis(t, buff, buff_len);
+}
+
+void DescribeHeapAddress(uptr addr, uptr access_size) {
+ AsanChunkView chunk = FindHeapChunkByAddress(addr);
+ if (!chunk.IsValid()) return;
+ DescribeAccessToHeapChunk(chunk, addr, access_size);
+ CHECK(chunk.AllocTid() != kInvalidTid);
+ AsanThreadSummary *alloc_thread =
+ asanThreadRegistry().FindByTid(chunk.AllocTid());
+ StackTrace alloc_stack;
+ chunk.GetAllocStack(&alloc_stack);
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ CHECK(t);
+ char tname[128];
+ Decorator d;
+ if (chunk.FreeTid() != kInvalidTid) {
+ AsanThreadSummary *free_thread =
+ asanThreadRegistry().FindByTid(chunk.FreeTid());
+ Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
+ free_thread->tid(),
+ ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
+ d.EndAllocation());
+ StackTrace free_stack;
+ chunk.GetFreeStack(&free_stack);
+ PrintStack(&free_stack);
+ Printf("%spreviously allocated by thread T%d%s here:%s\n",
+ d.Allocation(), alloc_thread->tid(),
+ ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
+ d.EndAllocation());
+ PrintStack(&alloc_stack);
+ DescribeThread(t->summary());
+ DescribeThread(free_thread);
+ DescribeThread(alloc_thread);
+ } else {
+ Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(),
+ alloc_thread->tid(),
+ ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
+ d.EndAllocation());
+ PrintStack(&alloc_stack);
+ DescribeThread(t->summary());
+ DescribeThread(alloc_thread);
+ }
+}
+
+void DescribeAddress(uptr addr, uptr access_size) {
+ // Check if this is shadow or shadow gap.
+ if (DescribeAddressIfShadow(addr))
+ return;
+ CHECK(AddrIsInMem(addr));
+ if (DescribeAddressIfGlobal(addr))
+ return;
+ if (DescribeAddressIfStack(addr, access_size))
+ return;
+ // Assume it is a heap address.
+ DescribeHeapAddress(addr, access_size);
+}
+
+// ------------------- Thread description -------------------- {{{1
+
+void DescribeThread(AsanThreadSummary *summary) {
+ CHECK(summary);
+ // No need to announce the main thread.
+ if (summary->tid() == 0 || summary->announced()) {
+ return;
+ }
+ summary->set_announced(true);
+ char tname[128];
+ Printf("Thread T%d%s", summary->tid(),
+ ThreadNameWithParenthesis(summary->tid(), tname, sizeof(tname)));
+ Printf(" created by T%d%s here:\n",
+ summary->parent_tid(),
+ ThreadNameWithParenthesis(summary->parent_tid(),
+ tname, sizeof(tname)));
+ PrintStack(summary->stack());
+ // Recursively described parent thread if needed.
+ if (flags()->print_full_thread_history) {
+ AsanThreadSummary *parent_summary =
+ asanThreadRegistry().FindByTid(summary->parent_tid());
+ DescribeThread(parent_summary);
+ }
+}
+
+// -------------------- Different kinds of reports ----------------- {{{1
+
+// Use ScopedInErrorReport to run common actions just before and
+// immediately after printing error report.
+class ScopedInErrorReport {
+ public:
+ ScopedInErrorReport() {
+ static atomic_uint32_t num_calls;
+ static u32 reporting_thread_tid;
+ if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
+ // Do not print more than one report, otherwise they will mix up.
+ // Error reporting functions shouldn't return at this situation, as
+ // they are defined as no-return.
+ Report("AddressSanitizer: while reporting a bug found another one."
+ "Ignoring.\n");
+ u32 current_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
+ if (current_tid != reporting_thread_tid) {
+ // ASan found two bugs in different threads simultaneously. Sleep
+ // long enough to make sure that the thread which started to print
+ // an error report will finish doing it.
+ SleepForSeconds(Max(100, flags()->sleep_before_dying + 1));
+ }
+ // If we're still not dead for some reason, use raw Exit() instead of
+ // Die() to bypass any additional checks.
+ Exit(flags()->exitcode);
+ }
+ ASAN_ON_ERROR();
+ reporting_thread_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
+ Printf("===================================================="
+ "=============\n");
+ if (reporting_thread_tid != kInvalidTid) {
+ // We started reporting an error message. Stop using the fake stack
+ // in case we call an instrumented function from a symbolizer.
+ AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
+ CHECK(curr_thread);
+ curr_thread->fake_stack().StopUsingFakeStack();
+ }
+ }
+ // Destructor is NORETURN, as functions that report errors are.
+ NORETURN ~ScopedInErrorReport() {
+ // Make sure the current thread is announced.
+ AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
+ if (curr_thread) {
+ DescribeThread(curr_thread->summary());
+ }
+ // Print memory stats.
+ __asan_print_accumulated_stats();
+ if (error_report_callback) {
+ error_report_callback(error_message_buffer);
+ }
+ Report("ABORTING\n");
+ Die();
+ }
+};
+
+void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: SEGV on unknown address %p"
+ " (pc %p sp %p bp %p T%d)\n",
+ (void*)addr, (void*)pc, (void*)sp, (void*)bp,
+ asanThreadRegistry().GetCurrentTidOrInvalid());
+ Printf("%s", d.EndWarning());
+ Printf("AddressSanitizer can not provide additional info.\n");
+ GET_STACK_TRACE_FATAL(pc, bp);
+ PrintStack(&stack);
+}
+
+void ReportDoubleFree(uptr addr, StackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: attempting double-free on %p:\n", addr);
+ Printf("%s", d.EndWarning());
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+void ReportFreeNotMalloced(uptr addr, StackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: attempting free on address "
+ "which was not malloc()-ed: %p\n", addr);
+ Printf("%s", d.EndWarning());
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+void ReportAllocTypeMismatch(uptr addr, StackTrace *stack,
+ AllocType alloc_type,
+ AllocType dealloc_type) {
+ static const char *alloc_names[] =
+ {"INVALID", "malloc", "operator new", "operator new []"};
+ static const char *dealloc_names[] =
+ {"INVALID", "free", "operator delete", "operator delete []"};
+ CHECK_NE(alloc_type, dealloc_type);
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n",
+ alloc_names[alloc_type], dealloc_names[dealloc_type], addr);
+ Printf("%s", d.EndWarning());
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+ Report("HINT: if you don't care about these warnings you may set "
+ "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
+}
+
+void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: attempting to call "
+ "malloc_usable_size() for pointer which is "
+ "not owned: %p\n", addr);
+ Printf("%s", d.EndWarning());
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: attempting to call "
+ "__asan_get_allocated_size() for pointer which is "
+ "not owned: %p\n", addr);
+ Printf("%s", d.EndWarning());
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+void ReportStringFunctionMemoryRangesOverlap(
+ const char *function, const char *offset1, uptr length1,
+ const char *offset2, uptr length2, StackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: %s-param-overlap: "
+ "memory ranges [%p,%p) and [%p, %p) overlap\n", \
+ function, offset1, offset1 + length1, offset2, offset2 + length2);
+ Printf("%s", d.EndWarning());
+ PrintStack(stack);
+ DescribeAddress((uptr)offset1, length1);
+ DescribeAddress((uptr)offset2, length2);
+}
+
+// ----------------------- Mac-specific reports ----------------- {{{1
+
+void WarnMacFreeUnallocated(
+ uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
+ // Just print a warning here.
+ Printf("free_common(%p) -- attempting to free unallocated memory.\n"
+ "AddressSanitizer is ignoring this error on Mac OS now.\n",
+ addr);
+ PrintZoneForPointer(addr, zone_ptr, zone_name);
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+void ReportMacMzReallocUnknown(
+ uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n"
+ "This is an unrecoverable problem, exiting now.\n",
+ addr);
+ PrintZoneForPointer(addr, zone_ptr, zone_name);
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+void ReportMacCfReallocUnknown(
+ uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n"
+ "This is an unrecoverable problem, exiting now.\n",
+ addr);
+ PrintZoneForPointer(addr, zone_ptr, zone_name);
+ PrintStack(stack);
+ DescribeHeapAddress(addr, 1);
+}
+
+} // namespace __asan
+
+// --------------------------- Interface --------------------- {{{1
+using namespace __asan; // NOLINT
+
+void __asan_report_error(uptr pc, uptr bp, uptr sp,
+ uptr addr, bool is_write, uptr access_size) {
+ ScopedInErrorReport in_report;
+
+ // Determine the error type.
+ const char *bug_descr = "unknown-crash";
+ if (AddrIsInMem(addr)) {
+ u8 *shadow_addr = (u8*)MemToShadow(addr);
+ // If we are accessing 16 bytes, look at the second shadow byte.
+ if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY)
+ shadow_addr++;
+ // If we are in the partial right redzone, look at the next shadow byte.
+ if (*shadow_addr > 0 && *shadow_addr < 128)
+ shadow_addr++;
+ switch (*shadow_addr) {
+ case kAsanHeapLeftRedzoneMagic:
+ case kAsanHeapRightRedzoneMagic:
+ bug_descr = "heap-buffer-overflow";
+ break;
+ case kAsanHeapFreeMagic:
+ bug_descr = "heap-use-after-free";
+ break;
+ case kAsanStackLeftRedzoneMagic:
+ bug_descr = "stack-buffer-underflow";
+ break;
+ case kAsanInitializationOrderMagic:
+ bug_descr = "initialization-order-fiasco";
+ break;
+ case kAsanStackMidRedzoneMagic:
+ case kAsanStackRightRedzoneMagic:
+ case kAsanStackPartialRedzoneMagic:
+ bug_descr = "stack-buffer-overflow";
+ break;
+ case kAsanStackAfterReturnMagic:
+ bug_descr = "stack-use-after-return";
+ break;
+ case kAsanUserPoisonedMemoryMagic:
+ bug_descr = "use-after-poison";
+ break;
+ case kAsanStackUseAfterScopeMagic:
+ bug_descr = "stack-use-after-scope";
+ break;
+ case kAsanGlobalRedzoneMagic:
+ bug_descr = "global-buffer-overflow";
+ break;
+ }
+ }
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report("ERROR: AddressSanitizer: %s on address "
+ "%p at pc 0x%zx bp 0x%zx sp 0x%zx\n",
+ bug_descr, (void*)addr, pc, bp, sp);
+ Printf("%s", d.EndWarning());
+
+ u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
+ char tname[128];
+ Printf("%s%s of size %zu at %p thread T%d%s%s\n",
+ d.Access(),
+ access_size ? (is_write ? "WRITE" : "READ") : "ACCESS",
+ access_size, (void*)addr, curr_tid,
+ ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)),
+ d.EndAccess());
+
+ GET_STACK_TRACE_FATAL(pc, bp);
+ PrintStack(&stack);
+
+ DescribeAddress(addr, access_size);
+
+ PrintShadowMemoryForAddress(addr);
+}
+
+void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
+ error_report_callback = callback;
+ if (callback) {
+ error_message_buffer_size = 1 << 16;
+ error_message_buffer =
+ (char*)MmapOrDie(error_message_buffer_size, __FUNCTION__);
+ error_message_buffer_pos = 0;
+ }
+}
+
+void __asan_describe_address(uptr addr) {
+ DescribeAddress(addr, 1);
+}
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+// Provide default implementation of __asan_on_error that does nothing
+// and may be overriden by user.
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
+void __asan_on_error() {}
+#endif
diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h
new file mode 100644
index 000000000000..f0617f91970e
--- /dev/null
+++ b/lib/asan/asan_report.h
@@ -0,0 +1,57 @@
+//===-- asan_report.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan-private header for error reporting functions.
+//===----------------------------------------------------------------------===//
+
+#include "asan_allocator.h"
+#include "asan_internal.h"
+#include "asan_thread.h"
+#include "sanitizer/asan_interface.h"
+
+namespace __asan {
+
+// The following functions prints address description depending
+// on the memory type (shadow/heap/stack/global).
+void DescribeHeapAddress(uptr addr, uptr access_size);
+bool DescribeAddressIfGlobal(uptr addr);
+bool DescribeAddressRelativeToGlobal(uptr addr, const __asan_global &g);
+bool DescribeAddressIfShadow(uptr addr);
+bool DescribeAddressIfStack(uptr addr, uptr access_size);
+// Determines memory type on its own.
+void DescribeAddress(uptr addr, uptr access_size);
+
+void DescribeThread(AsanThreadSummary *summary);
+
+// Different kinds of error reports.
+void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr);
+void NORETURN ReportDoubleFree(uptr addr, StackTrace *stack);
+void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *stack);
+void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *stack,
+ AllocType alloc_type,
+ AllocType dealloc_type);
+void NORETURN ReportMallocUsableSizeNotOwned(uptr addr,
+ StackTrace *stack);
+void NORETURN ReportAsanGetAllocatedSizeNotOwned(uptr addr,
+ StackTrace *stack);
+void NORETURN ReportStringFunctionMemoryRangesOverlap(
+ const char *function, const char *offset1, uptr length1,
+ const char *offset2, uptr length2, StackTrace *stack);
+
+// Mac-specific errors and warnings.
+void WarnMacFreeUnallocated(
+ uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
+void NORETURN ReportMacMzReallocUnknown(
+ uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
+void NORETURN ReportMacCfReallocUnknown(
+ uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
+
+} // namespace __asan
diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc
index 34324fa16d08..11adbee5bdea 100644
--- a/lib/asan/asan_rtl.cc
+++ b/lib/asan/asan_rtl.cc
@@ -13,29 +13,29 @@
//===----------------------------------------------------------------------===//
#include "asan_allocator.h"
#include "asan_interceptors.h"
-#include "asan_interface.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_mapping.h"
+#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"
#include "asan_thread_registry.h"
+#include "sanitizer/asan_interface.h"
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
-namespace __sanitizer {
-using namespace __asan;
+namespace __asan {
-void Die() {
+static void AsanDie() {
static atomic_uint32_t num_calls;
if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
// Don't die twice - run a busy loop.
while (1) { }
}
if (flags()->sleep_before_dying) {
- Report("Sleeping for %zd second(s)\n", flags()->sleep_before_dying);
+ Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying);
SleepForSeconds(flags()->sleep_before_dying);
}
if (flags()->unmap_shadow_on_exit)
@@ -47,19 +47,17 @@ void Die() {
Exit(flags()->exitcode);
}
-void CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) {
- AsanReport("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n",
+static void AsanCheckFailed(const char *file, int line, const char *cond,
+ u64 v1, u64 v2) {
+ Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n",
file, line, cond, (uptr)v1, (uptr)v2);
+ // FIXME: check for infinite recursion without a thread-local counter here.
PRINT_CURRENT_STACK();
- ShowStatsAndAbort();
+ Die();
}
-} // namespace __sanitizer
-
-namespace __asan {
-
// -------------------------- Flags ------------------------- {{{1
-static const int kMallocContextSize = 30;
+static const int kDeafultMallocContextSize = 30;
static Flags asan_flags;
@@ -67,6 +65,10 @@ Flags *flags() {
return &asan_flags;
}
+static const char *MaybeCallAsanDefaultOptions() {
+ return (&__asan_default_options) ? __asan_default_options() : "";
+}
+
static void ParseFlagsFromString(Flags *f, const char *str) {
ParseFlag(str, &f->quarantine_size, "quarantine_size");
ParseFlag(str, &f->symbolize, "symbolize");
@@ -77,8 +79,9 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
ParseFlag(str, &f->debug, "debug");
ParseFlag(str, &f->report_globals, "report_globals");
+ ParseFlag(str, &f->check_initialization_order, "initialization_order");
ParseFlag(str, &f->malloc_context_size, "malloc_context_size");
- CHECK(f->malloc_context_size <= kMallocContextSize);
+ CHECK((uptr)f->malloc_context_size <= kStackTraceMax);
ParseFlag(str, &f->replace_str, "replace_str");
ParseFlag(str, &f->replace_intrin, "replace_intrin");
@@ -96,22 +99,28 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
ParseFlag(str, &f->abort_on_error, "abort_on_error");
ParseFlag(str, &f->atexit, "atexit");
ParseFlag(str, &f->disable_core, "disable_core");
+ ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix");
+ ParseFlag(str, &f->allow_reexec, "allow_reexec");
+ ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history");
+ ParseFlag(str, &f->log_path, "log_path");
+ ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal");
+ ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc");
+ ParseFlag(str, &f->poison_heap, "poison_heap");
+ ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch");
+ ParseFlag(str, &f->use_stack_depot, "use_stack_depot");
}
-extern "C" {
-const char* WEAK __asan_default_options() { return ""; }
-} // extern "C"
-
void InitializeFlags(Flags *f, const char *env) {
internal_memset(f, 0, sizeof(*f));
- f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 24 : 1UL << 28;
+ f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28;
f->symbolize = false;
f->verbosity = 0;
- f->redzone = (ASAN_LOW_MEMORY) ? 64 : 128;
+ f->redzone = ASAN_ALLOCATOR_VERSION == 2 ? 16 : (ASAN_LOW_MEMORY) ? 64 : 128;
f->debug = false;
f->report_globals = 1;
- f->malloc_context_size = kMallocContextSize;
+ f->check_initialization_order = true;
+ f->malloc_context_size = kDeafultMallocContextSize;
f->replace_str = true;
f->replace_intrin = true;
f->replace_cfallocator = true;
@@ -127,13 +136,24 @@ void InitializeFlags(Flags *f, const char *env) {
f->unmap_shadow_on_exit = false;
f->abort_on_error = false;
f->atexit = false;
- f->disable_core = (__WORDSIZE == 64);
+ f->disable_core = (SANITIZER_WORDSIZE == 64);
+ f->strip_path_prefix = "";
+ f->allow_reexec = true;
+ f->print_full_thread_history = true;
+ f->log_path = 0;
+ f->fast_unwind_on_fatal = false;
+ f->fast_unwind_on_malloc = true;
+ f->poison_heap = true;
+ // Turn off alloc/dealloc mismatch checker on Mac for now.
+ // TODO(glider): Fix known issues and enable this back.
+ f->alloc_dealloc_mismatch = (ASAN_MAC == 0);
+ f->use_stack_depot = true; // Only affects allocator2.
// Override from user-specified string.
- ParseFlagsFromString(f, __asan_default_options());
+ ParseFlagsFromString(f, MaybeCallAsanDefaultOptions());
if (flags()->verbosity) {
Report("Using the defaults from __asan_default_options: %s\n",
- __asan_default_options());
+ MaybeCallAsanDefaultOptions());
}
// Override from command line.
@@ -144,10 +164,6 @@ void InitializeFlags(Flags *f, const char *env) {
int asan_inited;
bool asan_init_is_running;
void (*death_callback)(void);
-static void (*error_report_callback)(const char*);
-char *error_message_buffer = 0;
-uptr error_message_buffer_pos = 0;
-uptr error_message_buffer_size = 0;
// -------------------------- Misc ---------------- {{{1
void ShowStatsAndAbort() {
@@ -155,146 +171,23 @@ void ShowStatsAndAbort() {
Die();
}
-static void PrintBytes(const char *before, uptr *a) {
- u8 *bytes = (u8*)a;
- uptr byte_num = (__WORDSIZE) / 8;
- AsanPrintf("%s%p:", before, (void*)a);
- for (uptr i = 0; i < byte_num; i++) {
- AsanPrintf(" %x%x", bytes[i] >> 4, bytes[i] & 15);
- }
- AsanPrintf("\n");
-}
-
-void AppendToErrorMessageBuffer(const char *buffer) {
- if (error_message_buffer) {
- uptr length = internal_strlen(buffer);
- CHECK_GE(error_message_buffer_size, error_message_buffer_pos);
- uptr remaining = error_message_buffer_size - error_message_buffer_pos;
- internal_strncpy(error_message_buffer + error_message_buffer_pos,
- buffer, remaining);
- error_message_buffer[error_message_buffer_size - 1] = '\0';
- // FIXME: reallocate the buffer instead of truncating the message.
- error_message_buffer_pos += remaining > length ? length : remaining;
- }
-}
-
// ---------------------- mmap -------------------- {{{1
// Reserve memory range [beg, end].
static void ReserveShadowMemoryRange(uptr beg, uptr end) {
- CHECK((beg % kPageSize) == 0);
- CHECK(((end + 1) % kPageSize) == 0);
+ CHECK((beg % GetPageSizeCached()) == 0);
+ CHECK(((end + 1) % GetPageSizeCached()) == 0);
uptr size = end - beg + 1;
void *res = MmapFixedNoReserve(beg, size);
- CHECK(res == (void*)beg && "ReserveShadowMemoryRange failed");
-}
-
-// ---------------------- LowLevelAllocator ------------- {{{1
-void *LowLevelAllocator::Allocate(uptr size) {
- CHECK((size & (size - 1)) == 0 && "size must be a power of two");
- if (allocated_end_ - allocated_current_ < (sptr)size) {
- uptr size_to_allocate = Max(size, kPageSize);
- allocated_current_ =
- (char*)MmapOrDie(size_to_allocate, __FUNCTION__);
- allocated_end_ = allocated_current_ + size_to_allocate;
- PoisonShadow((uptr)allocated_current_, size_to_allocate,
- kAsanInternalHeapMagic);
- }
- CHECK(allocated_end_ - allocated_current_ >= (sptr)size);
- void *res = allocated_current_;
- allocated_current_ += size;
- return res;
-}
-
-// ---------------------- DescribeAddress -------------------- {{{1
-static bool DescribeStackAddress(uptr addr, uptr access_size) {
- AsanThread *t = asanThreadRegistry().FindThreadByStackAddress(addr);
- if (!t) return false;
- const sptr kBufSize = 4095;
- char buf[kBufSize];
- uptr offset = 0;
- const char *frame_descr = t->GetFrameNameByAddr(addr, &offset);
- // This string is created by the compiler and has the following form:
- // "FunctioName n alloc_1 alloc_2 ... alloc_n"
- // where alloc_i looks like "offset size len ObjectName ".
- CHECK(frame_descr);
- // Report the function name and the offset.
- const char *name_end = internal_strchr(frame_descr, ' ');
- CHECK(name_end);
- buf[0] = 0;
- internal_strncat(buf, frame_descr,
- Min(kBufSize,
- static_cast<sptr>(name_end - frame_descr)));
- AsanPrintf("Address %p is located at offset %zu "
- "in frame <%s> of T%d's stack:\n",
- (void*)addr, offset, buf, t->tid());
- // Report the number of stack objects.
- char *p;
- uptr n_objects = internal_simple_strtoll(name_end, &p, 10);
- CHECK(n_objects > 0);
- AsanPrintf(" This frame has %zu object(s):\n", n_objects);
- // Report all objects in this frame.
- for (uptr i = 0; i < n_objects; i++) {
- uptr beg, size;
- sptr len;
- beg = internal_simple_strtoll(p, &p, 10);
- size = internal_simple_strtoll(p, &p, 10);
- len = internal_simple_strtoll(p, &p, 10);
- if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') {
- AsanPrintf("AddressSanitizer can't parse the stack frame "
- "descriptor: |%s|\n", frame_descr);
- break;
- }
- p++;
- buf[0] = 0;
- internal_strncat(buf, p, Min(kBufSize, len));
- p += len;
- AsanPrintf(" [%zu, %zu) '%s'\n", beg, beg + size, buf);
- }
- AsanPrintf("HINT: this may be a false positive if your program uses "
- "some custom stack unwind mechanism\n"
- " (longjmp and C++ exceptions *are* supported)\n");
- t->summary()->Announce();
- return true;
-}
-
-static bool DescribeAddrIfShadow(uptr addr) {
- if (AddrIsInMem(addr))
- return false;
- static const char kAddrInShadowReport[] =
- "Address %p is located in the %s.\n";
- if (AddrIsInShadowGap(addr)) {
- AsanPrintf(kAddrInShadowReport, addr, "shadow gap area");
- return true;
- }
- if (AddrIsInHighShadow(addr)) {
- AsanPrintf(kAddrInShadowReport, addr, "high shadow area");
- return true;
- }
- if (AddrIsInLowShadow(addr)) {
- AsanPrintf(kAddrInShadowReport, addr, "low shadow area");
- return true;
+ if (res != (void*)beg) {
+ Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
+ "Perhaps you're using ulimit -v\n", size);
+ Abort();
}
-
- CHECK(0); // Unreachable.
- return false;
}
-static NOINLINE void DescribeAddress(uptr addr, uptr access_size) {
- // Check if this is shadow or shadow gap.
- if (DescribeAddrIfShadow(addr))
- return;
-
- CHECK(AddrIsInMem(addr));
-
- // Check if this is a global.
- if (DescribeAddrIfGlobal(addr))
- return;
-
- if (DescribeStackAddress(addr, access_size))
- return;
-
- // finally, check if this is a heap.
- DescribeHeapAddress(addr, access_size);
+// --------------- LowLevelAllocateCallbac ---------- {{{1
+static void OnLowLevelAllocate(uptr ptr, uptr size) {
+ PoisonShadow(ptr, size, kAsanInternalHeapMagic);
}
// -------------------------- Run-time entry ------------------- {{{1
@@ -325,29 +218,48 @@ ASAN_REPORT_ERROR(store, true, 16)
// time.
static NOINLINE void force_interface_symbols() {
volatile int fake_condition = 0; // prevent dead condition elimination.
- if (fake_condition) {
- __asan_report_load1(0);
- __asan_report_load2(0);
- __asan_report_load4(0);
- __asan_report_load8(0);
- __asan_report_load16(0);
- __asan_report_store1(0);
- __asan_report_store2(0);
- __asan_report_store4(0);
- __asan_report_store8(0);
- __asan_report_store16(0);
- __asan_register_global(0, 0, 0);
- __asan_register_globals(0, 0);
- __asan_unregister_globals(0, 0);
- __asan_set_death_callback(0);
- __asan_set_error_report_callback(0);
- __asan_handle_no_return();
+ // __asan_report_* functions are noreturn, so we need a switch to prevent
+ // the compiler from removing any of them.
+ switch (fake_condition) {
+ case 1: __asan_report_load1(0); break;
+ case 2: __asan_report_load2(0); break;
+ case 3: __asan_report_load4(0); break;
+ case 4: __asan_report_load8(0); break;
+ case 5: __asan_report_load16(0); break;
+ case 6: __asan_report_store1(0); break;
+ case 7: __asan_report_store2(0); break;
+ case 8: __asan_report_store4(0); break;
+ case 9: __asan_report_store8(0); break;
+ case 10: __asan_report_store16(0); break;
+ case 12: __asan_register_globals(0, 0); break;
+ case 13: __asan_unregister_globals(0, 0); break;
+ case 14: __asan_set_death_callback(0); break;
+ case 15: __asan_set_error_report_callback(0); break;
+ case 16: __asan_handle_no_return(); break;
+ case 17: __asan_address_is_poisoned(0); break;
+ case 18: __asan_get_allocated_size(0); break;
+ case 19: __asan_get_current_allocated_bytes(); break;
+ case 20: __asan_get_estimated_allocated_size(0); break;
+ case 21: __asan_get_free_bytes(); break;
+ case 22: __asan_get_heap_size(); break;
+ case 23: __asan_get_ownership(0); break;
+ case 24: __asan_get_unmapped_bytes(); break;
+ case 25: __asan_poison_memory_region(0, 0); break;
+ case 26: __asan_unpoison_memory_region(0, 0); break;
+ case 27: __asan_set_error_exit_code(0); break;
+ case 28: __asan_stack_free(0, 0, 0); break;
+ case 29: __asan_stack_malloc(0, 0); break;
+ case 30: __asan_before_dynamic_init(0, 0); break;
+ case 31: __asan_after_dynamic_init(); break;
+ case 32: __asan_poison_stack_memory(0, 0); break;
+ case 33: __asan_unpoison_stack_memory(0, 0); break;
+ case 34: __asan_region_is_poisoned(0, 0); break;
+ case 35: __asan_describe_address(0); break;
}
}
-// -------------------------- Init ------------------- {{{1
static void asan_atexit() {
- AsanPrintf("AddressSanitizer exit stats:\n");
+ Printf("AddressSanitizer exit stats:\n");
__asan_print_accumulated_stats();
}
@@ -356,7 +268,14 @@ static void asan_atexit() {
// ---------------------- Interface ---------------- {{{1
using namespace __asan; // NOLINT
-int __asan_set_error_exit_code(int exit_code) {
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+extern "C" {
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+const char* __asan_default_options() { return ""; }
+} // extern "C"
+#endif
+
+int NOINLINE __asan_set_error_exit_code(int exit_code) {
int old = flags()->exitcode;
flags()->exitcode = exit_code;
return old;
@@ -366,8 +285,9 @@ void NOINLINE __asan_handle_no_return() {
int local_stack;
AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
CHECK(curr_thread);
+ uptr PageSize = GetPageSizeCached();
uptr top = curr_thread->stack_top();
- uptr bottom = ((uptr)&local_stack - kPageSize) & ~(kPageSize-1);
+ uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1);
PoisonShadow(bottom, top - bottom, 0);
}
@@ -375,134 +295,35 @@ void NOINLINE __asan_set_death_callback(void (*callback)(void)) {
death_callback = callback;
}
-void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
- error_report_callback = callback;
- if (callback) {
- error_message_buffer_size = 1 << 16;
- error_message_buffer =
- (char*)MmapOrDie(error_message_buffer_size, __FUNCTION__);
- error_message_buffer_pos = 0;
- }
-}
-
-void __asan_report_error(uptr pc, uptr bp, uptr sp,
- uptr addr, bool is_write, uptr access_size) {
- static atomic_uint32_t num_calls;
- if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
- // Do not print more than one report, otherwise they will mix up.
- // We can not return here because the function is marked as never-return.
- AsanPrintf("AddressSanitizer: while reporting a bug found another one."
- "Ignoring.\n");
- SleepForSeconds(5);
- Die();
- }
-
- AsanPrintf("===================================================="
- "=============\n");
- const char *bug_descr = "unknown-crash";
- if (AddrIsInMem(addr)) {
- u8 *shadow_addr = (u8*)MemToShadow(addr);
- // If we are accessing 16 bytes, look at the second shadow byte.
- if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY)
- shadow_addr++;
- // If we are in the partial right redzone, look at the next shadow byte.
- if (*shadow_addr > 0 && *shadow_addr < 128)
- shadow_addr++;
- switch (*shadow_addr) {
- case kAsanHeapLeftRedzoneMagic:
- case kAsanHeapRightRedzoneMagic:
- bug_descr = "heap-buffer-overflow";
- break;
- case kAsanHeapFreeMagic:
- bug_descr = "heap-use-after-free";
- break;
- case kAsanStackLeftRedzoneMagic:
- bug_descr = "stack-buffer-underflow";
- break;
- case kAsanStackMidRedzoneMagic:
- case kAsanStackRightRedzoneMagic:
- case kAsanStackPartialRedzoneMagic:
- bug_descr = "stack-buffer-overflow";
- break;
- case kAsanStackAfterReturnMagic:
- bug_descr = "stack-use-after-return";
- break;
- case kAsanUserPoisonedMemoryMagic:
- bug_descr = "use-after-poison";
- break;
- case kAsanGlobalRedzoneMagic:
- bug_descr = "global-buffer-overflow";
- break;
- }
- }
-
- AsanThread *curr_thread = asanThreadRegistry().GetCurrent();
- u32 curr_tid = asanThreadRegistry().GetCurrentTidOrInvalid();
-
- if (curr_thread) {
- // We started reporting an error message. Stop using the fake stack
- // in case we will call an instrumented function from a symbolizer.
- curr_thread->fake_stack().StopUsingFakeStack();
- }
-
- AsanReport("ERROR: AddressSanitizer %s on address "
- "%p at pc 0x%zx bp 0x%zx sp 0x%zx\n",
- bug_descr, (void*)addr, pc, bp, sp);
-
- AsanPrintf("%s of size %zu at %p thread T%d\n",
- access_size ? (is_write ? "WRITE" : "READ") : "ACCESS",
- access_size, (void*)addr, curr_tid);
-
- if (flags()->debug) {
- PrintBytes("PC: ", (uptr*)pc);
- }
-
- GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp);
- stack.PrintStack();
-
- DescribeAddress(addr, access_size);
-
- if (AddrIsInMem(addr)) {
- uptr shadow_addr = MemToShadow(addr);
- AsanReport("ABORTING\n");
- __asan_print_accumulated_stats();
- AsanPrintf("Shadow byte and word:\n");
- AsanPrintf(" %p: %x\n", (void*)shadow_addr, *(unsigned char*)shadow_addr);
- uptr aligned_shadow = shadow_addr & ~(kWordSize - 1);
- PrintBytes(" ", (uptr*)(aligned_shadow));
- AsanPrintf("More shadow bytes:\n");
- PrintBytes(" ", (uptr*)(aligned_shadow-4*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow-3*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow-2*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow-1*kWordSize));
- PrintBytes("=>", (uptr*)(aligned_shadow+0*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow+1*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow+2*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow+3*kWordSize));
- PrintBytes(" ", (uptr*)(aligned_shadow+4*kWordSize));
- }
- if (error_report_callback) {
- error_report_callback(error_message_buffer);
- }
- Die();
-}
-
-
void __asan_init() {
if (asan_inited) return;
+ CHECK(!asan_init_is_running && "ASan init calls itself!");
asan_init_is_running = true;
// Make sure we are not statically linked.
AsanDoesNotSupportStaticLinkage();
- // Initialize flags.
+ // Install tool-specific callbacks in sanitizer_common.
+ SetDieCallback(AsanDie);
+ SetCheckFailedCallback(AsanCheckFailed);
+ SetPrintfAndReportCallback(AppendToErrorMessageBuffer);
+
+ // Initialize flags. This must be done early, because most of the
+ // initialization steps look at flags().
const char *options = GetEnv("ASAN_OPTIONS");
InitializeFlags(flags(), options);
+ __sanitizer_set_report_path(flags()->log_path);
if (flags()->verbosity && options) {
Report("Parsed ASAN_OPTIONS: %s\n", options);
}
+ // Re-exec ourselves if we need to set additional env or command line args.
+ MaybeReexec();
+
+ // Setup internal allocator callback.
+ SetLowLevelAllocateCallback(OnLowLevelAllocate);
+
if (flags()->atexit) {
Atexit(asan_atexit);
}
@@ -543,12 +364,13 @@ void __asan_init() {
}
uptr shadow_start = kLowShadowBeg;
- if (kLowShadowBeg > 0) shadow_start -= kMmapGranularity;
+ if (kLowShadowBeg > 0) shadow_start -= GetMmapGranularity();
uptr shadow_end = kHighShadowEnd;
if (MemoryRangeIsAvailable(shadow_start, shadow_end)) {
if (kLowShadowBeg != kLowShadowEnd) {
// mmap the low shadow plus at least one page.
- ReserveShadowMemoryRange(kLowShadowBeg - kMmapGranularity, kLowShadowEnd);
+ ReserveShadowMemoryRange(kLowShadowBeg - GetMmapGranularity(),
+ kLowShadowEnd);
}
// mmap the high shadow.
ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd);
@@ -563,6 +385,13 @@ void __asan_init() {
}
InstallSignalHandlers();
+ // Start symbolizer process if necessary.
+ if (flags()->symbolize) {
+ const char *external_symbolizer = GetEnv("ASAN_SYMBOLIZER_PATH");
+ if (external_symbolizer) {
+ InitializeExternalSymbolizer(external_symbolizer);
+ }
+ }
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
// should be set to 1 prior to initializing the threads.
diff --git a/lib/asan/asan_stack.cc b/lib/asan/asan_stack.cc
index d6103c2c98fa..ebf22fd34ca1 100644
--- a/lib/asan/asan_stack.cc
+++ b/lib/asan/asan_stack.cc
@@ -11,222 +11,33 @@
//
// Code for ASan stack trace.
//===----------------------------------------------------------------------===//
-#include "asan_interceptors.h"
-#include "asan_lock.h"
+#include "asan_flags.h"
#include "asan_stack.h"
-#include "asan_thread.h"
-#include "asan_thread_registry.h"
-#include "sanitizer_common/sanitizer_procmaps.h"
-#include "sanitizer_common/sanitizer_symbolizer.h"
-
-#ifdef ASAN_USE_EXTERNAL_SYMBOLIZER
-extern bool
-ASAN_USE_EXTERNAL_SYMBOLIZER(const void *pc, char *out, int out_size);
-#endif
+#include "sanitizer/asan_interface.h"
namespace __asan {
-// ----------------------- AsanStackTrace ----------------------------- {{{1
-// PCs in stack traces are actually the return addresses, that is,
-// addresses of the next instructions after the call. That's why we
-// decrement them.
-static uptr patch_pc(uptr pc) {
-#ifdef __arm__
- // Cancel Thumb bit.
- pc = pc & (~1);
-#endif
- return pc - 1;
+static bool MaybeCallAsanSymbolize(const void *pc, char *out_buffer,
+ int out_size) {
+ return (&__asan_symbolize) ? __asan_symbolize(pc, out_buffer, out_size)
+ : false;
}
-#if defined(ASAN_USE_EXTERNAL_SYMBOLIZER)
-void AsanStackTrace::PrintStack(uptr *addr, uptr size) {
- for (uptr i = 0; i < size && addr[i]; i++) {
- uptr pc = addr[i];
- if (i < size - 1 && addr[i + 1])
- pc = patch_pc(pc);
- char buff[4096];
- ASAN_USE_EXTERNAL_SYMBOLIZER((void*)pc, buff, sizeof(buff));
- AsanPrintf(" #%zu 0x%zx %s\n", i, pc, buff);
- }
+void PrintStack(StackTrace *stack) {
+ stack->PrintStack(stack->trace, stack->size, flags()->symbolize,
+ flags()->strip_path_prefix, MaybeCallAsanSymbolize);
}
-#else // ASAN_USE_EXTERNAL_SYMBOLIZER
-void AsanStackTrace::PrintStack(uptr *addr, uptr size) {
- ProcessMaps proc_maps;
- uptr frame_num = 0;
- for (uptr i = 0; i < size && addr[i]; i++) {
- uptr pc = addr[i];
- if (i < size - 1 && addr[i + 1])
- pc = patch_pc(pc);
- AddressInfo addr_frames[64];
- uptr addr_frames_num = 0;
- if (flags()->symbolize) {
- addr_frames_num = SymbolizeCode(pc, addr_frames,
- ASAN_ARRAY_SIZE(addr_frames));
- }
- if (addr_frames_num > 0) {
- for (uptr j = 0; j < addr_frames_num; j++) {
- AddressInfo &info = addr_frames[j];
- AsanPrintf(" #%zu 0x%zx", frame_num, pc);
- if (info.function) {
- AsanPrintf(" in %s", info.function);
- }
- if (info.file) {
- AsanPrintf(" %s:%d:%d", info.file, info.line, info.column);
- } else if (info.module) {
- AsanPrintf(" (%s+0x%zx)", info.module, info.module_offset);
- }
- AsanPrintf("\n");
- info.Clear();
- frame_num++;
- }
- } else {
- uptr offset;
- char filename[4096];
- if (proc_maps.GetObjectNameAndOffset(pc, &offset,
- filename, sizeof(filename))) {
- AsanPrintf(" #%zu 0x%zx (%s+0x%zx)\n", frame_num, pc, filename,
- offset);
- } else {
- AsanPrintf(" #%zu 0x%zx\n", frame_num, pc);
- }
- frame_num++;
- }
- }
-}
-#endif // ASAN_USE_EXTERNAL_SYMBOLIZER
+} // namespace __asan
-uptr AsanStackTrace::GetCurrentPc() {
- return GET_CALLER_PC();
-}
+// ------------------ Interface -------------- {{{1
-void AsanStackTrace::FastUnwindStack(uptr pc, uptr bp) {
- CHECK(size == 0 && trace[0] == pc);
- size = 1;
- if (!asan_inited) return;
- AsanThread *t = asanThreadRegistry().GetCurrent();
- if (!t) return;
- uptr *frame = (uptr*)bp;
- uptr *prev_frame = frame;
- uptr *top = (uptr*)t->stack_top();
- uptr *bottom = (uptr*)t->stack_bottom();
- while (frame >= prev_frame &&
- frame < top - 2 &&
- frame > bottom &&
- size < max_size) {
- uptr pc1 = frame[1];
- if (pc1 != pc) {
- trace[size++] = pc1;
- }
- prev_frame = frame;
- frame = (uptr*)frame[0];
- }
+// Provide default implementation of __asan_symbolize that does nothing
+// and may be overriden by user if he wants to use his own symbolization.
+// ASan on Windows has its own implementation of this.
+#if !defined(_WIN32) && !SANITIZER_SUPPORTS_WEAK_HOOKS
+SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
+bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) {
+ return false;
}
-
-// On 32-bits we don't compress stack traces.
-// On 64-bits we compress stack traces: if a given pc differes slightly from
-// the previous one, we record a 31-bit offset instead of the full pc.
-uptr AsanStackTrace::CompressStack(AsanStackTrace *stack,
- u32 *compressed, uptr size) {
-#if __WORDSIZE == 32
- // Don't compress, just copy.
- uptr res = 0;
- for (uptr i = 0; i < stack->size && i < size; i++) {
- compressed[i] = stack->trace[i];
- res++;
- }
- if (stack->size < size)
- compressed[stack->size] = 0;
-#else // 64 bits, compress.
- uptr prev_pc = 0;
- const uptr kMaxOffset = (1ULL << 30) - 1;
- uptr c_index = 0;
- uptr res = 0;
- for (uptr i = 0, n = stack->size; i < n; i++) {
- uptr pc = stack->trace[i];
- if (!pc) break;
- if ((s64)pc < 0) break;
- // Printf("C pc[%zu] %zx\n", i, pc);
- if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) {
- uptr offset = (s64)(pc - prev_pc);
- offset |= (1U << 31);
- if (c_index >= size) break;
- // Printf("C co[%zu] offset %zx\n", i, offset);
- compressed[c_index++] = offset;
- } else {
- uptr hi = pc >> 32;
- uptr lo = (pc << 32) >> 32;
- CHECK((hi & (1 << 31)) == 0);
- if (c_index + 1 >= size) break;
- // Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo);
- compressed[c_index++] = hi;
- compressed[c_index++] = lo;
- }
- res++;
- prev_pc = pc;
- }
- if (c_index < size)
- compressed[c_index] = 0;
- if (c_index + 1 < size)
- compressed[c_index + 1] = 0;
-#endif // __WORDSIZE
-
- // debug-only code
-#if 0
- AsanStackTrace check_stack;
- UncompressStack(&check_stack, compressed, size);
- if (res < check_stack.size) {
- Printf("res %zu check_stack.size %zu; c_size %zu\n", res,
- check_stack.size, size);
- }
- // |res| may be greater than check_stack.size, because
- // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames.
- CHECK(res >= check_stack.size);
- CHECK(0 == REAL(memcmp)(check_stack.trace, stack->trace,
- check_stack.size * sizeof(uptr)));
#endif
-
- return res;
-}
-
-void AsanStackTrace::UncompressStack(AsanStackTrace *stack,
- u32 *compressed, uptr size) {
-#if __WORDSIZE == 32
- // Don't uncompress, just copy.
- stack->size = 0;
- for (uptr i = 0; i < size && i < kStackTraceMax; i++) {
- if (!compressed[i]) break;
- stack->size++;
- stack->trace[i] = compressed[i];
- }
-#else // 64 bits, uncompress
- uptr prev_pc = 0;
- stack->size = 0;
- for (uptr i = 0; i < size && stack->size < kStackTraceMax; i++) {
- u32 x = compressed[i];
- uptr pc = 0;
- if (x & (1U << 31)) {
- // Printf("U co[%zu] offset: %x\n", i, x);
- // this is an offset
- s32 offset = x;
- offset = (offset << 1) >> 1; // remove the 31-byte and sign-extend.
- pc = prev_pc + offset;
- CHECK(pc);
- } else {
- // CHECK(i + 1 < size);
- if (i + 1 >= size) break;
- uptr hi = x;
- uptr lo = compressed[i+1];
- // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo);
- i++;
- pc = (hi << 32) | lo;
- if (!pc) break;
- }
- // Printf("U pc[%zu] %zx\n", stack->size, pc);
- stack->trace[stack->size++] = pc;
- prev_pc = pc;
- }
-#endif // __WORDSIZE
-}
-
-} // namespace __asan
diff --git a/lib/asan/asan_stack.h b/lib/asan/asan_stack.h
index 6ca9a0b28bfc..46c9f3408725 100644
--- a/lib/asan/asan_stack.h
+++ b/lib/asan/asan_stack.h
@@ -14,91 +14,53 @@
#ifndef ASAN_STACK_H
#define ASAN_STACK_H
-#include "asan_internal.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "asan_flags.h"
namespace __asan {
-static const uptr kStackTraceMax = 64;
-
-struct AsanStackTrace {
- uptr size;
- uptr max_size;
- uptr trace[kStackTraceMax];
- static void PrintStack(uptr *addr, uptr size);
- void PrintStack() {
- PrintStack(this->trace, this->size);
- }
- void CopyTo(uptr *dst, uptr dst_size) {
- for (uptr i = 0; i < size && i < dst_size; i++)
- dst[i] = trace[i];
- for (uptr i = size; i < dst_size; i++)
- dst[i] = 0;
- }
-
- void CopyFrom(uptr *src, uptr src_size) {
- size = src_size;
- if (size > kStackTraceMax) size = kStackTraceMax;
- for (uptr i = 0; i < size; i++) {
- trace[i] = src[i];
- }
- }
-
- void GetStackTrace(uptr max_s, uptr pc, uptr bp);
-
- void FastUnwindStack(uptr pc, uptr bp);
-
- static uptr GetCurrentPc();
-
- static uptr CompressStack(AsanStackTrace *stack,
- u32 *compressed, uptr size);
- static void UncompressStack(AsanStackTrace *stack,
- u32 *compressed, uptr size);
-};
+void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast);
+void PrintStack(StackTrace *stack);
} // namespace __asan
-// Use this macro if you want to print stack trace with the caller
-// of the current function in the top frame.
-#define GET_CALLER_PC_BP_SP \
- uptr bp = GET_CURRENT_FRAME(); \
- uptr pc = GET_CALLER_PC(); \
- uptr local_stack; \
- uptr sp = (uptr)&local_stack
-
-// Use this macro if you want to print stack trace with the current
-// function in the top frame.
-#define GET_CURRENT_PC_BP_SP \
- uptr bp = GET_CURRENT_FRAME(); \
- uptr pc = AsanStackTrace::GetCurrentPc(); \
- uptr local_stack; \
- uptr sp = (uptr)&local_stack
-
// Get the stack trace with the given pc and bp.
// The pc will be in the position 0 of the resulting stack trace.
// The bp may refer to the current frame or to the caller's frame.
// fast_unwind is currently unused.
-#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp) \
- AsanStackTrace stack; \
- stack.GetStackTrace(max_s, pc, bp)
+#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \
+ StackTrace stack; \
+ GetStackTrace(&stack, max_s, pc, bp, fast)
// NOTE: A Rule of thumb is to retrieve stack trace in the interceptors
// as early as possible (in functions exposed to the user), as we generally
// don't want stack trace to contain functions from ASan internals.
-#define GET_STACK_TRACE_HERE(max_size) \
+#define GET_STACK_TRACE(max_size, fast) \
GET_STACK_TRACE_WITH_PC_AND_BP(max_size, \
- AsanStackTrace::GetCurrentPc(), GET_CURRENT_FRAME())
+ StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), fast)
+
+#define GET_STACK_TRACE_FATAL(pc, bp) \
+ GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp, \
+ flags()->fast_unwind_on_fatal)
+
+#define GET_STACK_TRACE_FATAL_HERE \
+ GET_STACK_TRACE(kStackTraceMax, flags()->fast_unwind_on_fatal)
+
+#define GET_STACK_TRACE_THREAD \
+ GET_STACK_TRACE(kStackTraceMax, true)
-#define GET_STACK_TRACE_HERE_FOR_MALLOC \
- GET_STACK_TRACE_HERE(flags()->malloc_context_size)
+#define GET_STACK_TRACE_MALLOC \
+ GET_STACK_TRACE(flags()->malloc_context_size, \
+ flags()->fast_unwind_on_malloc)
-#define GET_STACK_TRACE_HERE_FOR_FREE(ptr) \
- GET_STACK_TRACE_HERE(flags()->malloc_context_size)
+#define GET_STACK_TRACE_FREE GET_STACK_TRACE_MALLOC
#define PRINT_CURRENT_STACK() \
{ \
- GET_STACK_TRACE_HERE(kStackTraceMax); \
- stack.PrintStack(); \
+ GET_STACK_TRACE(kStackTraceMax, \
+ flags()->fast_unwind_on_fatal); \
+ PrintStack(&stack); \
}
#endif // ASAN_STACK_H
diff --git a/lib/asan/asan_stats.cc b/lib/asan/asan_stats.cc
index ef5e53a8bf33..c57c8cc61aed 100644
--- a/lib/asan/asan_stats.cc
+++ b/lib/asan/asan_stats.cc
@@ -12,11 +12,11 @@
// Code related to statistics collected by AddressSanitizer.
//===----------------------------------------------------------------------===//
#include "asan_interceptors.h"
-#include "asan_interface.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_stats.h"
#include "asan_thread_registry.h"
+#include "sanitizer/asan_interface.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
namespace __asan {
@@ -27,39 +27,45 @@ AsanStats::AsanStats() {
static void PrintMallocStatsArray(const char *prefix,
uptr (&array)[kNumberOfSizeClasses]) {
- AsanPrintf("%s", prefix);
+ Printf("%s", prefix);
for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
if (!array[i]) continue;
- AsanPrintf("%zu:%zu; ", i, array[i]);
+ Printf("%zu:%zu; ", i, array[i]);
}
- AsanPrintf("\n");
+ Printf("\n");
}
void AsanStats::Print() {
- AsanPrintf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n",
+ Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n",
malloced>>20, malloced_redzones>>20, mallocs);
- AsanPrintf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs);
- AsanPrintf("Stats: %zuM freed by %zu calls\n", freed>>20, frees);
- AsanPrintf("Stats: %zuM really freed by %zu calls\n",
+ Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs);
+ Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees);
+ Printf("Stats: %zuM really freed by %zu calls\n",
really_freed>>20, real_frees);
- AsanPrintf("Stats: %zuM (%zu full pages) mmaped in %zu calls\n",
- mmaped>>20, mmaped / kPageSize, mmaps);
+ Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n",
+ (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20,
+ mmaps, munmaps);
PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size);
PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size);
PrintMallocStatsArray(" frees by size class: ", freed_by_size);
PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size);
- AsanPrintf("Stats: malloc large: %zu small slow: %zu\n",
+ Printf("Stats: malloc large: %zu small slow: %zu\n",
malloc_large, malloc_small_slow);
}
-static AsanLock print_lock(LINKER_INITIALIZED);
+static BlockingMutex print_lock(LINKER_INITIALIZED);
static void PrintAccumulatedStats() {
- AsanStats stats = asanThreadRegistry().GetAccumulatedStats();
+ AsanStats stats;
+ asanThreadRegistry().GetAccumulatedStats(&stats);
// Use lock to keep reports from mixing up.
- ScopedLock lock(&print_lock);
+ BlockingMutexLock lock(&print_lock);
stats.Print();
+ StackDepotStats *stack_depot_stats = StackDepotGetStats();
+ Printf("Stats: StackDepot: %zd ids; %zdM mapped\n",
+ stack_depot_stats->n_uniq_ids, stack_depot_stats->mapped >> 20);
+ PrintInternalAllocatorStats();
}
} // namespace __asan
diff --git a/lib/asan/asan_stats.h b/lib/asan/asan_stats.h
index b4c63f44fc68..37846bc92ad2 100644
--- a/lib/asan/asan_stats.h
+++ b/lib/asan/asan_stats.h
@@ -37,6 +37,8 @@ struct AsanStats {
uptr realloced;
uptr mmaps;
uptr mmaped;
+ uptr munmaps;
+ uptr munmaped;
uptr mmaped_by_size[kNumberOfSizeClasses];
uptr malloced_by_size[kNumberOfSizeClasses];
uptr freed_by_size[kNumberOfSizeClasses];
@@ -54,6 +56,14 @@ struct AsanStats {
void Print();
};
+// A cross-platform equivalent of malloc_statistics_t on Mac OS.
+struct AsanMallocStats {
+ uptr blocks_in_use;
+ uptr size_in_use;
+ uptr max_size_in_use;
+ uptr size_allocated;
+};
+
} // namespace __asan
#endif // ASAN_STATS_H
diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc
index 05a41ea9685a..778e91932ed5 100644
--- a/lib/asan/asan_thread.cc
+++ b/lib/asan/asan_thread.cc
@@ -26,24 +26,18 @@ AsanThread::AsanThread(LinkerInitialized x)
malloc_storage_(x),
stats_(x) { }
-static AsanLock mu_for_thread_summary(LINKER_INITIALIZED);
-static LowLevelAllocator allocator_for_thread_summary(LINKER_INITIALIZED);
-
AsanThread *AsanThread::Create(u32 parent_tid, thread_callback_t start_routine,
- void *arg, AsanStackTrace *stack) {
- uptr size = RoundUpTo(sizeof(AsanThread), kPageSize);
+ void *arg, StackTrace *stack) {
+ uptr PageSize = GetPageSizeCached();
+ uptr size = RoundUpTo(sizeof(AsanThread), PageSize);
AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__);
thread->start_routine_ = start_routine;
thread->arg_ = arg;
- const uptr kSummaryAllocSize = 1024;
+ const uptr kSummaryAllocSize = PageSize;
CHECK_LE(sizeof(AsanThreadSummary), kSummaryAllocSize);
- AsanThreadSummary *summary;
- {
- ScopedLock lock(&mu_for_thread_summary);
- summary = (AsanThreadSummary*)
- allocator_for_thread_summary.Allocate(kSummaryAllocSize);
- }
+ AsanThreadSummary *summary =
+ (AsanThreadSummary*)MmapOrDie(PageSize, "AsanThreadSummary");
summary->Init(parent_tid, stack);
summary->set_thread(thread);
thread->set_summary(summary);
@@ -73,14 +67,14 @@ void AsanThread::Destroy() {
// and we don't want it to have any poisoned stack.
ClearShadowForThreadStack();
fake_stack().Cleanup();
- uptr size = RoundUpTo(sizeof(AsanThread), kPageSize);
+ uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached());
UnmapOrDie(this, size);
}
void AsanThread::Init() {
SetThreadStackTopAndBottom();
CHECK(AddrIsInMem(stack_bottom_));
- CHECK(AddrIsInMem(stack_top_));
+ CHECK(AddrIsInMem(stack_top_ - 1));
ClearShadowForThreadStack();
if (flags()->verbosity >= 1) {
int local = 0;
@@ -125,25 +119,25 @@ void AsanThread::ClearShadowForThreadStack() {
const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) {
uptr bottom = 0;
- bool is_fake_stack = false;
if (AddrIsInStack(addr)) {
bottom = stack_bottom();
} else {
bottom = fake_stack().AddrIsInFakeStack(addr);
CHECK(bottom);
- is_fake_stack = true;
+ *offset = addr - bottom;
+ return (const char *)((uptr*)bottom)[1];
}
- uptr aligned_addr = addr & ~(__WORDSIZE/8 - 1); // align addr.
+ uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr.
u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr);
u8 *shadow_bottom = (u8*)MemToShadow(bottom);
while (shadow_ptr >= shadow_bottom &&
- *shadow_ptr != kAsanStackLeftRedzoneMagic) {
+ *shadow_ptr != kAsanStackLeftRedzoneMagic) {
shadow_ptr--;
}
while (shadow_ptr >= shadow_bottom &&
- *shadow_ptr == kAsanStackLeftRedzoneMagic) {
+ *shadow_ptr == kAsanStackLeftRedzoneMagic) {
shadow_ptr--;
}
@@ -153,8 +147,7 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) {
}
uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1));
- CHECK((ptr[0] == kCurrentStackFrameMagic) ||
- (is_fake_stack && ptr[0] == kRetiredStackFrameMagic));
+ CHECK(ptr[0] == kCurrentStackFrameMagic);
*offset = addr - (uptr)ptr;
return (const char*)ptr[1];
}
diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h
index 9a032fe3e66c..acc27e52e224 100644
--- a/lib/asan/asan_thread.h
+++ b/lib/asan/asan_thread.h
@@ -31,7 +31,7 @@ class AsanThread;
class AsanThreadSummary {
public:
explicit AsanThreadSummary(LinkerInitialized) { } // for T0.
- void Init(u32 parent_tid, AsanStackTrace *stack) {
+ void Init(u32 parent_tid, StackTrace *stack) {
parent_tid_ = parent_tid;
announced_ = false;
tid_ = kInvalidTid;
@@ -39,35 +39,40 @@ class AsanThreadSummary {
internal_memcpy(&stack_, stack, sizeof(*stack));
}
thread_ = 0;
- }
- void Announce() {
- if (tid_ == 0) return; // no need to announce the main thread.
- if (!announced_) {
- announced_ = true;
- AsanPrintf("Thread T%d created by T%d here:\n", tid_, parent_tid_);
- stack_.PrintStack();
- }
+ name_[0] = 0;
}
u32 tid() { return tid_; }
void set_tid(u32 tid) { tid_ = tid; }
+ u32 parent_tid() { return parent_tid_; }
+ bool announced() { return announced_; }
+ void set_announced(bool announced) { announced_ = announced; }
+ StackTrace *stack() { return &stack_; }
AsanThread *thread() { return thread_; }
void set_thread(AsanThread *thread) { thread_ = thread; }
static void TSDDtor(void *tsd);
+ void set_name(const char *name) {
+ internal_strncpy(name_, name, sizeof(name_) - 1);
+ }
+ const char *name() { return name_; }
private:
u32 tid_;
u32 parent_tid_;
bool announced_;
- AsanStackTrace stack_;
+ StackTrace stack_;
AsanThread *thread_;
+ char name_[128];
};
+// AsanThreadSummary objects are never freed, so we need many of them.
+COMPILER_CHECK(sizeof(AsanThreadSummary) <= 4094);
+
// AsanThread are stored in TSD and destroyed when the thread dies.
class AsanThread {
public:
explicit AsanThread(LinkerInitialized); // for T0.
static AsanThread *Create(u32 parent_tid, thread_callback_t start_routine,
- void *arg, AsanStackTrace *stack);
+ void *arg, StackTrace *stack);
void Destroy();
void Init(); // Should be called from the thread itself.
@@ -91,7 +96,6 @@ class AsanThread {
AsanStats &stats() { return stats_; }
private:
-
void SetThreadStackTopAndBottom();
void ClearShadowForThreadStack();
AsanThreadSummary *summary_;
diff --git a/lib/asan/asan_thread_registry.cc b/lib/asan/asan_thread_registry.cc
index 4540d589c552..80675405fbd5 100644
--- a/lib/asan/asan_thread_registry.cc
+++ b/lib/asan/asan_thread_registry.cc
@@ -20,7 +20,7 @@
namespace __asan {
-static AsanThreadRegistry asan_thread_registry(__asan::LINKER_INITIALIZED);
+static AsanThreadRegistry asan_thread_registry(LINKER_INITIALIZED);
AsanThreadRegistry &asanThreadRegistry() {
return asan_thread_registry;
@@ -30,6 +30,7 @@ AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x)
: main_thread_(x),
main_thread_summary_(x),
accumulated_stats_(x),
+ max_malloced_memory_(x),
mu_(x) { }
void AsanThreadRegistry::Init() {
@@ -43,7 +44,7 @@ void AsanThreadRegistry::Init() {
}
void AsanThreadRegistry::RegisterThread(AsanThread *thread) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
u32 tid = n_threads_;
n_threads_++;
CHECK(n_threads_ < kMaxNumberOfThreads);
@@ -55,7 +56,7 @@ void AsanThreadRegistry::RegisterThread(AsanThread *thread) {
}
void AsanThreadRegistry::UnregisterThread(AsanThread *thread) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
FlushToAccumulatedStatsUnlocked(&thread->stats());
AsanThreadSummary *summary = thread->summary();
CHECK(summary);
@@ -69,7 +70,7 @@ AsanThread *AsanThreadRegistry::GetMain() {
AsanThread *AsanThreadRegistry::GetCurrent() {
AsanThreadSummary *summary = (AsanThreadSummary *)AsanTSDGet();
if (!summary) {
-#ifdef ANDROID
+#if ASAN_ANDROID
// On Android, libc constructor is called _after_ asan_init, and cleans up
// TSD. Try to figure out if this is still the main thread by the stack
// address. We are not entirely sure that we have correct main thread
@@ -103,32 +104,51 @@ AsanStats &AsanThreadRegistry::GetCurrentThreadStats() {
return (t) ? t->stats() : main_thread_.stats();
}
-AsanStats AsanThreadRegistry::GetAccumulatedStats() {
- ScopedLock lock(&mu_);
+void AsanThreadRegistry::GetAccumulatedStats(AsanStats *stats) {
+ BlockingMutexLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
- return accumulated_stats_;
+ internal_memcpy(stats, &accumulated_stats_, sizeof(accumulated_stats_));
}
uptr AsanThreadRegistry::GetCurrentAllocatedBytes() {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
- return accumulated_stats_.malloced - accumulated_stats_.freed;
+ uptr malloced = accumulated_stats_.malloced;
+ uptr freed = accumulated_stats_.freed;
+ // Return sane value if malloced < freed due to racy
+ // way we update accumulated stats.
+ return (malloced > freed) ? malloced - freed : 1;
}
uptr AsanThreadRegistry::GetHeapSize() {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
- return accumulated_stats_.mmaped;
+ return accumulated_stats_.mmaped - accumulated_stats_.munmaped;
}
uptr AsanThreadRegistry::GetFreeBytes() {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
UpdateAccumulatedStatsUnlocked();
- return accumulated_stats_.mmaped
- - accumulated_stats_.malloced
- - accumulated_stats_.malloced_redzones
- + accumulated_stats_.really_freed
- + accumulated_stats_.really_freed_redzones;
+ uptr total_free = accumulated_stats_.mmaped
+ - accumulated_stats_.munmaped
+ + accumulated_stats_.really_freed
+ + accumulated_stats_.really_freed_redzones;
+ uptr total_used = accumulated_stats_.malloced
+ + accumulated_stats_.malloced_redzones;
+ // Return sane value if total_free < total_used due to racy
+ // way we update accumulated stats.
+ return (total_free > total_used) ? total_free - total_used : 1;
+}
+
+// Return several stats counters with a single call to
+// UpdateAccumulatedStatsUnlocked().
+void AsanThreadRegistry::FillMallocStatistics(AsanMallocStats *malloc_stats) {
+ BlockingMutexLock lock(&mu_);
+ UpdateAccumulatedStatsUnlocked();
+ malloc_stats->blocks_in_use = accumulated_stats_.mallocs;
+ malloc_stats->size_in_use = accumulated_stats_.malloced;
+ malloc_stats->max_size_in_use = max_malloced_memory_;
+ malloc_stats->size_allocated = accumulated_stats_.mmaped;
}
AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) {
@@ -138,7 +158,7 @@ AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) {
}
AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) {
- ScopedLock lock(&mu_);
+ BlockingMutexLock lock(&mu_);
for (u32 tid = 0; tid < n_threads_; tid++) {
AsanThread *t = thread_summaries_[tid]->thread();
if (!t || !(t->fake_stack().StackSize())) continue;
@@ -156,6 +176,12 @@ void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() {
FlushToAccumulatedStatsUnlocked(&t->stats());
}
}
+ // This is not very accurate: we may miss allocation peaks that happen
+ // between two updates of accumulated_stats_. For more accurate bookkeeping
+ // the maximum should be updated on every malloc(), which is unacceptable.
+ if (max_malloced_memory_ < accumulated_stats_.malloced) {
+ max_malloced_memory_ = accumulated_stats_.malloced;
+ }
}
void AsanThreadRegistry::FlushToAccumulatedStatsUnlocked(AsanStats *stats) {
diff --git a/lib/asan/asan_thread_registry.h b/lib/asan/asan_thread_registry.h
index 7037b9edc161..adb1a6d4f32d 100644
--- a/lib/asan/asan_thread_registry.h
+++ b/lib/asan/asan_thread_registry.h
@@ -15,10 +15,10 @@
#ifndef ASAN_THREAD_REGISTRY_H
#define ASAN_THREAD_REGISTRY_H
-#include "asan_lock.h"
#include "asan_stack.h"
#include "asan_stats.h"
#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_mutex.h"
namespace __asan {
@@ -47,12 +47,13 @@ class AsanThreadRegistry {
// Returns stats for GetCurrent(), or stats for
// T0 if GetCurrent() returns 0.
AsanStats &GetCurrentThreadStats();
- // Flushes all thread-local stats to accumulated stats, and returns
+ // Flushes all thread-local stats to accumulated stats, and makes
// a copy of accumulated stats.
- AsanStats GetAccumulatedStats();
+ void GetAccumulatedStats(AsanStats *stats);
uptr GetCurrentAllocatedBytes();
uptr GetHeapSize();
uptr GetFreeBytes();
+ void FillMallocStatistics(AsanMallocStats *malloc_stats);
AsanThreadSummary *FindByTid(u32 tid);
AsanThread *FindThreadByStackAddress(uptr addr);
@@ -68,8 +69,11 @@ class AsanThreadRegistry {
AsanThread main_thread_;
AsanThreadSummary main_thread_summary_;
AsanStats accumulated_stats_;
+ // Required for malloc_zone_statistics() on OS X. This can't be stored in
+ // per-thread AsanStats.
+ uptr max_malloced_memory_;
u32 n_threads_;
- AsanLock mu_;
+ BlockingMutex mu_;
bool inited_;
};
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index 9e899d5865fa..d8ce050641bc 100644
--- a/lib/asan/asan_win.cc
+++ b/lib/asan/asan_win.cc
@@ -17,30 +17,29 @@
#include <dbghelp.h>
#include <stdlib.h>
-#include <new> // FIXME: temporarily needed for placement new in AsanLock.
-
#include "asan_interceptors.h"
#include "asan_internal.h"
-#include "asan_lock.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_mutex.h"
namespace __asan {
// ---------------------- Stacktraces, symbols, etc. ---------------- {{{1
-static AsanLock dbghelp_lock(LINKER_INITIALIZED);
+static BlockingMutex dbghelp_lock(LINKER_INITIALIZED);
static bool dbghelp_initialized = false;
#pragma comment(lib, "dbghelp.lib")
-void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) {
- max_size = max_s;
+void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, bool fast) {
+ (void)fast;
+ stack->max_size = max_s;
void *tmp[kStackTraceMax];
// FIXME: CaptureStackBackTrace might be too slow for us.
// FIXME: Compare with StackWalk64.
// FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc
- uptr cs_ret = CaptureStackBackTrace(1, max_size, tmp, 0),
- offset = 0;
+ uptr cs_ret = CaptureStackBackTrace(1, stack->max_size, tmp, 0);
+ uptr offset = 0;
// Skip the RTL frames by searching for the PC in the stacktrace.
// FIXME: this doesn't work well for the malloc/free stacks yet.
for (uptr i = 0; i < cs_ret; i++) {
@@ -50,86 +49,9 @@ void AsanStackTrace::GetStackTrace(uptr max_s, uptr pc, uptr bp) {
break;
}
- size = cs_ret - offset;
- for (uptr i = 0; i < size; i++)
- trace[i] = (uptr)tmp[i + offset];
-}
-
-bool __asan_WinSymbolize(const void *addr, char *out_buffer, int buffer_size) {
- ScopedLock lock(&dbghelp_lock);
- if (!dbghelp_initialized) {
- SymSetOptions(SYMOPT_DEFERRED_LOADS |
- SYMOPT_UNDNAME |
- SYMOPT_LOAD_LINES);
- CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE));
- // FIXME: We don't call SymCleanup() on exit yet - should we?
- dbghelp_initialized = true;
- }
-
- // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
- char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
- PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
- symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
- symbol->MaxNameLen = MAX_SYM_NAME;
- DWORD64 offset = 0;
- BOOL got_objname = SymFromAddr(GetCurrentProcess(),
- (DWORD64)addr, &offset, symbol);
- if (!got_objname)
- return false;
-
- DWORD unused;
- IMAGEHLP_LINE64 info;
- info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
- BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(),
- (DWORD64)addr, &unused, &info);
- int written = 0;
- out_buffer[0] = '\0';
- // FIXME: it might be useful to print out 'obj' or 'obj+offset' info too.
- if (got_fileline) {
- written += internal_snprintf(out_buffer + written, buffer_size - written,
- " %s %s:%d", symbol->Name,
- info.FileName, info.LineNumber);
- } else {
- written += internal_snprintf(out_buffer + written, buffer_size - written,
- " %s+0x%p", symbol->Name, offset);
- }
- return true;
-}
-
-// ---------------------- AsanLock ---------------- {{{1
-enum LockState {
- LOCK_UNINITIALIZED = 0,
- LOCK_READY = -1,
-};
-
-AsanLock::AsanLock(LinkerInitialized li) {
- // FIXME: see comments in AsanLock::Lock() for the details.
- CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);
-
- CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
- InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
- owner_ = LOCK_READY;
-}
-
-void AsanLock::Lock() {
- if (owner_ == LOCK_UNINITIALIZED) {
- // FIXME: hm, global AsanLock objects are not initialized?!?
- // This might be a side effect of the clang+cl+link Frankenbuild...
- new(this) AsanLock((LinkerInitialized)(LINKER_INITIALIZED + 1));
-
- // FIXME: If it turns out the linker doesn't invoke our
- // constructors, we should probably manually Lock/Unlock all the global
- // locks while we're starting in one thread to avoid double-init races.
- }
- EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
- CHECK(owner_ == LOCK_READY);
- owner_ = GetThreadSelf();
-}
-
-void AsanLock::Unlock() {
- CHECK(owner_ == GetThreadSelf());
- owner_ = LOCK_READY;
- LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+ stack->size = cs_ret - offset;
+ for (uptr i = 0; i < stack->size; i++)
+ stack->trace[i] = (uptr)tmp[i + offset];
}
// ---------------------- TSD ---------------- {{{1
@@ -153,6 +75,10 @@ void AsanTSDSet(void *tsd) {
}
// ---------------------- Various stuff ---------------- {{{1
+void MaybeReexec() {
+ // No need to re-exec on Windows.
+}
+
void *AsanDoesNotSupportStaticLinkage() {
#if defined(_DEBUG)
#error Please build the runtime with a non-debug CRT: /MD or /MT
@@ -176,6 +102,58 @@ void AsanPlatformThreadInit() {
// Nothing here for now.
}
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
+ UNIMPLEMENTED();
+}
+
} // namespace __asan
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE NOINLINE
+bool __asan_symbolize(const void *addr, char *out_buffer, int buffer_size) {
+ BlockingMutexLock lock(&dbghelp_lock);
+ if (!dbghelp_initialized) {
+ SymSetOptions(SYMOPT_DEFERRED_LOADS |
+ SYMOPT_UNDNAME |
+ SYMOPT_LOAD_LINES);
+ CHECK(SymInitialize(GetCurrentProcess(), 0, TRUE));
+ // FIXME: We don't call SymCleanup() on exit yet - should we?
+ dbghelp_initialized = true;
+ }
+
+ // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
+ char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
+ PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ symbol->MaxNameLen = MAX_SYM_NAME;
+ DWORD64 offset = 0;
+ BOOL got_objname = SymFromAddr(GetCurrentProcess(),
+ (DWORD64)addr, &offset, symbol);
+ if (!got_objname)
+ return false;
+
+ DWORD unused;
+ IMAGEHLP_LINE64 info;
+ info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(),
+ (DWORD64)addr, &unused, &info);
+ int written = 0;
+ out_buffer[0] = '\0';
+ // FIXME: it might be useful to print out 'obj' or 'obj+offset' info too.
+ if (got_fileline) {
+ written += internal_snprintf(out_buffer + written, buffer_size - written,
+ " %s %s:%d", symbol->Name,
+ info.FileName, info.LineNumber);
+ } else {
+ written += internal_snprintf(out_buffer + written, buffer_size - written,
+ " %s+0x%p", symbol->Name, offset);
+ }
+ return true;
+}
+} // extern "C"
+
+
#endif // _WIN32
diff --git a/lib/asan/dynamic/Makefile.mk b/lib/asan/dynamic/Makefile.mk
new file mode 100644
index 000000000000..897844e7eed4
--- /dev/null
+++ b/lib/asan/dynamic/Makefile.mk
@@ -0,0 +1,25 @@
+#===- lib/asan/dynamic/Makefile.mk -------------------------*- Makefile -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+ModuleName := asan_dynamic
+SubDirs :=
+
+Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
+ObjNames := $(Sources:%.cc=%.o)
+
+Implementation := Generic
+
+# FIXME: use automatic dependencies?
+Dependencies := $(wildcard $(Dir)/*.h)
+Dependencies += $(wildcard $(Dir)/../../interception/*.h)
+Dependencies += $(wildcard $(Dir)/../../interception/mach_override/*.h)
+Dependencies += $(wildcard $(Dir)/../../sanitizer_common/*.h)
+
+# Define a convenience variable for the asan dynamic functions.
+AsanDynamicFunctions := $(Sources:%.cc=%)
diff --git a/lib/asan/dynamic/asan_interceptors_dynamic.cc b/lib/asan/dynamic/asan_interceptors_dynamic.cc
new file mode 100644
index 000000000000..4f0f7bd2d5f8
--- /dev/null
+++ b/lib/asan/dynamic/asan_interceptors_dynamic.cc
@@ -0,0 +1,111 @@
+//===-- asan_interceptors_dynamic.cc --------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// __DATA,__interpose section of the dynamic runtime library for Mac OS.
+//===----------------------------------------------------------------------===//
+
+#if defined(__APPLE__)
+
+#include "../asan_interceptors.h"
+#include "../asan_intercepted_functions.h"
+
+namespace __asan {
+
+#if !MAC_INTERPOSE_FUNCTIONS
+# error \
+ Dynamic interposing library should be built with -DMAC_INTERPOSE_FUNCTIONS
+#endif
+
+#define INTERPOSE_FUNCTION(function) \
+ { reinterpret_cast<const uptr>(WRAP(function)), \
+ reinterpret_cast<const uptr>(function) }
+
+#define INTERPOSE_FUNCTION_2(function, wrapper) \
+ { reinterpret_cast<const uptr>(wrapper), \
+ reinterpret_cast<const uptr>(function) }
+
+struct interpose_substitution {
+ const uptr replacement;
+ const uptr original;
+};
+
+__attribute__((used))
+const interpose_substitution substitutions[]
+ __attribute__((section("__DATA, __interpose"))) = {
+ INTERPOSE_FUNCTION(strlen),
+ INTERPOSE_FUNCTION(memcmp),
+ INTERPOSE_FUNCTION(memcpy),
+ INTERPOSE_FUNCTION(memmove),
+ INTERPOSE_FUNCTION(memset),
+ INTERPOSE_FUNCTION(strchr),
+ INTERPOSE_FUNCTION(strcat),
+ INTERPOSE_FUNCTION(strncat),
+ INTERPOSE_FUNCTION(strcpy),
+ INTERPOSE_FUNCTION(strncpy),
+ INTERPOSE_FUNCTION(pthread_create),
+ INTERPOSE_FUNCTION(longjmp),
+#if ASAN_INTERCEPT__LONGJMP
+ INTERPOSE_FUNCTION(_longjmp),
+#endif
+#if ASAN_INTERCEPT_SIGLONGJMP
+ INTERPOSE_FUNCTION(siglongjmp),
+#endif
+#if ASAN_INTERCEPT_STRDUP
+ INTERPOSE_FUNCTION(strdup),
+#endif
+#if ASAN_INTERCEPT_STRNLEN
+ INTERPOSE_FUNCTION(strnlen),
+#endif
+#if ASAN_INTERCEPT_INDEX
+ INTERPOSE_FUNCTION_2(index, WRAP(strchr)),
+#endif
+ INTERPOSE_FUNCTION(strcmp),
+ INTERPOSE_FUNCTION(strncmp),
+#if ASAN_INTERCEPT_STRCASECMP_AND_STRNCASECMP
+ INTERPOSE_FUNCTION(strcasecmp),
+ INTERPOSE_FUNCTION(strncasecmp),
+#endif
+ INTERPOSE_FUNCTION(atoi),
+ INTERPOSE_FUNCTION(atol),
+ INTERPOSE_FUNCTION(strtol),
+#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
+ INTERPOSE_FUNCTION(atoll),
+ INTERPOSE_FUNCTION(strtoll),
+#endif
+#if ASAN_INTERCEPT_MLOCKX
+ INTERPOSE_FUNCTION(mlock),
+ INTERPOSE_FUNCTION(munlock),
+ INTERPOSE_FUNCTION(mlockall),
+ INTERPOSE_FUNCTION(munlockall),
+#endif
+ INTERPOSE_FUNCTION(dispatch_async_f),
+ INTERPOSE_FUNCTION(dispatch_sync_f),
+ INTERPOSE_FUNCTION(dispatch_after_f),
+ INTERPOSE_FUNCTION(dispatch_barrier_async_f),
+ INTERPOSE_FUNCTION(dispatch_group_async_f),
+#ifndef MISSING_BLOCKS_SUPPORT
+ INTERPOSE_FUNCTION(dispatch_group_async),
+ INTERPOSE_FUNCTION(dispatch_async),
+ INTERPOSE_FUNCTION(dispatch_after),
+ INTERPOSE_FUNCTION(dispatch_source_set_event_handler),
+ INTERPOSE_FUNCTION(dispatch_source_set_cancel_handler),
+#endif
+ INTERPOSE_FUNCTION(signal),
+ INTERPOSE_FUNCTION(sigaction),
+
+ INTERPOSE_FUNCTION(__CFInitialize),
+ INTERPOSE_FUNCTION(CFStringCreateCopy),
+ INTERPOSE_FUNCTION(free),
+};
+
+} // namespace __asan
+
+#endif // __APPLE__
diff --git a/lib/asan/lit_tests/CMakeLists.txt b/lib/asan/lit_tests/CMakeLists.txt
new file mode 100644
index 000000000000..1609032d4670
--- /dev/null
+++ b/lib/asan/lit_tests/CMakeLists.txt
@@ -0,0 +1,32 @@
+set(ASAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)
+set(ASAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..)
+
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg
+ )
+
+if(COMPILER_RT_CAN_EXECUTE_TESTS)
+ # Run ASan tests only if we're sure we may produce working binaries.
+ set(ASAN_TEST_DEPS
+ clang clang-headers FileCheck count not llvm-nm llvm-symbolizer
+ ${ASAN_RUNTIME_LIBRARIES}
+ )
+ set(ASAN_TEST_PARAMS
+ asan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+ if(LLVM_INCLUDE_TESTS)
+ list(APPEND ASAN_TEST_DEPS AsanUnitTests)
+ endif()
+ add_lit_testsuite(check-asan "Running the AddressSanitizer tests"
+ ${CMAKE_CURRENT_BINARY_DIR}
+ PARAMS ${ASAN_TEST_PARAMS}
+ DEPENDS ${ASAN_TEST_DEPS}
+ )
+ set_target_properties(check-asan PROPERTIES FOLDER "ASan tests")
+endif()
diff --git a/lib/asan/lit_tests/Helpers/blacklist-extra.cc b/lib/asan/lit_tests/Helpers/blacklist-extra.cc
new file mode 100644
index 000000000000..627115cdda2b
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/blacklist-extra.cc
@@ -0,0 +1,5 @@
+// This function is broken, but this file is blacklisted
+int externalBrokenFunction(int argc) {
+ char x[10] = {0};
+ return x[argc * 10]; // BOOM
+}
diff --git a/lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc b/lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc
new file mode 100644
index 000000000000..09aed2112d5e
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/initialization-blacklist-extra.cc
@@ -0,0 +1,15 @@
+int zero_init() { return 0; }
+int badGlobal = zero_init();
+int readBadGlobal() { return badGlobal; }
+
+namespace badNamespace {
+class BadClass {
+ public:
+ BadClass() { value = 0; }
+ int value;
+};
+// Global object with non-trivial constructor.
+BadClass bad_object;
+} // namespace badNamespace
+
+int accessBadObject() { return badNamespace::bad_object.value; }
diff --git a/lib/asan/lit_tests/Helpers/initialization-blacklist.txt b/lib/asan/lit_tests/Helpers/initialization-blacklist.txt
new file mode 100644
index 000000000000..c5f6610937f0
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/initialization-blacklist.txt
@@ -0,0 +1,2 @@
+global-init:*badGlobal*
+global-init-type:*badNamespace::BadClass*
diff --git a/lib/asan/lit_tests/Helpers/initialization-bug-extra.cc b/lib/asan/lit_tests/Helpers/initialization-bug-extra.cc
new file mode 100644
index 000000000000..3c4cb411defa
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/initialization-bug-extra.cc
@@ -0,0 +1,5 @@
+// This file simply declares a dynamically initialized var by the name of 'y'.
+int initY() {
+ return 5;
+}
+int y = initY();
diff --git a/lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc b/lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc
new file mode 100644
index 000000000000..a3d8f190e58b
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/initialization-bug-extra2.cc
@@ -0,0 +1,6 @@
+// 'z' is dynamically initialized global from different TU.
+extern int z;
+int __attribute__((noinline)) initY() {
+ return z + 1;
+}
+int y = initY();
diff --git a/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc b/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc
new file mode 100644
index 000000000000..490b3339054a
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/initialization-nobug-extra.cc
@@ -0,0 +1,9 @@
+// Linker initialized:
+int getAB();
+static int ab = getAB();
+// Function local statics:
+int countCalls();
+static int one = countCalls();
+// Constexpr:
+int getCoolestInteger();
+static int coolest_integer = getCoolestInteger();
diff --git a/lib/asan/lit_tests/Helpers/lit.local.cfg b/lib/asan/lit_tests/Helpers/lit.local.cfg
new file mode 100644
index 000000000000..2fc4d99456b0
--- /dev/null
+++ b/lib/asan/lit_tests/Helpers/lit.local.cfg
@@ -0,0 +1,3 @@
+# Sources in this directory are helper files for tests which test functionality
+# involving multiple translation units.
+config.suffixes = []
diff --git a/lib/asan/lit_tests/Linux/clone_test.cc b/lib/asan/lit_tests/Linux/clone_test.cc
new file mode 100644
index 000000000000..ca13b22425f2
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/clone_test.cc
@@ -0,0 +1,48 @@
+// Regression test for:
+// http://code.google.com/p/address-sanitizer/issues/detail?id=37
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t | FileCheck %s
+
+#include <stdio.h>
+#include <sched.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+int Child(void *arg) {
+ char x[32] = {0}; // Stack gets poisoned.
+ printf("Child: %p\n", x);
+ _exit(1); // NoReturn, stack will remain unpoisoned unless we do something.
+}
+
+int main(int argc, char **argv) {
+ const int kStackSize = 1 << 20;
+ char child_stack[kStackSize + 1];
+ char *sp = child_stack + kStackSize; // Stack grows down.
+ printf("Parent: %p\n", sp);
+ pid_t clone_pid = clone(Child, sp, CLONE_FILES | CLONE_VM, NULL, 0, 0, 0);
+ int status;
+ pid_t wait_result = waitpid(clone_pid, &status, __WCLONE);
+ if (wait_result < 0) {
+ perror("waitpid");
+ return 0;
+ }
+ if (wait_result == clone_pid && WIFEXITED(status)) {
+ // Make sure the child stack was indeed unpoisoned.
+ for (int i = 0; i < kStackSize; i++)
+ child_stack[i] = i;
+ int ret = child_stack[argc - 1];
+ printf("PASSED\n");
+ // CHECK: PASSED
+ return ret;
+ }
+ return 0;
+}
diff --git a/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc b/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc
new file mode 100644
index 000000000000..645fe1c85ed4
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/initialization-bug-any-order.cc
@@ -0,0 +1,37 @@
+// Test to make sure basic initialization order errors are caught.
+// Check that on Linux initialization order bugs are caught
+// independently on order in which we list source files.
+
+// RUN: %clangxx_asan -m64 -O0 %s %p/../Helpers/initialization-bug-extra.cc\
+// RUN: -fsanitize=init-order -o %t && %t 2>&1 \
+// RUN: | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O0 %p/../Helpers/initialization-bug-extra.cc %s\
+// RUN: -fsanitize=init-order -o %t && %t 2>&1 \
+// RUN: | %symbolize | FileCheck %s
+
+// Do not test with optimization -- the error may be optimized away.
+
+#include <cstdio>
+
+// 'y' is a dynamically initialized global residing in a different TU. This
+// dynamic initializer will read the value of 'y' before main starts. The
+// result is undefined behavior, which should be caught by initialization order
+// checking.
+extern int y;
+int __attribute__((noinline)) initX() {
+ return y + 1;
+ // CHECK: {{AddressSanitizer: initialization-order-fiasco}}
+ // CHECK: {{READ of size .* at 0x.* thread T0}}
+ // CHECK: {{#0 0x.* in .*initX.* .*initialization-bug-any-order.cc:}}[[@LINE-3]]
+ // CHECK: {{0x.* is located 0 bytes inside of global variable .*y.*}}
+}
+
+// This initializer begins our initialization order problems.
+static int x = initX();
+
+int main() {
+ // ASan should have caused an exit before main runs.
+ printf("PASS\n");
+ // CHECK-NOT: PASS
+ return 0;
+}
diff --git a/lib/asan/lit_tests/Linux/interception_failure_test.cc b/lib/asan/lit_tests/Linux/interception_failure_test.cc
new file mode 100644
index 000000000000..dfad909f528c
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/interception_failure_test.cc
@@ -0,0 +1,26 @@
+// If user provides his own libc functions, ASan doesn't
+// intercept these functions.
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdlib.h>
+#include <stdio.h>
+
+extern "C" long strtol(const char *nptr, char **endptr, int base) {
+ fprintf(stderr, "my_strtol_interceptor\n");
+ return 0;
+}
+
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return (int)strtol(x, 0, 10);
+ // CHECK: my_strtol_interceptor
+ // CHECK-NOT: heap-use-after-free
+}
diff --git a/lib/asan/lit_tests/Linux/interception_malloc_test.cc b/lib/asan/lit_tests/Linux/interception_malloc_test.cc
new file mode 100644
index 000000000000..8f66788e9a82
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/interception_malloc_test.cc
@@ -0,0 +1,27 @@
+// ASan interceptor can be accessed with __interceptor_ prefix.
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+extern "C" void *__interceptor_malloc(size_t size);
+extern "C" void *malloc(size_t size) {
+ write(2, "malloc call\n", sizeof("malloc call\n") - 1);
+ return __interceptor_malloc(size);
+}
+
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return (int)strtol(x, 0, 10);
+ // CHECK: malloc call
+ // CHECK: heap-use-after-free
+}
diff --git a/lib/asan/lit_tests/Linux/interception_test.cc b/lib/asan/lit_tests/Linux/interception_test.cc
new file mode 100644
index 000000000000..94fb499f2f87
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/interception_test.cc
@@ -0,0 +1,26 @@
+// ASan interceptor can be accessed with __interceptor_ prefix.
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdlib.h>
+#include <stdio.h>
+
+extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base);
+extern "C" long strtol(const char *nptr, char **endptr, int base) {
+ fprintf(stderr, "my_strtol_interceptor\n");
+ return __interceptor_strtol(nptr, endptr, base);
+}
+
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return (int)strtol(x, 0, 10);
+ // CHECK: my_strtol_interceptor
+ // CHECK: heap-use-after-free
+}
diff --git a/lib/asan/lit_tests/Linux/lit.local.cfg b/lib/asan/lit_tests/Linux/lit.local.cfg
new file mode 100644
index 000000000000..57271b8078a4
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/lit.local.cfg
@@ -0,0 +1,9 @@
+def getRoot(config):
+ if not config.parent:
+ return config
+ return getRoot(config.parent)
+
+root = getRoot(config)
+
+if root.host_os not in ['Linux']:
+ config.unsupported = True
diff --git a/lib/asan/lit_tests/Linux/malloc-in-qsort.cc b/lib/asan/lit_tests/Linux/malloc-in-qsort.cc
new file mode 100644
index 000000000000..a3fa255b186d
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/malloc-in-qsort.cc
@@ -0,0 +1,50 @@
+// RUN: %clangxx_asan -O2 %s -o %t
+// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=1 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-FAST
+// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=0 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-SLOW
+
+// Test how well we unwind in presence of qsort in the stack
+// (i.e. if we can unwind through a function compiled w/o frame pointers).
+// https://code.google.com/p/address-sanitizer/issues/detail?id=137
+#include <stdlib.h>
+#include <stdio.h>
+
+int *GlobalPtr;
+
+extern "C" {
+int QsortCallback(const void *a, const void *b) {
+ char *x = (char*)a;
+ char *y = (char*)b;
+ printf("Calling QsortCallback\n");
+ GlobalPtr = new int[10];
+ return (int)*x - (int)*y;
+}
+
+__attribute__((noinline))
+void MyQsort(char *a, size_t size) {
+ printf("Calling qsort\n");
+ qsort(a, size, sizeof(char), QsortCallback);
+ printf("Done\n"); // Avoid tail call.
+}
+} // extern "C"
+
+int main() {
+ char a[2] = {1, 2};
+ MyQsort(a, 2);
+ return GlobalPtr[10];
+}
+
+// Fast unwind: can not unwind through qsort.
+// FIXME: this test does not properly work with slow unwind yet.
+
+// CHECK-FAST: ERROR: AddressSanitizer: heap-buffer-overflow
+// CHECK-FAST: is located 0 bytes to the right
+// CHECK-FAST: #0{{.*}}operator new
+// CHECK-FAST-NEXT: #1{{.*}}QsortCallback
+// CHECK-FAST-NOT: MyQsort
+//
+// CHECK-SLOW: ERROR: AddressSanitizer: heap-buffer-overflow
+// CHECK-SLOW: is located 0 bytes to the right
+// CHECK-SLOW: #0{{.*}}operator new
+// CHECK-SLOW-NEXT: #1{{.*}}QsortCallback
+// CHECK-SLOW: #{{.*}}MyQsort
+// CHECK-SLOW-NEXT: #{{.*}}main
diff --git a/lib/asan/lit_tests/Linux/overflow-in-qsort.cc b/lib/asan/lit_tests/Linux/overflow-in-qsort.cc
new file mode 100644
index 000000000000..c298991a8348
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/overflow-in-qsort.cc
@@ -0,0 +1,47 @@
+// RUN: %clangxx_asan -O2 %s -o %t
+// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=1 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-FAST
+// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=0 %t 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK-SLOW
+
+// Test how well we unwind in presence of qsort in the stack
+// (i.e. if we can unwind through a function compiled w/o frame pointers).
+// https://code.google.com/p/address-sanitizer/issues/detail?id=137
+#include <stdlib.h>
+#include <stdio.h>
+
+int global_array[10];
+volatile int one = 1;
+
+extern "C" {
+int QsortCallback(const void *a, const void *b) {
+ char *x = (char*)a;
+ char *y = (char*)b;
+ printf("Calling QsortCallback\n");
+ global_array[one * 10] = 0; // BOOM
+ return (int)*x - (int)*y;
+}
+
+__attribute__((noinline))
+void MyQsort(char *a, size_t size) {
+ printf("Calling qsort\n");
+ qsort(a, size, sizeof(char), QsortCallback);
+ printf("Done\n"); // Avoid tail call.
+}
+} // extern "C"
+
+int main() {
+ char a[2] = {1, 2};
+ MyQsort(a, 2);
+}
+
+// Fast unwind: can not unwind through qsort.
+
+// CHECK-FAST: ERROR: AddressSanitizer: global-buffer-overflow
+// CHECK-FAST: #0{{.*}} in QsortCallback
+// CHECK-FAST-NOT: MyQsort
+// CHECK-FAST: is located 0 bytes to the right of global variable 'global_array
+
+// CHECK-SLOW: ERROR: AddressSanitizer: global-buffer-overflow
+// CHECK-SLOW: #0{{.*}} in QsortCallback
+// CHECK-SLOW: #{{.*}} in MyQsort
+// CHECK-SLOW: #{{.*}} in main
+// CHECK-SLOW: is located 0 bytes to the right of global variable 'global_array
diff --git a/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc b/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc
new file mode 100644
index 000000000000..5026e24e424d
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/rlimit_mmap_test.cc
@@ -0,0 +1,16 @@
+// Check that we properly report mmap failure.
+// RUN: %clangxx_asan %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+static volatile void *x;
+
+int main(int argc, char **argv) {
+ struct rlimit mmap_resource_limit = { 0, 0 };
+ assert(0 == setrlimit(RLIMIT_AS, &mmap_resource_limit));
+ x = malloc(10000000);
+// CHECK: AddressSanitizer is unable to mmap
+ return 0;
+}
diff --git a/lib/asan/lit_tests/Linux/swapcontext_test.cc b/lib/asan/lit_tests/Linux/swapcontext_test.cc
new file mode 100644
index 000000000000..0404b4f602bd
--- /dev/null
+++ b/lib/asan/lit_tests/Linux/swapcontext_test.cc
@@ -0,0 +1,66 @@
+// Check that ASan plays well with easy cases of makecontext/swapcontext.
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+ucontext_t orig_context;
+ucontext_t child_context;
+
+void Child(int mode) {
+ char x[32] = {0}; // Stack gets poisoned.
+ printf("Child: %p\n", x);
+ // (a) Do nothing, just return to parent function.
+ // (b) Jump into the original function. Stack remains poisoned unless we do
+ // something.
+ if (mode == 1) {
+ if (swapcontext(&child_context, &orig_context) < 0) {
+ perror("swapcontext");
+ _exit(0);
+ }
+ }
+}
+
+int Run(int arg, int mode) {
+ const int kStackSize = 1 << 20;
+ char child_stack[kStackSize + 1];
+ printf("Child stack: %p\n", child_stack);
+ // Setup child context.
+ getcontext(&child_context);
+ child_context.uc_stack.ss_sp = child_stack;
+ child_context.uc_stack.ss_size = kStackSize / 2;
+ if (mode == 0) {
+ child_context.uc_link = &orig_context;
+ }
+ makecontext(&child_context, (void (*)())Child, 1, mode);
+ if (swapcontext(&orig_context, &child_context) < 0) {
+ perror("swapcontext");
+ return 0;
+ }
+ // Touch childs's stack to make sure it's unpoisoned.
+ for (int i = 0; i < kStackSize; i++) {
+ child_stack[i] = i;
+ }
+ return child_stack[arg];
+}
+
+int main(int argc, char **argv) {
+ // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext
+ int ret = 0;
+ ret += Run(argc - 1, 0);
+ printf("Test1 passed\n");
+ // CHECK: Test1 passed
+ ret += Run(argc - 1, 1);
+ printf("Test2 passed\n");
+ // CHECK: Test2 passed
+ return ret;
+}
diff --git a/lib/asan/output_tests/dlclose-test-so.cc b/lib/asan/lit_tests/SharedLibs/dlclose-test-so.cc
index 73e00507358a..73e00507358a 100644
--- a/lib/asan/output_tests/dlclose-test-so.cc
+++ b/lib/asan/lit_tests/SharedLibs/dlclose-test-so.cc
diff --git a/lib/asan/lit_tests/SharedLibs/lit.local.cfg b/lib/asan/lit_tests/SharedLibs/lit.local.cfg
new file mode 100644
index 000000000000..b3677c17a0f2
--- /dev/null
+++ b/lib/asan/lit_tests/SharedLibs/lit.local.cfg
@@ -0,0 +1,4 @@
+# Sources in this directory are compiled as shared libraries and used by
+# tests in parent directory.
+
+config.suffixes = []
diff --git a/lib/asan/output_tests/shared-lib-test-so.cc b/lib/asan/lit_tests/SharedLibs/shared-lib-test-so.cc
index 686a24578082..686a24578082 100644
--- a/lib/asan/output_tests/shared-lib-test-so.cc
+++ b/lib/asan/lit_tests/SharedLibs/shared-lib-test-so.cc
diff --git a/lib/asan/lit_tests/Unit/lit.cfg b/lib/asan/lit_tests/Unit/lit.cfg
new file mode 100644
index 000000000000..243eb7fbeec0
--- /dev/null
+++ b/lib/asan/lit_tests/Unit/lit.cfg
@@ -0,0 +1,27 @@
+# -*- Python -*-
+
+import os
+
+def get_required_attr(config, attr_name):
+ attr_value = getattr(config, attr_name, None)
+ if not attr_value:
+ lit.fatal("No attribute %r in test configuration! You may need to run "
+ "tests from your build directory or add this attribute "
+ "to lit.site.cfg " % attr_name)
+ return attr_value
+
+# Setup attributes common for all compiler-rt projects.
+llvm_src_root = get_required_attr(config, 'llvm_src_root')
+compiler_rt_lit_unit_cfg = os.path.join(llvm_src_root, "projects",
+ "compiler-rt", "lib",
+ "lit.common.unit.cfg")
+lit.load_config(config, compiler_rt_lit_unit_cfg)
+
+# Setup config name.
+config.name = 'AddressSanitizer-Unit'
+
+# Setup test source and exec root. For unit tests, we define
+# it as build directory with ASan unit tests.
+asan_binary_dir = get_required_attr(config, "asan_binary_dir")
+config.test_exec_root = os.path.join(asan_binary_dir, "tests")
+config.test_source_root = config.test_exec_root
diff --git a/lib/asan/lit_tests/Unit/lit.site.cfg.in b/lib/asan/lit_tests/Unit/lit.site.cfg.in
new file mode 100644
index 000000000000..401c3a8cc2eb
--- /dev/null
+++ b/lib/asan/lit_tests/Unit/lit.site.cfg.in
@@ -0,0 +1,10 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+config.target_triple = "@TARGET_TRIPLE@"
+config.llvm_src_root = "@LLVM_SOURCE_DIR@"
+config.build_type = "@CMAKE_BUILD_TYPE@"
+config.asan_binary_dir = "@ASAN_BINARY_DIR@"
+
+# Let the main config do the real work.
+lit.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/Unit/lit.cfg")
diff --git a/lib/asan/lit_tests/blacklist.cc b/lib/asan/lit_tests/blacklist.cc
new file mode 100644
index 000000000000..6cfc1500c503
--- /dev/null
+++ b/lib/asan/lit_tests/blacklist.cc
@@ -0,0 +1,44 @@
+// Test the blacklist functionality of ASan
+
+// RUN: echo "fun:*brokenFunction*" > %tmp
+// RUN: echo "global:*badGlobal*" >> %tmp
+// RUN: echo "src:*blacklist-extra.cc" >> %tmp
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O0 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O1 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O2 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m64 -O3 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O0 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O1 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O2 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -m32 -O3 %s -o %t \
+// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1
+
+// badGlobal is accessed improperly, but we blacklisted it.
+int badGlobal;
+int readBadGlobal() {
+ return (&badGlobal)[1];
+}
+
+// A function which is broken, but excluded in the blacklist.
+int brokenFunction(int argc) {
+ char x[10] = {0};
+ return x[argc * 10]; // BOOM
+}
+
+// This function is defined in Helpers/blacklist-extra.cc, a source file which
+// is blacklisted by name
+int externalBrokenFunction(int x);
+
+int main(int argc, char **argv) {
+ brokenFunction(argc);
+ int x = readBadGlobal();
+ externalBrokenFunction(argc);
+ return 0;
+}
diff --git a/lib/asan/lit_tests/deep_stack_uaf.cc b/lib/asan/lit_tests/deep_stack_uaf.cc
new file mode 100644
index 000000000000..7b32798fefcc
--- /dev/null
+++ b/lib/asan/lit_tests/deep_stack_uaf.cc
@@ -0,0 +1,36 @@
+// Check that we can store lots of stack frames if asked to.
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t 2>&1
+// RUN: ASAN_OPTIONS=malloc_context_size=120:redzone=512 %t 2>&1 | \
+// RUN: %symbolize | FileCheck %s
+
+// RUN: %clangxx_asan -m32 -O0 %s -o %t 2>&1
+// RUN: ASAN_OPTIONS=malloc_context_size=120:redzone=512 %t 2>&1 | \
+// RUN: %symbolize | FileCheck %s
+#include <stdlib.h>
+#include <stdio.h>
+
+template <int depth>
+struct DeepFree {
+ static void free(char *x) {
+ DeepFree<depth - 1>::free(x);
+ }
+};
+
+template<>
+struct DeepFree<0> {
+ static void free(char *x) {
+ ::free(x);
+ }
+};
+
+int main() {
+ char *x = (char*)malloc(10);
+ // deep_free(x);
+ DeepFree<200>::free(x);
+ return x[5];
+ // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
+ // CHECK: DeepFree<36>
+ // CHECK: DeepFree<98>
+ // CHECK: DeepFree<115>
+}
diff --git a/lib/asan/lit_tests/deep_tail_call.cc b/lib/asan/lit_tests/deep_tail_call.cc
new file mode 100644
index 000000000000..6aa15e81f6ec
--- /dev/null
+++ b/lib/asan/lit_tests/deep_tail_call.cc
@@ -0,0 +1,24 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+// CHECK: AddressSanitizer: global-buffer-overflow
+int global[10];
+// CHECK: {{#0.*call4}}
+void __attribute__((noinline)) call4(int i) { global[i+10]++; }
+// CHECK: {{#1.*call3}}
+void __attribute__((noinline)) call3(int i) { call4(i); }
+// CHECK: {{#2.*call2}}
+void __attribute__((noinline)) call2(int i) { call3(i); }
+// CHECK: {{#3.*call1}}
+void __attribute__((noinline)) call1(int i) { call2(i); }
+// CHECK: {{#4.*main}}
+int main(int argc, char **argv) {
+ call1(argc);
+ return global[0];
+}
diff --git a/lib/asan/lit_tests/deep_thread_stack.cc b/lib/asan/lit_tests/deep_thread_stack.cc
new file mode 100644
index 000000000000..781508d61616
--- /dev/null
+++ b/lib/asan/lit_tests/deep_thread_stack.cc
@@ -0,0 +1,61 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+#include <pthread.h>
+
+int *x;
+
+void *AllocThread(void *arg) {
+ x = new int;
+ *x = 42;
+ return NULL;
+}
+
+void *FreeThread(void *arg) {
+ delete x;
+ return NULL;
+}
+
+void *AccessThread(void *arg) {
+ *x = 43; // BOOM
+ return NULL;
+}
+
+typedef void* (*callback_type)(void* arg);
+
+void *RunnerThread(void *function) {
+ pthread_t thread;
+ pthread_create(&thread, NULL, (callback_type)function, NULL);
+ pthread_join(thread, NULL);
+ return NULL;
+}
+
+void RunThread(callback_type function) {
+ pthread_t runner;
+ pthread_create(&runner, NULL, RunnerThread, (void*)function);
+ pthread_join(runner, NULL);
+}
+
+int main(int argc, char *argv[]) {
+ RunThread(AllocThread);
+ RunThread(FreeThread);
+ RunThread(AccessThread);
+ return (x != 0);
+}
+
+// CHECK: AddressSanitizer: heap-use-after-free
+// CHECK: WRITE of size 4 at 0x{{.*}} thread T[[ACCESS_THREAD:[0-9]+]]
+// CHECK: freed by thread T[[FREE_THREAD:[0-9]+]] here:
+// CHECK: previously allocated by thread T[[ALLOC_THREAD:[0-9]+]] here:
+// CHECK: Thread T[[ACCESS_THREAD]] created by T[[ACCESS_RUNNER:[0-9]+]] here:
+// CHECK: Thread T[[ACCESS_RUNNER]] created by T0 here:
+// CHECK: Thread T[[FREE_THREAD]] created by T[[FREE_RUNNER:[0-9]+]] here:
+// CHECK: Thread T[[FREE_RUNNER]] created by T0 here:
+// CHECK: Thread T[[ALLOC_THREAD]] created by T[[ALLOC_RUNNER:[0-9]+]] here:
+// CHECK: Thread T[[ALLOC_RUNNER]] created by T0 here:
diff --git a/lib/asan/output_tests/default_options.cc b/lib/asan/lit_tests/default_options.cc
index d6c70291e6ff..950a7d879194 100644
--- a/lib/asan/output_tests/default_options.cc
+++ b/lib/asan/lit_tests/default_options.cc
@@ -1,12 +1,15 @@
+// RUN: %clangxx_asan -O2 %s -o %t
+// RUN: %t 2>&1 | FileCheck %s
+
const char *kAsanDefaultOptions="verbosity=1 foo=bar";
extern "C"
__attribute__((no_address_safety_analysis))
const char *__asan_default_options() {
+ // CHECK: Using the defaults from __asan_default_options: {{.*}} foo=bar
return kAsanDefaultOptions;
}
int main() {
- // Check-Common: foo=bar
return 0;
}
diff --git a/lib/asan/output_tests/dlclose-test.cc b/lib/asan/lit_tests/dlclose-test.cc
index 16126eb9042f..229f508294bf 100644
--- a/lib/asan/output_tests/dlclose-test.cc
+++ b/lib/asan/lit_tests/dlclose-test.cc
@@ -1,14 +1,3 @@
-//===----------- dlclose-test.cc --------------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
// Regression test for
// http://code.google.com/p/address-sanitizer/issues/detail?id=19
// Bug description:
@@ -19,7 +8,32 @@
// 5. application starts using this mmaped memory, but asan still thinks there
// are globals.
// 6. BOOM
-//===----------------------------------------------------------------------===//
+
+// RUN: %clangxx_asan -m64 -O0 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %p/SharedLibs/dlclose-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s
+
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
@@ -69,6 +83,6 @@ int main(int argc, char *argv[]) {
}
addr[1] = 2; // BOOM (if the bug is not fixed).
printf("PASS\n");
- // Check-Common: PASS
+ // CHECK: PASS
return 0;
}
diff --git a/lib/asan/lit_tests/force_inline_opt0.cc b/lib/asan/lit_tests/force_inline_opt0.cc
new file mode 100644
index 000000000000..955ce38156fb
--- /dev/null
+++ b/lib/asan/lit_tests/force_inline_opt0.cc
@@ -0,0 +1,14 @@
+// This test checks that we are no instrumenting a memory access twice
+// (before and after inlining)
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t
+__attribute__((always_inline))
+void foo(int *x) {
+ *x = 0;
+}
+
+int main() {
+ int x;
+ foo(&x);
+ return x;
+}
diff --git a/lib/asan/lit_tests/global-overflow.cc b/lib/asan/lit_tests/global-overflow.cc
new file mode 100644
index 000000000000..6a2f12e106fe
--- /dev/null
+++ b/lib/asan/lit_tests/global-overflow.cc
@@ -0,0 +1,25 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+#include <string.h>
+int main(int argc, char **argv) {
+ static char XXX[10];
+ static char YYY[10];
+ static char ZZZ[10];
+ memset(XXX, 0, 10);
+ memset(YYY, 0, 10);
+ memset(ZZZ, 0, 10);
+ int res = YYY[argc * 10]; // BOOOM
+ // CHECK: {{READ of size 1 at 0x.* thread T0}}
+ // CHECK: {{ #0 0x.* in _?main .*global-overflow.cc:}}[[@LINE-2]]
+ // CHECK: {{0x.* is located 0 bytes to the right of global variable}}
+ // CHECK: {{.*YYY.* of size 10}}
+ res += XXX[argc] + ZZZ[argc];
+ return res;
+}
diff --git a/lib/asan/lit_tests/heap-overflow.cc b/lib/asan/lit_tests/heap-overflow.cc
new file mode 100644
index 000000000000..2648ec7e5f1f
--- /dev/null
+++ b/lib/asan/lit_tests/heap-overflow.cc
@@ -0,0 +1,38 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+
+#include <stdlib.h>
+#include <string.h>
+int main(int argc, char **argv) {
+ char *x = (char*)malloc(10 * sizeof(char));
+ memset(x, 0, 10);
+ int res = x[argc * 10]; // BOOOM
+ // CHECK: {{READ of size 1 at 0x.* thread T0}}
+ // CHECK: {{ #0 0x.* in _?main .*heap-overflow.cc:}}[[@LINE-2]]
+ // CHECK: {{0x.* is located 0 bytes to the right of 10-byte region}}
+ // CHECK: {{allocated by thread T0 here:}}
+
+ // CHECK-Linux: {{ #0 0x.* in .*malloc}}
+ // CHECK-Linux: {{ #1 0x.* in main .*heap-overflow.cc:21}}
+
+ // CHECK-Darwin: {{ #0 0x.* in .*mz_malloc.*}}
+ // CHECK-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}}
+ // CHECK-Darwin: {{ #2 0x.* in malloc.*}}
+ // CHECK-Darwin: {{ #3 0x.* in _?main .*heap-overflow.cc:21}}
+ free(x);
+ return res;
+}
diff --git a/lib/asan/lit_tests/initialization-blacklist.cc b/lib/asan/lit_tests/initialization-blacklist.cc
new file mode 100644
index 000000000000..f8df24c68ea6
--- /dev/null
+++ b/lib/asan/lit_tests/initialization-blacklist.cc
@@ -0,0 +1,32 @@
+// Test for blacklist functionality of initialization-order checker.
+
+// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
+// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
+// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
+// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
+// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
+// RUN: -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-blacklist-extra.cc\
+// RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt \
+// RUN: -fsanitize=init-order -o %t && %t 2>&1
+
+// Function is defined in another TU.
+int readBadGlobal();
+int x = readBadGlobal(); // init-order bug.
+
+// Function is defined in another TU.
+int accessBadObject();
+int y = accessBadObject(); // init-order bug.
+
+int main(int argc, char **argv) {
+ return argc + x + y - 1;
+}
diff --git a/lib/asan/lit_tests/initialization-bug.cc b/lib/asan/lit_tests/initialization-bug.cc
new file mode 100644
index 000000000000..8f4e33ef5a35
--- /dev/null
+++ b/lib/asan/lit_tests/initialization-bug.cc
@@ -0,0 +1,46 @@
+// Test to make sure basic initialization order errors are caught.
+
+// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-bug-extra2.cc\
+// RUN: -fsanitize=init-order -o %t && %t 2>&1 \
+// RUN: | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-bug-extra2.cc\
+// RUN: -fsanitize=init-order -o %t && %t 2>&1 \
+// RUN: | %symbolize | FileCheck %s
+
+// Do not test with optimization -- the error may be optimized away.
+
+#include <cstdio>
+
+// The structure of the test is:
+// "x", "y", "z" are dynamically initialized globals.
+// Value of "x" depends on "y", value of "y" depends on "z".
+// "x" and "z" are defined in this TU, "y" is defined in another one.
+// Thus we shoud stably report initialization order fiasco independently of
+// the translation unit order.
+
+int initZ() {
+ return 5;
+}
+int z = initZ();
+
+// 'y' is a dynamically initialized global residing in a different TU. This
+// dynamic initializer will read the value of 'y' before main starts. The
+// result is undefined behavior, which should be caught by initialization order
+// checking.
+extern int y;
+int __attribute__((noinline)) initX() {
+ return y + 1;
+ // CHECK: {{AddressSanitizer: initialization-order-fiasco}}
+ // CHECK: {{READ of size .* at 0x.* thread T0}}
+ // CHECK: {{0x.* is located 0 bytes inside of global variable .*(y|z).*}}
+}
+
+// This initializer begins our initialization order problems.
+static int x = initX();
+
+int main() {
+ // ASan should have caused an exit before main runs.
+ printf("PASS\n");
+ // CHECK-NOT: PASS
+ return 0;
+}
diff --git a/lib/asan/lit_tests/initialization-nobug.cc b/lib/asan/lit_tests/initialization-nobug.cc
new file mode 100644
index 000000000000..1b8961606811
--- /dev/null
+++ b/lib/asan/lit_tests/initialization-nobug.cc
@@ -0,0 +1,67 @@
+// A collection of various initializers which shouldn't trip up initialization
+// order checking. If successful, this will just return 0.
+
+// RUN: %clangxx_asan -m64 -O0 %s %p/Helpers/initialization-nobug-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m64 -O1 %s %p/Helpers/initialization-nobug-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m64 -O2 %s %p/Helpers/initialization-nobug-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m64 -O3 %s %p/Helpers/initialization-nobug-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-nobug-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m32 -O0 %s %p/Helpers/initialization-nobug-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m32 -O1 %s %p/Helpers/initialization-nobug-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m32 -O2 %s %p/Helpers/initialization-nobug-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
+// RUN: %clangxx_asan -m32 -O3 %s %p/Helpers/initialization-nobug-extra.cc\
+// RUN: --std=c++11 -fsanitize=init-order -o %t && %t 2>&1
+
+// Simple access:
+// Make sure that accessing a global in the same TU is safe
+
+bool condition = true;
+int initializeSameTU() {
+ return condition ? 0x2a : 052;
+}
+int sameTU = initializeSameTU();
+
+// Linker initialized:
+// Check that access to linker initialized globals originating from a different
+// TU's initializer is safe.
+
+int A = (1 << 1) + (1 << 3) + (1 << 5), B;
+int getAB() {
+ return A * B;
+}
+
+// Function local statics:
+// Check that access to function local statics originating from a different
+// TU's initializer is safe.
+
+int countCalls() {
+ static int calls;
+ return ++calls;
+}
+
+// Constexpr:
+// We need to check that a global variable initialized with a constexpr
+// constructor can be accessed during dynamic initialization (as a constexpr
+// constructor implies that it was initialized during constant initialization,
+// not dynamic initialization).
+
+class Integer {
+ private:
+ int value;
+
+ public:
+ constexpr Integer(int x = 0) : value(x) {}
+ int getValue() {return value;}
+};
+Integer coolestInteger(42);
+int getCoolestInteger() { return coolestInteger.getValue(); }
+
+int main() { return 0; }
diff --git a/lib/asan/lit_tests/interface_symbols.c b/lib/asan/lit_tests/interface_symbols.c
new file mode 100644
index 000000000000..f3167f562922
--- /dev/null
+++ b/lib/asan/lit_tests/interface_symbols.c
@@ -0,0 +1,28 @@
+// Check the presense of interface symbols in compiled file.
+
+// RUN: %clang -fsanitize=address -dead_strip -O2 %s -o %t.exe
+// RUN: nm %t.exe | grep " T " | sed "s/.* T //" \
+// RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \
+// RUN: | grep -v "__asan_malloc_hook" \
+// RUN: | grep -v "__asan_free_hook" \
+// RUN: | grep -v "__asan_symbolize" \
+// RUN: | grep -v "__asan_default_options" \
+// RUN: | grep -v "__asan_on_error" > %t.symbols
+// RUN: cat %p/../../../include/sanitizer/asan_interface.h \
+// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \
+// RUN: | grep -v "OPTIONAL" \
+// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \
+// RUN: > %t.interface
+// RUN: echo __asan_report_load1 >> %t.interface
+// RUN: echo __asan_report_load2 >> %t.interface
+// RUN: echo __asan_report_load4 >> %t.interface
+// RUN: echo __asan_report_load8 >> %t.interface
+// RUN: echo __asan_report_load16 >> %t.interface
+// RUN: echo __asan_report_store1 >> %t.interface
+// RUN: echo __asan_report_store2 >> %t.interface
+// RUN: echo __asan_report_store4 >> %t.interface
+// RUN: echo __asan_report_store8 >> %t.interface
+// RUN: echo __asan_report_store16 >> %t.interface
+// RUN: cat %t.interface | sort -u | diff %t.symbols -
+
+int main() { return 0; }
diff --git a/lib/asan/lit_tests/large_func_test.cc b/lib/asan/lit_tests/large_func_test.cc
new file mode 100644
index 000000000000..a74828811f74
--- /dev/null
+++ b/lib/asan/lit_tests/large_func_test.cc
@@ -0,0 +1,62 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+
+#include <stdlib.h>
+__attribute__((noinline))
+static void LargeFunction(int *x, int zero) {
+ x[0]++;
+ x[1]++;
+ x[2]++;
+ x[3]++;
+ x[4]++;
+ x[5]++;
+ x[6]++;
+ x[7]++;
+ x[8]++;
+ x[9]++;
+
+ // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}}
+ // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}}
+ // CHECK: {{READ of size 4 at 0x.* thread T0}}
+ x[zero + 111]++; // we should report this exact line
+ // atos incorrectly extracts the symbol name for the static functions on
+ // Darwin.
+ // CHECK-Linux: {{#0 0x.* in LargeFunction.*large_func_test.cc:}}[[@LINE-3]]
+ // CHECK-Darwin: {{#0 0x.* in .*LargeFunction.*large_func_test.cc}}:[[@LINE-4]]
+
+ x[10]++;
+ x[11]++;
+ x[12]++;
+ x[13]++;
+ x[14]++;
+ x[15]++;
+ x[16]++;
+ x[17]++;
+ x[18]++;
+ x[19]++;
+}
+
+int main(int argc, char **argv) {
+ int *x = new int[100];
+ LargeFunction(x, argc - 1);
+ // CHECK: {{ #1 0x.* in _?main .*large_func_test.cc:}}[[@LINE-1]]
+ // CHECK: {{0x.* is located 44 bytes to the right of 400-byte region}}
+ // CHECK: {{allocated by thread T0 here:}}
+ // CHECK: {{ #0 0x.* in operator new.*}}
+ // CHECK: {{ #1 0x.* in _?main .*large_func_test.cc:}}[[@LINE-6]]
+ delete x;
+}
diff --git a/lib/asan/lit_tests/lit.cfg b/lib/asan/lit_tests/lit.cfg
new file mode 100644
index 000000000000..7875281b1f2f
--- /dev/null
+++ b/lib/asan/lit_tests/lit.cfg
@@ -0,0 +1,97 @@
+# -*- Python -*-
+
+import os
+
+# Setup config name.
+config.name = 'AddressSanitizer'
+
+# Setup source root.
+config.test_source_root = os.path.dirname(__file__)
+
+def DisplayNoConfigMessage():
+ lit.fatal("No site specific configuration available! " +
+ "Try running your test from the build tree or running " +
+ "make check-asan")
+
+# Figure out LLVM source root.
+llvm_src_root = getattr(config, 'llvm_src_root', None)
+if llvm_src_root is None:
+ # We probably haven't loaded the site-specific configuration: the user
+ # is likely trying to run a test file directly, and the site configuration
+ # wasn't created by the build system.
+ asan_site_cfg = lit.params.get('asan_site_config', None)
+ if (asan_site_cfg) and (os.path.exists(asan_site_cfg)):
+ lit.load_config(config, asan_site_cfg)
+ raise SystemExit
+
+ # Try to guess the location of site-specific configuration using llvm-config
+ # util that can point where the build tree is.
+ llvm_config = lit.util.which("llvm-config", config.environment["PATH"])
+ if not llvm_config:
+ DisplayNoConfigMessage()
+
+ # Validate that llvm-config points to the same source tree.
+ llvm_src_root = lit.util.capture(["llvm-config", "--src-root"]).strip()
+ asan_test_src_root = os.path.join(llvm_src_root, "projects", "compiler-rt",
+ "lib", "asan", "lit_tests")
+ if (os.path.realpath(asan_test_src_root) !=
+ os.path.realpath(config.test_source_root)):
+ DisplayNoConfigMessage()
+
+ # Find out the presumed location of generated site config.
+ llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip()
+ asan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt",
+ "lib", "asan", "lit_tests", "lit.site.cfg")
+ if (not asan_site_cfg) or (not os.path.exists(asan_site_cfg)):
+ DisplayNoConfigMessage()
+
+ lit.load_config(config, asan_site_cfg)
+ raise SystemExit
+
+# Setup attributes common for all compiler-rt projects.
+compiler_rt_lit_cfg = os.path.join(llvm_src_root, "projects", "compiler-rt",
+ "lib", "lit.common.cfg")
+if (not compiler_rt_lit_cfg) or (not os.path.exists(compiler_rt_lit_cfg)):
+ lit.fatal("Can't find common compiler-rt lit config at: %r"
+ % compiler_rt_lit_cfg)
+lit.load_config(config, compiler_rt_lit_cfg)
+
+# Setup default compiler flags used with -fsanitize=address option.
+# FIXME: Review the set of required flags and check if it can be reduced.
+clang_asan_cxxflags = ("-ccc-cxx "
+ + "-fsanitize=address "
+ + "-mno-omit-leaf-frame-pointer "
+ + "-fno-omit-frame-pointer "
+ + "-fno-optimize-sibling-calls "
+ + "-g")
+config.substitutions.append( ("%clangxx_asan ", (" " + config.clang + " " +
+ clang_asan_cxxflags + " ")) )
+
+# Setup path to external LLVM symbolizer to run AddressSanitizer output tests.
+llvm_tools_dir = getattr(config, 'llvm_tools_dir', None)
+if llvm_tools_dir:
+ config.environment['LLVM_SYMBOLIZER_PATH'] = os.path.join(
+ llvm_tools_dir, "llvm-symbolizer")
+
+# Setup path to symbolizer script.
+# FIXME: Instead we should copy this script to the build tree and point
+# at it there.
+asan_source_dir = os.path.join(config.test_source_root, "..")
+symbolizer = os.path.join(asan_source_dir,
+ 'scripts', 'asan_symbolize.py')
+if not os.path.exists(symbolizer):
+ lit.fatal("Can't find symbolizer script on path %r" % symbolizer)
+# Define %symbolize substitution that filters output through
+# symbolizer and c++filt (for demangling).
+config.substitutions.append( ("%symbolize ", (" " + symbolizer +
+ " | c++filt " )))
+
+# Define CHECK-%os to check for OS-dependent output.
+config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os)))
+
+# Default test suffixes.
+config.suffixes = ['.c', '.cc', '.cpp']
+
+# AddressSanitizer tests are currently supported on Linux and Darwin only.
+if config.host_os not in ['Linux', 'Darwin']:
+ config.unsupported = True
diff --git a/lib/asan/lit_tests/lit.site.cfg.in b/lib/asan/lit_tests/lit.site.cfg.in
new file mode 100644
index 000000000000..cf439309c6ad
--- /dev/null
+++ b/lib/asan/lit_tests/lit.site.cfg.in
@@ -0,0 +1,20 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+config.target_triple = "@TARGET_TRIPLE@"
+config.host_os = "@HOST_OS@"
+config.llvm_src_root = "@LLVM_SOURCE_DIR@"
+config.llvm_obj_root = "@LLVM_BINARY_DIR@"
+config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
+config.clang = "@LLVM_BINARY_DIR@/bin/clang"
+
+# LLVM tools dir can be passed in lit parameters, so try to
+# apply substitution.
+try:
+ config.llvm_tools_dir = config.llvm_tools_dir % lit.params
+except KeyError,e:
+ key, = e.args
+ lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key))
+
+# Let the main config do the real work.
+lit.load_config(config, "@ASAN_SOURCE_DIR@/lit_tests/lit.cfg")
diff --git a/lib/asan/lit_tests/log-path_test.cc b/lib/asan/lit_tests/log-path_test.cc
new file mode 100644
index 000000000000..1072670fbff4
--- /dev/null
+++ b/lib/asan/lit_tests/log-path_test.cc
@@ -0,0 +1,39 @@
+// RUN: %clangxx_asan %s -o %t
+
+// Regular run.
+// RUN: not %t 2> %t.out
+// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.out
+
+// Good log_path.
+// RUN: rm -f %t.log.*
+// RUN: ASAN_OPTIONS=log_path=%t.log not %t 2> %t.out
+// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.log.*
+
+// Invalid log_path.
+// RUN: ASAN_OPTIONS=log_path=/INVALID not %t 2> %t.out
+// RUN: FileCheck %s --check-prefix=CHECK-INVALID < %t.out
+
+// Too long log_path.
+// RUN: ASAN_OPTIONS=log_path=`for((i=0;i<10000;i++)); do echo -n $i; done` \
+// RUN: not %t 2> %t.out
+// RUN: FileCheck %s --check-prefix=CHECK-LONG < %t.out
+
+// Run w/o errors should not produce any log.
+// RUN: rm -f %t.log.*
+// RUN: ASAN_OPTIONS=log_path=%t.log %t ARG ARG ARG
+// RUN: not cat %t.log.*
+
+
+#include <stdlib.h>
+#include <string.h>
+int main(int argc, char **argv) {
+ if (argc > 2) return 0;
+ char *x = (char*)malloc(10);
+ memset(x, 0, 10);
+ int res = x[argc * 10]; // BOOOM
+ free(x);
+ return res;
+}
+// CHECK-ERROR: ERROR: AddressSanitizer
+// CHECK-INVALID: ERROR: Can't open file: /INVALID
+// CHECK-LONG: ERROR: Path is too long: 01234
diff --git a/lib/asan/lit_tests/log_path_fork_test.cc b/lib/asan/lit_tests/log_path_fork_test.cc
new file mode 100644
index 000000000000..c6c1b49e994d
--- /dev/null
+++ b/lib/asan/lit_tests/log_path_fork_test.cc
@@ -0,0 +1,22 @@
+// RUN: %clangxx_asan %s -o %t
+// RUN: rm -f %t.log.*
+// Set verbosity to 1 so that the log files are opened prior to fork().
+// RUN: ASAN_OPTIONS="log_path=%t.log verbosity=1" not %t 2> %t.out
+// RUN: for f in %t.log.* ; do FileCheck %s < $f; done
+// RUN: [ `ls %t.log.* | wc -l` == 2 ]
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int main(int argc, char **argv) {
+ void *x = malloc(10);
+ free(x);
+ if (fork() == -1) return 1;
+ // There are two processes at this point, thus there should be two distinct
+ // error logs.
+ free(x);
+ return 0;
+}
+
+// CHECK: ERROR: AddressSanitizer
diff --git a/lib/asan/lit_tests/malloc_delete_mismatch.cc b/lib/asan/lit_tests/malloc_delete_mismatch.cc
new file mode 100644
index 000000000000..f34b33a38fb3
--- /dev/null
+++ b/lib/asan/lit_tests/malloc_delete_mismatch.cc
@@ -0,0 +1,26 @@
+// Check that we detect malloc/delete mismatch only if the approptiate flag
+// is set.
+
+// RUN: %clangxx_asan -g %s -o %t 2>&1
+// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1 %t 2>&1 | \
+// RUN: %symbolize | FileCheck %s
+
+// No error here.
+// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=0 %t
+#include <stdlib.h>
+
+static volatile char *x;
+
+int main() {
+ x = (char*)malloc(10);
+ x[0] = 0;
+ delete x;
+}
+// CHECK: ERROR: AddressSanitizer: alloc-dealloc-mismatch (malloc vs operator delete) on 0x
+// CHECK-NEXT: #0{{.*}}operator delete
+// CHECK: #{{.*}}main
+// CHECK: is located 0 bytes inside of 10-byte region
+// CHECK-NEXT: allocated by thread T0 here:
+// CHECK-NEXT: #0{{.*}}malloc
+// CHECK: #{{.*}}main
+// CHECK: HINT: {{.*}} you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0
diff --git a/lib/asan/lit_tests/malloc_hook.cc b/lib/asan/lit_tests/malloc_hook.cc
new file mode 100644
index 000000000000..6435d105ee26
--- /dev/null
+++ b/lib/asan/lit_tests/malloc_hook.cc
@@ -0,0 +1,24 @@
+// RUN: %clangxx_asan -O2 %s -o %t
+// RUN: %t 2>&1 | FileCheck %s
+#include <stdlib.h>
+#include <unistd.h>
+
+extern "C" {
+// Note: avoid calling functions that allocate memory in malloc/free
+// to avoid infinite recursion.
+void __asan_malloc_hook(void *ptr, size_t sz) {
+ write(1, "MallocHook\n", sizeof("MallocHook\n"));
+}
+void __asan_free_hook(void *ptr) {
+ write(1, "FreeHook\n", sizeof("FreeHook\n"));
+}
+} // extern "C"
+
+int main() {
+ volatile int *x = new int;
+ // CHECK: MallocHook
+ *x = 0;
+ delete x;
+ // CHECK: FreeHook
+ return 0;
+}
diff --git a/lib/asan/lit_tests/memcmp_test.cc b/lib/asan/lit_tests/memcmp_test.cc
new file mode 100644
index 000000000000..ac3f7f32ea75
--- /dev/null
+++ b/lib/asan/lit_tests/memcmp_test.cc
@@ -0,0 +1,19 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+#include <string.h>
+int main(int argc, char **argv) {
+ char a1[] = {argc, 2, 3, 4};
+ char a2[] = {1, 2*argc, 3, 4};
+ int res = memcmp(a1, a2, 4 + argc); // BOOM
+ // CHECK: AddressSanitizer: stack-buffer-overflow
+ // CHECK: {{#0.*memcmp}}
+ // CHECK: {{#1.*main}}
+ return res;
+}
diff --git a/lib/asan/lit_tests/null_deref.cc b/lib/asan/lit_tests/null_deref.cc
new file mode 100644
index 000000000000..60a521d1c210
--- /dev/null
+++ b/lib/asan/lit_tests/null_deref.cc
@@ -0,0 +1,31 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+
+__attribute__((noinline))
+static void NullDeref(int *ptr) {
+ // CHECK: ERROR: AddressSanitizer: SEGV on unknown address
+ // CHECK: {{0x0*00028 .*pc 0x.*}}
+ // CHECK: {{AddressSanitizer can not provide additional info.}}
+ ptr[10]++; // BOOM
+ // atos on Mac cannot extract the symbol name correctly.
+ // CHECK-Linux: {{ #0 0x.* in NullDeref.*null_deref.cc:}}[[@LINE-2]]
+ // CHECK-Darwin: {{ #0 0x.* in .*NullDeref.*null_deref.cc:}}[[@LINE-3]]
+}
+int main() {
+ NullDeref((int*)0);
+ // CHECK: {{ #1 0x.* in _?main.*null_deref.cc:}}[[@LINE-1]]
+}
diff --git a/lib/asan/lit_tests/on_error_callback.cc b/lib/asan/lit_tests/on_error_callback.cc
new file mode 100644
index 000000000000..bb94d9fb579b
--- /dev/null
+++ b/lib/asan/lit_tests/on_error_callback.cc
@@ -0,0 +1,16 @@
+// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern "C"
+void __asan_on_error() {
+ fprintf(stderr, "__asan_on_error called");
+}
+
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+ // CHECK: __asan_on_error called
+}
diff --git a/lib/asan/lit_tests/sanity_check_pure_c.c b/lib/asan/lit_tests/sanity_check_pure_c.c
new file mode 100644
index 000000000000..3d830653e33e
--- /dev/null
+++ b/lib/asan/lit_tests/sanity_check_pure_c.c
@@ -0,0 +1,19 @@
+// Sanity checking a test in pure C.
+// RUN: %clang -g -fsanitize=address -O2 %s -o %t
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+
+// Sanity checking a test in pure C with -pie.
+// RUN: %clang -g -fsanitize=address -O2 %s -pie -o %t
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+
+#include <stdlib.h>
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+ // CHECK: heap-use-after-free
+ // CHECK: free
+ // CHECK: main{{.*}}sanity_check_pure_c.c:[[@LINE-4]]
+ // CHECK: malloc
+ // CHECK: main{{.*}}sanity_check_pure_c.c:[[@LINE-7]]
+}
diff --git a/lib/asan/lit_tests/shared-lib-test.cc b/lib/asan/lit_tests/shared-lib-test.cc
new file mode 100644
index 000000000000..05bf3ecdf4f9
--- /dev/null
+++ b/lib/asan/lit_tests/shared-lib-test.cc
@@ -0,0 +1,54 @@
+// RUN: %clangxx_asan -m64 -O0 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %p/SharedLibs/shared-lib-test-so.cc \
+// RUN: -fPIC -shared -o %t-so.so
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+
+using std::string;
+
+typedef void (fun_t)(int x);
+
+int main(int argc, char *argv[]) {
+ string path = string(argv[0]) + "-so.so";
+ printf("opening %s ... \n", path.c_str());
+ void *lib = dlopen(path.c_str(), RTLD_NOW);
+ if (!lib) {
+ printf("error in dlopen(): %s\n", dlerror());
+ return 1;
+ }
+ fun_t *inc = (fun_t*)dlsym(lib, "inc");
+ if (!inc) return 1;
+ printf("ok\n");
+ inc(1);
+ inc(-1); // BOOM
+ // CHECK: {{.*ERROR: AddressSanitizer: global-buffer-overflow}}
+ // CHECK: {{READ of size 4 at 0x.* thread T0}}
+ // CHECK: {{ #0 0x.*}}
+ // CHECK: {{ #1 0x.* in _?main .*shared-lib-test.cc:}}[[@LINE-4]]
+ return 0;
+}
diff --git a/lib/asan/lit_tests/sleep_before_dying.c b/lib/asan/lit_tests/sleep_before_dying.c
new file mode 100644
index 000000000000..df9eba276039
--- /dev/null
+++ b/lib/asan/lit_tests/sleep_before_dying.c
@@ -0,0 +1,10 @@
+// RUN: %clang -g -fsanitize=address -O2 %s -o %t
+// RUN: ASAN_OPTIONS="sleep_before_dying=1" %t 2>&1 | FileCheck %s
+
+#include <stdlib.h>
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+ // CHECK: Sleeping for 1 second
+}
diff --git a/lib/asan/lit_tests/stack-frame-demangle.cc b/lib/asan/lit_tests/stack-frame-demangle.cc
new file mode 100644
index 000000000000..7f4d59fc5838
--- /dev/null
+++ b/lib/asan/lit_tests/stack-frame-demangle.cc
@@ -0,0 +1,24 @@
+// Check that ASan is able to print demangled frame name even w/o
+// symbolization.
+
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s
+
+#include <string.h>
+
+namespace XXX {
+struct YYY {
+ static int ZZZ(int x) {
+ char array[10];
+ memset(array, 0, 10);
+ return array[x]; // BOOOM
+ // CHECK: {{ERROR: AddressSanitizer: stack-buffer-overflow}}
+ // CHECK: {{READ of size 1 at 0x.* thread T0}}
+ // CHECK: {{Address 0x.* is .* frame <XXX::YYY::ZZZ(.*)>}}
+ }
+};
+};
+
+int main(int argc, char **argv) {
+ int res = XXX::YYY::ZZZ(argc + 10);
+ return res;
+}
diff --git a/lib/asan/lit_tests/stack-overflow.cc b/lib/asan/lit_tests/stack-overflow.cc
new file mode 100644
index 000000000000..3deb1e91de6c
--- /dev/null
+++ b/lib/asan/lit_tests/stack-overflow.cc
@@ -0,0 +1,19 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s
+
+#include <string.h>
+int main(int argc, char **argv) {
+ char x[10];
+ memset(x, 0, 10);
+ int res = x[argc * 10]; // BOOOM
+ // CHECK: {{READ of size 1 at 0x.* thread T0}}
+ // CHECK: {{ #0 0x.* in _?main .*stack-overflow.cc:}}[[@LINE-2]]
+ // CHECK: {{Address 0x.* is .* frame <main>}}
+ return res;
+}
diff --git a/lib/asan/lit_tests/stack-use-after-return.cc b/lib/asan/lit_tests/stack-use-after-return.cc
new file mode 100644
index 000000000000..f8d8a1a2ae39
--- /dev/null
+++ b/lib/asan/lit_tests/stack-use-after-return.cc
@@ -0,0 +1,45 @@
+// XFAIL: *
+// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O0 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O1 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O2 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -fsanitize=use-after-return -m64 -O3 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O0 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O1 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O2 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -fsanitize=use-after-return -m32 -O3 %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+
+#include <stdio.h>
+
+__attribute__((noinline))
+char *Ident(char *x) {
+ fprintf(stderr, "1: %p\n", x);
+ return x;
+}
+
+__attribute__((noinline))
+char *Func1() {
+ char local;
+ return Ident(&local);
+}
+
+__attribute__((noinline))
+void Func2(char *x) {
+ fprintf(stderr, "2: %p\n", x);
+ *x = 1;
+ // CHECK: WRITE of size 1 {{.*}} thread T0
+ // CHECK: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-2]]
+ // CHECK: is located {{.*}} in frame <{{.*}}Func1{{.*}}> of T0's stack
+}
+
+int main(int argc, char **argv) {
+ Func2(Func1());
+ return 0;
+}
diff --git a/lib/asan/lit_tests/strip_path_prefix.c b/lib/asan/lit_tests/strip_path_prefix.c
new file mode 100644
index 000000000000..ef7bf98ab3c2
--- /dev/null
+++ b/lib/asan/lit_tests/strip_path_prefix.c
@@ -0,0 +1,12 @@
+// RUN: %clang -g -fsanitize=address -O2 %s -o %t
+// RUN: ASAN_OPTIONS="strip_path_prefix='/'" %t 2>&1 | FileCheck %s
+
+#include <stdlib.h>
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+ // Check that paths in error report don't start with slash.
+ // CHECK: heap-use-after-free
+ // CHECK-NOT: #0 0x{{.*}} ({{[/].*}})
+}
diff --git a/lib/asan/lit_tests/strncpy-overflow.cc b/lib/asan/lit_tests/strncpy-overflow.cc
new file mode 100644
index 000000000000..18711843c4c8
--- /dev/null
+++ b/lib/asan/lit_tests/strncpy-overflow.cc
@@ -0,0 +1,40 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+
+#include <string.h>
+#include <stdlib.h>
+int main(int argc, char **argv) {
+ char *hello = (char*)malloc(6);
+ strcpy(hello, "hello");
+ char *short_buffer = (char*)malloc(9);
+ strncpy(short_buffer, hello, 10); // BOOM
+ // CHECK: {{WRITE of size 1 at 0x.* thread T0}}
+ // CHECK-Linux: {{ #0 0x.* in .*strncpy}}
+ // CHECK-Darwin: {{ #0 0x.* in _?wrap_strncpy}}
+ // CHECK: {{ #1 0x.* in _?main .*strncpy-overflow.cc:}}[[@LINE-4]]
+ // CHECK: {{0x.* is located 0 bytes to the right of 9-byte region}}
+ // CHECK: {{allocated by thread T0 here:}}
+
+ // CHECK-Linux: {{ #0 0x.* in .*malloc}}
+ // CHECK-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-10]]
+
+ // CHECK-Darwin: {{ #0 0x.* in .*mz_malloc.*}}
+ // CHECK-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}}
+ // CHECK-Darwin: {{ #2 0x.* in malloc.*}}
+ // CHECK-Darwin: {{ #3 0x.* in _?main .*strncpy-overflow.cc:}}[[@LINE-15]]
+ return short_buffer[8];
+}
diff --git a/lib/asan/lit_tests/symbolize_callback.cc b/lib/asan/lit_tests/symbolize_callback.cc
new file mode 100644
index 000000000000..0691d501e24d
--- /dev/null
+++ b/lib/asan/lit_tests/symbolize_callback.cc
@@ -0,0 +1,17 @@
+// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern "C"
+bool __asan_symbolize(const void *pc, char *out_buffer, int out_size) {
+ snprintf(out_buffer, out_size, "MySymbolizer");
+ return true;
+}
+
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+ // CHECK: MySymbolizer
+}
diff --git a/lib/asan/lit_tests/use-after-free.cc b/lib/asan/lit_tests/use-after-free.cc
new file mode 100644
index 000000000000..24d5a2a54807
--- /dev/null
+++ b/lib/asan/lit_tests/use-after-free.cc
@@ -0,0 +1,48 @@
+// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | %symbolize > %t.out
+// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-%os < %t.out
+
+#include <stdlib.h>
+int main() {
+ char *x = (char*)malloc(10 * sizeof(char));
+ free(x);
+ return x[5];
+ // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
+ // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}}
+ // CHECK: {{READ of size 1 at 0x.* thread T0}}
+ // CHECK: {{ #0 0x.* in _?main .*use-after-free.cc:22}}
+ // CHECK: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}}
+ // CHECK: {{freed by thread T0 here:}}
+
+ // CHECK-Linux: {{ #0 0x.* in .*free}}
+ // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:21}}
+
+ // CHECK-Darwin: {{ #0 0x.* in .*free_common.*}}
+ // CHECK-Darwin: {{ #1 0x.* in .*mz_free.*}}
+ // We override free() on Darwin, thus no malloc_zone_free
+ // CHECK-Darwin: {{ #2 0x.* in _?wrap_free}}
+ // CHECK-Darwin: {{ #3 0x.* in _?main .*use-after-free.cc:21}}
+
+ // CHECK: {{previously allocated by thread T0 here:}}
+
+ // CHECK-Linux: {{ #0 0x.* in .*malloc}}
+ // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:20}}
+
+ // CHECK-Darwin: {{ #0 0x.* in .*mz_malloc.*}}
+ // CHECK-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}}
+ // CHECK-Darwin: {{ #2 0x.* in malloc.*}}
+ // CHECK-Darwin: {{ #3 0x.* in _?main .*use-after-free.cc:20}}
+}
diff --git a/lib/asan/lit_tests/use-after-scope-inlined.cc b/lib/asan/lit_tests/use-after-scope-inlined.cc
new file mode 100644
index 000000000000..3d730de6ab35
--- /dev/null
+++ b/lib/asan/lit_tests/use-after-scope-inlined.cc
@@ -0,0 +1,29 @@
+// Test with "-O2" only to make sure inlining (leading to use-after-scope)
+// happens. "always_inline" is not enough, as Clang doesn't emit
+// llvm.lifetime intrinsics at -O0.
+//
+// RUN: %clangxx_asan -m64 -O2 -fsanitize=use-after-scope %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+// RUN: %clangxx_asan -m32 -O2 -fsanitize=use-after-scope %s -o %t && \
+// RUN: %t 2>&1 | %symbolize | FileCheck %s
+
+int *arr;
+
+__attribute__((always_inline))
+void inlined(int arg) {
+ int x[5];
+ for (int i = 0; i < arg; i++) x[i] = i;
+ arr = x;
+}
+
+int main(int argc, char *argv[]) {
+ inlined(argc);
+ return arr[argc - 1]; // BOOM
+ // CHECK: ERROR: AddressSanitizer: stack-use-after-scope
+ // CHECK: READ of size 4 at 0x{{.*}} thread T0
+ // CHECK: #0 0x{{.*}} in {{_?}}main
+ // CHECK: {{.*}}use-after-scope-inlined.cc:[[@LINE-4]]
+ // CHECK: Address 0x{{.*}} is located at offset
+ // CHECK: [[OFFSET:[^ ]*]] in frame <main> of T0{{.*}}:
+ // CHECK: {{\[}}[[OFFSET]], {{.*}}) 'x.i'
+}
diff --git a/lib/asan/output_tests/clone_test.cc b/lib/asan/output_tests/clone_test.cc
deleted file mode 100644
index b18d2550bc7c..000000000000
--- a/lib/asan/output_tests/clone_test.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifdef __linux__
-#include <stdio.h>
-#include <sched.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-int Child(void *arg) {
- char x[32] = {0}; // Stack gets poisoned.
- printf("Child: %p\n", x);
- _exit(1); // NoReturn, stack will remain unpoisoned unless we do something.
-}
-
-int main(int argc, char **argv) {
- const int kStackSize = 1 << 20;
- char child_stack[kStackSize + 1];
- char *sp = child_stack + kStackSize; // Stack grows down.
- printf("Parent: %p\n", sp);
- pid_t clone_pid = clone(Child, sp, CLONE_FILES | CLONE_VM, NULL, 0, 0, 0);
- waitpid(clone_pid, NULL, 0);
- for (int i = 0; i < kStackSize; i++)
- child_stack[i] = i;
- int ret = child_stack[argc - 1];
- printf("PASSED\n");
- return ret;
-}
-#else // not __linux__
-#include <stdio.h>
-int main() {
- printf("PASSED\n");
- // Check-Common: PASSED
-}
-#endif
diff --git a/lib/asan/output_tests/deep_tail_call.cc b/lib/asan/output_tests/deep_tail_call.cc
deleted file mode 100644
index cb69e8925197..000000000000
--- a/lib/asan/output_tests/deep_tail_call.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Check-Common: AddressSanitizer global-buffer-overflow
-int global[10];
-// Check-Common: {{#0.*call4}}
-void __attribute__((noinline)) call4(int i) { global[i+10]++; }
-// Check-Common: {{#1.*call3}}
-void __attribute__((noinline)) call3(int i) { call4(i); }
-// Check-Common: {{#2.*call2}}
-void __attribute__((noinline)) call2(int i) { call3(i); }
-// Check-Common: {{#3.*call1}}
-void __attribute__((noinline)) call1(int i) { call2(i); }
-// Check-Common: {{#4.*main}}
-int main(int argc, char **argv) {
- call1(argc);
- return global[0];
-}
diff --git a/lib/asan/output_tests/global-overflow.cc b/lib/asan/output_tests/global-overflow.cc
deleted file mode 100644
index a63eb733365f..000000000000
--- a/lib/asan/output_tests/global-overflow.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <string.h>
-int main(int argc, char **argv) {
- static char XXX[10];
- static char YYY[10];
- static char ZZZ[10];
- memset(XXX, 0, 10);
- memset(YYY, 0, 10);
- memset(ZZZ, 0, 10);
- int res = YYY[argc * 10]; // BOOOM
- // Check-Common: {{READ of size 1 at 0x.* thread T0}}
- // Check-Common: {{ #0 0x.* in main .*global-overflow.cc:9}}
- // Check-Common: {{0x.* is located 0 bytes to the right of global variable}}
- // Check-Common: {{.*YYY.* of size 10}}
- res += XXX[argc] + ZZZ[argc];
- return res;
-}
diff --git a/lib/asan/output_tests/heap-overflow.cc b/lib/asan/output_tests/heap-overflow.cc
deleted file mode 100644
index 534fbe00b355..000000000000
--- a/lib/asan/output_tests/heap-overflow.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <stdlib.h>
-#include <string.h>
-int main(int argc, char **argv) {
- char *x = (char*)malloc(10 * sizeof(char));
- memset(x, 0, 10);
- int res = x[argc * 10]; // BOOOM
- free(x);
- return res;
-}
-
-// Check-Common: {{READ of size 1 at 0x.* thread T0}}
-// Check-Common: {{ #0 0x.* in main .*heap-overflow.cc:6}}
-// Check-Common: {{0x.* is located 0 bytes to the right of 10-byte region}}
-// Check-Common: {{allocated by thread T0 here:}}
-
-// Check-Linux: {{ #0 0x.* in .*malloc}}
-// Check-Linux: {{ #1 0x.* in main .*heap-overflow.cc:4}}
-
-// Check-Darwin: {{ #0 0x.* in .*mz_malloc.*}}
-// Check-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}}
-// Check-Darwin: {{ #2 0x.* in malloc.*}}
-// Check-Darwin: {{ #3 0x.* in main heap-overflow.cc:4}}
diff --git a/lib/asan/output_tests/interception_failure_test-linux.cc b/lib/asan/output_tests/interception_failure_test-linux.cc
deleted file mode 100644
index 9e8b7536906b..000000000000
--- a/lib/asan/output_tests/interception_failure_test-linux.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-
-extern "C" long strtol(const char *nptr, char **endptr, int base) {
- fprintf(stderr, "my_strtol_interceptor\n");
- return 0;
-}
-
-int main() {
- char *x = (char*)malloc(10 * sizeof(char));
- free(x);
- return (int)strtol(x, 0, 10);
-}
-
-// Check-Common: my_strtol_interceptor
-// CHECK-NOT: heap-use-after-free
-
diff --git a/lib/asan/output_tests/interception_malloc_test-linux.cc b/lib/asan/output_tests/interception_malloc_test-linux.cc
deleted file mode 100644
index 4bb3bd66de33..000000000000
--- a/lib/asan/output_tests/interception_malloc_test-linux.cc
+++ /dev/null
@@ -1,19 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-
-extern "C" void *__interceptor_malloc(size_t size);
-extern "C" void *malloc(size_t size) {
- write(2, "malloc call\n", sizeof("malloc call\n") - 1);
- return __interceptor_malloc(size);
-}
-
-int main() {
- char *x = (char*)malloc(10 * sizeof(char));
- free(x);
- return (int)strtol(x, 0, 10);
-}
-
-// Check-Common: malloc call
-// Check-Common: heap-use-after-free
-
diff --git a/lib/asan/output_tests/interception_test-linux.cc b/lib/asan/output_tests/interception_test-linux.cc
deleted file mode 100644
index 0523510465a1..000000000000
--- a/lib/asan/output_tests/interception_test-linux.cc
+++ /dev/null
@@ -1,18 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-
-extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base);
-extern "C" long strtol(const char *nptr, char **endptr, int base) {
- fprintf(stderr, "my_strtol_interceptor\n");
- return __interceptor_strtol(nptr, endptr, base);
-}
-
-int main() {
- char *x = (char*)malloc(10 * sizeof(char));
- free(x);
- return (int)strtol(x, 0, 10);
-}
-
-// Check-Common: my_strtol_interceptor
-// Check-Common: heap-use-after-free
-
diff --git a/lib/asan/output_tests/large_func_test.cc b/lib/asan/output_tests/large_func_test.cc
deleted file mode 100644
index 49751b39277a..000000000000
--- a/lib/asan/output_tests/large_func_test.cc
+++ /dev/null
@@ -1,48 +0,0 @@
-#include <stdlib.h>
-__attribute__((noinline))
-static void LargeFunction(int *x, int zero) {
- x[0]++;
- x[1]++;
- x[2]++;
- x[3]++;
- x[4]++;
- x[5]++;
- x[6]++;
- x[7]++;
- x[8]++;
- x[9]++;
-
- x[zero + 111]++; // we should report this exact line
-
- x[10]++;
- x[11]++;
- x[12]++;
- x[13]++;
- x[14]++;
- x[15]++;
- x[16]++;
- x[17]++;
- x[18]++;
- x[19]++;
-}
-
-int main(int argc, char **argv) {
- int *x = new int[100];
- LargeFunction(x, argc - 1);
- delete x;
-}
-
-// Check-Common: {{.*ERROR: AddressSanitizer heap-buffer-overflow on address}}
-// Check-Common: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}}
-// Check-Common: {{READ of size 4 at 0x.* thread T0}}
-
-// atos incorrectly extracts the symbol name for the static functions on
-// Darwin.
-// Check-Linux: {{ #0 0x.* in LargeFunction.*large_func_test.cc:15}}
-// Check-Darwin: {{ #0 0x.* in .*LargeFunction.*large_func_test.cc:15}}
-
-// Check-Common: {{ #1 0x.* in main .*large_func_test.cc:31}}
-// Check-Common: {{0x.* is located 44 bytes to the right of 400-byte region}}
-// Check-Common: {{allocated by thread T0 here:}}
-// Check-Common: {{ #0 0x.* in operator new.*}}
-// Check-Common: {{ #1 0x.* in main .*large_func_test.cc:30}}
diff --git a/lib/asan/output_tests/memcmp_test.cc b/lib/asan/output_tests/memcmp_test.cc
deleted file mode 100644
index d0e5a43b4355..000000000000
--- a/lib/asan/output_tests/memcmp_test.cc
+++ /dev/null
@@ -1,10 +0,0 @@
-#include <string.h>
-int main(int argc, char **argv) {
- char a1[] = {argc, 2, 3, 4};
- char a2[] = {1, 2*argc, 3, 4};
-// Check-Common: AddressSanitizer stack-buffer-overflow
-// Check-Common: {{#0.*memcmp}}
-// Check-Common: {{#1.*main}}
- int res = memcmp(a1, a2, 4 + argc); // BOOM
- return res;
-}
diff --git a/lib/asan/output_tests/null_deref.cc b/lib/asan/output_tests/null_deref.cc
deleted file mode 100644
index c152a4202e33..000000000000
--- a/lib/asan/output_tests/null_deref.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-__attribute__((noinline))
-static void NullDeref(int *ptr) {
- ptr[10]++;
-}
-int main() {
- NullDeref((int*)0);
-}
-
-// Check-Common: {{.*ERROR: AddressSanitizer crashed on unknown address}}
-// Check-Common: {{0x0*00028 .*pc 0x.*}}
-// Check-Common: {{AddressSanitizer can not provide additional info. ABORTING}}
-
-// atos on Mac cannot extract the symbol name correctly.
-// Check-Linux: {{ #0 0x.* in NullDeref.*null_deref.cc:3}}
-// Check-Darwin: {{ #0 0x.* in .*NullDeref.*null_deref.cc:3}}
-
-// Check-Common: {{ #1 0x.* in main.*null_deref.cc:6}}
diff --git a/lib/asan/output_tests/shared-lib-test.cc b/lib/asan/output_tests/shared-lib-test.cc
deleted file mode 100644
index 060fcde35f0d..000000000000
--- a/lib/asan/output_tests/shared-lib-test.cc
+++ /dev/null
@@ -1,42 +0,0 @@
-//===----------- shared-lib-test.cc -----------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-//===----------------------------------------------------------------------===//
-#include <dlfcn.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <string>
-
-using std::string;
-
-typedef void (fun_t)(int x);
-
-int main(int argc, char *argv[]) {
- string path = string(argv[0]) + "-so.so";
- printf("opening %s ... \n", path.c_str());
- void *lib = dlopen(path.c_str(), RTLD_NOW);
- if (!lib) {
- printf("error in dlopen(): %s\n", dlerror());
- return 1;
- }
- fun_t *inc = (fun_t*)dlsym(lib, "inc");
- if (!inc) return 1;
- printf("ok\n");
- inc(1);
- inc(-1); // BOOM
- return 0;
-}
-
-// Check-Common: {{.*ERROR: AddressSanitizer global-buffer-overflow}}
-// Check-Common: {{READ of size 4 at 0x.* thread T0}}
-// Check-Common: {{ #0 0x.*}}
-// Check-Common: {{ #1 0x.* in main .*shared-lib-test.cc:35}}
diff --git a/lib/asan/output_tests/stack-overflow.cc b/lib/asan/output_tests/stack-overflow.cc
deleted file mode 100644
index 35fa8a66c34c..000000000000
--- a/lib/asan/output_tests/stack-overflow.cc
+++ /dev/null
@@ -1,11 +0,0 @@
-#include <string.h>
-int main(int argc, char **argv) {
- char x[10];
- memset(x, 0, 10);
- int res = x[argc * 10]; // BOOOM
- return res;
-}
-
-// Check-Common: {{READ of size 1 at 0x.* thread T0}}
-// Check-Common: {{ #0 0x.* in main .*stack-overflow.cc:5}}
-// Check-Common: {{Address 0x.* is .* frame <main>}}
diff --git a/lib/asan/output_tests/stack-use-after-return.cc.disabled b/lib/asan/output_tests/stack-use-after-return.cc.disabled
deleted file mode 100644
index f49715737430..000000000000
--- a/lib/asan/output_tests/stack-use-after-return.cc.disabled
+++ /dev/null
@@ -1,27 +0,0 @@
-#include <stdio.h>
-
-__attribute__((noinline))
-char *Ident(char *x) {
- fprintf(stderr, "1: %p\n", x);
- return x;
-}
-
-__attribute__((noinline))
-char *Func1() {
- char local;
- return Ident(&local);
-}
-
-__attribute__((noinline))
-void Func2(char *x) {
- fprintf(stderr, "2: %p\n", x);
- *x = 1;
- // Check-Common: {{WRITE of size 1 .* thread T0}}
- // Check-Common: {{ #0.*Func2.*stack-use-after-return.cc:18}}
- // Check-Common: {{is located in frame <.*Func1.*> of T0's stack}}
-}
-
-int main(int argc, char **argv) {
- Func2(Func1());
- return 0;
-}
diff --git a/lib/asan/output_tests/strncpy-overflow.cc b/lib/asan/output_tests/strncpy-overflow.cc
deleted file mode 100644
index 66d5810b7040..000000000000
--- a/lib/asan/output_tests/strncpy-overflow.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <string.h>
-#include <stdlib.h>
-int main(int argc, char **argv) {
- char *hello = (char*)malloc(6);
- strcpy(hello, "hello");
- char *short_buffer = (char*)malloc(9);
- strncpy(short_buffer, hello, 10); // BOOM
- return short_buffer[8];
-}
-
-// Check-Common: {{WRITE of size 1 at 0x.* thread T0}}
-// Check-Linux: {{ #0 0x.* in .*strncpy}}
-// Check-Darwin: {{ #0 0x.* in wrap_strncpy}}
-// Check-Common: {{ #1 0x.* in main .*strncpy-overflow.cc:7}}
-// Check-Common: {{0x.* is located 0 bytes to the right of 9-byte region}}
-// Check-Common: {{allocated by thread T0 here:}}
-
-// Check-Linux: {{ #0 0x.* in .*malloc}}
-// Check-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:6}}
-
-// Check-Darwin: {{ #0 0x.* in .*mz_malloc.*}}
-// Check-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}}
-// Check-Darwin: {{ #2 0x.* in malloc.*}}
-// Check-Darwin: {{ #3 0x.* in main .*strncpy-overflow.cc:6}}
diff --git a/lib/asan/output_tests/test_output.sh b/lib/asan/output_tests/test_output.sh
deleted file mode 100755
index 6510043396e4..000000000000
--- a/lib/asan/output_tests/test_output.sh
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/bin/bash
-
-set -e # fail on any error
-
-OS=`uname`
-CXX=$1
-CC=$2
-FILE_CHECK=$3
-CXXFLAGS="-mno-omit-leaf-frame-pointer -fno-omit-frame-pointer -fno-optimize-sibling-calls -g"
-SYMBOLIZER=../scripts/asan_symbolize.py
-TMP_ASAN_REPORT=asan_report.tmp
-
-run_program() {
- ./$1 2>&1 | $SYMBOLIZER 2> /dev/null | c++filt > $TMP_ASAN_REPORT
-}
-
-# check_program exe_file source_file check_prefixf
-check_program() {
- run_program $1
- $FILE_CHECK $2 --check-prefix=$3 < $TMP_ASAN_REPORT
- rm -f $TMP_ASAN_REPORT
-}
-
-C_TEST=use-after-free
-echo "Sanity checking a test in pure C"
-$CC -g -faddress-sanitizer -O2 $C_TEST.c
-check_program a.out $C_TEST.c CHECK
-rm ./a.out
-
-echo "Sanity checking a test in pure C with -pie"
-$CC -g -faddress-sanitizer -O2 $C_TEST.c -pie
-check_program a.out $C_TEST.c CHECK
-rm ./a.out
-
-echo "Testing sleep_before_dying"
-$CC -g -faddress-sanitizer -O2 $C_TEST.c
-export ASAN_OPTIONS="sleep_before_dying=1"
-check_program a.out $C_TEST.c CHECKSLEEP
-export ASAN_OPTIONS=""
-rm ./a.out
-
-# FIXME: some tests do not need to be ran for all the combinations of arch
-# and optimization mode.
-for t in *.cc; do
- for b in 32 64; do
- for O in 0 1 2 3; do
- c=`basename $t .cc`
- if [[ "$c" == *"-so" ]]; then
- continue
- fi
- if [[ "$c" == *"-linux" ]]; then
- if [[ "$OS" != "Linux" ]]; then
- continue
- fi
- fi
- c_so=$c-so
- exe=$c.$b.O$O
- so=$c.$b.O$O-so.so
- echo testing $exe
- build_command="$CXX $CXXFLAGS -m$b -faddress-sanitizer -O$O $c.cc -o $exe"
- [ "$DEBUG" == "1" ] && echo $build_command
- $build_command
- [ -e "$c_so.cc" ] && $CXX $CXXFLAGS -m$b -faddress-sanitizer -O$O $c_so.cc -fPIC -shared -o $so
- run_program $exe
- # Check common expected lines for OS.
- $FILE_CHECK $c.cc --check-prefix="Check-Common" < $TMP_ASAN_REPORT
- # Check OS-specific lines.
- if [ `grep -c "Check-$OS" $c.cc` -gt 0 ]
- then
- $FILE_CHECK $c.cc --check-prefix="Check-$OS" < $TMP_ASAN_REPORT
- fi
- rm ./$exe
- rm ./$TMP_ASAN_REPORT
- [ -e "$so" ] && rm ./$so
- done
- done
-done
-
-exit 0
diff --git a/lib/asan/output_tests/use-after-free.c b/lib/asan/output_tests/use-after-free.c
deleted file mode 100644
index 801d3f68a466..000000000000
--- a/lib/asan/output_tests/use-after-free.c
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <stdlib.h>
-int main() {
- char *x = (char*)malloc(10 * sizeof(char));
- free(x);
- return x[5];
-}
-
-// CHECK: heap-use-after-free
-// CHECKSLEEP: Sleeping for 1 second
diff --git a/lib/asan/output_tests/use-after-free.cc b/lib/asan/output_tests/use-after-free.cc
deleted file mode 100644
index c3e9dbe6db75..000000000000
--- a/lib/asan/output_tests/use-after-free.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-#include <stdlib.h>
-int main() {
- char *x = (char*)malloc(10 * sizeof(char));
- free(x);
- return x[5];
-}
-
-// Check-Common: {{.*ERROR: AddressSanitizer heap-use-after-free on address}}
-// Check-Common: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}}
-// Check-Common: {{READ of size 1 at 0x.* thread T0}}
-// Check-Common: {{ #0 0x.* in main .*use-after-free.cc:5}}
-// Check-Common: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}}
-// Check-Common: {{freed by thread T0 here:}}
-
-// Check-Linux: {{ #0 0x.* in .*free}}
-// Check-Linux: {{ #1 0x.* in main .*use-after-free.cc:4}}
-
-// Check-Darwin: {{ #0 0x.* in .*mz_free.*}}
-// We override free() on Darwin, thus no malloc_zone_free
-// Check-Darwin: {{ #1 0x.* in wrap_free}}
-// Check-Darwin: {{ #2 0x.* in main .*use-after-free.cc:4}}
-
-// Check-Common: {{previously allocated by thread T0 here:}}
-
-// Check-Linux: {{ #0 0x.* in .*malloc}}
-// Check-Linux: {{ #1 0x.* in main .*use-after-free.cc:3}}
-
-// Check-Darwin: {{ #0 0x.* in .*mz_malloc.*}}
-// Check-Darwin: {{ #1 0x.* in malloc_zone_malloc.*}}
-// Check-Darwin: {{ #2 0x.* in malloc.*}}
-// Check-Darwin: {{ #3 0x.* in main .*use-after-free.cc:3}}
diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py
index e4897d0c7649..7b30bb55914e 100755
--- a/lib/asan/scripts/asan_symbolize.py
+++ b/lib/asan/scripts/asan_symbolize.py
@@ -7,121 +7,350 @@
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
+import bisect
import os
import re
-import sys
-import string
import subprocess
+import sys
-pipes = {}
+llvm_symbolizer = None
+symbolizers = {}
filetypes = {}
-DEBUG=False
+vmaddrs = {}
+DEBUG = False
+
+# FIXME: merge the code that calls fix_filename().
def fix_filename(file_name):
for path_to_cut in sys.argv[1:]:
- file_name = re.sub(".*" + path_to_cut, "", file_name)
- file_name = re.sub(".*asan_[a-z_]*.cc:[0-9]*", "_asan_rtl_", file_name)
- file_name = re.sub(".*crtstuff.c:0", "???:0", file_name)
+ file_name = re.sub('.*' + path_to_cut, '', file_name)
+ file_name = re.sub('.*asan_[a-z_]*.cc:[0-9]*', '_asan_rtl_', file_name)
+ file_name = re.sub('.*crtstuff.c:0', '???:0', file_name)
return file_name
-# TODO(glider): need some refactoring here
-def symbolize_addr2line(line):
- #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
- match = re.match('^( *#([0-9]+) *0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line)
- if match:
- frameno = match.group(2)
- binary = match.group(3)
- addr = match.group(4)
- if not pipes.has_key(binary):
- pipes[binary] = subprocess.Popen(["addr2line", "-f", "-e", binary],
- stdin=subprocess.PIPE, stdout=subprocess.PIPE)
- p = pipes[binary]
+class Symbolizer(object):
+ def __init__(self):
+ pass
+
+ def symbolize(self, addr, binary, offset):
+ """Symbolize the given address (pair of binary and offset).
+
+ Overriden in subclasses.
+ Args:
+ addr: virtual address of an instruction.
+ binary: path to executable/shared object containing this instruction.
+ offset: instruction offset in the @binary.
+ Returns:
+ list of strings (one string for each inlined frame) describing
+ the code locations for this instruction (that is, function name, file
+ name, line and column numbers).
+ """
+ return None
+
+
+class LLVMSymbolizer(Symbolizer):
+ def __init__(self, symbolizer_path):
+ super(LLVMSymbolizer, self).__init__()
+ self.symbolizer_path = symbolizer_path
+ self.pipe = self.open_llvm_symbolizer()
+
+ def open_llvm_symbolizer(self):
+ if not os.path.exists(self.symbolizer_path):
+ return None
+ cmd = [self.symbolizer_path,
+ '--use-symbol-table=true',
+ '--demangle=false',
+ '--functions=true',
+ '--inlining=true']
+ if DEBUG:
+ print ' '.join(cmd)
+ return subprocess.Popen(cmd, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+
+ def symbolize(self, addr, binary, offset):
+ """Overrides Symbolizer.symbolize."""
+ if not self.pipe:
+ return None
+ result = []
try:
- print >>p.stdin, addr
- function_name = p.stdout.readline().rstrip()
- file_name = p.stdout.readline().rstrip()
- except:
- function_name = ""
- file_name = ""
- file_name = fix_filename(file_name)
+ symbolizer_input = '%s %s' % (binary, offset)
+ if DEBUG:
+ print symbolizer_input
+ print >> self.pipe.stdin, symbolizer_input
+ while True:
+ function_name = self.pipe.stdout.readline().rstrip()
+ if not function_name:
+ break
+ file_name = self.pipe.stdout.readline().rstrip()
+ file_name = fix_filename(file_name)
+ if (not function_name.startswith('??') and
+ not file_name.startswith('??')):
+ # Append only valid frames.
+ result.append('%s in %s %s' % (addr, function_name,
+ file_name))
+ except Exception:
+ result = []
+ if not result:
+ result = None
+ return result
- print match.group(1), "in", function_name, file_name
- else:
- print line.rstrip()
-
-
-def get_macho_filetype(binary):
- if not filetypes.has_key(binary):
- otool_pipe = subprocess.Popen(["otool", "-Vh", binary],
- stdin=subprocess.PIPE, stdout=subprocess.PIPE)
- otool_line = "".join(otool_pipe.stdout.readlines())
- for t in ["DYLIB", "EXECUTE"]:
- if t in otool_line:
- filetypes[binary] = t
- otool_pipe.stdin.close()
- return filetypes[binary]
-
-
-def symbolize_atos(line):
- #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
- match = re.match('^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line)
- if match:
- #print line
- prefix = match.group(1)
- frameno = match.group(2)
- orig_addr = match.group(3)
- binary = match.group(4)
- offset = match.group(5)
- addr = orig_addr
- load_addr = hex(int(orig_addr, 16) - int(offset, 16))
- filetype = get_macho_filetype(binary)
-
- if not pipes.has_key(binary):
- # Guess which arch we're running. 10 = len("0x") + 8 hex digits.
- if len(addr) > 10:
- arch = "x86_64"
- else:
- arch = "i386"
- if filetype == "DYLIB":
- load_addr = "0x0"
+def LLVMSymbolizerFactory(system):
+ symbolizer_path = os.getenv('LLVM_SYMBOLIZER_PATH')
+ if not symbolizer_path:
+ # Assume llvm-symbolizer is in PATH.
+ symbolizer_path = 'llvm-symbolizer'
+ return LLVMSymbolizer(symbolizer_path)
+
+
+class Addr2LineSymbolizer(Symbolizer):
+ def __init__(self, binary):
+ super(Addr2LineSymbolizer, self).__init__()
+ self.binary = binary
+ self.pipe = self.open_addr2line()
+
+ def open_addr2line(self):
+ cmd = ['addr2line', '-f', '-e', self.binary]
if DEBUG:
- print "atos -o %s -arch %s -l %s" % (binary, arch, load_addr)
- cmd = ["atos", "-o", binary, "-arch", arch, "-l", load_addr]
- pipes[binary] = subprocess.Popen(cmd,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- p = pipes[binary]
- if filetype == "DYLIB":
- print >>p.stdin, "%s" % offset
+ print ' '.join(cmd)
+ return subprocess.Popen(cmd,
+ stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+
+ def symbolize(self, addr, binary, offset):
+ """Overrides Symbolizer.symbolize."""
+ if self.binary != binary:
+ return None
+ try:
+ print >> self.pipe.stdin, offset
+ function_name = self.pipe.stdout.readline().rstrip()
+ file_name = self.pipe.stdout.readline().rstrip()
+ except Exception:
+ function_name = ''
+ file_name = ''
+ file_name = fix_filename(file_name)
+ return ['%s in %s %s' % (addr, function_name, file_name)]
+
+
+class DarwinSymbolizer(Symbolizer):
+ def __init__(self, addr, binary):
+ super(DarwinSymbolizer, self).__init__()
+ self.binary = binary
+ # Guess which arch we're running. 10 = len('0x') + 8 hex digits.
+ if len(addr) > 10:
+ self.arch = 'x86_64'
else:
- print >>p.stdin, "%s" % addr
- # TODO(glider): it's more efficient to make a batch atos run for each binary.
- p.stdin.close()
- atos_line = p.stdout.readline().rstrip()
+ self.arch = 'i386'
+ self.vmaddr = None
+ self.pipe = None
+
+ def write_addr_to_pipe(self, offset):
+ print >> self.pipe.stdin, '0x%x' % int(offset, 16)
+
+ def open_atos(self):
+ if DEBUG:
+ print 'atos -o %s -arch %s' % (self.binary, self.arch)
+ cmdline = ['atos', '-o', self.binary, '-arch', self.arch]
+ self.pipe = subprocess.Popen(cmdline,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+ def symbolize(self, addr, binary, offset):
+ """Overrides Symbolizer.symbolize."""
+ if self.binary != binary:
+ return None
+ self.open_atos()
+ self.write_addr_to_pipe(offset)
+ self.pipe.stdin.close()
+ atos_line = self.pipe.stdout.readline().rstrip()
# A well-formed atos response looks like this:
# foo(type1, type2) (in object.name) (filename.cc:80)
match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line)
- #print "atos_line: ", atos_line
+ if DEBUG:
+ print 'atos_line: ', atos_line
if match:
function_name = match.group(1)
- function_name = re.sub("\(.*?\)", "", function_name)
+ function_name = re.sub('\(.*?\)', '', function_name)
file_name = fix_filename(match.group(3))
- print "%s%s in %s %s" % (prefix, addr, function_name, file_name)
+ return ['%s in %s %s' % (addr, function_name, file_name)]
+ else:
+ return ['%s in %s' % (addr, atos_line)]
+
+
+# Chain several symbolizers so that if one symbolizer fails, we fall back
+# to the next symbolizer in chain.
+class ChainSymbolizer(Symbolizer):
+ def __init__(self, symbolizer_list):
+ super(ChainSymbolizer, self).__init__()
+ self.symbolizer_list = symbolizer_list
+
+ def symbolize(self, addr, binary, offset):
+ """Overrides Symbolizer.symbolize."""
+ for symbolizer in self.symbolizer_list:
+ if symbolizer:
+ result = symbolizer.symbolize(addr, binary, offset)
+ if result:
+ return result
+ return None
+
+ def append_symbolizer(self, symbolizer):
+ self.symbolizer_list.append(symbolizer)
+
+
+def BreakpadSymbolizerFactory(binary):
+ suffix = os.getenv('BREAKPAD_SUFFIX')
+ if suffix:
+ filename = binary + suffix
+ if os.access(filename, os.F_OK):
+ return BreakpadSymbolizer(filename)
+ return None
+
+
+def SystemSymbolizerFactory(system, addr, binary):
+ if system == 'Darwin':
+ return DarwinSymbolizer(addr, binary)
+ elif system == 'Linux':
+ return Addr2LineSymbolizer(binary)
+
+
+class BreakpadSymbolizer(Symbolizer):
+ def __init__(self, filename):
+ super(BreakpadSymbolizer, self).__init__()
+ self.filename = filename
+ lines = file(filename).readlines()
+ self.files = []
+ self.symbols = {}
+ self.address_list = []
+ self.addresses = {}
+ # MODULE mac x86_64 A7001116478B33F18FF9BEDE9F615F190 t
+ fragments = lines[0].rstrip().split()
+ self.arch = fragments[2]
+ self.debug_id = fragments[3]
+ self.binary = ' '.join(fragments[4:])
+ self.parse_lines(lines[1:])
+
+ def parse_lines(self, lines):
+ cur_function_addr = ''
+ for line in lines:
+ fragments = line.split()
+ if fragments[0] == 'FILE':
+ assert int(fragments[1]) == len(self.files)
+ self.files.append(' '.join(fragments[2:]))
+ elif fragments[0] == 'PUBLIC':
+ self.symbols[int(fragments[1], 16)] = ' '.join(fragments[3:])
+ elif fragments[0] in ['CFI', 'STACK']:
+ pass
+ elif fragments[0] == 'FUNC':
+ cur_function_addr = int(fragments[1], 16)
+ if not cur_function_addr in self.symbols.keys():
+ self.symbols[cur_function_addr] = ' '.join(fragments[4:])
+ else:
+ # Line starting with an address.
+ addr = int(fragments[0], 16)
+ self.address_list.append(addr)
+ # Tuple of symbol address, size, line, file number.
+ self.addresses[addr] = (cur_function_addr,
+ int(fragments[1], 16),
+ int(fragments[2]),
+ int(fragments[3]))
+ self.address_list.sort()
+
+ def get_sym_file_line(self, addr):
+ key = None
+ if addr in self.addresses.keys():
+ key = addr
else:
- print "%s%s in %s" % (prefix, addr, atos_line)
- del pipes[binary]
- else:
- print line.rstrip()
-
-system = os.uname()[0]
-if system in ['Linux', 'Darwin']:
- for line in sys.stdin:
- if system == 'Linux':
- symbolize_addr2line(line)
- elif system == 'Darwin':
- symbolize_atos(line)
-else:
- print 'Unknown system: ', system
+ index = bisect.bisect_left(self.address_list, addr)
+ if index == 0:
+ return None
+ else:
+ key = self.address_list[index - 1]
+ sym_id, size, line_no, file_no = self.addresses[key]
+ symbol = self.symbols[sym_id]
+ filename = self.files[file_no]
+ if addr < key + size:
+ return symbol, filename, line_no
+ else:
+ return None
+
+ def symbolize(self, addr, binary, offset):
+ if self.binary != binary:
+ return None
+ res = self.get_sym_file_line(int(offset, 16))
+ if res:
+ function_name, file_name, line_no = res
+ result = ['%s in %s %s:%d' % (
+ addr, function_name, file_name, line_no)]
+ print result
+ return result
+ else:
+ return None
+
+
+class SymbolizationLoop(object):
+ def __init__(self, binary_name_filter=None):
+ # Used by clients who may want to supply a different binary name.
+ # E.g. in Chrome several binaries may share a single .dSYM.
+ self.binary_name_filter = binary_name_filter
+ self.system = os.uname()[0]
+ if self.system in ['Linux', 'Darwin']:
+ self.llvm_symbolizer = LLVMSymbolizerFactory(self.system)
+ else:
+ raise Exception('Unknown system')
+
+ def symbolize_address(self, addr, binary, offset):
+ # Use the chain of symbolizers:
+ # Breakpad symbolizer -> LLVM symbolizer -> addr2line/atos
+ # (fall back to next symbolizer if the previous one fails).
+ if not binary in symbolizers:
+ symbolizers[binary] = ChainSymbolizer(
+ [BreakpadSymbolizerFactory(binary), self.llvm_symbolizer])
+ result = symbolizers[binary].symbolize(addr, binary, offset)
+ if result is None:
+ # Initialize system symbolizer only if other symbolizers failed.
+ symbolizers[binary].append_symbolizer(
+ SystemSymbolizerFactory(self.system, addr, binary))
+ result = symbolizers[binary].symbolize(addr, binary, offset)
+ # The system symbolizer must produce some result.
+ assert result
+ return result
+
+ def print_symbolized_lines(self, symbolized_lines):
+ if not symbolized_lines:
+ print self.current_line
+ else:
+ for symbolized_frame in symbolized_lines:
+ print ' #' + str(self.frame_no) + ' ' + symbolized_frame.rstrip()
+ self.frame_no += 1
+
+ def process_stdin(self):
+ self.frame_no = 0
+ for line in sys.stdin:
+ self.current_line = line.rstrip()
+ #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
+ stack_trace_line_format = (
+ '^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)')
+ match = re.match(stack_trace_line_format, line)
+ if not match:
+ print self.current_line
+ continue
+ if DEBUG:
+ print line
+ _, frameno_str, addr, binary, offset = match.groups()
+ if frameno_str == '0':
+ # Assume that frame #0 is the first frame of new stack trace.
+ self.frame_no = 0
+ original_binary = binary
+ if self.binary_name_filter:
+ binary = self.binary_name_filter(binary)
+ symbolized_line = self.symbolize_address(addr, binary, offset)
+ if not symbolized_line:
+ if original_binary != binary:
+ symbolized_line = self.symbolize_address(addr, binary, offset)
+ self.print_symbolized_lines(symbolized_line)
+
+
+if __name__ == '__main__':
+ loop = SymbolizationLoop()
+ loop.process_stdin()
diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt
index d409d50b995e..272950bc5450 100644
--- a/lib/asan/tests/CMakeLists.txt
+++ b/lib/asan/tests/CMakeLists.txt
@@ -10,109 +10,178 @@
# instrumentation against the just-built runtime library.
include(CheckCXXCompilerFlag)
+include(CompilerRTCompile)
include_directories(..)
include_directories(../..)
+set(ASAN_UNITTEST_HEADERS
+ asan_mac_test.h
+ asan_test_config.h
+ asan_test_utils.h)
+
set(ASAN_UNITTEST_COMMON_CFLAGS
+ ${COMPILER_RT_GTEST_INCLUDE_CFLAGS}
+ -I${COMPILER_RT_SOURCE_DIR}/include
+ -I${COMPILER_RT_SOURCE_DIR}/lib
+ -I${COMPILER_RT_SOURCE_DIR}/lib/asan
-Wall
-Wno-format
- -fvisibility=hidden
+ -Werror
+ -g
+ -O2
)
-# Support 64-bit and 32-bit builds.
-if(LLVM_BUILD_32_BITS)
- list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -m32)
+
+if(SUPPORTS_NO_VARIADIC_MACROS_FLAG)
+ list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -Wno-variadic-macros)
+endif()
+
+# Use -D instead of definitions to please custom compile command.
+if(ANDROID)
+ list(APPEND ASAN_UNITTEST_COMMON_CFLAGS
+ -DASAN_LOW_MEMORY=1
+ -DASAN_HAS_BLACKLIST=1
+ -DASAN_HAS_EXCEPTIONS=1
+ -DASAN_NEEDS_SEGV=0
+ -DASAN_UAR=0
+ -fPIE
+ )
else()
- list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -m64)
+ list(APPEND ASAN_UNITTEST_COMMON_CFLAGS
+ -DASAN_HAS_BLACKLIST=1
+ -DASAN_HAS_EXCEPTIONS=1
+ -DASAN_NEEDS_SEGV=1
+ -DASAN_UAR=0
+ )
endif()
-set(ASAN_GTEST_INCLUDE_CFLAGS
- -I${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include
- -I${LLVM_MAIN_SRC_DIR}/include
- -I${LLVM_BINARY_DIR}/include
- -D__STDC_CONSTANT_MACROS
- -D__STDC_LIMIT_MACROS
-)
+set(ASAN_LINK_FLAGS)
+if(ANDROID)
+ # On Android, we link with ASan runtime manually
+ list(APPEND ASAN_LINK_FLAGS -pie)
+else()
+ # On other platforms, we depend on Clang driver behavior,
+ # passing -fsanitize=address flag.
+ list(APPEND ASAN_LINK_FLAGS -fsanitize=address)
+endif()
+
+# Unit tests on Mac depend on Foundation.
+if(APPLE)
+ list(APPEND ASAN_LINK_FLAGS -framework Foundation)
+endif()
+# Unit tests require libstdc++.
+list(APPEND ASAN_LINK_FLAGS -lstdc++)
+
+set(ASAN_BLACKLIST_FILE "${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore")
set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS
${ASAN_UNITTEST_COMMON_CFLAGS}
- ${ASAN_GTEST_INCLUDE_CFLAGS}
- -faddress-sanitizer
- -O2
- -g
- -mllvm "-asan-blacklist=${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore"
- -DASAN_HAS_BLACKLIST=1
- -DASAN_HAS_EXCEPTIONS=1
- -DASAN_NEEDS_SEGV=1
- -DASAN_UAR=0
+ -fsanitize=address
+ -mllvm "-asan-blacklist=${ASAN_BLACKLIST_FILE}"
+ -mllvm -asan-stack=1
+ -mllvm -asan-globals=1
+ -mllvm -asan-mapping-scale=0 # default will be used
+ -mllvm -asan-mapping-offset-log=-1 # default will be used
+ -mllvm -asan-use-after-return=0
)
-add_custom_target(AsanTests)
-set_target_properties(AsanTests PROPERTIES FOLDER "ASan tests")
-function(add_asan_test testname)
- add_unittest(AsanTests ${testname} ${ARGN})
- if(LLVM_BUILD_32_BITS)
- target_link_libraries(${testname} clang_rt.asan-i386)
- else()
- target_link_libraries(${testname} clang_rt.asan-x86_64)
- endif()
- if (APPLE)
- # Darwin-specific linker flags.
- set_property(TARGET ${testname} APPEND PROPERTY
- LINK_FLAGS "-framework Foundation")
- elseif (UNIX)
- # Linux-specific linker flags.
- set_property(TARGET ${testname} APPEND PROPERTY
- LINK_FLAGS "-lpthread -ldl -export-dynamic")
- endif()
- set(add_compile_flags "")
- get_property(compile_flags TARGET ${testname} PROPERTY COMPILE_FLAGS)
- foreach(arg ${ASAN_UNITTEST_COMMON_CFLAGS})
- set(add_compile_flags "${add_compile_flags} ${arg}")
- endforeach(arg ${ASAN_UNITTEST_COMMON_CFLAGS})
- set_property(TARGET ${testname} PROPERTY COMPILE_FLAGS
- "${compile_flags} ${add_compile_flags}")
-endfunction()
-
-set(ASAN_NOINST_TEST_SOURCES
- asan_noinst_test.cc
- asan_break_optimization.cc
-)
+# Compile source for the given architecture, using compiler
+# options in ${ARGN}, and add it to the object list.
+macro(asan_compile obj_list source arch)
+ get_filename_component(basename ${source} NAME)
+ set(output_obj "${basename}.${arch}.o")
+ get_target_flags_for_arch(${arch} TARGET_CFLAGS)
+ clang_compile(${output_obj} ${source}
+ CFLAGS ${ARGN} ${TARGET_CFLAGS}
+ DEPS gtest ${ASAN_RUNTIME_LIBRARIES}
+ ${ASAN_UNITTEST_HEADERS}
+ ${ASAN_BLACKLIST_FILE})
+ list(APPEND ${obj_list} ${output_obj})
+endmacro()
-set(ASAN_INST_TEST_OBJECTS)
-
-# We only support building instrumented tests when we're not cross compiling
-# and targeting a unix-like system where we can predict viable compilation and
-# linking strategies.
-if("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND UNIX)
-
- # This function is a custom routine to manage manually compiling source files
- # for unit tests with the just-built Clang binary, using the ASan
- # instrumentation, and linking them into a test executable.
- function(add_asan_compile_command source extra_cflags)
- set(output_obj "${source}.asan.o")
- add_custom_command(
- OUTPUT ${output_obj}
- COMMAND clang
- ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS}
- ${extra_cflags}
- -c -o "${output_obj}"
- ${CMAKE_CURRENT_SOURCE_DIR}/${source}
- MAIN_DEPENDENCY ${source}
- DEPENDS clang clang_rt.asan-i386 clang_rt.asan-x86_64 ${ARGN}
- )
- endfunction()
-
- add_asan_compile_command(asan_globals_test.cc "")
- add_asan_compile_command(asan_test.cc "")
- list(APPEND ASAN_INST_TEST_OBJECTS asan_globals_test.cc.asan.o
- asan_test.cc.asan.o)
+# Link ASan unit test for a given architecture from a set
+# of objects in ${ARGN}.
+macro(add_asan_test test_suite test_name arch)
+ get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS)
+ add_compiler_rt_test(${test_suite} ${test_name}
+ OBJECTS ${ARGN}
+ DEPS ${ASAN_RUNTIME_LIBRARIES} ${ARGN}
+ LINK_FLAGS ${ASAN_LINK_FLAGS}
+ ${TARGET_LINK_FLAGS})
+endmacro()
+
+# Main AddressSanitizer unit tests.
+add_custom_target(AsanUnitTests)
+set_target_properties(AsanUnitTests PROPERTIES FOLDER "ASan unit tests")
+# ASan benchmarks (not actively used now).
+add_custom_target(AsanBenchmarks)
+set_target_properties(AsanBenchmarks PROPERTIES FOLDER "Asan benchmarks")
+
+# Adds ASan unit tests and benchmarks for architecture.
+macro(add_asan_tests_for_arch arch)
+ # Build gtest instrumented with ASan.
+ set(ASAN_INST_GTEST)
+ asan_compile(ASAN_INST_GTEST ${COMPILER_RT_GTEST_SOURCE} ${arch}
+ ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS})
+ # Instrumented tests.
+ set(ASAN_INST_TEST_OBJECTS)
+ asan_compile(ASAN_INST_TEST_OBJECTS asan_globals_test.cc ${arch}
+ ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS})
+ asan_compile(ASAN_INST_TEST_OBJECTS asan_test.cc ${arch}
+ ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS})
if (APPLE)
- add_asan_compile_command(asan_mac_test.mm "-ObjC")
- list(APPEND ASAN_INST_TEST_OBJECTS asan_mac_test.mm.asan.o)
+ asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test.mm ${arch}
+ ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -ObjC)
endif()
+ # Uninstrumented tests.
+ set(ASAN_NOINST_TEST_OBJECTS)
+ asan_compile(ASAN_NOINST_TEST_OBJECTS asan_noinst_test.cc ${arch}
+ ${ASAN_UNITTEST_COMMON_CFLAGS})
+ asan_compile(ASAN_NOINST_TEST_OBJECTS asan_test_main.cc ${arch}
+ ${ASAN_UNITTEST_COMMON_CFLAGS})
+ # Link everything together.
+ add_asan_test(AsanUnitTests "Asan-${arch}-Test" ${arch}
+ ${ASAN_NOINST_TEST_OBJECTS}
+ ${ASAN_INST_TEST_OBJECTS} ${ASAN_INST_GTEST})
+
+ # Instrumented benchmarks.
+ set(ASAN_BENCHMARKS_OBJECTS)
+ asan_compile(ASAN_BENCHMARKS_OBJECTS asan_benchmarks_test.cc ${arch}
+ ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS})
+ # Link benchmarks.
+ add_asan_test(AsanBenchmarks "Asan-${arch}-Benchmark" ${arch}
+ ${ASAN_BENCHMARKS_OBJECTS} ${ASAN_INST_GTEST})
+endmacro()
+if(COMPILER_RT_CAN_EXECUTE_TESTS)
+ if(CAN_TARGET_x86_64)
+ add_asan_tests_for_arch(x86_64)
+ endif()
+ if(CAN_TARGET_i386)
+ add_asan_tests_for_arch(i386)
+ endif()
endif()
-add_asan_test(AsanTest ${ASAN_NOINST_TEST_SOURCES}
- ${ASAN_INST_TEST_OBJECTS})
+if(ANDROID)
+ # We assume that unit tests on Android are built in a build
+ # tree with fresh Clang as a host compiler.
+ set(ASAN_NOINST_TEST_SOURCES asan_noinst_test.cc asan_test_main.cc)
+ set(ASAN_INST_TEST_SOURCES asan_globals_test.cc asan_test.cc)
+ add_library(asan_noinst_test OBJECT ${ASAN_NOINST_TEST_SOURCES})
+ set_target_compile_flags(asan_noinst_test ${ASAN_UNITTEST_COMMON_CFLAGS})
+ add_library(asan_inst_test OBJECT
+ ${ASAN_INST_TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE})
+ set_target_compile_flags(asan_inst_test ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS})
+ add_executable(AsanTest
+ $<TARGET_OBJECTS:asan_noinst_test>
+ $<TARGET_OBJECTS:asan_inst_test>
+ )
+ # Setup correct output directory and link flags.
+ get_unittest_directory(OUTPUT_DIR)
+ set_target_properties(AsanTest PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
+ set_target_link_flags(AsanTest ${ASAN_LINK_FLAGS})
+ target_link_libraries(AsanTest clang_rt.asan-arm-android)
+ # Add unit test to test suite.
+ add_dependencies(AsanUnitTests AsanTest)
+endif()
diff --git a/lib/asan/tests/asan_benchmarks_test.cc b/lib/asan/tests/asan_benchmarks_test.cc
index a142fd23e1b8..fc522de475fa 100644
--- a/lib/asan/tests/asan_benchmarks_test.cc
+++ b/lib/asan/tests/asan_benchmarks_test.cc
@@ -12,7 +12,6 @@
// Some benchmarks for the instrumented code.
//===----------------------------------------------------------------------===//
-#include "asan_test_config.h"
#include "asan_test_utils.h"
template<class T>
diff --git a/lib/asan/tests/asan_globals_test.cc b/lib/asan/tests/asan_globals_test.cc
index 6467524ca32c..dc2e9bbb0530 100644
--- a/lib/asan/tests/asan_globals_test.cc
+++ b/lib/asan/tests/asan_globals_test.cc
@@ -1,4 +1,4 @@
-//===-- asan_globals_test.cc ----------------------===//
+//===-- asan_globals_test.cc ----------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/lib/asan/tests/asan_mac_test.mm b/lib/asan/tests/asan_mac_test.mm
index 4e5873b74485..4cbd2bb247fd 100644
--- a/lib/asan/tests/asan_mac_test.mm
+++ b/lib/asan/tests/asan_mac_test.mm
@@ -57,7 +57,7 @@ char kStartupStr[] =
@implementation LoadSomething
+(void) load {
- for (int i = 0; i < strlen(kStartupStr); i++) {
+ for (size_t i = 0; i < strlen(kStartupStr); i++) {
access_memory(&kStartupStr[i]); // make sure no optimizations occur.
}
// Don't print anything here not to interfere with the death tests.
@@ -66,13 +66,13 @@ char kStartupStr[] =
@end
void worker_do_alloc(int size) {
- char * volatile mem = malloc(size);
+ char * volatile mem = (char * volatile)malloc(size);
mem[0] = 0; // Ok
free(mem);
}
void worker_do_crash(int size) {
- char * volatile mem = malloc(size);
+ char * volatile mem = (char * volatile)malloc(size);
access_memory(&mem[size]); // BOOM
free(mem);
}
@@ -167,7 +167,7 @@ void TestGCDSourceEvent() {
dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
- char * volatile mem = malloc(10);
+ char * volatile mem = (char * volatile)malloc(10);
dispatch_source_set_event_handler(timer, ^{
access_memory(&mem[10]);
});
@@ -184,7 +184,7 @@ void TestGCDSourceCancel() {
dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
- char * volatile mem = malloc(10);
+ char * volatile mem = (char * volatile)malloc(10);
// Both dispatch_source_set_cancel_handler() and
// dispatch_source_set_event_handler() use dispatch_barrier_async_f().
// It's tricky to test dispatch_source_set_cancel_handler() separately,
@@ -202,7 +202,7 @@ void TestGCDSourceCancel() {
void TestGCDGroupAsync() {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
- char * volatile mem = malloc(10);
+ char * volatile mem = (char * volatile)malloc(10);
dispatch_group_async(group, queue, ^{
access_memory(&mem[10]);
});
diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc
index 44d4c3c845b2..576312bf319f 100644
--- a/lib/asan/tests/asan_noinst_test.cc
+++ b/lib/asan/tests/asan_noinst_test.cc
@@ -1,4 +1,4 @@
-//===-- asan_noinst_test.cc ----------------------===//
+//===-- asan_noinst_test.cc -----------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -11,12 +11,13 @@
//
// This test file should be compiled w/o asan instrumentation.
//===----------------------------------------------------------------------===//
+
#include "asan_allocator.h"
-#include "asan_interface.h"
#include "asan_internal.h"
#include "asan_mapping.h"
#include "asan_stack.h"
#include "asan_test_utils.h"
+#include "sanitizer/asan_interface.h"
#include <assert.h>
#include <stdio.h>
@@ -24,15 +25,6 @@
#include <string.h> // for memset()
#include <algorithm>
#include <vector>
-#include "gtest/gtest.h"
-
-// Simple stand-alone pseudorandom number generator.
-// Current algorithm is ANSI C linear congruential PRNG.
-static inline u32 my_rand(u32* state) {
- return (*state = *state * 1103515245 + 12345) >> 16;
-}
-
-static u32 global_seed = 0;
TEST(AddressSanitizer, InternalSimpleDeathTest) {
@@ -40,18 +32,18 @@ TEST(AddressSanitizer, InternalSimpleDeathTest) {
}
static void MallocStress(size_t n) {
- u32 seed = my_rand(&global_seed);
- __asan::AsanStackTrace stack1;
+ u32 seed = my_rand();
+ __asan::StackTrace stack1;
stack1.trace[0] = 0xa123;
stack1.trace[1] = 0xa456;
stack1.size = 2;
- __asan::AsanStackTrace stack2;
+ __asan::StackTrace stack2;
stack2.trace[0] = 0xb123;
stack2.trace[1] = 0xb456;
stack2.size = 2;
- __asan::AsanStackTrace stack3;
+ __asan::StackTrace stack3;
stack3.trace[0] = 0xc123;
stack3.trace[1] = 0xc456;
stack3.size = 2;
@@ -60,20 +52,21 @@ static void MallocStress(size_t n) {
for (size_t i = 0; i < n; i++) {
if ((i % 3) == 0) {
if (vec.empty()) continue;
- size_t idx = my_rand(&seed) % vec.size();
+ size_t idx = my_rand_r(&seed) % vec.size();
void *ptr = vec[idx];
vec[idx] = vec.back();
vec.pop_back();
- __asan::asan_free(ptr, &stack1);
+ __asan::asan_free(ptr, &stack1, __asan::FROM_MALLOC);
} else {
- size_t size = my_rand(&seed) % 1000 + 1;
- switch ((my_rand(&seed) % 128)) {
+ size_t size = my_rand_r(&seed) % 1000 + 1;
+ switch ((my_rand_r(&seed) % 128)) {
case 0: size += 1024; break;
case 1: size += 2048; break;
case 2: size += 4096; break;
}
- size_t alignment = 1 << (my_rand(&seed) % 10 + 1);
- char *ptr = (char*)__asan::asan_memalign(alignment, size, &stack2);
+ size_t alignment = 1 << (my_rand_r(&seed) % 10 + 1);
+ char *ptr = (char*)__asan::asan_memalign(alignment, size,
+ &stack2, __asan::FROM_MALLOC);
vec.push_back(ptr);
ptr[0] = 0;
ptr[size-1] = 0;
@@ -81,7 +74,7 @@ static void MallocStress(size_t n) {
}
}
for (size_t i = 0; i < vec.size(); i++)
- __asan::asan_free(vec[i], &stack3);
+ __asan::asan_free(vec[i], &stack3, __asan::FROM_MALLOC);
}
@@ -118,7 +111,7 @@ TEST(AddressSanitizer, DISABLED_InternalPrintShadow) {
}
static uptr pc_array[] = {
-#if __WORDSIZE == 64
+#if SANITIZER_WORDSIZE == 64
0x7effbf756068ULL,
0x7effbf75e5abULL,
0x7effc0625b7cULL,
@@ -164,7 +157,7 @@ static uptr pc_array[] = {
0x7effbcc3e726ULL,
0x7effbcc40852ULL,
0x7effb681ec4dULL,
-#endif // __WORDSIZE
+#endif // SANITIZER_WORDSIZE
0xB0B5E768,
0x7B682EC1,
0x367F9918,
@@ -208,22 +201,22 @@ static uptr pc_array[] = {
};
void CompressStackTraceTest(size_t n_iter) {
- u32 seed = my_rand(&global_seed);
- const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array);
+ u32 seed = my_rand();
+ const size_t kNumPcs = ARRAY_SIZE(pc_array);
u32 compressed[2 * kNumPcs];
for (size_t iter = 0; iter < n_iter; iter++) {
std::random_shuffle(pc_array, pc_array + kNumPcs);
- __asan::AsanStackTrace stack0, stack1;
+ __asan::StackTrace stack0, stack1;
stack0.CopyFrom(pc_array, kNumPcs);
- stack0.size = std::max((size_t)1, (size_t)(my_rand(&seed) % stack0.size));
+ stack0.size = std::max((size_t)1, (size_t)(my_rand_r(&seed) % stack0.size));
size_t compress_size =
- std::max((size_t)2, (size_t)my_rand(&seed) % (2 * kNumPcs));
+ std::max((size_t)2, (size_t)my_rand_r(&seed) % (2 * kNumPcs));
size_t n_frames =
- __asan::AsanStackTrace::CompressStack(&stack0, compressed, compress_size);
+ __asan::StackTrace::CompressStack(&stack0, compressed, compress_size);
Ident(n_frames);
assert(n_frames <= stack0.size);
- __asan::AsanStackTrace::UncompressStack(&stack1, compressed, compress_size);
+ __asan::StackTrace::UncompressStack(&stack1, compressed, compress_size);
assert(stack1.size == n_frames);
for (size_t i = 0; i < stack1.size; i++) {
assert(stack0.trace[i] == stack1.trace[i]);
@@ -236,17 +229,17 @@ TEST(AddressSanitizer, CompressStackTraceTest) {
}
void CompressStackTraceBenchmark(size_t n_iter) {
- const size_t kNumPcs = ASAN_ARRAY_SIZE(pc_array);
+ const size_t kNumPcs = ARRAY_SIZE(pc_array);
u32 compressed[2 * kNumPcs];
std::random_shuffle(pc_array, pc_array + kNumPcs);
- __asan::AsanStackTrace stack0;
+ __asan::StackTrace stack0;
stack0.CopyFrom(pc_array, kNumPcs);
stack0.size = kNumPcs;
for (size_t iter = 0; iter < n_iter; iter++) {
size_t compress_size = kNumPcs;
size_t n_frames =
- __asan::AsanStackTrace::CompressStack(&stack0, compressed, compress_size);
+ __asan::StackTrace::CompressStack(&stack0, compressed, compress_size);
Ident(n_frames);
}
}
@@ -256,18 +249,18 @@ TEST(AddressSanitizer, CompressStackTraceBenchmark) {
}
TEST(AddressSanitizer, QuarantineTest) {
- __asan::AsanStackTrace stack;
+ __asan::StackTrace stack;
stack.trace[0] = 0x890;
stack.size = 1;
const int size = 32;
void *p = __asan::asan_malloc(size, &stack);
- __asan::asan_free(p, &stack);
+ __asan::asan_free(p, &stack, __asan::FROM_MALLOC);
size_t i;
size_t max_i = 1 << 30;
for (i = 0; i < max_i; i++) {
void *p1 = __asan::asan_malloc(size, &stack);
- __asan::asan_free(p1, &stack);
+ __asan::asan_free(p1, &stack, __asan::FROM_MALLOC);
if (p1 == p) break;
}
// fprintf(stderr, "i=%ld\n", i);
@@ -277,14 +270,14 @@ TEST(AddressSanitizer, QuarantineTest) {
void *ThreadedQuarantineTestWorker(void *unused) {
(void)unused;
- u32 seed = my_rand(&global_seed);
- __asan::AsanStackTrace stack;
+ u32 seed = my_rand();
+ __asan::StackTrace stack;
stack.trace[0] = 0x890;
stack.size = 1;
for (size_t i = 0; i < 1000; i++) {
- void *p = __asan::asan_malloc(1 + (my_rand(&seed) % 4000), &stack);
- __asan::asan_free(p, &stack);
+ void *p = __asan::asan_malloc(1 + (my_rand_r(&seed) % 4000), &stack);
+ __asan::asan_free(p, &stack, __asan::FROM_MALLOC);
}
return NULL;
}
@@ -296,8 +289,8 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) {
size_t mmaped1 = __asan_get_heap_size();
for (int i = 0; i < n_threads; i++) {
pthread_t t;
- pthread_create(&t, NULL, ThreadedQuarantineTestWorker, 0);
- pthread_join(t, 0);
+ PTHREAD_CREATE(&t, NULL, ThreadedQuarantineTestWorker, 0);
+ PTHREAD_JOIN(t, 0);
size_t mmaped2 = __asan_get_heap_size();
EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20));
}
@@ -305,7 +298,7 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) {
void *ThreadedOneSizeMallocStress(void *unused) {
(void)unused;
- __asan::AsanStackTrace stack;
+ __asan::StackTrace stack;
stack.trace[0] = 0x890;
stack.size = 1;
const size_t kNumMallocs = 1000;
@@ -315,7 +308,7 @@ void *ThreadedOneSizeMallocStress(void *unused) {
p[i] = __asan::asan_malloc(32, &stack);
}
for (size_t i = 0; i < kNumMallocs; i++) {
- __asan::asan_free(p[i], &stack);
+ __asan::asan_free(p[i], &stack, __asan::FROM_MALLOC);
}
}
return NULL;
@@ -325,10 +318,10 @@ TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) {
const int kNumThreads = 4;
pthread_t t[kNumThreads];
for (int i = 0; i < kNumThreads; i++) {
- pthread_create(&t[i], 0, ThreadedOneSizeMallocStress, 0);
+ PTHREAD_CREATE(&t[i], 0, ThreadedOneSizeMallocStress, 0);
}
for (int i = 0; i < kNumThreads; i++) {
- pthread_join(t[i], 0);
+ PTHREAD_JOIN(t[i], 0);
}
}
@@ -336,16 +329,20 @@ TEST(AddressSanitizer, MemsetWildAddressTest) {
typedef void*(*memset_p)(void*, int, size_t);
// Prevent inlining of memset().
volatile memset_p libc_memset = (memset_p)memset;
- EXPECT_DEATH(libc_memset((void*)(kLowShadowBeg + kPageSize), 0, 100),
+ EXPECT_DEATH(libc_memset((void*)(kLowShadowBeg + 200), 0, 100),
"unknown-crash.*low shadow");
- EXPECT_DEATH(libc_memset((void*)(kShadowGapBeg + kPageSize), 0, 100),
+ EXPECT_DEATH(libc_memset((void*)(kShadowGapBeg + 200), 0, 100),
"unknown-crash.*shadow gap");
- EXPECT_DEATH(libc_memset((void*)(kHighShadowBeg + kPageSize), 0, 100),
+ EXPECT_DEATH(libc_memset((void*)(kHighShadowBeg + 200), 0, 100),
"unknown-crash.*high shadow");
}
TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) {
+#if ASAN_ALLOCATOR_VERSION == 1
EXPECT_EQ(1U, __asan_get_estimated_allocated_size(0));
+#elif ASAN_ALLOCATOR_VERSION == 2
+ EXPECT_EQ(0U, __asan_get_estimated_allocated_size(0));
+#endif
const size_t sizes[] = { 1, 30, 1<<30 };
for (size_t i = 0; i < 3; i++) {
EXPECT_EQ(sizes[i], __asan_get_estimated_allocated_size(sizes[i]));
@@ -370,23 +367,32 @@ TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) {
// We cannot call GetAllocatedSize from the memory we didn't map,
// and from the interior pointers (not returned by previous malloc).
void *wild_addr = (void*)0x1;
- EXPECT_EQ(false, __asan_get_ownership(wild_addr));
+ EXPECT_FALSE(__asan_get_ownership(wild_addr));
EXPECT_DEATH(__asan_get_allocated_size(wild_addr), kGetAllocatedSizeErrorMsg);
- EXPECT_EQ(false, __asan_get_ownership(array + kArraySize / 2));
+ EXPECT_FALSE(__asan_get_ownership(array + kArraySize / 2));
EXPECT_DEATH(__asan_get_allocated_size(array + kArraySize / 2),
kGetAllocatedSizeErrorMsg);
// NULL is not owned, but is a valid argument for __asan_get_allocated_size().
- EXPECT_EQ(false, __asan_get_ownership(NULL));
+ EXPECT_FALSE(__asan_get_ownership(NULL));
EXPECT_EQ(0U, __asan_get_allocated_size(NULL));
// When memory is freed, it's not owned, and call to GetAllocatedSize
// is forbidden.
free(array);
- EXPECT_EQ(false, __asan_get_ownership(array));
+ EXPECT_FALSE(__asan_get_ownership(array));
EXPECT_DEATH(__asan_get_allocated_size(array), kGetAllocatedSizeErrorMsg);
-
delete int_ptr;
+
+ void *zero_alloc = Ident(malloc(0));
+ if (zero_alloc != 0) {
+ // If malloc(0) is not null, this pointer is owned and should have valid
+ // allocated size.
+ EXPECT_TRUE(__asan_get_ownership(zero_alloc));
+ // Allocated size is 0 or 1 depending on the allocator used.
+ EXPECT_LT(__asan_get_allocated_size(zero_alloc), 2U);
+ }
+ free(zero_alloc);
}
TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) {
@@ -410,6 +416,7 @@ static void DoDoubleFree() {
delete Ident(x);
}
+#if ASAN_ALLOCATOR_VERSION == 1
// This test is run in a separate process, so that large malloced
// chunk won't remain in the free lists after the test.
// Note: use ASSERT_* instead of EXPECT_* here.
@@ -441,9 +448,26 @@ static void RunGetHeapSizeTestAndDie() {
TEST(AddressSanitizerInterface, GetHeapSizeTest) {
EXPECT_DEATH(RunGetHeapSizeTestAndDie(), "double-free");
}
+#elif ASAN_ALLOCATOR_VERSION == 2
+TEST(AddressSanitizerInterface, GetHeapSizeTest) {
+ // asan_allocator2 does not keep huge chunks in free list, but unmaps them.
+ // The chunk should be greater than the quarantine size,
+ // otherwise it will be stuck in quarantine instead of being unmaped.
+ static const size_t kLargeMallocSize = 1 << 29; // 512M
+ uptr old_heap_size = __asan_get_heap_size();
+ for (int i = 0; i < 3; i++) {
+ // fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
+ free(Ident(malloc(kLargeMallocSize)));
+ EXPECT_EQ(old_heap_size, __asan_get_heap_size());
+ }
+}
+#endif
// Note: use ASSERT_* instead of EXPECT_* here.
static void DoLargeMallocForGetFreeBytesTestAndDie() {
+#if ASAN_ALLOCATOR_VERSION == 1
+ // asan_allocator2 does not keep large chunks in free_lists, so this test
+ // will not work.
size_t old_free_bytes, new_free_bytes;
static const size_t kLargeMallocSize = 1 << 29; // 512M
// If we malloc and free a large memory chunk, it will not fall
@@ -455,33 +479,42 @@ static void DoLargeMallocForGetFreeBytesTestAndDie() {
new_free_bytes = __asan_get_free_bytes();
fprintf(stderr, "free bytes after malloc and free: %zu\n", new_free_bytes);
ASSERT_GE(new_free_bytes, old_free_bytes + kLargeMallocSize);
+#endif // ASAN_ALLOCATOR_VERSION
// Test passed.
DoDoubleFree();
}
TEST(AddressSanitizerInterface, GetFreeBytesTest) {
- static const size_t kNumOfChunks = 100;
- static const size_t kChunkSize = 100;
- char *chunks[kNumOfChunks];
- size_t i;
- size_t old_free_bytes, new_free_bytes;
+#if ASAN_ALLOCATOR_VERSION == 1
// Allocate a small chunk. Now allocator probably has a lot of these
// chunks to fulfill future requests. So, future requests will decrease
- // the number of free bytes.
- chunks[0] = Ident((char*)malloc(kChunkSize));
- old_free_bytes = __asan_get_free_bytes();
- for (i = 1; i < kNumOfChunks; i++) {
- chunks[i] = Ident((char*)malloc(kChunkSize));
- new_free_bytes = __asan_get_free_bytes();
- EXPECT_LT(new_free_bytes, old_free_bytes);
- old_free_bytes = new_free_bytes;
+ // the number of free bytes. Do this only on systems where there
+ // is enough memory for such assumptions.
+ if (SANITIZER_WORDSIZE == 64 && !ASAN_LOW_MEMORY) {
+ static const size_t kNumOfChunks = 100;
+ static const size_t kChunkSize = 100;
+ char *chunks[kNumOfChunks];
+ size_t i;
+ size_t old_free_bytes, new_free_bytes;
+ chunks[0] = Ident((char*)malloc(kChunkSize));
+ old_free_bytes = __asan_get_free_bytes();
+ for (i = 1; i < kNumOfChunks; i++) {
+ chunks[i] = Ident((char*)malloc(kChunkSize));
+ new_free_bytes = __asan_get_free_bytes();
+ EXPECT_LT(new_free_bytes, old_free_bytes);
+ old_free_bytes = new_free_bytes;
+ }
+ for (i = 0; i < kNumOfChunks; i++)
+ free(chunks[i]);
}
+#endif
EXPECT_DEATH(DoLargeMallocForGetFreeBytesTestAndDie(), "double-free");
}
-static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<20, 357};
+static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357};
static const size_t kManyThreadsIterations = 250;
-static const size_t kManyThreadsNumThreads = (__WORDSIZE == 32) ? 40 : 200;
+static const size_t kManyThreadsNumThreads =
+ (SANITIZER_WORDSIZE == 32) ? 40 : 200;
void *ManyThreadsWithStatsWorker(void *arg) {
(void)arg;
@@ -490,6 +523,8 @@ void *ManyThreadsWithStatsWorker(void *arg) {
free(Ident(malloc(kManyThreadsMallocSizes[size_index])));
}
}
+ // Just one large allocation.
+ free(Ident(malloc(1 << 20)));
return 0;
}
@@ -498,11 +533,11 @@ TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) {
pthread_t threads[kManyThreadsNumThreads];
before_test = __asan_get_current_allocated_bytes();
for (i = 0; i < kManyThreadsNumThreads; i++) {
- pthread_create(&threads[i], 0,
+ PTHREAD_CREATE(&threads[i], 0,
(void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i);
}
for (i = 0; i < kManyThreadsNumThreads; i++) {
- pthread_join(threads[i], 0);
+ PTHREAD_JOIN(threads[i], 0);
}
after_test = __asan_get_current_allocated_bytes();
// ASan stats also reflect memory usage of internal ASan RTL structs,
@@ -649,6 +684,45 @@ TEST(AddressSanitizerInterface, PoisoningStressTest) {
}
}
+TEST(AddressSanitizerInterface, PoisonedRegion) {
+ size_t rz = 16;
+ for (size_t size = 1; size <= 64; size++) {
+ char *p = new char[size];
+ uptr x = reinterpret_cast<uptr>(p);
+ for (size_t beg = 0; beg < size + rz; beg++) {
+ for (size_t end = beg; end < size + rz; end++) {
+ uptr first_poisoned = __asan_region_is_poisoned(x + beg, end - beg);
+ if (beg == end) {
+ EXPECT_FALSE(first_poisoned);
+ } else if (beg < size && end <= size) {
+ EXPECT_FALSE(first_poisoned);
+ } else if (beg >= size) {
+ EXPECT_EQ(x + beg, first_poisoned);
+ } else {
+ EXPECT_GT(end, size);
+ EXPECT_EQ(x + size, first_poisoned);
+ }
+ }
+ }
+ delete [] p;
+ }
+}
+
+// This is a performance benchmark for manual runs.
+// asan's memset interceptor calls mem_is_zero for the entire shadow region.
+// the profile should look like this:
+// 89.10% [.] __memset_sse2
+// 10.50% [.] __sanitizer::mem_is_zero
+// I.e. mem_is_zero should consume ~ SHADOW_GRANULARITY less CPU cycles
+// than memset itself.
+TEST(AddressSanitizerInterface, DISABLED_Stress_memset) {
+ size_t size = 1 << 20;
+ char *x = new char[size];
+ for (int i = 0; i < 100000; i++)
+ Ident(memset)(x, 0, size);
+ delete [] x;
+}
+
static const char *kInvalidPoisonMessage = "invalid-poison-memory-range";
static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range";
@@ -670,20 +744,29 @@ TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) {
}
static void ErrorReportCallbackOneToZ(const char *report) {
- write(2, "ABCDEF", 6);
+ int report_len = strlen(report);
+ ASSERT_EQ(6, write(2, "ABCDEF", 6));
+ ASSERT_EQ(report_len, write(2, report, report_len));
+ ASSERT_EQ(6, write(2, "ABCDEF", 6));
+ _exit(1);
}
TEST(AddressSanitizerInterface, SetErrorReportCallbackTest) {
__asan_set_error_report_callback(ErrorReportCallbackOneToZ);
- EXPECT_DEATH(__asan_report_error(0, 0, 0, 0, true, 1), "ABCDEF");
+ EXPECT_DEATH(__asan_report_error(0, 0, 0, 0, true, 1),
+ ASAN_PCRE_DOTALL "ABCDEF.*AddressSanitizer.*WRITE.*ABCDEF");
__asan_set_error_report_callback(NULL);
}
TEST(AddressSanitizerInterface, GetOwnershipStressTest) {
std::vector<char *> pointers;
std::vector<size_t> sizes;
+#if ASAN_ALLOCATOR_VERSION == 1
const size_t kNumMallocs =
- (__WORDSIZE <= 32 || ASAN_LOW_MEMORY) ? 1 << 10 : 1 << 14;
+ (SANITIZER_WORDSIZE <= 32 || ASAN_LOW_MEMORY) ? 1 << 10 : 1 << 14;
+#elif ASAN_ALLOCATOR_VERSION == 2 // too slow with asan_allocator2. :(
+ const size_t kNumMallocs = 1 << 9;
+#endif
for (size_t i = 0; i < kNumMallocs; i++) {
size_t size = i * 100 + 1;
pointers.push_back((char*)malloc(size));
diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc
index 8e967e929899..5fa65b2af5dc 100644
--- a/lib/asan/tests/asan_test.cc
+++ b/lib/asan/tests/asan_test.cc
@@ -1,4 +1,4 @@
-//===-- asan_test.cc ----------------------===//
+//===-- asan_test.cc ------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -19,17 +19,26 @@
#include <stdint.h>
#include <setjmp.h>
#include <assert.h>
+#include <algorithm>
+
+#ifdef __linux__
+# include <sys/prctl.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+#include <unistd.h>
+#endif
#if defined(__i386__) || defined(__x86_64__)
#include <emmintrin.h>
#endif
-#include "asan_test_config.h"
#include "asan_test_utils.h"
#ifndef __APPLE__
#include <malloc.h>
#else
+#include <malloc/malloc.h>
#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_*
#include <CoreFoundation/CFString.h>
#endif // __APPLE__
@@ -47,17 +56,8 @@ typedef uint16_t U2;
typedef uint32_t U4;
typedef uint64_t U8;
-static const char *progname;
static const int kPageSize = 4096;
-// Simple stand-alone pseudorandom number generator.
-// Current algorithm is ANSI C linear congruential PRNG.
-static inline uint32_t my_rand(uint32_t* state) {
- return (*state = *state * 1103515245 + 12345) >> 16;
-}
-
-static uint32_t global_seed = 0;
-
const size_t kLargeMalloc = 1 << 24;
template<typename T>
@@ -66,7 +66,7 @@ NOINLINE void asan_write(T *a) {
}
NOINLINE void asan_write_sized_aligned(uint8_t *p, size_t size) {
- EXPECT_EQ(0, ((uintptr_t)p % size));
+ EXPECT_EQ(0U, ((uintptr_t)p % size));
if (size == 1) asan_write((uint8_t*)p);
else if (size == 2) asan_write((uint16_t*)p);
else if (size == 4) asan_write((uint32_t*)p);
@@ -130,6 +130,8 @@ NOINLINE void uaf_test(int size, int off) {
TEST(AddressSanitizer, HasFeatureAddressSanitizerTest) {
#if defined(__has_feature) && __has_feature(address_sanitizer)
bool asan = 1;
+#elif defined(__SANITIZE_ADDRESS__)
+ bool asan = 1;
#else
bool asan = 0;
#endif
@@ -141,29 +143,24 @@ TEST(AddressSanitizer, SimpleDeathTest) {
}
TEST(AddressSanitizer, VariousMallocsTest) {
- // fprintf(stderr, "malloc:\n");
int *a = (int*)malloc(100 * sizeof(int));
a[50] = 0;
free(a);
- // fprintf(stderr, "realloc:\n");
int *r = (int*)malloc(10);
r = (int*)realloc(r, 2000 * sizeof(int));
r[1000] = 0;
free(r);
- // fprintf(stderr, "operator new []\n");
int *b = new int[100];
b[50] = 0;
delete [] b;
- // fprintf(stderr, "operator new\n");
int *c = new int;
*c = 0;
delete c;
-#if !defined(__APPLE__) && !defined(ANDROID)
- // fprintf(stderr, "posix_memalign\n");
+#if !defined(__APPLE__) && !defined(ANDROID) && !defined(__ANDROID__)
int *pm;
int pm_res = posix_memalign((void**)&pm, kPageSize, kPageSize);
EXPECT_EQ(0, pm_res);
@@ -172,7 +169,7 @@ TEST(AddressSanitizer, VariousMallocsTest) {
#if !defined(__APPLE__)
int *ma = (int*)memalign(kPageSize, kPageSize);
- EXPECT_EQ(0, (uintptr_t)ma % kPageSize);
+ EXPECT_EQ(0U, (uintptr_t)ma % kPageSize);
ma[123] = 0;
free(ma);
#endif // __APPLE__
@@ -186,19 +183,19 @@ TEST(AddressSanitizer, CallocTest) {
TEST(AddressSanitizer, VallocTest) {
void *a = valloc(100);
- EXPECT_EQ(0, (uintptr_t)a % kPageSize);
+ EXPECT_EQ(0U, (uintptr_t)a % kPageSize);
free(a);
}
#ifndef __APPLE__
TEST(AddressSanitizer, PvallocTest) {
char *a = (char*)pvalloc(kPageSize + 100);
- EXPECT_EQ(0, (uintptr_t)a % kPageSize);
+ EXPECT_EQ(0U, (uintptr_t)a % kPageSize);
a[kPageSize + 101] = 1; // we should not report an error here.
free(a);
a = (char*)pvalloc(0); // pvalloc(0) should allocate at least one page.
- EXPECT_EQ(0, (uintptr_t)a % kPageSize);
+ EXPECT_EQ(0U, (uintptr_t)a % kPageSize);
a[101] = 1; // we should not report an error here.
free(a);
}
@@ -214,8 +211,8 @@ void *TSDWorker(void *test_key) {
void TSDDestructor(void *tsd) {
// Spawning a thread will check that the current thread id is not -1.
pthread_t th;
- pthread_create(&th, NULL, TSDWorker, NULL);
- pthread_join(th, NULL);
+ PTHREAD_CREATE(&th, NULL, TSDWorker, NULL);
+ PTHREAD_JOIN(th, NULL);
}
// This tests triggers the thread-specific data destruction fiasco which occurs
@@ -229,8 +226,8 @@ TEST(AddressSanitizer, DISABLED_TSDTest) {
pthread_t th;
pthread_key_t test_key;
pthread_key_create(&test_key, TSDDestructor);
- pthread_create(&th, NULL, TSDWorker, &test_key);
- pthread_join(th, NULL);
+ PTHREAD_CREATE(&th, NULL, TSDWorker, &test_key);
+ PTHREAD_JOIN(th, NULL);
pthread_key_delete(test_key);
}
@@ -245,10 +242,10 @@ void OOBTest() {
EXPECT_DEATH(oob_test<T>(size, i), expected_str);
}
- for (int i = 0; i < size - sizeof(T) + 1; i++)
+ for (int i = 0; i < (int)(size - sizeof(T) + 1); i++)
oob_test<T>(size, i);
- for (int i = size - sizeof(T) + 1; i <= size + 3 * sizeof(T); i++) {
+ for (int i = size - sizeof(T) + 1; i <= (int)(size + 2 * sizeof(T)); i++) {
const char *str =
"is located.*%d byte.*to the right";
int off = i >= size ? (i - size) : 0;
@@ -304,8 +301,20 @@ TEST(AddressSanitizer, OOBRightTest) {
}
}
+#if ASAN_ALLOCATOR_VERSION == 2 // Broken with the asan_allocator1
+TEST(AddressSanitizer, LargeOOBRightTest) {
+ size_t large_power_of_two = 1 << 19;
+ for (size_t i = 16; i <= 256; i *= 2) {
+ size_t size = large_power_of_two - i;
+ char *p = Ident(new char[size]);
+ EXPECT_DEATH(p[size] = 0, "is located 0 bytes to the right");
+ delete [] p;
+ }
+}
+#endif // ASAN_ALLOCATOR_VERSION == 2
+
TEST(AddressSanitizer, UAF_char) {
- const char *uaf_string = "AddressSanitizer.*heap-use-after-free";
+ const char *uaf_string = "AddressSanitizer:.*heap-use-after-free";
EXPECT_DEATH(uaf_test<U1>(1, 0), uaf_string);
EXPECT_DEATH(uaf_test<U1>(10, 0), uaf_string);
EXPECT_DEATH(uaf_test<U1>(10, 10), uaf_string);
@@ -335,7 +344,7 @@ TEST(AddressSanitizer, BitFieldPositiveTest) {
EXPECT_DEATH(x->bf2 = 0, "use-after-free");
EXPECT_DEATH(x->bf3 = 0, "use-after-free");
EXPECT_DEATH(x->bf4 = 0, "use-after-free");
-};
+}
struct StructWithBitFields_8_24 {
int a:8;
@@ -350,7 +359,7 @@ TEST(AddressSanitizer, BitFieldNegativeTest) {
}
TEST(AddressSanitizer, OutOfMemoryTest) {
- size_t size = __WORDSIZE == 64 ? (size_t)(1ULL << 48) : (0xf0000000);
+ size_t size = SANITIZER_WORDSIZE == 64 ? (size_t)(1ULL << 48) : (0xf0000000);
EXPECT_EQ(0, realloc(0, size));
EXPECT_EQ(0, realloc(0, ~Ident(0)));
EXPECT_EQ(0, malloc(size));
@@ -360,28 +369,61 @@ TEST(AddressSanitizer, OutOfMemoryTest) {
}
#if ASAN_NEEDS_SEGV
+namespace {
+
+const char kUnknownCrash[] = "AddressSanitizer: SEGV on unknown address";
+const char kOverriddenHandler[] = "ASan signal handler has been overridden\n";
+
TEST(AddressSanitizer, WildAddressTest) {
char *c = (char*)0x123;
- EXPECT_DEATH(*c = 0, "AddressSanitizer crashed on unknown address");
+ EXPECT_DEATH(*c = 0, kUnknownCrash);
+}
+
+void my_sigaction_sighandler(int, siginfo_t*, void*) {
+ fprintf(stderr, kOverriddenHandler);
+ exit(1);
}
+
+void my_signal_sighandler(int signum) {
+ fprintf(stderr, kOverriddenHandler);
+ exit(1);
+}
+
+TEST(AddressSanitizer, SignalTest) {
+ struct sigaction sigact;
+ memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_sigaction = my_sigaction_sighandler;
+ sigact.sa_flags = SA_SIGINFO;
+ // ASan should silently ignore sigaction()...
+ EXPECT_EQ(0, sigaction(SIGSEGV, &sigact, 0));
+#ifdef __APPLE__
+ EXPECT_EQ(0, sigaction(SIGBUS, &sigact, 0));
+#endif
+ char *c = (char*)0x123;
+ EXPECT_DEATH(*c = 0, kUnknownCrash);
+ // ... and signal().
+ EXPECT_EQ(0, signal(SIGSEGV, my_signal_sighandler));
+ EXPECT_DEATH(*c = 0, kUnknownCrash);
+}
+} // namespace
#endif
static void MallocStress(size_t n) {
- uint32_t seed = my_rand(&global_seed);
+ uint32_t seed = my_rand();
for (size_t iter = 0; iter < 10; iter++) {
vector<void *> vec;
for (size_t i = 0; i < n; i++) {
if ((i % 3) == 0) {
if (vec.empty()) continue;
- size_t idx = my_rand(&seed) % vec.size();
+ size_t idx = my_rand_r(&seed) % vec.size();
void *ptr = vec[idx];
vec[idx] = vec.back();
vec.pop_back();
free_aaa(ptr);
} else {
- size_t size = my_rand(&seed) % 1000 + 1;
+ size_t size = my_rand_r(&seed) % 1000 + 1;
#ifndef __APPLE__
- size_t alignment = 1 << (my_rand(&seed) % 7 + 3);
+ size_t alignment = 1 << (my_rand_r(&seed) % 7 + 3);
char *ptr = (char*)memalign_aaa(alignment, size);
#else
char *ptr = (char*) malloc_aaa(size);
@@ -421,24 +463,42 @@ TEST(AddressSanitizer, HugeMallocTest) {
// 32-bit Mac 10.7 gives even less (< 1G).
// (the libSystem malloc() allows allocating up to 2300 megabytes without
// ASan).
- size_t n_megs = __WORDSIZE == 32 ? 500 : 4100;
+ size_t n_megs = SANITIZER_WORDSIZE == 32 ? 500 : 4100;
#else
- size_t n_megs = __WORDSIZE == 32 ? 2600 : 4100;
+ size_t n_megs = SANITIZER_WORDSIZE == 32 ? 2600 : 4100;
#endif
TestLargeMalloc(n_megs << 20);
}
#endif
+#ifndef __APPLE__
+void MemalignRun(size_t align, size_t size, int idx) {
+ char *p = (char *)memalign(align, size);
+ Ident(p)[idx] = 0;
+ free(p);
+}
+
+TEST(AddressSanitizer, memalign) {
+ for (int align = 16; align <= (1 << 23); align *= 2) {
+ size_t size = align * 5;
+ EXPECT_DEATH(MemalignRun(align, size, -1),
+ "is located 1 bytes to the left");
+ EXPECT_DEATH(MemalignRun(align, size, size + 1),
+ "is located 1 bytes to the right");
+ }
+}
+#endif
+
TEST(AddressSanitizer, ThreadedMallocStressTest) {
const int kNumThreads = 4;
const int kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000;
pthread_t t[kNumThreads];
for (int i = 0; i < kNumThreads; i++) {
- pthread_create(&t[i], 0, (void* (*)(void *x))MallocStress,
+ PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))MallocStress,
(void*)kNumIterations);
}
for (int i = 0; i < kNumThreads; i++) {
- pthread_join(t[i], 0);
+ PTHREAD_JOIN(t[i], 0);
}
}
@@ -452,13 +512,14 @@ void *ManyThreadsWorker(void *a) {
}
TEST(AddressSanitizer, ManyThreadsTest) {
- const size_t kNumThreads = __WORDSIZE == 32 ? 30 : 1000;
+ const size_t kNumThreads =
+ (SANITIZER_WORDSIZE == 32 || ASAN_AVOID_EXPENSIVE_TESTS) ? 30 : 1000;
pthread_t t[kNumThreads];
for (size_t i = 0; i < kNumThreads; i++) {
- pthread_create(&t[i], 0, (void* (*)(void *x))ManyThreadsWorker, (void*)i);
+ PTHREAD_CREATE(&t[i], 0, ManyThreadsWorker, (void*)i);
}
for (size_t i = 0; i < kNumThreads; i++) {
- pthread_join(t[i], 0);
+ PTHREAD_JOIN(t[i], 0);
}
}
@@ -468,20 +529,20 @@ TEST(AddressSanitizer, ReallocTest) {
ptr[3] = 3;
for (int i = 0; i < 10000; i++) {
ptr = (int*)realloc(ptr,
- (my_rand(&global_seed) % 1000 + kMinElem) * sizeof(int));
+ (my_rand() % 1000 + kMinElem) * sizeof(int));
EXPECT_EQ(3, ptr[3]);
}
}
#ifndef __APPLE__
static const char *kMallocUsableSizeErrorMsg =
- "AddressSanitizer attempting to call malloc_usable_size()";
+ "AddressSanitizer: attempting to call malloc_usable_size()";
TEST(AddressSanitizer, MallocUsableSizeTest) {
const size_t kArraySize = 100;
char *array = Ident((char*)malloc(kArraySize));
int *int_ptr = Ident(new int);
- EXPECT_EQ(0, malloc_usable_size(NULL));
+ EXPECT_EQ(0U, malloc_usable_size(NULL));
EXPECT_EQ(kArraySize, malloc_usable_size(array));
EXPECT_EQ(sizeof(int), malloc_usable_size(int_ptr));
EXPECT_DEATH(malloc_usable_size((void*)0x123), kMallocUsableSizeErrorMsg);
@@ -501,7 +562,7 @@ void WrongFree() {
TEST(AddressSanitizer, WrongFreeTest) {
EXPECT_DEATH(WrongFree(),
- "ERROR: AddressSanitizer attempting free.*not malloc");
+ "ERROR: AddressSanitizer: attempting free.*not malloc");
}
void DoubleFree() {
@@ -515,7 +576,7 @@ void DoubleFree() {
TEST(AddressSanitizer, DoubleFreeTest) {
EXPECT_DEATH(DoubleFree(), ASAN_PCRE_DOTALL
- "ERROR: AddressSanitizer attempting double-free"
+ "ERROR: AddressSanitizer: attempting double-free"
".*is located 0 bytes inside of 400-byte region"
".*freed by thread T0 here"
".*previously allocated by thread T0 here");
@@ -610,6 +671,17 @@ NOINLINE void LongJmpFunc1(jmp_buf buf) {
longjmp(buf, 1);
}
+NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) {
+ // create three red zones for these two stack objects.
+ int a;
+ int b;
+
+ int *A = Ident(&a);
+ int *B = Ident(&b);
+ *A = *B;
+ __builtin_longjmp((void**)buf, 1);
+}
+
NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) {
// create three red zones for these two stack objects.
int a;
@@ -650,6 +722,17 @@ TEST(AddressSanitizer, LongJmpTest) {
}
}
+#if not defined(__ANDROID__)
+TEST(AddressSanitizer, BuiltinLongJmpTest) {
+ static jmp_buf buf;
+ if (!__builtin_setjmp((void**)buf)) {
+ BuiltinLongJmpFunc1(buf);
+ } else {
+ TouchStackFunc();
+ }
+}
+#endif // not defined(__ANDROID__)
+
TEST(AddressSanitizer, UnderscopeLongJmpTest) {
static jmp_buf buf;
if (!_setjmp(buf)) {
@@ -683,7 +766,7 @@ NOINLINE void ThrowFunc() {
TEST(AddressSanitizer, CxxExceptionTest) {
if (ASAN_UAR) return;
// TODO(kcc): this test crashes on 32-bit for some reason...
- if (__WORDSIZE == 32) return;
+ if (SANITIZER_WORDSIZE == 32) return;
try {
ThrowFunc();
} catch(...) {}
@@ -710,10 +793,10 @@ void *ThreadStackReuseFunc2(void *unused) {
TEST(AddressSanitizer, ThreadStackReuseTest) {
pthread_t t;
- pthread_create(&t, 0, ThreadStackReuseFunc1, 0);
- pthread_join(t, 0);
- pthread_create(&t, 0, ThreadStackReuseFunc2, 0);
- pthread_join(t, 0);
+ PTHREAD_CREATE(&t, 0, ThreadStackReuseFunc1, 0);
+ PTHREAD_JOIN(t, 0);
+ PTHREAD_CREATE(&t, 0, ThreadStackReuseFunc2, 0);
+ PTHREAD_JOIN(t, 0);
}
#if defined(__i386__) || defined(__x86_64__)
@@ -725,7 +808,7 @@ TEST(AddressSanitizer, Store128Test) {
assert(((uintptr_t)p % 16) == 0);
__m128i value_wide = _mm_set1_epi16(0x1234);
EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide),
- "AddressSanitizer heap-buffer-overflow");
+ "AddressSanitizer: heap-buffer-overflow");
EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide),
"WRITE of size 16");
EXPECT_DEATH(_mm_store_si128((__m128i*)p, value_wide),
@@ -734,14 +817,39 @@ TEST(AddressSanitizer, Store128Test) {
}
#endif
-static string RightOOBErrorMessage(int oob_distance) {
+static string RightOOBErrorMessage(int oob_distance, bool is_write) {
assert(oob_distance >= 0);
char expected_str[100];
- sprintf(expected_str, "located %d bytes to the right", oob_distance);
+ sprintf(expected_str, ASAN_PCRE_DOTALL "%s.*located %d bytes to the right",
+ is_write ? "WRITE" : "READ", oob_distance);
return string(expected_str);
}
-static string LeftOOBErrorMessage(int oob_distance) {
+static string RightOOBWriteMessage(int oob_distance) {
+ return RightOOBErrorMessage(oob_distance, /*is_write*/true);
+}
+
+static string RightOOBReadMessage(int oob_distance) {
+ return RightOOBErrorMessage(oob_distance, /*is_write*/false);
+}
+
+static string LeftOOBErrorMessage(int oob_distance, bool is_write) {
+ assert(oob_distance > 0);
+ char expected_str[100];
+ sprintf(expected_str, ASAN_PCRE_DOTALL "%s.*located %d bytes to the left",
+ is_write ? "WRITE" : "READ", oob_distance);
+ return string(expected_str);
+}
+
+static string LeftOOBWriteMessage(int oob_distance) {
+ return LeftOOBErrorMessage(oob_distance, /*is_write*/true);
+}
+
+static string LeftOOBReadMessage(int oob_distance) {
+ return LeftOOBErrorMessage(oob_distance, /*is_write*/false);
+}
+
+static string LeftOOBAccessMessage(int oob_distance) {
assert(oob_distance > 0);
char expected_str[100];
sprintf(expected_str, "located %d bytes to the left", oob_distance);
@@ -755,44 +863,48 @@ void MemSetOOBTestTemplate(size_t length) {
T *array = Ident((T*)malloc(size));
int element = Ident(42);
int zero = Ident(0);
+ void *(*MEMSET)(void *s, int c, size_t n) = Ident(memset);
// memset interval inside array
- memset(array, element, size);
- memset(array, element, size - 1);
- memset(array + length - 1, element, sizeof(T));
- memset(array, element, 1);
+ MEMSET(array, element, size);
+ MEMSET(array, element, size - 1);
+ MEMSET(array + length - 1, element, sizeof(T));
+ MEMSET(array, element, 1);
// memset 0 bytes
- memset(array - 10, element, zero);
- memset(array - 1, element, zero);
- memset(array, element, zero);
- memset(array + length, 0, zero);
- memset(array + length + 1, 0, zero);
+ MEMSET(array - 10, element, zero);
+ MEMSET(array - 1, element, zero);
+ MEMSET(array, element, zero);
+ MEMSET(array + length, 0, zero);
+ MEMSET(array + length + 1, 0, zero);
// try to memset bytes to the right of array
- EXPECT_DEATH(memset(array, 0, size + 1),
- RightOOBErrorMessage(0));
- EXPECT_DEATH(memset((char*)(array + length) - 1, element, 6),
- RightOOBErrorMessage(4));
- EXPECT_DEATH(memset(array + 1, element, size + sizeof(T)),
- RightOOBErrorMessage(2 * sizeof(T) - 1));
+ EXPECT_DEATH(MEMSET(array, 0, size + 1),
+ RightOOBWriteMessage(0));
+ EXPECT_DEATH(MEMSET((char*)(array + length) - 1, element, 6),
+ RightOOBWriteMessage(0));
+ EXPECT_DEATH(MEMSET(array + 1, element, size + sizeof(T)),
+ RightOOBWriteMessage(0));
// whole interval is to the right
- EXPECT_DEATH(memset(array + length + 1, 0, 10),
- RightOOBErrorMessage(sizeof(T)));
+ EXPECT_DEATH(MEMSET(array + length + 1, 0, 10),
+ RightOOBWriteMessage(sizeof(T)));
// try to memset bytes to the left of array
- EXPECT_DEATH(memset((char*)array - 1, element, size),
- LeftOOBErrorMessage(1));
- EXPECT_DEATH(memset((char*)array - 5, 0, 6),
- LeftOOBErrorMessage(5));
- EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)),
- LeftOOBErrorMessage(5 * sizeof(T)));
+ EXPECT_DEATH(MEMSET((char*)array - 1, element, size),
+ LeftOOBWriteMessage(1));
+ EXPECT_DEATH(MEMSET((char*)array - 5, 0, 6),
+ LeftOOBWriteMessage(5));
+ if (length >= 100) {
+ // Large OOB, we find it only if the redzone is large enough.
+ EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)),
+ LeftOOBWriteMessage(5 * sizeof(T)));
+ }
// whole interval is to the left
- EXPECT_DEATH(memset(array - 2, 0, sizeof(T)),
- LeftOOBErrorMessage(2 * sizeof(T)));
+ EXPECT_DEATH(MEMSET(array - 2, 0, sizeof(T)),
+ LeftOOBWriteMessage(2 * sizeof(T)));
// try to memset bytes both to the left & to the right
- EXPECT_DEATH(memset((char*)array - 2, element, size + 4),
- LeftOOBErrorMessage(2));
+ EXPECT_DEATH(MEMSET((char*)array - 2, element, size + 4),
+ LeftOOBWriteMessage(2));
free(array);
}
@@ -804,6 +916,51 @@ TEST(AddressSanitizer, MemSetOOBTest) {
// We can test arrays of structres/classes here, but what for?
}
+// Try to allocate two arrays of 'size' bytes that are near each other.
+// Strictly speaking we are not guaranteed to find such two pointers,
+// but given the structure of asan's allocator we will.
+static bool AllocateTwoAdjacentArrays(char **x1, char **x2, size_t size) {
+ vector<char *> v;
+ bool res = false;
+ for (size_t i = 0; i < 1000U && !res; i++) {
+ v.push_back(new char[size]);
+ if (i == 0) continue;
+ sort(v.begin(), v.end());
+ for (size_t j = 1; j < v.size(); j++) {
+ assert(v[j] > v[j-1]);
+ if ((size_t)(v[j] - v[j-1]) < size * 2) {
+ *x2 = v[j];
+ *x1 = v[j-1];
+ res = true;
+ break;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < v.size(); i++) {
+ if (res && v[i] == *x1) continue;
+ if (res && v[i] == *x2) continue;
+ delete [] v[i];
+ }
+ return res;
+}
+
+TEST(AddressSanitizer, LargeOOBInMemset) {
+ for (size_t size = 200; size < 100000; size += size / 2) {
+ char *x1, *x2;
+ if (!Ident(AllocateTwoAdjacentArrays)(&x1, &x2, size))
+ continue;
+ // fprintf(stderr, " large oob memset: %p %p %zd\n", x1, x2, size);
+ // Do a memset on x1 with huge out-of-bound access that will end up in x2.
+ EXPECT_DEATH(Ident(memset)(x1, 0, size * 2),
+ "is located 0 bytes to the right");
+ delete [] x1;
+ delete [] x2;
+ return;
+ }
+ assert(0 && "Did not find two adjacent malloc-ed pointers");
+}
+
// Same test for memcpy and memmove functions
template <typename T, class M>
void MemTransferOOBTestTemplate(size_t length) {
@@ -827,27 +984,27 @@ void MemTransferOOBTestTemplate(size_t length) {
// try to change mem to the right of dest
EXPECT_DEATH(M::transfer(dest + 1, src, size),
- RightOOBErrorMessage(sizeof(T) - 1));
+ RightOOBWriteMessage(0));
EXPECT_DEATH(M::transfer((char*)(dest + length) - 1, src, 5),
- RightOOBErrorMessage(3));
+ RightOOBWriteMessage(0));
// try to change mem to the left of dest
EXPECT_DEATH(M::transfer(dest - 2, src, size),
- LeftOOBErrorMessage(2 * sizeof(T)));
+ LeftOOBWriteMessage(2 * sizeof(T)));
EXPECT_DEATH(M::transfer((char*)dest - 3, src, 4),
- LeftOOBErrorMessage(3));
+ LeftOOBWriteMessage(3));
// try to access mem to the right of src
EXPECT_DEATH(M::transfer(dest, src + 2, size),
- RightOOBErrorMessage(2 * sizeof(T) - 1));
+ RightOOBReadMessage(0));
EXPECT_DEATH(M::transfer(dest, (char*)(src + length) - 3, 6),
- RightOOBErrorMessage(2));
+ RightOOBReadMessage(0));
// try to access mem to the left of src
EXPECT_DEATH(M::transfer(dest, src - 1, size),
- LeftOOBErrorMessage(sizeof(T)));
+ LeftOOBReadMessage(sizeof(T)));
EXPECT_DEATH(M::transfer(dest, (char*)src - 6, 7),
- LeftOOBErrorMessage(6));
+ LeftOOBReadMessage(6));
// Generally we don't need to test cases where both accessing src and writing
// to dest address to poisoned memory.
@@ -856,10 +1013,10 @@ void MemTransferOOBTestTemplate(size_t length) {
T *big_dest = Ident((T*)malloc(size * 2));
// try to change mem to both sides of dest
EXPECT_DEATH(M::transfer(dest - 1, big_src, size * 2),
- LeftOOBErrorMessage(sizeof(T)));
+ LeftOOBWriteMessage(sizeof(T)));
// try to access mem to both sides of src
EXPECT_DEATH(M::transfer(big_dest, src - 2, size * 2),
- LeftOOBErrorMessage(2 * sizeof(T)));
+ LeftOOBReadMessage(2 * sizeof(T)));
free(src);
free(dest);
@@ -870,7 +1027,7 @@ void MemTransferOOBTestTemplate(size_t length) {
class MemCpyWrapper {
public:
static void* transfer(void *to, const void *from, size_t size) {
- return memcpy(to, from, size);
+ return Ident(memcpy)(to, from, size);
}
};
TEST(AddressSanitizer, MemCpyOOBTest) {
@@ -881,7 +1038,7 @@ TEST(AddressSanitizer, MemCpyOOBTest) {
class MemMoveWrapper {
public:
static void* transfer(void *to, const void *from, size_t size) {
- return memmove(to, from, size);
+ return Ident(memmove)(to, from, size);
}
};
TEST(AddressSanitizer, MemMoveOOBTest) {
@@ -902,21 +1059,21 @@ void StrLenOOBTestTemplate(char *str, size_t length, bool is_global) {
// Normal strlen calls
EXPECT_EQ(strlen(str), length);
if (length > 0) {
- EXPECT_EQ(strlen(str + 1), length - 1);
- EXPECT_EQ(strlen(str + length), 0);
+ EXPECT_EQ(length - 1, strlen(str + 1));
+ EXPECT_EQ(0U, strlen(str + length));
}
// Arg of strlen is not malloced, OOB access
if (!is_global) {
// We don't insert RedZones to the left of global variables
- EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBErrorMessage(5));
+ EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBReadMessage(5));
}
- EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBReadMessage(0));
// Overwrite terminator
str[length] = 'a';
// String is not zero-terminated, strlen will lead to OOB access
- EXPECT_DEATH(Ident(strlen(str)), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(strlen(str + length)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strlen(str)), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(strlen(str + length)), RightOOBReadMessage(0));
// Restore terminator
str[length] = 0;
}
@@ -925,7 +1082,8 @@ TEST(AddressSanitizer, StrLenOOBTest) {
size_t length = Ident(10);
char *heap_string = Ident((char*)malloc(length + 1));
char stack_string[10 + 1];
- for (int i = 0; i < length; i++) {
+ break_optimization(&stack_string);
+ for (size_t i = 0; i < length; i++) {
heap_string[i] = 'a';
stack_string[i] = 'b';
}
@@ -959,11 +1117,11 @@ TEST(AddressSanitizer, StrNLenOOBTest) {
str[size - 1] = '\0';
Ident(strnlen(str, 2 * size));
// Argument points to not allocated memory.
- EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBReadMessage(0));
// Overwrite the terminating '\0' and hit unallocated memory.
str[size - 1] = 'z';
- EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBReadMessage(0));
free(str);
}
#endif
@@ -979,11 +1137,11 @@ TEST(AddressSanitizer, StrDupOOBTest) {
new_str = strdup(str + size - 1);
free(new_str);
// Argument points to not allocated memory.
- EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(strdup(str + size)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(strdup(str + size)), RightOOBReadMessage(0));
// Overwrite the terminating '\0' and hit unallocated memory.
str[size - 1] = 'z';
- EXPECT_DEATH(Ident(strdup(str)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strdup(str)), RightOOBReadMessage(0));
free(str);
}
@@ -997,15 +1155,15 @@ TEST(AddressSanitizer, StrCpyOOBTest) {
strcpy(to, from);
strcpy(to + to_size - from_size, from);
// Length of "from" is too small.
- EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBWriteMessage(0));
// "to" or "from" points to not allocated memory.
- EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBWriteMessage(1));
+ EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBWriteMessage(0));
// Overwrite the terminating '\0' character and hit unallocated memory.
from[from_size - 1] = '!';
- EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBReadMessage(0));
free(to);
free(from);
}
@@ -1027,31 +1185,37 @@ TEST(AddressSanitizer, StrNCpyOOBTest) {
strncpy(to + to_size - 1, from, 1);
// One of {to, from} points to not allocated memory
EXPECT_DEATH(Ident(strncpy(to, from - 1, from_size)),
- LeftOOBErrorMessage(1));
+ LeftOOBReadMessage(1));
EXPECT_DEATH(Ident(strncpy(to - 1, from, from_size)),
- LeftOOBErrorMessage(1));
+ LeftOOBWriteMessage(1));
EXPECT_DEATH(Ident(strncpy(to, from + from_size, 1)),
- RightOOBErrorMessage(0));
+ RightOOBReadMessage(0));
EXPECT_DEATH(Ident(strncpy(to + to_size, from, 1)),
- RightOOBErrorMessage(0));
+ RightOOBWriteMessage(0));
// Length of "to" is too small
EXPECT_DEATH(Ident(strncpy(to + to_size - from_size + 1, from, from_size)),
- RightOOBErrorMessage(0));
+ RightOOBWriteMessage(0));
EXPECT_DEATH(Ident(strncpy(to + 1, from, to_size)),
- RightOOBErrorMessage(0));
+ RightOOBWriteMessage(0));
// Overwrite terminator in from
from[from_size - 1] = '!';
// normal strncpy call
strncpy(to, from, from_size);
// Length of "from" is too small
EXPECT_DEATH(Ident(strncpy(to, from, to_size)),
- RightOOBErrorMessage(0));
+ RightOOBReadMessage(0));
free(to);
free(from);
}
-typedef char*(*PointerToStrChr)(const char*, int);
-void RunStrChrTest(PointerToStrChr StrChr) {
+// Users may have different definitions of "strchr" and "index", so provide
+// function pointer typedefs and overload RunStrChrTest implementation.
+// We can't use macro for RunStrChrTest body here, as this macro would
+// confuse EXPECT_DEATH gtest macro.
+typedef char*(*PointerToStrChr1)(const char*, int);
+typedef char*(*PointerToStrChr2)(char*, int);
+
+USED static void RunStrChrTest(PointerToStrChr1 StrChr) {
size_t size = Ident(100);
char *str = MallocAndMemsetString(size);
str[10] = 'q';
@@ -1060,13 +1224,30 @@ void RunStrChrTest(PointerToStrChr StrChr) {
EXPECT_EQ(str + 10, StrChr(str, 'q'));
EXPECT_EQ(NULL, StrChr(str, 'a'));
// StrChr argument points to not allocated memory.
- EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0));
// Overwrite the terminator and hit not allocated memory.
str[11] = 'z';
- EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0));
free(str);
}
+USED static void RunStrChrTest(PointerToStrChr2 StrChr) {
+ size_t size = Ident(100);
+ char *str = MallocAndMemsetString(size);
+ str[10] = 'q';
+ str[11] = '\0';
+ EXPECT_EQ(str, StrChr(str, 'z'));
+ EXPECT_EQ(str + 10, StrChr(str, 'q'));
+ EXPECT_EQ(NULL, StrChr(str, 'a'));
+ // StrChr argument points to not allocated memory.
+ EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0));
+ // Overwrite the terminator and hit not allocated memory.
+ str[11] = 'z';
+ EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0));
+ free(str);
+}
+
TEST(AddressSanitizer, StrChrAndIndexOOBTest) {
RunStrChrTest(&strchr);
RunStrChrTest(&index);
@@ -1124,8 +1305,9 @@ TEST(AddressSanitizer, StrCmpAndFriendsLogicTest) {
typedef int(*PointerToStrCmp)(const char*, const char*);
void RunStrCmpTest(PointerToStrCmp StrCmp) {
size_t size = Ident(100);
- char *s1 = MallocAndMemsetString(size);
- char *s2 = MallocAndMemsetString(size);
+ int fill = 'o';
+ char *s1 = MallocAndMemsetString(size, fill);
+ char *s2 = MallocAndMemsetString(size, fill);
s1[size - 1] = '\0';
s2[size - 1] = '\0';
// Normal StrCmp calls
@@ -1136,14 +1318,14 @@ void RunStrCmpTest(PointerToStrCmp StrCmp) {
s2[size - 1] = 'x';
Ident(StrCmp(s1, s2));
// One of arguments points to not allocated memory.
- EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBReadMessage(0));
// Hit unallocated memory and die.
- s2[size - 1] = 'z';
- EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBErrorMessage(0));
+ s1[size - 1] = fill;
+ EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBReadMessage(0));
free(s1);
free(s2);
}
@@ -1172,13 +1354,13 @@ void RunStrNCmpTest(PointerToStrNCmp StrNCmp) {
Ident(StrNCmp(s1 - 1, s2 - 1, 0));
Ident(StrNCmp(s1 + size - 1, s2 + size - 1, 1));
// One of arguments points to not allocated memory.
- EXPECT_DEATH(Ident(StrNCmp)(s1 - 1, s2, 1), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(StrNCmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBReadMessage(0));
// Hit unallocated memory and die.
- EXPECT_DEATH(Ident(StrNCmp)(s1 + 1, s2 + 1, size), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(StrNCmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0));
free(s1);
free(s2);
}
@@ -1200,22 +1382,23 @@ TEST(AddressSanitizer, MemCmpOOBTest) {
Ident(memcmp(s1 + size - 1, s2 + size - 1, 1));
Ident(memcmp(s1 - 1, s2 - 1, 0));
// One of arguments points to not allocated memory.
- EXPECT_DEATH(Ident(memcmp)(s1 - 1, s2, 1), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBErrorMessage(1));
- EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(memcmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1));
+ EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBReadMessage(0));
// Hit unallocated memory and die.
- EXPECT_DEATH(Ident(memcmp)(s1 + 1, s2 + 1, size), RightOOBErrorMessage(0));
- EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(memcmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0));
+ EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0));
// Zero bytes are not terminators and don't prevent from OOB.
s1[size - 1] = '\0';
s2[size - 1] = '\0';
- EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0));
free(s1);
free(s2);
}
TEST(AddressSanitizer, StrCatOOBTest) {
+ // strcat() reads strlen(to) bytes from |to| before concatenating.
size_t to_size = Ident(100);
char *to = MallocAndMemsetString(to_size);
to[0] = '\0';
@@ -1226,24 +1409,25 @@ TEST(AddressSanitizer, StrCatOOBTest) {
strcat(to, from);
strcat(to, from);
strcat(to + from_size, from + from_size - 2);
- // Catenate empty string is not always an error.
- strcat(to - 1, from + from_size - 1);
+ // Passing an invalid pointer is an error even when concatenating an empty
+ // string.
+ EXPECT_DEATH(strcat(to - 1, from + from_size - 1), LeftOOBAccessMessage(1));
// One of arguments points to not allocated memory.
- EXPECT_DEATH(strcat(to - 1, from), LeftOOBErrorMessage(1));
- EXPECT_DEATH(strcat(to, from - 1), LeftOOBErrorMessage(1));
- EXPECT_DEATH(strcat(to + to_size, from), RightOOBErrorMessage(0));
- EXPECT_DEATH(strcat(to, from + from_size), RightOOBErrorMessage(0));
+ EXPECT_DEATH(strcat(to - 1, from), LeftOOBAccessMessage(1));
+ EXPECT_DEATH(strcat(to, from - 1), LeftOOBReadMessage(1));
+ EXPECT_DEATH(strcat(to + to_size, from), RightOOBWriteMessage(0));
+ EXPECT_DEATH(strcat(to, from + from_size), RightOOBReadMessage(0));
// "from" is not zero-terminated.
from[from_size - 1] = 'z';
- EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0));
+ EXPECT_DEATH(strcat(to, from), RightOOBReadMessage(0));
from[from_size - 1] = '\0';
// "to" is not zero-terminated.
memset(to, 'z', to_size);
- EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0));
+ EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
// "to" is too short to fit "from".
to[to_size - from_size + 1] = '\0';
- EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0));
+ EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
// length of "to" is just enough.
strcat(to, from + 1);
@@ -1252,6 +1436,7 @@ TEST(AddressSanitizer, StrCatOOBTest) {
}
TEST(AddressSanitizer, StrNCatOOBTest) {
+ // strncat() reads strlen(to) bytes from |to| before concatenating.
size_t to_size = Ident(100);
char *to = MallocAndMemsetString(to_size);
to[0] = '\0';
@@ -1262,26 +1447,26 @@ TEST(AddressSanitizer, StrNCatOOBTest) {
strncat(to, from, from_size);
from[from_size - 1] = '\0';
strncat(to, from, 2 * from_size);
- // Catenating empty string is not an error.
- strncat(to - 1, from, 0);
+ // Catenating empty string with an invalid string is still an error.
+ EXPECT_DEATH(strncat(to - 1, from, 0), LeftOOBAccessMessage(1));
strncat(to, from + from_size - 1, 10);
// One of arguments points to not allocated memory.
- EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBErrorMessage(1));
- EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBErrorMessage(1));
- EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBErrorMessage(0));
- EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBErrorMessage(0));
+ EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBAccessMessage(1));
+ EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBReadMessage(1));
+ EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBWriteMessage(0));
+ EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBReadMessage(0));
memset(from, 'z', from_size);
memset(to, 'z', to_size);
to[0] = '\0';
// "from" is too short.
- EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBErrorMessage(0));
+ EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0));
// "to" is not zero-terminated.
- EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBErrorMessage(0));
+ EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBWriteMessage(0));
// "to" is too short to fit "from".
to[0] = 'z';
to[to_size - from_size + 1] = '\0';
- EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBErrorMessage(0));
+ EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBWriteMessage(0));
// "to" is just enough.
strncat(to, from, from_size - 2);
@@ -1336,7 +1521,7 @@ TEST(AddressSanitizer, StrArgsOverlapTest) {
str[10] = '\0';
str[20] = '\0';
strcat(str, str + 10);
- strcat(str, str + 11);
+ EXPECT_DEATH(strcat(str, str + 11), OverlapErrorMessage("strcat"));
str[10] = '\0';
strcat(str + 11, str);
EXPECT_DEATH(strcat(str, str + 9), OverlapErrorMessage("strcat"));
@@ -1347,7 +1532,7 @@ TEST(AddressSanitizer, StrArgsOverlapTest) {
memset(str, 'z', size);
str[10] = '\0';
strncat(str, str + 10, 10); // from is empty
- strncat(str, str + 11, 10);
+ EXPECT_DEATH(strncat(str, str + 11, 10), OverlapErrorMessage("strncat"));
str[10] = '\0';
str[20] = '\0';
strncat(str + 5, str, 5);
@@ -1372,10 +1557,10 @@ typedef void(*PointerToCallAtoi)(const char*);
void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
char *array = MallocAndMemsetString(10, '1');
// Invalid pointer to the string.
- EXPECT_DEATH(Atoi(array + 11), RightOOBErrorMessage(1));
- EXPECT_DEATH(Atoi(array - 1), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Atoi(array + 11), RightOOBReadMessage(1));
+ EXPECT_DEATH(Atoi(array - 1), LeftOOBReadMessage(1));
// Die if a buffer doesn't have terminating NULL.
- EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
// Make last symbol a terminating NULL or other non-digit.
array[9] = '\0';
Atoi(array);
@@ -1384,13 +1569,13 @@ void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
Atoi(array + 9);
// Sometimes we need to detect overflow if no digits are found.
memset(array, ' ', 10);
- EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
array[9] = '-';
- EXPECT_DEATH(Atoi(array), RightOOBErrorMessage(0));
- EXPECT_DEATH(Atoi(array + 9), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
+ EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0));
array[8] = '-';
Atoi(array);
- delete array;
+ free(array);
}
TEST(AddressSanitizer, AtoiAndFriendsOOBTest) {
@@ -1414,16 +1599,16 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
array[1] = '2';
array[2] = '3';
// Invalid pointer to the string.
- EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBErrorMessage(0));
- EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBErrorMessage(1));
+ EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBReadMessage(0));
+ EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBReadMessage(1));
// Buffer overflow if there is no terminating null (depends on base).
Strtol(array, &endptr, 3);
EXPECT_EQ(array + 2, endptr);
- EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
array[2] = 'z';
Strtol(array, &endptr, 35);
EXPECT_EQ(array + 2, endptr);
- EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBReadMessage(0));
// Add terminating zero to get rid of overflow.
array[2] = '\0';
Strtol(array, NULL, 36);
@@ -1432,11 +1617,11 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
Strtol(array + 3, NULL, 1);
// Sometimes we need to detect overflow if no digits are found.
array[0] = array[1] = array[2] = ' ';
- EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
array[2] = '+';
- EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
array[2] = '-';
- EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBErrorMessage(0));
+ EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
array[1] = '+';
Strtol(array, NULL, 0);
array[1] = array[2] = 'z';
@@ -1444,7 +1629,7 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
EXPECT_EQ(array, endptr);
Strtol(array + 2, NULL, 0);
EXPECT_EQ(array, endptr);
- delete array;
+ free(array);
}
TEST(AddressSanitizer, StrtollOOBTest) {
@@ -1463,7 +1648,7 @@ typedef void*(*PointerToMemSet)(void*, int, size_t);
void CallMemSetByPointer(PointerToMemSet MemSet) {
size_t size = Ident(100);
char *array = Ident((char*)malloc(size));
- EXPECT_DEATH(MemSet(array, 0, 101), RightOOBErrorMessage(0));
+ EXPECT_DEATH(MemSet(array, 0, 101), RightOOBWriteMessage(0));
free(array);
}
@@ -1471,7 +1656,7 @@ void CallMemTransferByPointer(PointerToMemTransfer MemTransfer) {
size_t size = Ident(100);
char *src = Ident((char*)malloc(size));
char *dst = Ident((char*)malloc(size));
- EXPECT_DEATH(MemTransfer(dst, src, 101), RightOOBErrorMessage(0));
+ EXPECT_DEATH(MemTransfer(dst, src, 101), RightOOBWriteMessage(0));
free(src);
free(dst);
}
@@ -1482,12 +1667,37 @@ TEST(AddressSanitizer, DISABLED_MemIntrinsicCallByPointerTest) {
CallMemTransferByPointer(&memmove);
}
+#if defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__)
+#define READ_TEST(READ_N_BYTES) \
+ char *x = new char[10]; \
+ int fd = open("/proc/self/stat", O_RDONLY); \
+ ASSERT_GT(fd, 0); \
+ EXPECT_DEATH(READ_N_BYTES, \
+ ASAN_PCRE_DOTALL \
+ "AddressSanitizer: heap-buffer-overflow" \
+ ".* is located 0 bytes to the right of 10-byte region"); \
+ close(fd); \
+ delete [] x; \
+
+TEST(AddressSanitizer, pread) {
+ READ_TEST(pread(fd, x, 15, 0));
+}
+
+TEST(AddressSanitizer, pread64) {
+ READ_TEST(pread64(fd, x, 15, 0));
+}
+
+TEST(AddressSanitizer, read) {
+ READ_TEST(read(fd, x, 15));
+}
+#endif // defined(__linux__) && !defined(ANDROID) && !defined(__ANDROID__)
+
// This test case fails
// Clang optimizes memcpy/memset calls which lead to unaligned access
TEST(AddressSanitizer, DISABLED_MemIntrinsicUnalignedAccessTest) {
int size = Ident(4096);
char *s = Ident((char*)malloc(size));
- EXPECT_DEATH(memset(s + size - 1, 0, 2), RightOOBErrorMessage(0));
+ EXPECT_DEATH(memset(s + size - 1, 0, 2), RightOOBWriteMessage(0));
free(s);
}
@@ -1529,7 +1739,7 @@ NOINLINE static int LargeFunction(bool do_bad_access) {
TEST(AddressSanitizer, DISABLED_LargeFunctionSymbolizeTest) {
int failing_line = LargeFunction(false);
char expected_warning[128];
- sprintf(expected_warning, "LargeFunction.*asan_test.cc:%d", failing_line);
+ sprintf(expected_warning, "LargeFunction.*asan_test.*:%d", failing_line);
EXPECT_DEATH(LargeFunction(true), expected_warning);
}
@@ -1542,19 +1752,30 @@ TEST(AddressSanitizer, DISABLED_MallocFreeUnwindAndSymbolizeTest) {
"malloc_fff.*malloc_eee.*malloc_ddd");
}
+static bool TryToSetThreadName(const char *name) {
+#if defined(__linux__) && defined(PR_SET_NAME)
+ return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0);
+#else
+ return false;
+#endif
+}
+
void *ThreadedTestAlloc(void *a) {
+ EXPECT_EQ(true, TryToSetThreadName("AllocThr"));
int **p = (int**)a;
*p = new int;
return 0;
}
void *ThreadedTestFree(void *a) {
+ EXPECT_EQ(true, TryToSetThreadName("FreeThr"));
int **p = (int**)a;
delete *p;
return 0;
}
void *ThreadedTestUse(void *a) {
+ EXPECT_EQ(true, TryToSetThreadName("UseThr"));
int **p = (int**)a;
**p = 1;
return 0;
@@ -1563,12 +1784,12 @@ void *ThreadedTestUse(void *a) {
void ThreadedTestSpawn() {
pthread_t t;
int *x;
- pthread_create(&t, 0, ThreadedTestAlloc, &x);
- pthread_join(t, 0);
- pthread_create(&t, 0, ThreadedTestFree, &x);
- pthread_join(t, 0);
- pthread_create(&t, 0, ThreadedTestUse, &x);
- pthread_join(t, 0);
+ PTHREAD_CREATE(&t, 0, ThreadedTestAlloc, &x);
+ PTHREAD_JOIN(t, 0);
+ PTHREAD_CREATE(&t, 0, ThreadedTestFree, &x);
+ PTHREAD_JOIN(t, 0);
+ PTHREAD_CREATE(&t, 0, ThreadedTestUse, &x);
+ PTHREAD_JOIN(t, 0);
}
TEST(AddressSanitizer, ThreadedTest) {
@@ -1579,14 +1800,38 @@ TEST(AddressSanitizer, ThreadedTest) {
".*Thread T.*created");
}
+void *ThreadedTestFunc(void *unused) {
+ // Check if prctl(PR_SET_NAME) is supported. Return if not.
+ if (!TryToSetThreadName("TestFunc"))
+ return 0;
+ EXPECT_DEATH(ThreadedTestSpawn(),
+ ASAN_PCRE_DOTALL
+ "WRITE .*thread T. .UseThr."
+ ".*freed by thread T. .FreeThr. here:"
+ ".*previously allocated by thread T. .AllocThr. here:"
+ ".*Thread T. .UseThr. created by T.*TestFunc"
+ ".*Thread T. .FreeThr. created by T"
+ ".*Thread T. .AllocThr. created by T"
+ "");
+ return 0;
+}
+
+TEST(AddressSanitizer, ThreadNamesTest) {
+ // Run ThreadedTestFunc in a separate thread because it tries to set a
+ // thread name and we don't want to change the main thread's name.
+ pthread_t t;
+ PTHREAD_CREATE(&t, 0, ThreadedTestFunc, 0);
+ PTHREAD_JOIN(t, 0);
+}
+
#if ASAN_NEEDS_SEGV
TEST(AddressSanitizer, ShadowGapTest) {
-#if __WORDSIZE == 32
+#if SANITIZER_WORDSIZE == 32
char *addr = (char*)0x22000000;
#else
char *addr = (char*)0x0000100000080000;
#endif
- EXPECT_DEATH(*addr = 1, "AddressSanitizer crashed on unknown");
+ EXPECT_DEATH(*addr = 1, "AddressSanitizer: SEGV on unknown");
}
#endif // ASAN_NEEDS_SEGV
@@ -1673,7 +1918,7 @@ TEST(AddressSanitizer, FileNameInGlobalReportTest) {
static char zoo[10];
const char *p = Ident(zoo);
// The file name should be present in the report.
- EXPECT_DEATH(Ident(p[15]), "zoo.*asan_test.cc");
+ EXPECT_DEATH(Ident(p[15]), "zoo.*asan_test.");
}
int *ReturnsPointerToALocalObject() {
@@ -1688,7 +1933,7 @@ TEST(AddressSanitizer, LocalReferenceReturnTest) {
// Call 'f' a few more times, 'p' should still be poisoned.
for (int i = 0; i < 32; i++)
f();
- EXPECT_DEATH(*p = 1, "AddressSanitizer stack-use-after-return");
+ EXPECT_DEATH(*p = 1, "AddressSanitizer: stack-use-after-return");
EXPECT_DEATH(*p = 1, "is located.*in frame .*ReturnsPointerToALocal");
}
#endif
@@ -1726,10 +1971,10 @@ TEST(AddressSanitizer, ThreadedStressStackReuseTest) {
const int kNumThreads = 20;
pthread_t t[kNumThreads];
for (int i = 0; i < kNumThreads; i++) {
- pthread_create(&t[i], 0, (void* (*)(void *x))LotsOfStackReuse, 0);
+ PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))LotsOfStackReuse, 0);
}
for (int i = 0; i < kNumThreads; i++) {
- pthread_join(t[i], 0);
+ PTHREAD_JOIN(t[i], 0);
}
}
@@ -1741,8 +1986,8 @@ static void *PthreadExit(void *a) {
TEST(AddressSanitizer, PthreadExitTest) {
pthread_t t;
for (int i = 0; i < 1000; i++) {
- pthread_create(&t, 0, PthreadExit, 0);
- pthread_join(t, 0);
+ PTHREAD_CREATE(&t, 0, PthreadExit, 0);
+ PTHREAD_JOIN(t, 0);
}
}
@@ -1782,7 +2027,7 @@ TEST(AddressSanitizer, LargeStructCopyTest) {
*Ident(&a) = *Ident(&a);
}
-__attribute__((no_address_safety_analysis))
+ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
static void NoAddressSafety() {
char *foo = new char[10];
Ident(foo)[10] = 0;
@@ -1793,6 +2038,29 @@ TEST(AddressSanitizer, AttributeNoAddressSafetyTest) {
Ident(NoAddressSafety)();
}
+// TODO(glider): Enable this test on Mac.
+// It doesn't work on Android, as calls to new/delete go through malloc/free.
+#if !defined(__APPLE__) && !defined(ANDROID) && !defined(__ANDROID__)
+static string MismatchStr(const string &str) {
+ return string("AddressSanitizer: alloc-dealloc-mismatch \\(") + str;
+}
+
+TEST(AddressSanitizer, AllocDeallocMismatch) {
+ EXPECT_DEATH(free(Ident(new int)),
+ MismatchStr("operator new vs free"));
+ EXPECT_DEATH(free(Ident(new int[2])),
+ MismatchStr("operator new \\[\\] vs free"));
+ EXPECT_DEATH(delete (Ident(new int[2])),
+ MismatchStr("operator new \\[\\] vs operator delete"));
+ EXPECT_DEATH(delete (Ident((int*)malloc(2 * sizeof(int)))),
+ MismatchStr("malloc vs operator delete"));
+ EXPECT_DEATH(delete [] (Ident(new int)),
+ MismatchStr("operator new vs operator delete \\[\\]"));
+ EXPECT_DEATH(delete [] (Ident((int*)malloc(2 * sizeof(int)))),
+ MismatchStr("malloc vs operator delete \\[\\]"));
+}
+#endif
+
// ------------------ demo tests; run each one-by-one -------------
// e.g. --gtest_filter=*DemoOOBLeftHigh --gtest_also_run_disabled_tests
TEST(AddressSanitizer, DISABLED_DemoThreadedTest) {
@@ -1811,8 +2079,8 @@ TEST(AddressSanitizer, DISABLED_DemoStackTest) {
TEST(AddressSanitizer, DISABLED_DemoThreadStackTest) {
pthread_t t;
- pthread_create(&t, 0, SimpleBugOnSTack, 0);
- pthread_join(t, 0);
+ PTHREAD_CREATE(&t, 0, SimpleBugOnSTack, 0);
+ PTHREAD_JOIN(t, 0);
}
TEST(AddressSanitizer, DISABLED_DemoUAFLowIn) {
@@ -1846,7 +2114,7 @@ TEST(AddressSanitizer, DISABLED_DemoOOBRightHigh) {
}
TEST(AddressSanitizer, DISABLED_DemoOOM) {
- size_t size = __WORDSIZE == 64 ? (size_t)(1ULL << 40) : (0xf0000000);
+ size_t size = SANITIZER_WORDSIZE == 64 ? (size_t)(1ULL << 40) : (0xf0000000);
printf("%p\n", malloc(size));
}
@@ -1878,7 +2146,7 @@ TEST(AddressSanitizer, DISABLED_DemoTooMuchMemoryTest) {
char *x = (char*)malloc(kAllocSize);
memset(x, 0, kAllocSize);
total_size += kAllocSize;
- fprintf(stderr, "total: %ldM\n", (long)total_size >> 20);
+ fprintf(stderr, "total: %ldM %p\n", (long)total_size >> 20, x);
}
}
@@ -1888,7 +2156,7 @@ TEST(AddressSanitizer, BufferOverflowAfterManyFrees) {
delete [] (Ident(new char [8644]));
}
char *x = new char[8192];
- EXPECT_DEATH(x[Ident(8192)] = 0, "AddressSanitizer heap-buffer-overflow");
+ EXPECT_DEATH(x[Ident(8192)] = 0, "AddressSanitizer: heap-buffer-overflow");
delete [] Ident(x);
}
@@ -1902,8 +2170,8 @@ TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) {
void CFAllocator_DoubleFreeOnPthread() {
pthread_t child;
- pthread_create(&child, NULL, CFAllocatorDefaultDoubleFree, NULL);
- pthread_join(child, NULL); // Shouldn't be reached.
+ PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL);
+ PTHREAD_JOIN(child, NULL); // Shouldn't be reached.
}
TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) {
@@ -1928,10 +2196,10 @@ void *CFAllocatorDeallocateFromGlob(void *unused) {
void CFAllocator_PassMemoryToAnotherThread() {
pthread_t th1, th2;
- pthread_create(&th1, NULL, CFAllocatorAllocateToGlob, NULL);
- pthread_join(th1, NULL);
- pthread_create(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL);
- pthread_join(th2, NULL);
+ PTHREAD_CREATE(&th1, NULL, CFAllocatorAllocateToGlob, NULL);
+ PTHREAD_JOIN(th1, NULL);
+ PTHREAD_CREATE(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL);
+ PTHREAD_JOIN(th2, NULL);
}
TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) {
@@ -1958,53 +2226,56 @@ TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) {
EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free");
}
+// For libdispatch tests below we check that ASan got to the shadow byte
+// legend, i.e. managed to print the thread stacks (this almost certainly
+// means that the libdispatch task creation has been intercepted correctly).
TEST(AddressSanitizerMac, GCDDispatchAsync) {
// Make sure the whole ASan report is printed, i.e. that we don't die
// on a CHECK.
- EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte and word");
+ EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte legend");
}
TEST(AddressSanitizerMac, GCDDispatchSync) {
// Make sure the whole ASan report is printed, i.e. that we don't die
// on a CHECK.
- EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte and word");
+ EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte legend");
}
TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) {
// Make sure the whole ASan report is printed, i.e. that we don't die
// on a CHECK.
- EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte and word");
+ EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte legend");
}
TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) {
// Make sure the whole ASan report is printed, i.e. that we don't die
// on a CHECK.
- EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte and word");
+ EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte legend");
}
TEST(AddressSanitizerMac, GCDDispatchAfter) {
// Make sure the whole ASan report is printed, i.e. that we don't die
// on a CHECK.
- EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte and word");
+ EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend");
}
TEST(AddressSanitizerMac, GCDSourceEvent) {
// Make sure the whole ASan report is printed, i.e. that we don't die
// on a CHECK.
- EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte and word");
+ EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte legend");
}
TEST(AddressSanitizerMac, GCDSourceCancel) {
// Make sure the whole ASan report is printed, i.e. that we don't die
// on a CHECK.
- EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte and word");
+ EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte legend");
}
TEST(AddressSanitizerMac, GCDGroupAsync) {
// Make sure the whole ASan report is printed, i.e. that we don't die
// on a CHECK.
- EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte and word");
+ EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend");
}
void *MallocIntrospectionLockWorker(void *_) {
@@ -2047,13 +2318,13 @@ TEST(AddressSanitizerMac, MallocIntrospectionLock) {
for (iter = 0; iter < kNumIterations; iter++) {
pthread_t workers[kNumWorkers], forker;
for (i = 0; i < kNumWorkers; i++) {
- pthread_create(&workers[i], 0, MallocIntrospectionLockWorker, 0);
+ PTHREAD_CREATE(&workers[i], 0, MallocIntrospectionLockWorker, 0);
}
- pthread_create(&forker, 0, MallocIntrospectionLockForker, 0);
+ PTHREAD_CREATE(&forker, 0, MallocIntrospectionLockForker, 0);
for (i = 0; i < kNumWorkers; i++) {
- pthread_join(workers[i], 0);
+ PTHREAD_JOIN(workers[i], 0);
}
- pthread_join(forker, 0);
+ PTHREAD_JOIN(forker, 0);
}
}
@@ -2069,8 +2340,8 @@ TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) {
pthread_t th;
pthread_key_t test_key;
pthread_key_create(&test_key, CallFreeOnWorkqueue);
- pthread_create(&th, NULL, TSDAllocWorker, &test_key);
- pthread_join(th, NULL);
+ PTHREAD_CREATE(&th, NULL, TSDAllocWorker, &test_key);
+ PTHREAD_JOIN(th, NULL);
pthread_key_delete(test_key);
}
@@ -2092,6 +2363,19 @@ TEST(AddressSanitizerMac, NSObjectOOB) {
TEST(AddressSanitizerMac, NSURLDeallocation) {
TestNSURLDeallocation();
}
+
+// See http://code.google.com/p/address-sanitizer/issues/detail?id=109.
+TEST(AddressSanitizerMac, Mstats) {
+ malloc_statistics_t stats1, stats2;
+ malloc_zone_statistics(/*all zones*/NULL, &stats1);
+ const size_t kMallocSize = 100000;
+ void *alloc = Ident(malloc(kMallocSize));
+ malloc_zone_statistics(/*all zones*/NULL, &stats2);
+ EXPECT_GT(stats2.blocks_in_use, stats1.blocks_in_use);
+ EXPECT_GE(stats2.size_in_use - stats1.size_in_use, kMallocSize);
+ free(alloc);
+ // Even the default OSX allocator may not change the stats after free().
+}
#endif // __APPLE__
// Test that instrumentation of stack allocations takes into account
@@ -2102,11 +2386,4 @@ TEST(AddressSanitizer, LongDoubleNegativeTest) {
static long double c;
memcpy(Ident(&a), Ident(&b), sizeof(long double));
memcpy(Ident(&c), Ident(&b), sizeof(long double));
-};
-
-int main(int argc, char **argv) {
- progname = argv[0];
- testing::GTEST_FLAG(death_test_style) = "threadsafe";
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
}
diff --git a/lib/asan/tests/asan_test.ignore b/lib/asan/tests/asan_test.ignore
index 7bafa83bf62e..ea5c26099e75 100644
--- a/lib/asan/tests/asan_test.ignore
+++ b/lib/asan/tests/asan_test.ignore
@@ -1,2 +1,3 @@
+# blacklisted functions for instrumented ASan unit test
fun:*IgnoreTest*
fun:*SomeOtherFunc*
diff --git a/lib/asan/tests/asan_test_config.h b/lib/asan/tests/asan_test_config.h
index 6cf0e6958484..1d28e99a4b10 100644
--- a/lib/asan/tests/asan_test_config.h
+++ b/lib/asan/tests/asan_test_config.h
@@ -1,4 +1,4 @@
-//===-- asan_test_config.h ------------*- C++ -*-===//
+//===-- asan_test_config.h --------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -10,6 +10,10 @@
// This file is a part of AddressSanitizer, an address sanity checker.
//
//===----------------------------------------------------------------------===//
+#if !defined(INCLUDED_FROM_ASAN_TEST_UTILS_H)
+# error "This file should be included into asan_test_utils.h only"
+#endif
+
#ifndef ASAN_TEST_CONFIG_H
#define ASAN_TEST_CONFIG_H
@@ -17,7 +21,11 @@
#include <string>
#include <map>
-#include "gtest/gtest.h"
+#if ASAN_USE_DEJAGNU_GTEST
+# include "dejagnu-gtest.h"
+#else
+# include "gtest/gtest.h"
+#endif
using std::string;
using std::vector;
@@ -40,7 +48,11 @@ using std::map;
#endif
#ifndef ASAN_LOW_MEMORY
-#define ASAN_LOW_MEMORY 0
+# define ASAN_LOW_MEMORY 0
+#endif
+
+#ifndef ASAN_AVOID_EXPENSIVE_TESTS
+# define ASAN_AVOID_EXPENSIVE_TESTS 0
#endif
#define ASAN_PCRE_DOTALL ""
diff --git a/lib/asan/tests/asan_break_optimization.cc b/lib/asan/tests/asan_test_main.cc
index 022a9f8b8505..1746c5f4837b 100644
--- a/lib/asan/tests/asan_break_optimization.cc
+++ b/lib/asan/tests/asan_test_main.cc
@@ -1,4 +1,4 @@
-//===-- asan_break_optimization.cc ----------------------===//
+//===-- asan_test_main.cc -------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -10,10 +10,10 @@
// This file is a part of AddressSanitizer, an address sanity checker.
//
//===----------------------------------------------------------------------===//
-
#include "asan_test_utils.h"
-// Have this function in a separate file to avoid inlining.
-// (Yes, we know about cross-file inlining, but let's assume we don't use it).
-extern "C" void break_optimization(void *x) {
- (void)x;
+
+int main(int argc, char **argv) {
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
}
diff --git a/lib/asan/tests/asan_test_utils.h b/lib/asan/tests/asan_test_utils.h
index fb509cc43e30..6ed9f90df906 100644
--- a/lib/asan/tests/asan_test_utils.h
+++ b/lib/asan/tests/asan_test_utils.h
@@ -1,4 +1,4 @@
-//===-- asan_test_utils.h ------------*- C++ -*-===//
+//===-- asan_test_utils.h ---------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -14,43 +14,16 @@
#ifndef ASAN_TEST_UTILS_H
#define ASAN_TEST_UTILS_H
-#if defined(_WIN32)
-typedef unsigned __int8 uint8_t;
-typedef unsigned __int16 uint16_t;
-typedef unsigned __int32 uint32_t;
-typedef unsigned __int64 uint64_t;
-typedef __int8 int8_t;
-typedef __int16 int16_t;
-typedef __int32 int32_t;
-typedef __int64 int64_t;
-# define NOINLINE __declspec(noinline)
-#else // defined(_WIN32)
-# define NOINLINE __attribute__((noinline))
-#endif // defined(_WIN32)
-
-#if !defined(__has_feature)
-#define __has_feature(x) 0
-#endif
-
-#ifndef __WORDSIZE
-#if __LP64__ || defined(_WIN64)
-#define __WORDSIZE 64
-#else
-#define __WORDSIZE 32
-#endif
+#if !defined(ASAN_EXTERNAL_TEST_CONFIG)
+# define INCLUDED_FROM_ASAN_TEST_UTILS_H
+# include "asan_test_config.h"
+# undef INCLUDED_FROM_ASAN_TEST_UTILS_H
#endif
-// Make the compiler think that something is going on there.
-extern "C" void break_optimization(void *);
+#include "sanitizer_common/tests/sanitizer_test_utils.h"
-// This function returns its parameter but in such a way that compiler
-// can not prove it.
-template<class T>
-NOINLINE
-static T Ident(T t) {
- T ret = t;
- break_optimization(&ret);
- return ret;
-}
+// Check that pthread_create/pthread_join return success.
+#define PTHREAD_CREATE(a, b, c, d) ASSERT_EQ(0, pthread_create(a, b, c, d))
+#define PTHREAD_JOIN(a, b) ASSERT_EQ(0, pthread_join(a, b))
#endif // ASAN_TEST_UTILS_H
diff --git a/lib/cmpdi2.c b/lib/cmpdi2.c
index c2b1f69f4fc2..52634d9c3362 100644
--- a/lib/cmpdi2.c
+++ b/lib/cmpdi2.c
@@ -36,3 +36,16 @@ __cmpdi2(di_int a, di_int b)
return 2;
return 1;
}
+
+#ifdef __ARM_EABI__
+/* Returns: if (a < b) returns -1
+* if (a == b) returns 0
+* if (a > b) returns 1
+*/
+COMPILER_RT_ABI si_int
+__aeabi_lcmp(di_int a, di_int b)
+{
+ return __cmpdi2(a, b) - 1;
+}
+#endif
+
diff --git a/lib/fixsfdi.c b/lib/fixsfdi.c
index 8a066907e136..4f6cfdd7a5c6 100644
--- a/lib/fixsfdi.c
+++ b/lib/fixsfdi.c
@@ -23,7 +23,7 @@
/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */
-ARM_EABI_FNALIAS(d2lz, fixsfdi)
+ARM_EABI_FNALIAS(f2lz, fixsfdi)
COMPILER_RT_ABI di_int
__fixsfdi(float a)
diff --git a/lib/int_endianness.h b/lib/int_endianness.h
index 70bd17730cb2..edb58c810e21 100644
--- a/lib/int_endianness.h
+++ b/lib/int_endianness.h
@@ -31,7 +31,7 @@
/* .. */
-#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__minix)
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__minix)
#include <sys/endian.h>
#if _BYTE_ORDER == _BIG_ENDIAN
@@ -44,6 +44,19 @@
#endif /* *BSD */
+#if defined(__OpenBSD__) || defined(__Bitrig__)
+#include <machine/endian.h>
+
+#if _BYTE_ORDER == _BIG_ENDIAN
+#define _YUGA_LITTLE_ENDIAN 0
+#define _YUGA_BIG_ENDIAN 1
+#elif _BYTE_ORDER == _LITTLE_ENDIAN
+#define _YUGA_LITTLE_ENDIAN 1
+#define _YUGA_BIG_ENDIAN 0
+#endif /* _BYTE_ORDER */
+
+#endif /* OpenBSD and Bitrig. */
+
/* .. */
/* Mac OSX has __BIG_ENDIAN__ or __LITTLE_ENDIAN__ automatically set by the compiler (at least with GCC) */
diff --git a/lib/interception/CMakeLists.txt b/lib/interception/CMakeLists.txt
index 033b05fc1b34..ca59f2b8211e 100644
--- a/lib/interception/CMakeLists.txt
+++ b/lib/interception/CMakeLists.txt
@@ -6,32 +6,34 @@ set(INTERCEPTION_SOURCES
interception_win.cc
)
+set(MACH_OVERRIDE_SOURCES
+ mach_override/mach_override.c
+ )
+
# Only add this C file if we're building on a Mac. Other source files can be
# harmlessly compiled on any platform, but the C file is complained about due
# to pedantic rules about empty translation units.
if (APPLE)
- list(APPEND INTERCEPTION_SOURCES mach_override/mach_override.c)
+ list(APPEND INTERCEPTION_SOURCES ${MACH_OVERRIDE_SOURCES})
+ set_source_files_properties(${MACH_OVERRIDE_SOURCES} PROPERTIES COMPILE_FLAGS "-std=c99 ${INTERCEPTION_CFLAGS}")
endif ()
-set(INTERCEPTION_CFLAGS "-fPIC -fno-exceptions -funwind-tables -fvisibility=hidden")
-if (SUPPORTS_NO_VARIADIC_MACROS_FLAG)
- set(INTERCEPTION_CFLAGS "${INTERCEPTION_CFLAGS} -Wno-variadic-macros")
-endif ()
+set(INTERCEPTION_CFLAGS ${SANITIZER_COMMON_CFLAGS})
-set(INTERCEPTION_COMMON_DEFINITIONS
- INTERCEPTION_HAS_EXCEPTIONS=1)
-
-if(CAN_TARGET_X86_64)
- add_library(RTInterception.x86_64 OBJECT ${INTERCEPTION_SOURCES})
- set_property(TARGET RTInterception.x86_64 PROPERTY COMPILE_FLAGS
- "${INTERCEPTION_CFLAGS} ${TARGET_X86_64_CFLAGS}")
- set_property(TARGET RTInterception.x86_64 APPEND PROPERTY COMPILE_DEFINITIONS
- ${INTERCEPTION_COMMON_DEFINITIONS})
-endif()
-if(CAN_TARGET_I386)
- add_library(RTInterception.i386 OBJECT ${INTERCEPTION_SOURCES})
- set_property(TARGET RTInterception.i386 PROPERTY COMPILE_FLAGS
- "${INTERCEPTION_CFLAGS} ${TARGET_I386_CFLAGS}")
- set_property(TARGET RTInterception.i386 APPEND PROPERTY COMPILE_DEFINITIONS
- ${INTERCEPTION_COMMON_DEFINITIONS})
+if(APPLE)
+ # Build universal binary on APPLE.
+ add_library(RTInterception.osx OBJECT ${INTERCEPTION_SOURCES})
+ set_target_compile_flags(RTInterception.osx ${INTERCEPTION_CFLAGS})
+ set_target_properties(RTInterception.osx PROPERTIES
+ OSX_ARCHITECTURES "${SANITIZER_COMMON_SUPPORTED_ARCH}")
+elseif(ANDROID)
+ add_library(RTInterception.arm.android OBJECT ${INTERCEPTION_SOURCES})
+ set_target_compile_flags(RTInterception.arm.android
+ ${INTERCEPTION_CFLAGS})
+else()
+ # Otherwise, build separate libraries for each target.
+ foreach(arch ${SANITIZER_COMMON_SUPPORTED_ARCH})
+ add_compiler_rt_object_library(RTInterception ${arch}
+ SOURCES ${INTERCEPTION_SOURCES} CFLAGS ${INTERCEPTION_CFLAGS})
+ endforeach()
endif()
diff --git a/lib/interception/interception.h b/lib/interception/interception.h
index b72bff2a6c02..030bda7cba0c 100644
--- a/lib/interception/interception.h
+++ b/lib/interception/interception.h
@@ -19,6 +19,17 @@
# error "Interception doesn't work on this operating system."
#endif
+#include "sanitizer/common_interface_defs.h"
+
+// These typedefs should be used only in the interceptor definitions to replace
+// the standard system types (e.g. SSIZE_T instead of ssize_t)
+typedef __sanitizer::uptr SIZE_T;
+typedef __sanitizer::sptr SSIZE_T;
+typedef __sanitizer::sptr PTRDIFF_T;
+typedef __sanitizer::s64 INTMAX_T;
+typedef __sanitizer::u64 OFF_T;
+typedef __sanitizer::u64 OFF64_T;
+
// How to use this library:
// 1) Include this header to define your own interceptors
// (see details below).
@@ -75,12 +86,22 @@
// mach_override, a handy framework for patching functions at runtime.
// To avoid possible name clashes, our replacement functions have
// the "wrap_" prefix on Mac.
+// An alternative to function patching is to create a dylib containing a
+// __DATA,__interpose section that associates library functions with their
+// wrappers. When this dylib is preloaded before an executable using
+// DYLD_INSERT_LIBRARIES, it routes all the calls to interposed functions done
+// through stubs to the wrapper functions. Such a library is built with
+// -DMAC_INTERPOSE_FUNCTIONS=1.
+
+#if !defined(MAC_INTERPOSE_FUNCTIONS) || !defined(__APPLE__)
+# define MAC_INTERPOSE_FUNCTIONS 0
+#endif
#if defined(__APPLE__)
# define WRAP(x) wrap_##x
# define WRAPPER_NAME(x) "wrap_"#x
# define INTERCEPTOR_ATTRIBUTE
-# define DECLARE_WRAPPER(ret_type, convention, func, ...)
+# define DECLARE_WRAPPER(ret_type, func, ...)
#elif defined(_WIN32)
# if defined(_DLL) // DLL CRT
# define WRAP(x) x
@@ -91,63 +112,82 @@
# define WRAPPER_NAME(x) "wrap_"#x
# define INTERCEPTOR_ATTRIBUTE
# endif
-# define DECLARE_WRAPPER(ret_type, convention, func, ...)
+# define DECLARE_WRAPPER(ret_type, func, ...)
#else
# define WRAP(x) __interceptor_ ## x
# define WRAPPER_NAME(x) "__interceptor_" #x
# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
-# define DECLARE_WRAPPER(ret_type, convention, func, ...) \
- extern "C" ret_type convention func(__VA_ARGS__) \
+# define DECLARE_WRAPPER(ret_type, func, ...) \
+ extern "C" ret_type func(__VA_ARGS__) \
__attribute__((weak, alias("__interceptor_" #func), visibility("default")));
#endif
-#define PTR_TO_REAL(x) real_##x
-#define REAL(x) __interception::PTR_TO_REAL(x)
-#define FUNC_TYPE(x) x##_f
-
-#define DECLARE_REAL(ret_type, func, ...) \
- typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
- namespace __interception { \
- extern FUNC_TYPE(func) PTR_TO_REAL(func); \
- }
+#if !MAC_INTERPOSE_FUNCTIONS
+# define PTR_TO_REAL(x) real_##x
+# define REAL(x) __interception::PTR_TO_REAL(x)
+# define FUNC_TYPE(x) x##_f
+
+# define DECLARE_REAL(ret_type, func, ...) \
+ typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
+ namespace __interception { \
+ extern FUNC_TYPE(func) PTR_TO_REAL(func); \
+ }
+#else // MAC_INTERPOSE_FUNCTIONS
+# define REAL(x) x
+# define DECLARE_REAL(ret_type, func, ...) \
+ extern "C" ret_type func(__VA_ARGS__);
+#endif // MAC_INTERPOSE_FUNCTIONS
#define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \
- DECLARE_REAL(ret_type, func, ##__VA_ARGS__) \
+ DECLARE_REAL(ret_type, func, __VA_ARGS__) \
extern "C" ret_type WRAP(func)(__VA_ARGS__);
-// FIXME(timurrrr): We might need to add DECLARE_REAL_EX etc to support
-// different calling conventions later.
-
-#define DEFINE_REAL_EX(ret_type, convention, func, ...) \
- typedef ret_type (convention *FUNC_TYPE(func))(__VA_ARGS__); \
- namespace __interception { \
- FUNC_TYPE(func) PTR_TO_REAL(func); \
- }
-
// Generally, you don't need to use DEFINE_REAL by itself, as INTERCEPTOR
// macros does its job. In exceptional cases you may need to call REAL(foo)
// without defining INTERCEPTOR(..., foo, ...). For example, if you override
// foo with an interceptor for other function.
-#define DEFAULT_CONVENTION
-
-#define DEFINE_REAL(ret_type, func, ...) \
- DEFINE_REAL_EX(ret_type, DEFAULT_CONVENTION, func, __VA_ARGS__)
+#if !MAC_INTERPOSE_FUNCTIONS
+# define DEFINE_REAL(ret_type, func, ...) \
+ typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
+ namespace __interception { \
+ FUNC_TYPE(func) PTR_TO_REAL(func); \
+ }
+#else
+# define DEFINE_REAL(ret_type, func, ...)
+#endif
-#define INTERCEPTOR_EX(ret_type, convention, func, ...) \
- DEFINE_REAL_EX(ret_type, convention, func, __VA_ARGS__) \
- DECLARE_WRAPPER(ret_type, convention, func, __VA_ARGS__) \
+#define INTERCEPTOR(ret_type, func, ...) \
+ DEFINE_REAL(ret_type, func, __VA_ARGS__) \
+ DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \
extern "C" \
INTERCEPTOR_ATTRIBUTE \
- ret_type convention WRAP(func)(__VA_ARGS__)
-
-#define INTERCEPTOR(ret_type, func, ...) \
- INTERCEPTOR_EX(ret_type, DEFAULT_CONVENTION, func, __VA_ARGS__)
+ ret_type WRAP(func)(__VA_ARGS__)
#if defined(_WIN32)
# define INTERCEPTOR_WINAPI(ret_type, func, ...) \
- INTERCEPTOR_EX(ret_type, __stdcall, func, __VA_ARGS__)
+ typedef ret_type (__stdcall *FUNC_TYPE(func))(__VA_ARGS__); \
+ namespace __interception { \
+ FUNC_TYPE(func) PTR_TO_REAL(func); \
+ } \
+ DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \
+ extern "C" \
+ INTERCEPTOR_ATTRIBUTE \
+ ret_type __stdcall WRAP(func)(__VA_ARGS__)
#endif
+// ISO C++ forbids casting between pointer-to-function and pointer-to-object,
+// so we use casting via an integral type __interception::uptr,
+// assuming that system is POSIX-compliant. Using other hacks seem
+// challenging, as we don't even pass function type to
+// INTERCEPT_FUNCTION macro, only its name.
+namespace __interception {
+#if defined(_WIN64)
+typedef unsigned long long uptr; // NOLINT
+#else
+typedef unsigned long uptr; // NOLINT
+#endif // _WIN64
+} // namespace __interception
+
#define INCLUDED_FROM_INTERCEPTION_LIB
#if defined(__linux__)
diff --git a/lib/interception/interception_linux.cc b/lib/interception/interception_linux.cc
index 37e593323d22..009098fbd657 100644
--- a/lib/interception/interception_linux.cc
+++ b/lib/interception/interception_linux.cc
@@ -13,14 +13,15 @@
//===----------------------------------------------------------------------===//
#ifdef __linux__
+#include "interception.h"
#include <stddef.h> // for NULL
#include <dlfcn.h> // for dlsym
namespace __interception {
-bool GetRealFunctionAddress(const char *func_name, void **func_addr,
- void *real, void *wrapper) {
- *func_addr = dlsym(RTLD_NEXT, func_name);
+bool GetRealFunctionAddress(const char *func_name, uptr *func_addr,
+ uptr real, uptr wrapper) {
+ *func_addr = (uptr)dlsym(RTLD_NEXT, func_name);
return real == wrapper;
}
} // namespace __interception
diff --git a/lib/interception/interception_linux.h b/lib/interception/interception_linux.h
index 76a29c6a99a9..dba60bf7315e 100644
--- a/lib/interception/interception_linux.h
+++ b/lib/interception/interception_linux.h
@@ -23,13 +23,15 @@
namespace __interception {
// returns true if a function with the given name was found.
-bool GetRealFunctionAddress(const char *func_name, void **func_addr,
- void *real, void *wrapper);
+bool GetRealFunctionAddress(const char *func_name, uptr *func_addr,
+ uptr real, uptr wrapper);
} // namespace __interception
#define INTERCEPT_FUNCTION_LINUX(func) \
- ::__interception::GetRealFunctionAddress(#func, (void**)&REAL(func), \
- (void*)&(func), (void*)&WRAP(func))
+ ::__interception::GetRealFunctionAddress( \
+ #func, (::__interception::uptr*)&REAL(func), \
+ (::__interception::uptr)&(func), \
+ (::__interception::uptr)&WRAP(func))
#endif // INTERCEPTION_LINUX_H
#endif // __linux__
diff --git a/lib/interception/interception_mac.cc b/lib/interception/interception_mac.cc
index cc9e4a70db8f..2c10a71210e9 100644
--- a/lib/interception/interception_mac.cc
+++ b/lib/interception/interception_mac.cc
@@ -14,19 +14,17 @@
#ifdef __APPLE__
-#define INCLUDED_FROM_INTERCEPTION_LIB
-#include "interception_mac.h"
-#undef INCLUDED_FROM_INTERCEPTION_LIB
+#include "interception.h"
#include "mach_override/mach_override.h"
namespace __interception {
-bool OverrideFunction(void *old_func, void *new_func, void **orig_old_func) {
- *orig_old_func = NULL;
- int res = __asan_mach_override_ptr_custom(old_func, new_func,
- orig_old_func,
+bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
+ *orig_old_func = 0;
+ int res = __asan_mach_override_ptr_custom((void*)old_func, (void*)new_func,
+ (void**)orig_old_func,
__interception_allocate_island,
__interception_deallocate_island);
- return (res == 0) && (*orig_old_func != NULL);
+ return (res == 0) && (*orig_old_func != 0);
}
} // namespace __interception
diff --git a/lib/interception/interception_mac.h b/lib/interception/interception_mac.h
index 224d961eefe0..6e9e80817cb3 100644
--- a/lib/interception/interception_mac.h
+++ b/lib/interception/interception_mac.h
@@ -35,12 +35,14 @@ mach_error_t __interception_deallocate_island(void *ptr);
namespace __interception {
// returns true if the old function existed.
-bool OverrideFunction(void *old_func, void *new_func, void **orig_old_func);
+bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func);
} // namespace __interception
# define OVERRIDE_FUNCTION_MAC(old_func, new_func) \
- ::__interception::OverrideFunction((void*)old_func, (void*)new_func, \
- (void**)&REAL(old_func))
+ ::__interception::OverrideFunction( \
+ (::__interception::uptr)old_func, \
+ (::__interception::uptr)new_func, \
+ (::__interception::uptr*)((::__interception::uptr)&REAL(old_func)))
# define INTERCEPT_FUNCTION_MAC(func) OVERRIDE_FUNCTION_MAC(func, WRAP(func))
#endif // INTERCEPTION_MAC_H
diff --git a/lib/interception/interception_win.cc b/lib/interception/interception_win.cc
index a60c741cb3de..abbab24970cd 100644
--- a/lib/interception/interception_win.cc
+++ b/lib/interception/interception_win.cc
@@ -14,22 +14,23 @@
#ifdef _WIN32
+#include "interception.h"
#include <windows.h>
namespace __interception {
-bool GetRealFunctionAddress(const char *func_name, void **func_addr) {
+bool GetRealFunctionAddress(const char *func_name, uptr *func_addr) {
const char *DLLS[] = {
"msvcr80.dll",
"msvcr90.dll",
"kernel32.dll",
NULL
};
- *func_addr = NULL;
- for (size_t i = 0; *func_addr == NULL && DLLS[i]; ++i) {
- *func_addr = GetProcAddress(GetModuleHandleA(DLLS[i]), func_name);
+ *func_addr = 0;
+ for (size_t i = 0; *func_addr == 0 && DLLS[i]; ++i) {
+ *func_addr = (uptr)GetProcAddress(GetModuleHandleA(DLLS[i]), func_name);
}
- return (*func_addr != NULL);
+ return (*func_addr != 0);
}
// FIXME: internal_str* and internal_mem* functions should be moved from the
@@ -55,7 +56,7 @@ static void WriteJumpInstruction(char *jmp_from, char *to) {
*(ptrdiff_t*)(jmp_from + 1) = offset;
}
-bool OverrideFunction(void *old_func, void *new_func, void **orig_old_func) {
+bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
#ifdef _WIN64
# error OverrideFunction was not tested on x64
#endif
@@ -125,20 +126,21 @@ bool OverrideFunction(void *old_func, void *new_func, void **orig_old_func) {
// Now put the "jump to trampoline" instruction into the original code.
DWORD old_prot, unused_prot;
- if (!VirtualProtect(old_func, head, PAGE_EXECUTE_READWRITE, &old_prot))
+ if (!VirtualProtect((void*)old_func, head, PAGE_EXECUTE_READWRITE,
+ &old_prot))
return false;
// Put the needed instructions into the trampoline bytes.
_memcpy(trampoline, old_bytes, head);
WriteJumpInstruction(trampoline + head, old_bytes + head);
- *orig_old_func = trampoline;
+ *orig_old_func = (uptr)trampoline;
pool_used += head + 5;
// Intercept the 'old_func'.
WriteJumpInstruction(old_bytes, (char*)new_func);
_memset(old_bytes + 5, 0xCC /* int 3 */, head - 5);
- if (!VirtualProtect(old_func, head, old_prot, &unused_prot))
+ if (!VirtualProtect((void*)old_func, head, old_prot, &unused_prot))
return false; // not clear if this failure bothers us.
return true;
diff --git a/lib/interception/interception_win.h b/lib/interception/interception_win.h
index 9d1586ecb173..c64af1baffe7 100644
--- a/lib/interception/interception_win.h
+++ b/lib/interception/interception_win.h
@@ -23,19 +23,22 @@
namespace __interception {
// returns true if a function with the given name was found.
-bool GetRealFunctionAddress(const char *func_name, void **func_addr);
+bool GetRealFunctionAddress(const char *func_name, uptr *func_addr);
// returns true if the old function existed, false on failure.
-bool OverrideFunction(void *old_func, void *new_func, void **orig_old_func);
+bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func);
} // namespace __interception
#if defined(_DLL)
# define INTERCEPT_FUNCTION_WIN(func) \
- ::__interception::GetRealFunctionAddress(#func, (void**)&REAL(func))
+ ::__interception::GetRealFunctionAddress( \
+ #func, (::__interception::uptr*)&REAL(func))
#else
# define INTERCEPT_FUNCTION_WIN(func) \
- ::__interception::OverrideFunction((void*)func, (void*)WRAP(func), \
- (void**)&REAL(func))
+ ::__interception::OverrideFunction( \
+ (::__interception::uptr)func, \
+ (::__interception::uptr)WRAP(func), \
+ (::__interception::uptr*)&REAL(func))
#endif
#endif // INTERCEPTION_WIN_H
diff --git a/lib/interception/mach_override/mach_override.c b/lib/interception/mach_override/mach_override.c
index 499cc029b187..7511a7bebb82 100644
--- a/lib/interception/mach_override/mach_override.c
+++ b/lib/interception/mach_override/mach_override.c
@@ -29,6 +29,7 @@
#if defined(__ppc__) || defined(__POWERPC__)
+static
long kIslandTemplate[] = {
0x9001FFFC, // stw r0,-4(SP)
0x3C00DEAD, // lis r0,0xDEAD
@@ -48,7 +49,8 @@ long kIslandTemplate[] = {
#define kOriginalInstructionsSize 16
-char kIslandTemplate[] = {
+static
+unsigned char kIslandTemplate[] = {
// kOriginalInstructionsSize nop instructions so that we
// should have enough space to host original instructions
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
@@ -65,7 +67,8 @@ char kIslandTemplate[] = {
#define kJumpAddress kOriginalInstructionsSize + 6
-char kIslandTemplate[] = {
+static
+unsigned char kIslandTemplate[] = {
// kOriginalInstructionsSize nop instructions so that we
// should have enough space to host original instructions
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
@@ -104,38 +107,41 @@ typedef struct {
#pragma mark -
#pragma mark (Funky Protos)
- mach_error_t
+
+ static mach_error_t
allocateBranchIsland(
BranchIsland **island,
int allocateHigh,
- void *originalFunctionAddress) __attribute__((visibility("hidden")));
+ void *originalFunctionAddress);
- mach_error_t
+ static mach_error_t
freeBranchIsland(
- BranchIsland *island ) __attribute__((visibility("hidden")));
+ BranchIsland *island );
- mach_error_t
+ static mach_error_t
defaultIslandMalloc(
- void **ptr, size_t unused_size, void *hint) __attribute__((visibility("hidden")));
+ void **ptr, size_t unused_size, void *hint);
- mach_error_t
+ static mach_error_t
defaultIslandFree(
- void *ptr) __attribute__((visibility("hidden")));
+ void *ptr);
#if defined(__ppc__) || defined(__POWERPC__)
- mach_error_t
+ static mach_error_t
setBranchIslandTarget(
BranchIsland *island,
const void *branchTo,
- long instruction ) __attribute__((visibility("hidden")));
+ long instruction );
#endif
#if defined(__i386__) || defined(__x86_64__)
-mach_error_t
+static mach_error_t
setBranchIslandTarget_i386(
BranchIsland *island,
const void *branchTo,
- char* instructions ) __attribute__((visibility("hidden")));
+ char* instructions );
+// Can't be made static because there's no C implementation for atomic_mov64
+// on i386.
void
atomic_mov64(
uint64_t *targetAddress,
@@ -148,7 +154,7 @@ eatKnownInstructions(
int *howManyEaten,
char *originalInstructions,
int *originalInstructionCount,
- uint8_t *originalInstructionSizes ) __attribute__((visibility("hidden")));
+ uint8_t *originalInstructionSizes );
static void
fixupInstructions(
@@ -156,7 +162,7 @@ fixupInstructions(
void *escapeIsland,
void *instructionsToFix,
int instructionCount,
- uint8_t *instructionSizes ) __attribute__((visibility("hidden")));
+ uint8_t *instructionSizes );
#ifdef DEBUG_DISASM
static void
@@ -174,7 +180,7 @@ dump16Bytes(
#pragma mark (Interface)
#if defined(__i386__) || defined(__x86_64__)
-mach_error_t makeIslandExecutable(void *address) {
+static mach_error_t makeIslandExecutable(void *address) {
mach_error_t err = err_none;
vm_size_t pageSize;
host_page_size( mach_host_self(), &pageSize );
@@ -189,12 +195,12 @@ mach_error_t makeIslandExecutable(void *address) {
}
#endif
- mach_error_t
+ static mach_error_t
defaultIslandMalloc(
void **ptr, size_t unused_size, void *hint) {
return allocateBranchIsland( (BranchIsland**)ptr, kAllocateHigh, hint );
}
- mach_error_t
+ static mach_error_t
defaultIslandFree(
void *ptr) {
return freeBranchIsland(ptr);
@@ -460,7 +466,7 @@ __asan_mach_override_ptr_custom(
***************************************************************************/
- mach_error_t
+ static mach_error_t
allocateBranchIsland(
BranchIsland **island,
int allocateHigh,
@@ -530,7 +536,7 @@ allocateBranchIsland(
***************************************************************************/
- mach_error_t
+ static mach_error_t
freeBranchIsland(
BranchIsland *island )
{
@@ -568,7 +574,7 @@ freeBranchIsland(
***************************************************************************/
#if defined(__ppc__) || defined(__POWERPC__)
- mach_error_t
+ static mach_error_t
setBranchIslandTarget(
BranchIsland *island,
const void *branchTo,
@@ -598,7 +604,7 @@ setBranchIslandTarget(
#endif
#if defined(__i386__)
- mach_error_t
+ static mach_error_t
setBranchIslandTarget_i386(
BranchIsland *island,
const void *branchTo,
@@ -622,7 +628,7 @@ setBranchIslandTarget_i386(
}
#elif defined(__x86_64__)
-mach_error_t
+static mach_error_t
setBranchIslandTarget_i386(
BranchIsland *island,
const void *branchTo,
@@ -675,7 +681,8 @@ static AsmInstructionMatch possibleInstructions[] = {
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x66, 0x0F, 0xEF, 0x00} }, // pxor xmm2/128, xmm1
{ 0x2, {0xFF, 0xFF}, {0xDB, 0xE3} }, // fninit
{ 0x5, {0xFF, 0x00, 0x00, 0x00, 0x00}, {0xE8, 0x00, 0x00, 0x00, 0x00} }, // call $imm
- { 0x0 }
+ { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x0F, 0xBE, 0x55, 0x00} }, // movsbl $imm(%ebp), %edx
+ { 0x0, {0x00}, {0x00} }
};
#elif defined(__x86_64__)
// TODO(glider): disassembling the "0x48, 0x89" sequences is trickier than it's done below.
@@ -694,6 +701,7 @@ static AsmInstructionMatch possibleInstructions[] = {
{ 0x3, {0xFB, 0xFF, 0x00}, {0x48, 0x89, 0x00} }, // mov %reg, %reg
{ 0x3, {0xFB, 0xFF, 0x00}, {0x49, 0x89, 0x00} }, // mov %reg, %reg (REX.WB)
{ 0x2, {0xFF, 0x00}, {0x41, 0x00} }, // push %rXX
+ { 0x2, {0xFF, 0x00}, {0x84, 0x00} }, // test %rX8,%rX8
{ 0x2, {0xFF, 0x00}, {0x85, 0x00} }, // test %rX,%rX
{ 0x2, {0xFF, 0x00}, {0x77, 0x00} }, // ja $i8
{ 0x2, {0xFF, 0x00}, {0x74, 0x00} }, // je $i8
@@ -715,11 +723,15 @@ static AsmInstructionMatch possibleInstructions[] = {
{0xFF, 0x25, 0x00, 0x00, 0x00, 0x00} }, // jmpq *(%rip)
{ 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x66, 0x0F, 0xEF, 0x00} }, // pxor xmm2/128, xmm1
{ 0x2, {0xFF, 0x00}, {0x89, 0x00} }, // mov r/m32,r32 or r/m16,r16
- { 0x3, {0xFF, 0xFF, 0xFF}, {0x49, 0x89, 0xF8} }, // mov %rdi,%r8
+ { 0x3, {0xFF, 0xFF, 0xFF}, {0x49, 0x89, 0xF8} }, // mov %rdi,%r8
+ { 0x4, {0xFF, 0xFF, 0xFF, 0xFF}, {0x40, 0x0F, 0xBE, 0xCE} }, // movsbl %sil,%ecx
+ { 0x7, {0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00},
+ {0x48, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00} }, // lea $imm(%rip),%rax
+ { 0x3, {0xFF, 0xFF, 0xFF}, {0x0F, 0xBE, 0xCE} }, // movsbl, %dh, %ecx
{ 0x3, {0xFF, 0xFF, 0x00}, {0xFF, 0x77, 0x00} }, // pushq $imm(%rdi)
{ 0x2, {0xFF, 0xFF}, {0xDB, 0xE3} }, // fninit
{ 0x3, {0xFF, 0xFF, 0xFF}, {0x48, 0x85, 0xD2} }, // test %rdx,%rdx
- { 0x0 }
+ { 0x0, {0x00}, {0x00} }
};
#endif
@@ -866,7 +878,7 @@ fixupInstructions(
// This is critical, otherwise a near jump will likely fall outside the original function.
uint32_t offset = (uintptr_t)initialOriginalFunction - (uintptr_t)escapeIsland;
uint32_t jumpOffset = *(uint8_t*)((uintptr_t)instructionsToFix + 1);
- *(uint8_t*)(instructionsToFix + 1) = *(uint8_t*)instructionsToFix + 0x10;
+ *((uint8_t*)instructionsToFix + 1) = *(uint8_t*)instructionsToFix + 0x10;
*(uint8_t*)instructionsToFix = 0x0F;
uint32_t *jumpOffsetPtr = (uint32_t*)((uintptr_t)instructionsToFix + 2 );
*jumpOffsetPtr = offset + jumpOffset;
diff --git a/lib/lit.common.cfg b/lib/lit.common.cfg
new file mode 100644
index 000000000000..428554ac77a2
--- /dev/null
+++ b/lib/lit.common.cfg
@@ -0,0 +1,54 @@
+# -*- Python -*-
+
+# Configuration file for 'lit' test runner.
+# This file contains common rules for various compiler-rt testsuites.
+# It is mostly copied from lit.cfg used by Clang.
+import os
+import platform
+
+# Setup test format
+execute_external = (platform.system() != 'Windows'
+ or lit.getBashPath() not in [None, ""])
+config.test_format = lit.formats.ShTest(execute_external)
+
+# Setup clang binary.
+clang_path = getattr(config, 'clang', None)
+if (not clang_path) or (not os.path.exists(clang_path)):
+ lit.fatal("Can't find Clang on path %r" % clang_path)
+if not lit.quiet:
+ lit.note("using clang: %r" % clang_path)
+
+# Clear some environment variables that might affect Clang.
+possibly_dangerous_env_vars = ['COMPILER_PATH', 'RC_DEBUG_OPTIONS',
+ 'CINDEXTEST_PREAMBLE_FILE', 'LIBRARY_PATH',
+ 'CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH',
+ 'OBJC_INCLUDE_PATH', 'OBJCPLUS_INCLUDE_PATH',
+ 'LIBCLANG_TIMING', 'LIBCLANG_OBJTRACKING',
+ 'LIBCLANG_LOGGING', 'LIBCLANG_BGPRIO_INDEX',
+ 'LIBCLANG_BGPRIO_EDIT', 'LIBCLANG_NOTHREADS',
+ 'LIBCLANG_RESOURCE_USAGE',
+ 'LIBCLANG_CODE_COMPLETION_LOGGING']
+# Clang/Win32 may refer to %INCLUDE%. vsvarsall.bat sets it.
+if platform.system() != 'Windows':
+ possibly_dangerous_env_vars.append('INCLUDE')
+for name in possibly_dangerous_env_vars:
+ if name in config.environment:
+ del config.environment[name]
+
+# Tweak PATH to include llvm tools dir.
+llvm_tools_dir = getattr(config, 'llvm_tools_dir', None)
+if (not llvm_tools_dir) or (not os.path.exists(llvm_tools_dir)):
+ lit.fatal("Invalid llvm_tools_dir config attribute: %r" % llvm_tools_dir)
+path = os.path.pathsep.join((llvm_tools_dir, config.environment['PATH']))
+config.environment['PATH'] = path
+
+# Define %clang and %clangxx substitutions to use in test RUN lines.
+config.substitutions.append( ("%clang ", (" " + config.clang + " ")) )
+config.substitutions.append( ("%clangxx ", (" " + config.clang +
+ " -ccc-cxx ")) )
+
+# Use ugly construction to explicitly prohibit "clang", "clang++" etc.
+# in RUN lines.
+config.substitutions.append(
+ (' clang', """\n\n*** Do not use 'clangXXX' in tests,
+ instead define '%clangXXX' substitution in lit config. ***\n\n""") )
diff --git a/lib/lit.common.unit.cfg b/lib/lit.common.unit.cfg
new file mode 100644
index 000000000000..8250b4a829c6
--- /dev/null
+++ b/lib/lit.common.unit.cfg
@@ -0,0 +1,21 @@
+# -*- Python -*-
+
+# Configuration file for 'lit' test runner.
+# This file contains common config setup rules for unit tests in various
+# compiler-rt testsuites.
+
+import os
+
+# Setup test format
+build_type = getattr(config, "build_type", "Debug")
+config.test_format = lit.formats.GoogleTest(build_type, "Test")
+
+# Setup test suffixes.
+config.suffixes = []
+
+# Propagate the temp directory. Windows requires this because it uses \Windows\
+# if none of these are present.
+if 'TMP' in os.environ:
+ config.environment['TMP'] = os.environ['TMP']
+if 'TEMP' in os.environ:
+ config.environment['TEMP'] = os.environ['TEMP']
diff --git a/lib/msan/CMakeLists.txt b/lib/msan/CMakeLists.txt
new file mode 100644
index 000000000000..bb8dbccbeba6
--- /dev/null
+++ b/lib/msan/CMakeLists.txt
@@ -0,0 +1,35 @@
+include_directories(..)
+
+# Runtime library sources and build flags.
+set(MSAN_RTL_SOURCES
+ msan.cc
+ msan_allocator.cc
+ msan_interceptors.cc
+ msan_linux.cc
+ msan_new_delete.cc
+ msan_platform_limits_posix.cc
+ msan_report.cc
+ )
+set(MSAN_RTL_CFLAGS
+ ${SANITIZER_COMMON_CFLAGS}
+ -fPIE
+ # Prevent clang from generating libc calls.
+ -ffreestanding)
+
+# Static runtime library.
+set(MSAN_RUNTIME_LIBRARIES)
+add_library(clang_rt.msan-x86_64 STATIC
+ ${MSAN_RTL_SOURCES}
+ $<TARGET_OBJECTS:RTInterception.x86_64>
+ $<TARGET_OBJECTS:RTSanitizerCommon.x86_64>
+ )
+set_target_compile_flags(clang_rt.msan-x86_64
+ ${MSAN_RTL_CFLAGS} ${TARGET_x86_64_CFLAGS}
+ )
+list(APPEND MSAN_RUNTIME_LIBRARIES clang_rt.msan-x86_64)
+
+add_clang_compiler_rt_libraries(${MSAN_RUNTIME_LIBRARIES})
+
+if(LLVM_INCLUDE_TESTS)
+ add_subdirectory(tests)
+endif()
diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc
new file mode 100644
index 000000000000..670213f011c7
--- /dev/null
+++ b/lib/msan/msan.cc
@@ -0,0 +1,420 @@
+//===-- msan.cc -----------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// MemorySanitizer runtime.
+//===----------------------------------------------------------------------===//
+
+#include "msan.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+#include "interception/interception.h"
+
+// ACHTUNG! No system header includes in this file.
+
+using namespace __sanitizer;
+
+// Globals.
+static THREADLOCAL int msan_expect_umr = 0;
+static THREADLOCAL int msan_expected_umr_found = 0;
+
+static int msan_running_under_dr = 0;
+
+SANITIZER_INTERFACE_ATTRIBUTE
+THREADLOCAL u64 __msan_param_tls[kMsanParamTlsSizeInWords];
+
+SANITIZER_INTERFACE_ATTRIBUTE
+THREADLOCAL u32 __msan_param_origin_tls[kMsanParamTlsSizeInWords];
+
+SANITIZER_INTERFACE_ATTRIBUTE
+THREADLOCAL u64 __msan_retval_tls[kMsanRetvalTlsSizeInWords];
+
+SANITIZER_INTERFACE_ATTRIBUTE
+THREADLOCAL u32 __msan_retval_origin_tls;
+
+SANITIZER_INTERFACE_ATTRIBUTE
+THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSizeInWords];
+
+SANITIZER_INTERFACE_ATTRIBUTE
+THREADLOCAL u64 __msan_va_arg_overflow_size_tls;
+
+SANITIZER_INTERFACE_ATTRIBUTE
+THREADLOCAL u32 __msan_origin_tls;
+
+static THREADLOCAL struct {
+ uptr stack_top, stack_bottom;
+} __msan_stack_bounds;
+
+extern const int __msan_track_origins;
+int __msan_get_track_origins() {
+ return __msan_track_origins;
+}
+
+namespace __msan {
+
+static bool IsRunningUnderDr() {
+ bool result = false;
+ MemoryMappingLayout proc_maps;
+ const sptr kBufSize = 4095;
+ char *filename = (char*)MmapOrDie(kBufSize, __FUNCTION__);
+ while (proc_maps.Next(/* start */0, /* end */0, /* file_offset */0,
+ filename, kBufSize)) {
+ if (internal_strstr(filename, "libdynamorio") != 0) {
+ result = true;
+ break;
+ }
+ }
+ UnmapOrDie(filename, kBufSize);
+ return result;
+}
+
+static Flags msan_flags;
+
+Flags *flags() {
+ return &msan_flags;
+}
+
+int msan_inited = 0;
+bool msan_init_is_running;
+
+int msan_report_count = 0;
+
+// Array of stack origins.
+// FIXME: make it resizable.
+static const uptr kNumStackOriginDescrs = 1024 * 1024;
+static const char *StackOriginDescr[kNumStackOriginDescrs];
+static atomic_uint32_t NumStackOriginDescrs;
+
+static void ParseFlagsFromString(Flags *f, const char *str) {
+ ParseFlag(str, &f->poison_heap_with_zeroes, "poison_heap_with_zeroes");
+ ParseFlag(str, &f->poison_stack_with_zeroes, "poison_stack_with_zeroes");
+ ParseFlag(str, &f->poison_in_malloc, "poison_in_malloc");
+ ParseFlag(str, &f->exit_code, "exit_code");
+ if (f->exit_code < 0 || f->exit_code > 127) {
+ Printf("Exit code not in [0, 128) range: %d\n", f->exit_code);
+ f->exit_code = 1;
+ Die();
+ }
+ ParseFlag(str, &f->num_callers, "num_callers");
+ ParseFlag(str, &f->report_umrs, "report_umrs");
+ ParseFlag(str, &f->verbosity, "verbosity");
+}
+
+static void InitializeFlags(Flags *f, const char *options) {
+ internal_memset(f, 0, sizeof(*f));
+
+ f->poison_heap_with_zeroes = false;
+ f->poison_stack_with_zeroes = false;
+ f->poison_in_malloc = true;
+ f->exit_code = 77;
+ f->num_callers = 20;
+ f->report_umrs = true;
+ f->verbosity = 0;
+
+ ParseFlagsFromString(f, options);
+}
+
+static void GetCurrentStackBounds(uptr *stack_top, uptr *stack_bottom) {
+ if (__msan_stack_bounds.stack_top == 0) {
+ // Break recursion (GetStackTrace -> GetThreadStackTopAndBottom ->
+ // realloc -> GetStackTrace).
+ __msan_stack_bounds.stack_top = __msan_stack_bounds.stack_bottom = 1;
+ GetThreadStackTopAndBottom(/* at_initialization */false,
+ &__msan_stack_bounds.stack_top,
+ &__msan_stack_bounds.stack_bottom);
+ }
+ *stack_top = __msan_stack_bounds.stack_top;
+ *stack_bottom = __msan_stack_bounds.stack_bottom;
+}
+
+void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp) {
+ uptr stack_top, stack_bottom;
+ GetCurrentStackBounds(&stack_top, &stack_bottom);
+ stack->size = 0;
+ stack->trace[0] = pc;
+ stack->max_size = max_s;
+ stack->FastUnwindStack(pc, bp, stack_top, stack_bottom);
+}
+
+void PrintWarning(uptr pc, uptr bp) {
+ PrintWarningWithOrigin(pc, bp, __msan_origin_tls);
+}
+
+bool OriginIsValid(u32 origin) {
+ return origin != 0 && origin != (u32)-1;
+}
+
+void PrintWarningWithOrigin(uptr pc, uptr bp, u32 origin) {
+ if (msan_expect_umr) {
+ // Printf("Expected UMR\n");
+ __msan_origin_tls = origin;
+ msan_expected_umr_found = 1;
+ return;
+ }
+
+ ++msan_report_count;
+
+ StackTrace stack;
+ GetStackTrace(&stack, kStackTraceMax, pc, bp);
+
+ u32 report_origin =
+ (__msan_track_origins && OriginIsValid(origin)) ? origin : 0;
+ ReportUMR(&stack, report_origin);
+
+ if (__msan_track_origins && !OriginIsValid(origin)) {
+ Printf(" ORIGIN: invalid (%x). Might be a bug in MemorySanitizer, "
+ "please report to MemorySanitizer developers.\n",
+ origin);
+ }
+}
+
+} // namespace __msan
+
+// Interface.
+
+using namespace __msan;
+
+void __msan_warning() {
+ GET_CALLER_PC_BP_SP;
+ (void)sp;
+ PrintWarning(pc, bp);
+}
+
+void __msan_warning_noreturn() {
+ GET_CALLER_PC_BP_SP;
+ (void)sp;
+ PrintWarning(pc, bp);
+ Printf("Exiting\n");
+ Die();
+}
+
+void __msan_init() {
+ if (msan_inited) return;
+ msan_init_is_running = 1;
+
+ InstallAtExitHandler();
+ SetDieCallback(MsanDie);
+ InitializeInterceptors();
+
+ ReplaceOperatorsNewAndDelete();
+ if (StackSizeIsUnlimited()) {
+ if (flags()->verbosity)
+ Printf("Unlimited stack, doing reexec\n");
+ // A reasonably large stack size. It is bigger than the usual 8Mb, because,
+ // well, the program could have been run with unlimited stack for a reason.
+ SetStackSizeLimitInBytes(32 * 1024 * 1024);
+ ReExec();
+ }
+ const char *msan_options = GetEnv("MSAN_OPTIONS");
+ InitializeFlags(&msan_flags, msan_options);
+ if (flags()->verbosity)
+ Printf("MSAN_OPTIONS: %s\n", msan_options ? msan_options : "<empty>");
+ msan_running_under_dr = IsRunningUnderDr();
+ __msan_clear_on_return();
+ if (__msan_track_origins && flags()->verbosity > 0)
+ Printf("msan_track_origins\n");
+ if (!InitShadow(/* prot1 */false, /* prot2 */true, /* map_shadow */true,
+ __msan_track_origins)) {
+ // FIXME: prot1 = false is only required when running under DR.
+ Printf("FATAL: MemorySanitizer can not mmap the shadow memory.\n");
+ Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
+ Printf("FATAL: Disabling ASLR is known to cause this error.\n");
+ Printf("FATAL: If running under GDB, try "
+ "'set disable-randomization off'.\n");
+ DumpProcessMap();
+ Die();
+ }
+
+ InstallTrapHandler();
+
+ const char *external_symbolizer = GetEnv("MSAN_SYMBOLIZER_PATH");
+ if (external_symbolizer && external_symbolizer[0]) {
+ CHECK(InitializeExternalSymbolizer(external_symbolizer));
+ }
+
+ GetThreadStackTopAndBottom(/* at_initialization */true,
+ &__msan_stack_bounds.stack_top,
+ &__msan_stack_bounds.stack_bottom);
+ if (flags()->verbosity)
+ Printf("MemorySanitizer init done\n");
+ msan_init_is_running = 0;
+ msan_inited = 1;
+}
+
+void __msan_set_exit_code(int exit_code) {
+ flags()->exit_code = exit_code;
+}
+
+void __msan_set_expect_umr(int expect_umr) {
+ if (expect_umr) {
+ msan_expected_umr_found = 0;
+ } else if (!msan_expected_umr_found) {
+ GET_CALLER_PC_BP_SP;
+ (void)sp;
+ StackTrace stack;
+ GetStackTrace(&stack, kStackTraceMax, pc, bp);
+ ReportExpectedUMRNotFound(&stack);
+ Die();
+ }
+ msan_expect_umr = expect_umr;
+}
+
+void __msan_print_shadow(const void *x, uptr size) {
+ unsigned char *s = (unsigned char*)MEM_TO_SHADOW(x);
+ u32 *o = (u32*)MEM_TO_ORIGIN(x);
+ for (uptr i = 0; i < size; i++) {
+ Printf("%x%x ", s[i] >> 4, s[i] & 0xf);
+ }
+ Printf("\n");
+ if (__msan_track_origins) {
+ for (uptr i = 0; i < size / 4; i++) {
+ Printf(" o: %x ", o[i]);
+ }
+ Printf("\n");
+ }
+}
+
+void __msan_print_param_shadow() {
+ for (int i = 0; i < 16; i++) {
+ Printf("#%d:%zx ", i, __msan_param_tls[i]);
+ }
+ Printf("\n");
+}
+
+sptr __msan_test_shadow(const void *x, uptr size) {
+ unsigned char *s = (unsigned char*)MEM_TO_SHADOW((uptr)x);
+ for (uptr i = 0; i < size; ++i)
+ if (s[i])
+ return i;
+ return -1;
+}
+
+int __msan_set_poison_in_malloc(int do_poison) {
+ int old = flags()->poison_in_malloc;
+ flags()->poison_in_malloc = do_poison;
+ return old;
+}
+
+void __msan_break_optimization(void *x) { }
+
+int __msan_has_dynamic_component() {
+ return msan_running_under_dr;
+}
+
+NOINLINE
+void __msan_clear_on_return() {
+ __msan_param_tls[0] = 0;
+}
+
+static void* get_tls_base() {
+ u64 p;
+ asm("mov %%fs:0, %0"
+ : "=r"(p) ::);
+ return (void*)p;
+}
+
+int __msan_get_retval_tls_offset() {
+ // volatile here is needed to avoid UB, because the compiler thinks that we
+ // are doing address arithmetics on unrelated pointers, and takes some
+ // shortcuts
+ volatile sptr retval_tls_p = (sptr)&__msan_retval_tls;
+ volatile sptr tls_base_p = (sptr)get_tls_base();
+ return retval_tls_p - tls_base_p;
+}
+
+int __msan_get_param_tls_offset() {
+ // volatile here is needed to avoid UB, because the compiler thinks that we
+ // are doing address arithmetics on unrelated pointers, and takes some
+ // shortcuts
+ volatile sptr param_tls_p = (sptr)&__msan_param_tls;
+ volatile sptr tls_base_p = (sptr)get_tls_base();
+ return param_tls_p - tls_base_p;
+}
+
+void __msan_partial_poison(void* data, void* shadow, uptr size) {
+ internal_memcpy((void*)MEM_TO_SHADOW((uptr)data), shadow, size);
+}
+
+void __msan_load_unpoisoned(void *src, uptr size, void *dst) {
+ internal_memcpy(dst, src, size);
+ __msan_unpoison(dst, size);
+}
+
+void __msan_set_origin(void *a, uptr size, u32 origin) {
+ // Origin mapping is 4 bytes per 4 bytes of application memory.
+ // Here we extend the range such that its left and right bounds are both
+ // 4 byte aligned.
+ if (!__msan_track_origins) return;
+ uptr x = MEM_TO_ORIGIN((uptr)a);
+ uptr beg = x & ~3UL; // align down.
+ uptr end = (x + size + 3) & ~3UL; // align up.
+ u64 origin64 = ((u64)origin << 32) | origin;
+ // This is like memset, but the value is 32-bit. We unroll by 2 two write
+ // 64-bits at once. May want to unroll further to get 128-bit stores.
+ if (beg & 7ULL) {
+ *(u32*)beg = origin;
+ beg += 4;
+ }
+ for (uptr addr = beg; addr < (end & ~7UL); addr += 8)
+ *(u64*)addr = origin64;
+ if (end & 7ULL)
+ *(u32*)(end - 4) = origin;
+}
+
+// 'descr' is created at compile time and contains '----' in the beginning.
+// When we see descr for the first time we replace '----' with a uniq id
+// and set the origin to (id | (31-th bit)).
+void __msan_set_alloca_origin(void *a, uptr size, const char *descr) {
+ static const u32 dash = '-';
+ static const u32 first_timer =
+ dash + (dash << 8) + (dash << 16) + (dash << 24);
+ u32 *id_ptr = (u32*)descr;
+ bool print = false; // internal_strstr(descr + 4, "AllocaTOTest") != 0;
+ u32 id = *id_ptr;
+ if (id == first_timer) {
+ id = atomic_fetch_add(&NumStackOriginDescrs,
+ 1, memory_order_relaxed);
+ *id_ptr = id;
+ CHECK_LT(id, kNumStackOriginDescrs);
+ StackOriginDescr[id] = descr + 4;
+ if (print)
+ Printf("First time: id=%d %s \n", id, descr + 4);
+ }
+ id |= 1U << 31;
+ if (print)
+ Printf("__msan_set_alloca_origin: descr=%s id=%x\n", descr + 4, id);
+ __msan_set_origin(a, size, id);
+}
+
+const char *__msan_get_origin_descr_if_stack(u32 id) {
+ if ((id >> 31) == 0) return 0;
+ id &= (1U << 31) - 1;
+ CHECK_LT(id, kNumStackOriginDescrs);
+ return StackOriginDescr[id];
+}
+
+
+u32 __msan_get_origin(void *a) {
+ if (!__msan_track_origins) return 0;
+ uptr x = (uptr)a;
+ uptr aligned = x & ~3ULL;
+ uptr origin_ptr = MEM_TO_ORIGIN(aligned);
+ return *(u32*)origin_ptr;
+}
+
+u32 __msan_get_origin_tls() {
+ return __msan_origin_tls;
+}
diff --git a/lib/msan/msan.h b/lib/msan/msan.h
new file mode 100644
index 000000000000..99d9a90d2dc7
--- /dev/null
+++ b/lib/msan/msan.h
@@ -0,0 +1,68 @@
+//===-- msan.h --------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// Private MSan header.
+//===----------------------------------------------------------------------===//
+
+#ifndef MSAN_H
+#define MSAN_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer/msan_interface.h"
+#include "msan_flags.h"
+
+#define MEM_TO_SHADOW(mem) (((uptr)mem) & ~0x400000000000ULL)
+#define MEM_TO_ORIGIN(mem) (MEM_TO_SHADOW(mem) + 0x200000000000ULL)
+#define MEM_IS_APP(mem) ((uptr)mem >= 0x600000000000ULL)
+#define MEM_IS_SHADOW(mem) ((uptr)mem >= 0x200000000000ULL && \
+ (uptr)mem <= 0x400000000000ULL)
+
+const int kMsanParamTlsSizeInWords = 100;
+const int kMsanRetvalTlsSizeInWords = 100;
+
+namespace __msan {
+extern int msan_inited;
+extern bool msan_init_is_running;
+extern int msan_report_count;
+
+bool ProtectRange(uptr beg, uptr end);
+bool InitShadow(bool prot1, bool prot2, bool map_shadow, bool init_origins);
+char *GetProcSelfMaps();
+void InitializeInterceptors();
+
+void *MsanReallocate(StackTrace *stack, void *oldp, uptr size,
+ uptr alignment, bool zeroise);
+void MsanDeallocate(void *ptr);
+void InstallTrapHandler();
+void InstallAtExitHandler();
+void ReplaceOperatorsNewAndDelete();
+
+void MsanDie();
+void PrintWarning(uptr pc, uptr bp);
+void PrintWarningWithOrigin(uptr pc, uptr bp, u32 origin);
+
+void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp);
+
+void ReportUMR(StackTrace *stack, u32 origin);
+void ReportExpectedUMRNotFound(StackTrace *stack);
+void ReportAtExitStatistics();
+
+#define GET_MALLOC_STACK_TRACE \
+ StackTrace stack; \
+ stack.size = 0; \
+ if (__msan_get_track_origins() && msan_inited) \
+ GetStackTrace(&stack, flags()->num_callers, \
+ StackTrace::GetCurrentPc(), GET_CURRENT_FRAME())
+
+} // namespace __msan
+
+#endif // MSAN_H
diff --git a/lib/msan/msan_allocator.cc b/lib/msan/msan_allocator.cc
new file mode 100644
index 000000000000..7435843ce61e
--- /dev/null
+++ b/lib/msan/msan_allocator.cc
@@ -0,0 +1,107 @@
+//===-- msan_allocator.cc --------------------------- ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// MemorySanitizer allocator.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "msan.h"
+
+namespace __msan {
+
+struct Metadata {
+ uptr requested_size;
+};
+
+static const uptr kAllocatorSpace = 0x600000000000ULL;
+static const uptr kAllocatorSize = 0x80000000000; // 8T.
+static const uptr kMetadataSize = sizeof(Metadata);
+
+typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, kMetadataSize,
+ DefaultSizeClassMap> PrimaryAllocator;
+typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
+typedef LargeMmapAllocator<> SecondaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
+ SecondaryAllocator> Allocator;
+
+static THREADLOCAL AllocatorCache cache;
+static Allocator allocator;
+
+static int inited = 0;
+
+static inline void Init() {
+ if (inited) return;
+ __msan_init();
+ inited = true; // this must happen before any threads are created.
+ allocator.Init();
+}
+
+static void *MsanAllocate(StackTrace *stack, uptr size,
+ uptr alignment, bool zeroise) {
+ Init();
+ void *res = allocator.Allocate(&cache, size, alignment, false);
+ Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(res));
+ meta->requested_size = size;
+ if (zeroise)
+ __msan_clear_and_unpoison(res, size);
+ else if (flags()->poison_in_malloc)
+ __msan_poison(res, size);
+ if (__msan_get_track_origins()) {
+ u32 stack_id = StackDepotPut(stack->trace, stack->size);
+ CHECK(stack_id);
+ CHECK_EQ((stack_id >> 31), 0); // Higher bit is occupied by stack origins.
+ __msan_set_origin(res, size, stack_id);
+ }
+ return res;
+}
+
+void MsanDeallocate(void *p) {
+ CHECK(p);
+ Init();
+ Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(p));
+ uptr size = meta->requested_size;
+ // This memory will not be reused by anyone else, so we are free to keep it
+ // poisoned.
+ __msan_poison(p, size);
+ if (__msan_get_track_origins())
+ __msan_set_origin(p, size, -1);
+ allocator.Deallocate(&cache, p);
+}
+
+void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size,
+ uptr alignment, bool zeroise) {
+ if (!old_p)
+ return MsanAllocate(stack, new_size, alignment, zeroise);
+ if (!new_size) {
+ MsanDeallocate(old_p);
+ return 0;
+ }
+ Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(old_p));
+ uptr old_size = meta->requested_size;
+ uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(old_p);
+ if (new_size <= actually_allocated_size) {
+ // We are not reallocating here.
+ meta->requested_size = new_size;
+ if (new_size > old_size)
+ __msan_poison((char*)old_p + old_size, new_size - old_size);
+ return old_p;
+ }
+ uptr memcpy_size = Min(new_size, old_size);
+ void *new_p = MsanAllocate(stack, new_size, alignment, zeroise);
+ // Printf("realloc: old_size %zd new_size %zd\n", old_size, new_size);
+ if (new_p)
+ __msan_memcpy(new_p, old_p, memcpy_size);
+ MsanDeallocate(old_p);
+ return new_p;
+}
+
+} // namespace __msan
diff --git a/lib/msan/msan_flags.h b/lib/msan/msan_flags.h
new file mode 100644
index 000000000000..a85fc57253c9
--- /dev/null
+++ b/lib/msan/msan_flags.h
@@ -0,0 +1,34 @@
+//===-- msan_flags.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// MemorySanitizer allocator.
+//===----------------------------------------------------------------------===//
+#ifndef MSAN_FLAGS_H
+#define MSAN_FLAGS_H
+
+namespace __msan {
+
+// Flags.
+struct Flags {
+ int exit_code;
+ int num_callers;
+ int verbosity;
+ bool poison_heap_with_zeroes; // default: false
+ bool poison_stack_with_zeroes; // default: false
+ bool poison_in_malloc; // default: true
+ bool report_umrs;
+};
+
+Flags *flags();
+
+} // namespace __msan
+
+#endif // MSAN_FLAGS_H
diff --git a/lib/msan/msan_interceptors.cc b/lib/msan/msan_interceptors.cc
new file mode 100644
index 000000000000..462920413d4e
--- /dev/null
+++ b/lib/msan/msan_interceptors.cc
@@ -0,0 +1,942 @@
+//===-- msan_interceptors.cc ----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// Interceptors for standard library functions.
+//
+// FIXME: move as many interceptors as possible into
+// sanitizer_common/sanitizer_common_interceptors.h
+//===----------------------------------------------------------------------===//
+
+#include "interception/interception.h"
+#include "msan.h"
+#include "msan_platform_limits_posix.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+
+#include <stdarg.h>
+// ACHTUNG! No other system header includes in this file.
+// Ideally, we should get rid of stdarg.h as well.
+
+using namespace __msan;
+
+#define ENSURE_MSAN_INITED() do { \
+ CHECK(!msan_init_is_running); \
+ if (!msan_inited) { \
+ __msan_init(); \
+ } \
+} while (0)
+
+#define CHECK_UNPOISONED(x, n) \
+ do { \
+ sptr offset = __msan_test_shadow(x, n); \
+ if (offset >= 0 && flags()->report_umrs) { \
+ GET_CALLER_PC_BP_SP; \
+ (void)sp; \
+ Printf("UMR in %s at offset %d inside [%p, +%d) \n", \
+ __FUNCTION__, offset, x, n); \
+ __msan::PrintWarningWithOrigin( \
+ pc, bp, __msan_get_origin((char*)x + offset)); \
+ } \
+ } while (0)
+
+static void *fast_memset(void *ptr, int c, SIZE_T n);
+static void *fast_memcpy(void *dst, const void *src, SIZE_T n);
+
+INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) {
+ ENSURE_MSAN_INITED();
+ SIZE_T res = REAL(fread)(ptr, size, nmemb, file);
+ if (res > 0)
+ __msan_unpoison(ptr, res *size);
+ return res;
+}
+
+INTERCEPTOR(SIZE_T, fread_unlocked, void *ptr, SIZE_T size, SIZE_T nmemb,
+ void *file) {
+ ENSURE_MSAN_INITED();
+ SIZE_T res = REAL(fread_unlocked)(ptr, size, nmemb, file);
+ if (res > 0)
+ __msan_unpoison(ptr, res *size);
+ return res;
+}
+
+INTERCEPTOR(SSIZE_T, readlink, const char *path, char *buf, SIZE_T bufsiz) {
+ ENSURE_MSAN_INITED();
+ SSIZE_T res = REAL(readlink)(path, buf, bufsiz);
+ if (res > 0)
+ __msan_unpoison(buf, res);
+ return res;
+}
+
+INTERCEPTOR(void *, readdir, void *a) {
+ ENSURE_MSAN_INITED();
+ void *res = REAL(readdir)(a);
+ __msan_unpoison(res, __msan::struct_dirent_sz);
+ return res;
+}
+
+INTERCEPTOR(void *, memcpy, void *dest, const void *src, SIZE_T n) {
+ return __msan_memcpy(dest, src, n);
+}
+
+INTERCEPTOR(void *, memmove, void *dest, const void *src, SIZE_T n) {
+ return __msan_memmove(dest, src, n);
+}
+
+INTERCEPTOR(void *, memset, void *s, int c, SIZE_T n) {
+ return __msan_memset(s, c, n);
+}
+
+INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) {
+ GET_MALLOC_STACK_TRACE;
+ CHECK_EQ(alignment & (alignment - 1), 0);
+ *memptr = MsanReallocate(&stack, 0, size, alignment, false);
+ CHECK_NE(memptr, 0);
+ return 0;
+}
+
+INTERCEPTOR(void, free, void *ptr) {
+ ENSURE_MSAN_INITED();
+ if (ptr == 0) return;
+ MsanDeallocate(ptr);
+}
+
+INTERCEPTOR(SIZE_T, strlen, const char *s) {
+ ENSURE_MSAN_INITED();
+ SIZE_T res = REAL(strlen)(s);
+ CHECK_UNPOISONED(s, res + 1);
+ return res;
+}
+
+INTERCEPTOR(SIZE_T, strnlen, const char *s, SIZE_T n) {
+ ENSURE_MSAN_INITED();
+ SIZE_T res = REAL(strnlen)(s, n);
+ SIZE_T scan_size = (res == n) ? res : res + 1;
+ CHECK_UNPOISONED(s, scan_size);
+ return res;
+}
+
+// FIXME: Add stricter shadow checks in str* interceptors (ex.: strcpy should
+// check the shadow of the terminating \0 byte).
+
+INTERCEPTOR(char *, strcpy, char *dest, const char *src) { // NOLINT
+ ENSURE_MSAN_INITED();
+ SIZE_T n = REAL(strlen)(src);
+ char *res = REAL(strcpy)(dest, src); // NOLINT
+ __msan_copy_poison(dest, src, n + 1);
+ return res;
+}
+
+INTERCEPTOR(char *, strncpy, char *dest, const char *src, SIZE_T n) { // NOLINT
+ ENSURE_MSAN_INITED();
+ SIZE_T copy_size = REAL(strnlen)(src, n);
+ if (copy_size < n)
+ copy_size++; // trailing \0
+ char *res = REAL(strncpy)(dest, src, n); // NOLINT
+ __msan_copy_poison(dest, src, copy_size);
+ return res;
+}
+
+INTERCEPTOR(char *, strdup, char *src) {
+ ENSURE_MSAN_INITED();
+ SIZE_T n = REAL(strlen)(src);
+ char *res = REAL(strdup)(src);
+ __msan_copy_poison(res, src, n + 1);
+ return res;
+}
+
+INTERCEPTOR(char *, gcvt, double number, SIZE_T ndigit, char *buf) {
+ ENSURE_MSAN_INITED();
+ char *res = REAL(gcvt)(number, ndigit, buf);
+ // DynamoRio tool will take care of unpoisoning gcvt result for us.
+ if (!__msan_has_dynamic_component()) {
+ SIZE_T n = REAL(strlen)(buf);
+ __msan_unpoison(buf, n + 1);
+ }
+ return res;
+}
+
+INTERCEPTOR(char *, strcat, char *dest, const char *src) { // NOLINT
+ ENSURE_MSAN_INITED();
+ SIZE_T src_size = REAL(strlen)(src);
+ SIZE_T dest_size = REAL(strlen)(dest);
+ char *res = REAL(strcat)(dest, src); // NOLINT
+ __msan_copy_poison(dest + dest_size, src, src_size + 1);
+ return res;
+}
+
+INTERCEPTOR(char *, strncat, char *dest, const char *src, SIZE_T n) { // NOLINT
+ ENSURE_MSAN_INITED();
+ SIZE_T dest_size = REAL(strlen)(dest);
+ SIZE_T copy_size = REAL(strlen)(src);
+ if (copy_size < n)
+ copy_size++; // trailing \0
+ char *res = REAL(strncat)(dest, src, n); // NOLINT
+ __msan_copy_poison(dest + dest_size, src, copy_size);
+ return res;
+}
+
+INTERCEPTOR(long, strtol, const char *nptr, char **endptr, // NOLINT
+ int base) {
+ ENSURE_MSAN_INITED();
+ long res = REAL(strtol)(nptr, endptr, base); // NOLINT
+ if (!__msan_has_dynamic_component()) {
+ __msan_unpoison(endptr, sizeof(*endptr));
+ }
+ return res;
+}
+
+INTERCEPTOR(long long, strtoll, const char *nptr, char **endptr, // NOLINT
+ int base) {
+ ENSURE_MSAN_INITED();
+ long res = REAL(strtoll)(nptr, endptr, base); //NOLINT
+ if (!__msan_has_dynamic_component()) {
+ __msan_unpoison(endptr, sizeof(*endptr));
+ }
+ return res;
+}
+
+INTERCEPTOR(unsigned long, strtoul, const char *nptr, char **endptr, // NOLINT
+ int base) {
+ ENSURE_MSAN_INITED();
+ unsigned long res = REAL(strtoul)(nptr, endptr, base); // NOLINT
+ if (!__msan_has_dynamic_component()) {
+ __msan_unpoison(endptr, sizeof(*endptr));
+ }
+ return res;
+}
+
+INTERCEPTOR(unsigned long long, strtoull, const char *nptr, // NOLINT
+ char **endptr, int base) {
+ ENSURE_MSAN_INITED();
+ unsigned long res = REAL(strtoull)(nptr, endptr, base); // NOLINT
+ if (!__msan_has_dynamic_component()) {
+ __msan_unpoison(endptr, sizeof(*endptr));
+ }
+ return res;
+}
+
+INTERCEPTOR(double, strtod, const char *nptr, char **endptr) { // NOLINT
+ ENSURE_MSAN_INITED();
+ double res = REAL(strtod)(nptr, endptr); // NOLINT
+ if (!__msan_has_dynamic_component()) {
+ __msan_unpoison(endptr, sizeof(*endptr));
+ }
+ return res;
+}
+
+INTERCEPTOR(float, strtof, const char *nptr, char **endptr) { // NOLINT
+ ENSURE_MSAN_INITED();
+ float res = REAL(strtof)(nptr, endptr); // NOLINT
+ if (!__msan_has_dynamic_component()) {
+ __msan_unpoison(endptr, sizeof(*endptr));
+ }
+ return res;
+}
+
+INTERCEPTOR(long double, strtold, const char *nptr, char **endptr) { // NOLINT
+ ENSURE_MSAN_INITED();
+ long double res = REAL(strtold)(nptr, endptr); // NOLINT
+ if (!__msan_has_dynamic_component()) {
+ __msan_unpoison(endptr, sizeof(*endptr));
+ }
+ return res;
+}
+
+INTERCEPTOR(int, vsnprintf, char *str, uptr size,
+ const char *format, va_list ap) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(vsnprintf)(str, size, format, ap);
+ if (!__msan_has_dynamic_component()) {
+ __msan_unpoison(str, res + 1);
+ }
+ return res;
+}
+
+INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(vsprintf)(str, format, ap);
+ if (!__msan_has_dynamic_component()) {
+ __msan_unpoison(str, res + 1);
+ }
+ return res;
+}
+
+INTERCEPTOR(int, vswprintf, void *str, uptr size, void *format, va_list ap) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(vswprintf)(str, size, format, ap);
+ if (!__msan_has_dynamic_component()) {
+ __msan_unpoison(str, 4 * (res + 1));
+ }
+ return res;
+}
+
+INTERCEPTOR(int, sprintf, char *str, const char *format, ...) { // NOLINT
+ ENSURE_MSAN_INITED();
+ va_list ap;
+ va_start(ap, format);
+ int res = vsprintf(str, format, ap); // NOLINT
+ va_end(ap);
+ return res;
+}
+
+INTERCEPTOR(int, snprintf, char *str, uptr size, const char *format, ...) {
+ ENSURE_MSAN_INITED();
+ va_list ap;
+ va_start(ap, format);
+ int res = vsnprintf(str, size, format, ap);
+ va_end(ap);
+ return res;
+}
+
+INTERCEPTOR(int, swprintf, void *str, uptr size, void *format, ...) {
+ ENSURE_MSAN_INITED();
+ va_list ap;
+ va_start(ap, format);
+ int res = vswprintf(str, size, format, ap);
+ va_end(ap);
+ return res;
+}
+
+// SIZE_T strftime(char *s, SIZE_T max, const char *format,const struct tm *tm);
+INTERCEPTOR(SIZE_T, strftime, char *s, SIZE_T max, const char *format,
+ void *tm) {
+ ENSURE_MSAN_INITED();
+ SIZE_T res = REAL(strftime)(s, max, format, tm);
+ if (res) __msan_unpoison(s, res + 1);
+ return res;
+}
+
+INTERCEPTOR(SIZE_T, wcstombs, void *dest, void *src, SIZE_T size) {
+ ENSURE_MSAN_INITED();
+ SIZE_T res = REAL(wcstombs)(dest, src, size);
+ if (res != (SIZE_T)-1) __msan_unpoison(dest, res + 1);
+ return res;
+}
+
+// SIZE_T mbstowcs(wchar_t *dest, const char *src, SIZE_T n);
+INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T n) {
+ ENSURE_MSAN_INITED();
+ SIZE_T res = REAL(mbstowcs)(dest, src, n);
+ if (res != (SIZE_T)-1) __msan_unpoison(dest, (res + 1) * sizeof(wchar_t));
+ return res;
+}
+
+INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) {
+ ENSURE_MSAN_INITED();
+ SIZE_T res = REAL(wcslen)(s);
+ CHECK_UNPOISONED(s, sizeof(wchar_t) * (res + 1));
+ return res;
+}
+
+// wchar_t *wcschr(const wchar_t *wcs, wchar_t wc);
+INTERCEPTOR(wchar_t *, wcschr, void *s, wchar_t wc, void *ps) {
+ ENSURE_MSAN_INITED();
+ wchar_t *res = REAL(wcschr)(s, wc, ps);
+ return res;
+}
+
+// wchar_t *wcscpy(wchar_t *dest, const wchar_t *src);
+INTERCEPTOR(wchar_t *, wcscpy, wchar_t *dest, const wchar_t *src) {
+ ENSURE_MSAN_INITED();
+ wchar_t *res = REAL(wcscpy)(dest, src);
+ __msan_copy_poison(dest, src, sizeof(wchar_t) * (REAL(wcslen)(src) + 1));
+ return res;
+}
+
+// wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, SIZE_T n);
+INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) {
+ ENSURE_MSAN_INITED();
+ wchar_t *res = REAL(wmemcpy)(dest, src, n);
+ __msan_copy_poison(dest, src, n * sizeof(wchar_t));
+ return res;
+}
+
+INTERCEPTOR(wchar_t *, wmemset, wchar_t *s, wchar_t c, SIZE_T n) {
+ CHECK(MEM_IS_APP(s));
+ ENSURE_MSAN_INITED();
+ wchar_t *res = (wchar_t *)fast_memset(s, c, n * sizeof(wchar_t));
+ __msan_unpoison(s, n * sizeof(wchar_t));
+ return res;
+}
+
+INTERCEPTOR(wchar_t *, wmemmove, wchar_t *dest, const wchar_t *src, SIZE_T n) {
+ ENSURE_MSAN_INITED();
+ wchar_t *res = REAL(wmemmove)(dest, src, n);
+ __msan_move_poison(dest, src, n * sizeof(wchar_t));
+ return res;
+}
+
+INTERCEPTOR(int, wcscmp, const wchar_t *s1, const wchar_t *s2) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(wcscmp)(s1, s2);
+ return res;
+}
+
+INTERCEPTOR(double, wcstod, const wchar_t *nptr, wchar_t **endptr) {
+ ENSURE_MSAN_INITED();
+ double res = REAL(wcstod)(nptr, endptr);
+ __msan_unpoison(endptr, sizeof(*endptr));
+ return res;
+}
+
+// #define UNSUPPORTED(name) \
+// INTERCEPTOR(void, name, void) { \
+// Printf("MSAN: Unsupported %s\n", __FUNCTION__); \
+// Die(); \
+// }
+
+// FIXME: intercept the following functions:
+// Note, they only matter when running without a dynamic tool.
+// UNSUPPORTED(wcscoll_l)
+// UNSUPPORTED(wcsnrtombs)
+// UNSUPPORTED(wcstol)
+// UNSUPPORTED(wcstoll)
+// UNSUPPORTED(wcstold)
+// UNSUPPORTED(wcstoul)
+// UNSUPPORTED(wcstoull)
+// UNSUPPORTED(wcsxfrm_l)
+// UNSUPPORTED(wcsdup)
+// UNSUPPORTED(wcsftime)
+// UNSUPPORTED(wcsstr)
+// UNSUPPORTED(wcsrchr)
+// UNSUPPORTED(wctob)
+
+INTERCEPTOR(int, gettimeofday, void *tv, void *tz) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(gettimeofday)(tv, tz);
+ if (tv)
+ __msan_unpoison(tv, 16);
+ if (tz)
+ __msan_unpoison(tz, 8);
+ return res;
+}
+
+INTERCEPTOR(char *, fcvt, double x, int a, int *b, int *c) {
+ ENSURE_MSAN_INITED();
+ char *res = REAL(fcvt)(x, a, b, c);
+ if (!__msan_has_dynamic_component()) {
+ __msan_unpoison(b, sizeof(*b));
+ __msan_unpoison(c, sizeof(*c));
+ }
+ return res;
+}
+
+INTERCEPTOR(char *, getenv, char *name) {
+ ENSURE_MSAN_INITED();
+ char *res = REAL(getenv)(name);
+ if (!__msan_has_dynamic_component()) {
+ if (res)
+ __msan_unpoison(res, REAL(strlen)(res) + 1);
+ }
+ return res;
+}
+
+INTERCEPTOR(int, __fxstat, int magic, int fd, void *buf) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(__fxstat)(magic, fd, buf);
+ if (!res)
+ __msan_unpoison(buf, __msan::struct_stat_sz);
+ return res;
+}
+
+INTERCEPTOR(int, __fxstat64, int magic, int fd, void *buf) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(__fxstat64)(magic, fd, buf);
+ if (!res)
+ __msan_unpoison(buf, __msan::struct_stat64_sz);
+ return res;
+}
+
+INTERCEPTOR(int, __xstat, int magic, char *path, void *buf) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(__xstat)(magic, path, buf);
+ if (!res)
+ __msan_unpoison(buf, __msan::struct_stat_sz);
+ return res;
+}
+
+INTERCEPTOR(int, __xstat64, int magic, char *path, void *buf) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(__xstat64)(magic, path, buf);
+ if (!res)
+ __msan_unpoison(buf, __msan::struct_stat64_sz);
+ return res;
+}
+
+INTERCEPTOR(int, __lxstat, int magic, char *path, void *buf) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(__lxstat)(magic, path, buf);
+ if (!res)
+ __msan_unpoison(buf, __msan::struct_stat_sz);
+ return res;
+}
+
+INTERCEPTOR(int, __lxstat64, int magic, char *path, void *buf) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(__lxstat64)(magic, path, buf);
+ if (!res)
+ __msan_unpoison(buf, __msan::struct_stat64_sz);
+ return res;
+}
+
+INTERCEPTOR(int, pipe, int pipefd[2]) {
+ if (msan_init_is_running)
+ return REAL(pipe)(pipefd);
+ ENSURE_MSAN_INITED();
+ int res = REAL(pipe)(pipefd);
+ if (!res)
+ __msan_unpoison(pipefd, sizeof(int[2]));
+ return res;
+}
+
+INTERCEPTOR(int, wait, int *status) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(wait)(status);
+ if (status)
+ __msan_unpoison(status, sizeof(*status));
+ return res;
+}
+
+INTERCEPTOR(int, waitpid, int pid, int *status, int options) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(waitpid)(pid, status, options);
+ if (status)
+ __msan_unpoison(status, sizeof(*status));
+ return res;
+}
+
+INTERCEPTOR(char *, fgets, char *s, int size, void *stream) {
+ ENSURE_MSAN_INITED();
+ char *res = REAL(fgets)(s, size, stream);
+ if (res)
+ __msan_unpoison(s, REAL(strlen)(s) + 1);
+ return res;
+}
+
+INTERCEPTOR(char *, fgets_unlocked, char *s, int size, void *stream) {
+ ENSURE_MSAN_INITED();
+ char *res = REAL(fgets_unlocked)(s, size, stream);
+ if (res)
+ __msan_unpoison(s, REAL(strlen)(s) + 1);
+ return res;
+}
+
+INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) {
+ ENSURE_MSAN_INITED();
+ char *res = REAL(getcwd)(buf, size);
+ if (res)
+ __msan_unpoison(buf, REAL(strlen)(buf) + 1);
+ return res;
+}
+
+INTERCEPTOR(char *, realpath, char *path, char *abspath) {
+ ENSURE_MSAN_INITED();
+ char *res = REAL(realpath)(path, abspath);
+ if (res)
+ __msan_unpoison(abspath, REAL(strlen)(abspath) + 1);
+ return res;
+}
+
+INTERCEPTOR(int, getrlimit, int resource, void *rlim) {
+ if (msan_init_is_running)
+ return REAL(getrlimit)(resource, rlim);
+ ENSURE_MSAN_INITED();
+ int res = REAL(getrlimit)(resource, rlim);
+ if (!res)
+ __msan_unpoison(rlim, __msan::struct_rlimit_sz);
+ return res;
+}
+
+INTERCEPTOR(int, getrlimit64, int resource, void *rlim) {
+ if (msan_init_is_running)
+ return REAL(getrlimit64)(resource, rlim);
+ ENSURE_MSAN_INITED();
+ int res = REAL(getrlimit64)(resource, rlim);
+ if (!res)
+ __msan_unpoison(rlim, __msan::struct_rlimit64_sz);
+ return res;
+}
+
+INTERCEPTOR(int, statfs, const char *s, void *buf) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(statfs)(s, buf);
+ if (!res)
+ __msan_unpoison(buf, __msan::struct_statfs_sz);
+ return res;
+}
+
+INTERCEPTOR(int, fstatfs, int fd, void *buf) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(fstatfs)(fd, buf);
+ if (!res)
+ __msan_unpoison(buf, __msan::struct_statfs_sz);
+ return res;
+}
+
+INTERCEPTOR(int, statfs64, const char *s, void *buf) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(statfs64)(s, buf);
+ if (!res)
+ __msan_unpoison(buf, __msan::struct_statfs64_sz);
+ return res;
+}
+
+INTERCEPTOR(int, fstatfs64, int fd, void *buf) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(fstatfs64)(fd, buf);
+ if (!res)
+ __msan_unpoison(buf, __msan::struct_statfs64_sz);
+ return res;
+}
+
+INTERCEPTOR(int, uname, void *utsname) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(uname)(utsname);
+ if (!res) {
+ __msan_unpoison(utsname, __msan::struct_utsname_sz);
+ }
+ return res;
+}
+
+INTERCEPTOR(int, epoll_wait, int epfd, void *events, int maxevents,
+ int timeout) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(epoll_wait)(epfd, events, maxevents, timeout);
+ if (res > 0) {
+ __msan_unpoison(events, __msan::struct_epoll_event_sz * res);
+ }
+ return res;
+}
+
+INTERCEPTOR(int, epoll_pwait, int epfd, void *events, int maxevents,
+ int timeout, void *sigmask) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(epoll_pwait)(epfd, events, maxevents, timeout, sigmask);
+ if (res > 0) {
+ __msan_unpoison(events, __msan::struct_epoll_event_sz * res);
+ }
+ return res;
+}
+
+INTERCEPTOR(SSIZE_T, recv, int fd, void *buf, SIZE_T len, int flags) {
+ ENSURE_MSAN_INITED();
+ SSIZE_T res = REAL(recv)(fd, buf, len, flags);
+ if (res > 0)
+ __msan_unpoison(buf, res);
+ return res;
+}
+
+INTERCEPTOR(SSIZE_T, recvfrom, int fd, void *buf, SIZE_T len, int flags,
+ void *srcaddr, void *addrlen) {
+ ENSURE_MSAN_INITED();
+ SIZE_T srcaddr_sz;
+ if (srcaddr)
+ srcaddr_sz = __msan_get_socklen_t(addrlen);
+ SSIZE_T res = REAL(recvfrom)(fd, buf, len, flags, srcaddr, addrlen);
+ if (res > 0) {
+ __msan_unpoison(buf, res);
+ if (srcaddr) {
+ SIZE_T sz = __msan_get_socklen_t(addrlen);
+ __msan_unpoison(srcaddr, (sz < srcaddr_sz) ? sz : srcaddr_sz);
+ }
+ }
+ return res;
+}
+
+INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct msghdr *msg, int flags) {
+ ENSURE_MSAN_INITED();
+ SSIZE_T res = REAL(recvmsg)(fd, msg, flags);
+ if (res > 0) {
+ for (SIZE_T i = 0; i < __msan_get_msghdr_iovlen(msg); ++i)
+ __msan_unpoison(__msan_get_msghdr_iov_iov_base(msg, i),
+ __msan_get_msghdr_iov_iov_len(msg, i));
+ }
+ return res;
+}
+
+INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) {
+ GET_MALLOC_STACK_TRACE;
+ if (!msan_inited) {
+ // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
+ const SIZE_T kCallocPoolSize = 1024;
+ static uptr calloc_memory_for_dlsym[kCallocPoolSize];
+ static SIZE_T allocated;
+ SIZE_T size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
+ void *mem = (void*)&calloc_memory_for_dlsym[allocated];
+ allocated += size_in_words;
+ CHECK(allocated < kCallocPoolSize);
+ return mem;
+ }
+ return MsanReallocate(&stack, 0, nmemb * size, sizeof(u64), true);
+}
+
+INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) {
+ GET_MALLOC_STACK_TRACE;
+ return MsanReallocate(&stack, ptr, size, sizeof(u64), false);
+}
+
+INTERCEPTOR(void *, malloc, SIZE_T size) {
+ GET_MALLOC_STACK_TRACE;
+ return MsanReallocate(&stack, 0, size, sizeof(u64), false);
+}
+
+INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags,
+ int fd, OFF_T offset) {
+ ENSURE_MSAN_INITED();
+ void *res = REAL(mmap)(addr, length, prot, flags, fd, offset);
+ if (res != (void*)-1)
+ __msan_unpoison(res, RoundUpTo(length, GetPageSize()));
+ return res;
+}
+
+INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags,
+ int fd, OFF64_T offset) {
+ ENSURE_MSAN_INITED();
+ void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset);
+ if (res != (void*)-1)
+ __msan_unpoison(res, RoundUpTo(length, GetPageSize()));
+ return res;
+}
+
+struct dlinfo {
+ char *dli_fname;
+ void *dli_fbase;
+ char *dli_sname;
+ void *dli_saddr;
+};
+
+INTERCEPTOR(int, dladdr, void *addr, dlinfo *info) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(dladdr)(addr, info);
+ if (res != 0) {
+ __msan_unpoison(info, sizeof(*info));
+ if (info->dli_fname)
+ __msan_unpoison(info->dli_fname, REAL(strlen)(info->dli_fname) + 1);
+ if (info->dli_sname)
+ __msan_unpoison(info->dli_sname, REAL(strlen)(info->dli_sname) + 1);
+ }
+ return res;
+}
+
+INTERCEPTOR(int, getrusage, int who, void *usage) {
+ ENSURE_MSAN_INITED();
+ int res = REAL(getrusage)(who, usage);
+ if (res == 0) {
+ __msan_unpoison(usage, __msan::struct_rusage_sz);
+ }
+ return res;
+}
+
+#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
+ __msan_unpoison(ptr, size)
+#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) do { } while (false)
+#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
+ do { \
+ ctx = 0; \
+ (void)ctx; \
+ ENSURE_MSAN_INITED(); \
+ } while (false)
+#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) do { } while (false)
+#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) do { } while (false)
+#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
+ do { } while (false) // FIXME
+#include "sanitizer_common/sanitizer_common_interceptors.inc"
+
+// static
+void *fast_memset(void *ptr, int c, SIZE_T n) {
+ // hack until we have a really fast internal_memset
+ if (sizeof(uptr) == 8 &&
+ (n % 8) == 0 &&
+ ((uptr)ptr % 8) == 0 &&
+ (c == 0 || c == -1)) {
+ // Printf("memset %p %zd %x\n", ptr, n, c);
+ uptr to_store = c ? -1L : 0L;
+ uptr *p = (uptr*)ptr;
+ for (SIZE_T i = 0; i < n / 8; i++)
+ p[i] = to_store;
+ return ptr;
+ }
+ return internal_memset(ptr, c, n);
+}
+
+// static
+void *fast_memcpy(void *dst, const void *src, SIZE_T n) {
+ // Same hack as in fast_memset above.
+ if (sizeof(uptr) == 8 &&
+ (n % 8) == 0 &&
+ ((uptr)dst % 8) == 0 &&
+ ((uptr)src % 8) == 0) {
+ uptr *d = (uptr*)dst;
+ uptr *s = (uptr*)src;
+ for (SIZE_T i = 0; i < n / 8; i++)
+ d[i] = s[i];
+ return dst;
+ }
+ return internal_memcpy(dst, src, n);
+}
+
+// These interface functions reside here so that they can use
+// fast_memset, etc.
+void __msan_unpoison(void *a, uptr size) {
+ if (!MEM_IS_APP(a)) return;
+ fast_memset((void*)MEM_TO_SHADOW((uptr)a), 0, size);
+}
+
+void __msan_poison(void *a, uptr size) {
+ if (!MEM_IS_APP(a)) return;
+ fast_memset((void*)MEM_TO_SHADOW((uptr)a),
+ __msan::flags()->poison_heap_with_zeroes ? 0 : -1, size);
+}
+
+void __msan_poison_stack(void *a, uptr size) {
+ if (!MEM_IS_APP(a)) return;
+ fast_memset((void*)MEM_TO_SHADOW((uptr)a),
+ __msan::flags()->poison_stack_with_zeroes ? 0 : -1, size);
+}
+
+void __msan_clear_and_unpoison(void *a, uptr size) {
+ fast_memset(a, 0, size);
+ fast_memset((void*)MEM_TO_SHADOW((uptr)a), 0, size);
+}
+
+void __msan_copy_origin(void *dst, const void *src, uptr size) {
+ if (!__msan_get_track_origins()) return;
+ if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) return;
+ uptr d = MEM_TO_ORIGIN(dst);
+ uptr s = MEM_TO_ORIGIN(src);
+ uptr beg = d & ~3UL; // align down.
+ uptr end = (d + size + 3) & ~3UL; // align up.
+ s = s & ~3UL; // align down.
+ fast_memcpy((void*)beg, (void*)s, end - beg);
+}
+
+void __msan_copy_poison(void *dst, const void *src, uptr size) {
+ if (!MEM_IS_APP(dst)) return;
+ if (!MEM_IS_APP(src)) return;
+ fast_memcpy((void*)MEM_TO_SHADOW((uptr)dst),
+ (void*)MEM_TO_SHADOW((uptr)src), size);
+ __msan_copy_origin(dst, src, size);
+}
+
+void __msan_move_poison(void *dst, const void *src, uptr size) {
+ if (!MEM_IS_APP(dst)) return;
+ if (!MEM_IS_APP(src)) return;
+ internal_memmove((void*)MEM_TO_SHADOW((uptr)dst),
+ (void*)MEM_TO_SHADOW((uptr)src), size);
+ __msan_copy_origin(dst, src, size);
+}
+
+void *__msan_memcpy(void *dest, const void *src, SIZE_T n) {
+ ENSURE_MSAN_INITED();
+ void *res = fast_memcpy(dest, src, n);
+ __msan_copy_poison(dest, src, n);
+ return res;
+}
+
+void *__msan_memset(void *s, int c, SIZE_T n) {
+ ENSURE_MSAN_INITED();
+ void *res = fast_memset(s, c, n);
+ __msan_unpoison(s, n);
+ return res;
+}
+
+void *__msan_memmove(void *dest, const void *src, SIZE_T n) {
+ ENSURE_MSAN_INITED();
+ void *res = REAL(memmove)(dest, src, n);
+ __msan_move_poison(dest, src, n);
+ return res;
+}
+
+namespace __msan {
+void InitializeInterceptors() {
+ static int inited = 0;
+ CHECK_EQ(inited, 0);
+ SANITIZER_COMMON_INTERCEPTORS_INIT;
+
+ INTERCEPT_FUNCTION(mmap);
+ INTERCEPT_FUNCTION(mmap64);
+ INTERCEPT_FUNCTION(posix_memalign);
+ INTERCEPT_FUNCTION(malloc);
+ INTERCEPT_FUNCTION(calloc);
+ INTERCEPT_FUNCTION(realloc);
+ INTERCEPT_FUNCTION(free);
+ INTERCEPT_FUNCTION(fread);
+ INTERCEPT_FUNCTION(fread_unlocked);
+ INTERCEPT_FUNCTION(readlink);
+ INTERCEPT_FUNCTION(readdir);
+ INTERCEPT_FUNCTION(memcpy);
+ INTERCEPT_FUNCTION(memset);
+ INTERCEPT_FUNCTION(memmove);
+ INTERCEPT_FUNCTION(wmemset);
+ INTERCEPT_FUNCTION(wmemcpy);
+ INTERCEPT_FUNCTION(wmemmove);
+ INTERCEPT_FUNCTION(strcpy); // NOLINT
+ INTERCEPT_FUNCTION(strdup);
+ INTERCEPT_FUNCTION(strncpy); // NOLINT
+ INTERCEPT_FUNCTION(strlen);
+ INTERCEPT_FUNCTION(strnlen);
+ INTERCEPT_FUNCTION(gcvt);
+ INTERCEPT_FUNCTION(strcat); // NOLINT
+ INTERCEPT_FUNCTION(strncat); // NOLINT
+ INTERCEPT_FUNCTION(strtol);
+ INTERCEPT_FUNCTION(strtoll);
+ INTERCEPT_FUNCTION(strtoul);
+ INTERCEPT_FUNCTION(strtoull);
+ INTERCEPT_FUNCTION(strtod);
+ INTERCEPT_FUNCTION(strtof);
+ INTERCEPT_FUNCTION(strtold);
+ INTERCEPT_FUNCTION(vsprintf);
+ INTERCEPT_FUNCTION(vsnprintf);
+ INTERCEPT_FUNCTION(vswprintf);
+ INTERCEPT_FUNCTION(sprintf); // NOLINT
+ INTERCEPT_FUNCTION(snprintf);
+ INTERCEPT_FUNCTION(swprintf);
+ INTERCEPT_FUNCTION(strftime);
+ INTERCEPT_FUNCTION(wcstombs);
+ INTERCEPT_FUNCTION(mbstowcs);
+ INTERCEPT_FUNCTION(wcslen);
+ INTERCEPT_FUNCTION(wcschr);
+ INTERCEPT_FUNCTION(wcscpy);
+ INTERCEPT_FUNCTION(wcscmp);
+ INTERCEPT_FUNCTION(wcstod);
+ INTERCEPT_FUNCTION(getenv);
+ INTERCEPT_FUNCTION(gettimeofday);
+ INTERCEPT_FUNCTION(fcvt);
+ INTERCEPT_FUNCTION(__fxstat);
+ INTERCEPT_FUNCTION(__xstat);
+ INTERCEPT_FUNCTION(__lxstat);
+ INTERCEPT_FUNCTION(__fxstat64);
+ INTERCEPT_FUNCTION(__xstat64);
+ INTERCEPT_FUNCTION(__lxstat64);
+ INTERCEPT_FUNCTION(pipe);
+ INTERCEPT_FUNCTION(wait);
+ INTERCEPT_FUNCTION(waitpid);
+ INTERCEPT_FUNCTION(fgets);
+ INTERCEPT_FUNCTION(fgets_unlocked);
+ INTERCEPT_FUNCTION(getcwd);
+ INTERCEPT_FUNCTION(realpath);
+ INTERCEPT_FUNCTION(getrlimit);
+ INTERCEPT_FUNCTION(getrlimit64);
+ INTERCEPT_FUNCTION(statfs);
+ INTERCEPT_FUNCTION(fstatfs);
+ INTERCEPT_FUNCTION(statfs64);
+ INTERCEPT_FUNCTION(fstatfs64);
+ INTERCEPT_FUNCTION(uname);
+ INTERCEPT_FUNCTION(epoll_wait);
+ INTERCEPT_FUNCTION(epoll_pwait);
+ INTERCEPT_FUNCTION(recv);
+ INTERCEPT_FUNCTION(recvfrom);
+ INTERCEPT_FUNCTION(recvmsg);
+ INTERCEPT_FUNCTION(dladdr);
+ INTERCEPT_FUNCTION(getrusage);
+ inited = 1;
+}
+} // namespace __msan
diff --git a/lib/msan/msan_linux.cc b/lib/msan/msan_linux.cc
new file mode 100644
index 000000000000..2203980c638d
--- /dev/null
+++ b/lib/msan/msan_linux.cc
@@ -0,0 +1,108 @@
+//===-- msan_linux.cc -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// Linux-specific code.
+//===----------------------------------------------------------------------===//
+
+#ifdef __linux__
+
+#include "msan.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <unwind.h>
+#include <execinfo.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+
+namespace __msan {
+
+static const uptr kMemBeg = 0x600000000000;
+static const uptr kMemEnd = 0x7fffffffffff;
+static const uptr kShadowBeg = MEM_TO_SHADOW(kMemBeg);
+static const uptr kShadowEnd = MEM_TO_SHADOW(kMemEnd);
+static const uptr kBad1Beg = 0x100000000; // 4G
+static const uptr kBad1End = kShadowBeg - 1;
+static const uptr kBad2Beg = kShadowEnd + 1;
+static const uptr kBad2End = kMemBeg - 1;
+static const uptr kOriginsBeg = kBad2Beg;
+static const uptr kOriginsEnd = kBad2End;
+
+bool InitShadow(bool prot1, bool prot2, bool map_shadow, bool init_origins) {
+ if (flags()->verbosity) {
+ Printf("__msan_init %p\n", &__msan_init);
+ Printf("Memory : %p %p\n", kMemBeg, kMemEnd);
+ Printf("Bad2 : %p %p\n", kBad2Beg, kBad2End);
+ Printf("Origins : %p %p\n", kOriginsBeg, kOriginsEnd);
+ Printf("Shadow : %p %p\n", kShadowBeg, kShadowEnd);
+ Printf("Bad1 : %p %p\n", kBad1Beg, kBad1End);
+ }
+
+ if (!MemoryRangeIsAvailable(kShadowBeg,
+ init_origins ? kOriginsEnd : kShadowEnd)) {
+ Printf("FATAL: Shadow memory range is not available.\n");
+ return false;
+ }
+
+ if (prot1 && !Mprotect(kBad1Beg, kBad1End - kBad1Beg))
+ return false;
+ if (prot2 && !Mprotect(kBad2Beg, kBad2End - kBad2Beg))
+ return false;
+ if (map_shadow) {
+ void *shadow = MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg);
+ if (shadow != (void*)kShadowBeg) return false;
+ }
+ if (init_origins) {
+ void *origins = MmapFixedNoReserve(kOriginsBeg, kOriginsEnd - kOriginsBeg);
+ if (origins != (void*)kOriginsBeg) return false;
+ }
+ return true;
+}
+
+static void MsanTrap(int, siginfo_t *siginfo, void *context) {
+ ucontext_t *ucontext = (ucontext_t*)context;
+ uptr pc = ucontext->uc_mcontext.gregs[REG_RIP];
+ uptr bp = ucontext->uc_mcontext.gregs[REG_RBP];
+ PrintWarning(pc + 1 /*1 will be subtracted in StackTrace::Print */, bp);
+ ucontext->uc_mcontext.gregs[REG_RIP] += 2;
+}
+
+void InstallTrapHandler() {
+ struct sigaction sigact;
+ internal_memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_sigaction = MsanTrap;
+ sigact.sa_flags = SA_SIGINFO;
+ CHECK_EQ(0, sigaction(SIGILL, &sigact, 0));
+}
+
+void MsanDie() {
+ _exit(flags()->exit_code);
+}
+
+static void MsanAtExit(void) {
+ if (msan_report_count > 0) {
+ ReportAtExitStatistics();
+ if (flags()->exit_code)
+ _exit(flags()->exit_code);
+ }
+}
+
+void InstallAtExitHandler() {
+ atexit(MsanAtExit);
+}
+}
+
+#endif // __linux__
diff --git a/lib/msan/msan_new_delete.cc b/lib/msan/msan_new_delete.cc
new file mode 100644
index 000000000000..c4efe2ef70ce
--- /dev/null
+++ b/lib/msan/msan_new_delete.cc
@@ -0,0 +1,51 @@
+//===-- msan_new_delete.cc ------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// Interceptors for operators new and delete.
+//===----------------------------------------------------------------------===//
+
+#include "msan.h"
+
+#include <stddef.h>
+
+namespace __msan {
+// This function is a no-op. We need it to make sure that object file
+// with our replacements will actually be loaded from static MSan
+// run-time library at link-time.
+void ReplaceOperatorsNewAndDelete() { }
+}
+
+using namespace __msan; // NOLINT
+
+// Fake std::nothrow_t to avoid including <new>.
+namespace std {
+ struct nothrow_t {};
+} // namespace std
+
+
+#define OPERATOR_NEW_BODY \
+ GET_MALLOC_STACK_TRACE; \
+ return MsanReallocate(&stack, 0, size, sizeof(u64), false)
+
+void *operator new(size_t size) { OPERATOR_NEW_BODY; }
+void *operator new[](size_t size) { OPERATOR_NEW_BODY; }
+void *operator new(size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
+void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; }
+
+#define OPERATOR_DELETE_BODY \
+ if (ptr) MsanDeallocate(ptr)
+
+void operator delete(void *ptr) { OPERATOR_DELETE_BODY; }
+void operator delete[](void *ptr) { OPERATOR_DELETE_BODY; }
+void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; }
+void operator delete[](void *ptr, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY;
+}
diff --git a/lib/msan/msan_platform_limits_posix.cc b/lib/msan/msan_platform_limits_posix.cc
new file mode 100644
index 000000000000..19d6c5d0ab3f
--- /dev/null
+++ b/lib/msan/msan_platform_limits_posix.cc
@@ -0,0 +1,59 @@
+//===-- msan_platform_limits.cc -------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// Sizes and layouts of platform-specific POSIX data structures.
+//===----------------------------------------------------------------------===//
+
+#ifdef __linux__
+
+#include "msan.h"
+#include "msan_platform_limits_posix.h"
+
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/vfs.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <dirent.h>
+
+namespace __msan {
+ unsigned struct_utsname_sz = sizeof(struct utsname);
+ unsigned struct_stat_sz = sizeof(struct stat);
+ unsigned struct_stat64_sz = sizeof(struct stat64);
+ unsigned struct_rlimit_sz = sizeof(struct rlimit);
+ unsigned struct_rlimit64_sz = sizeof(struct rlimit64);
+ unsigned struct_dirent_sz = sizeof(struct dirent);
+ unsigned struct_statfs_sz = sizeof(struct statfs);
+ unsigned struct_statfs64_sz = sizeof(struct statfs64);
+ unsigned struct_epoll_event_sz = sizeof(struct epoll_event);
+ unsigned struct_rusage_sz = sizeof(struct rusage);
+
+ void* __msan_get_msghdr_iov_iov_base(void* msg, int idx) {
+ return ((struct msghdr *)msg)->msg_iov[idx].iov_base;
+ }
+
+ uptr __msan_get_msghdr_iov_iov_len(void* msg, int idx) {
+ return ((struct msghdr *)msg)->msg_iov[idx].iov_len;
+ }
+
+ uptr __msan_get_msghdr_iovlen(void* msg) {
+ return ((struct msghdr *)msg)->msg_iovlen;
+ }
+
+ uptr __msan_get_socklen_t(void* socklen_ptr) {
+ return *(socklen_t*)socklen_ptr;
+ }
+}
+
+#endif // __linux__
diff --git a/lib/msan/msan_platform_limits_posix.h b/lib/msan/msan_platform_limits_posix.h
new file mode 100644
index 000000000000..3cd90ce93f6c
--- /dev/null
+++ b/lib/msan/msan_platform_limits_posix.h
@@ -0,0 +1,36 @@
+//===-- msan_platform_limits.h ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// Sizes and layouts of platform-specific data structures.
+//===----------------------------------------------------------------------===//
+
+#ifndef MSAN_PLATFORM_LIMITS_H
+#define MSAN_PLATFORM_LIMITS_H
+
+namespace __msan {
+ extern unsigned struct_utsname_sz;
+ extern unsigned struct_stat_sz;
+ extern unsigned struct_stat64_sz;
+ extern unsigned struct_rlimit_sz;
+ extern unsigned struct_rlimit64_sz;
+ extern unsigned struct_dirent_sz;
+ extern unsigned struct_statfs_sz;
+ extern unsigned struct_statfs64_sz;
+ extern unsigned struct_epoll_event_sz;
+ extern unsigned struct_rusage_sz;
+
+ void* __msan_get_msghdr_iov_iov_base(void* msg, int idx);
+ uptr __msan_get_msghdr_iov_iov_len(void* msg, int idx);
+ uptr __msan_get_msghdr_iovlen(void* msg);
+ uptr __msan_get_socklen_t(void* socklen_ptr);
+} // namespace __msan
+
+#endif
diff --git a/lib/msan/msan_report.cc b/lib/msan/msan_report.cc
new file mode 100644
index 000000000000..872108999733
--- /dev/null
+++ b/lib/msan/msan_report.cc
@@ -0,0 +1,100 @@
+//===-- msan_report.cc ----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// Error reporting.
+//===----------------------------------------------------------------------===//
+
+#include "msan.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "sanitizer_common/sanitizer_report_decorator.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+
+using namespace __sanitizer;
+
+static StaticSpinMutex report_mu;
+
+namespace __msan {
+
+static bool PrintsToTtyCached() {
+ static int cached = 0;
+ static bool prints_to_tty;
+ if (!cached) { // Ok wrt threads since we are printing only from one thread.
+ prints_to_tty = PrintsToTty();
+ cached = 1;
+ }
+ return prints_to_tty;
+}
+
+class Decorator: private __sanitizer::AnsiColorDecorator {
+ public:
+ Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { }
+ const char *Warning() { return Red(); }
+ const char *Origin() { return Magenta(); }
+ const char *Name() { return Green(); }
+ const char *End() { return Default(); }
+};
+
+static void DescribeOrigin(u32 origin) {
+ Decorator d;
+ if (flags()->verbosity)
+ Printf(" raw origin id: %d\n", origin);
+ if (const char *so = __msan_get_origin_descr_if_stack(origin)) {
+ char* s = internal_strdup(so);
+ char* sep = internal_strchr(s, '@');
+ CHECK(sep);
+ *sep = '\0';
+ Printf("%s", d.Origin());
+ Printf(" %sUninitialised value was created by an allocation of '%s%s%s'"
+ " in the stack frame of function '%s%s%s'%s\n",
+ d.Origin(), d.Name(), s, d.Origin(), d.Name(), sep + 1,
+ d.Origin(), d.End());
+ InternalFree(s);
+ } else {
+ uptr size = 0;
+ const uptr *trace = StackDepotGet(origin, &size);
+ Printf(" %sUninitialised value was created by a heap allocation%s\n",
+ d.Origin(), d.End());
+ StackTrace::PrintStack(trace, size, true, "", 0);
+ }
+}
+
+void ReportUMR(StackTrace *stack, u32 origin) {
+ if (!__msan::flags()->report_umrs) return;
+
+ GenericScopedLock<StaticSpinMutex> lock(&report_mu);
+
+ Decorator d;
+ Printf("%s", d.Warning());
+ Report(" WARNING: Use of uninitialized value\n");
+ Printf("%s", d.End());
+ StackTrace::PrintStack(stack->trace, stack->size, true, "", 0);
+ if (origin) {
+ DescribeOrigin(origin);
+ }
+}
+
+void ReportExpectedUMRNotFound(StackTrace *stack) {
+ GenericScopedLock<StaticSpinMutex> lock(&report_mu);
+
+ Printf(" WARNING: Expected use of uninitialized value not found\n");
+ StackTrace::PrintStack(stack->trace, stack->size, true, "", 0);
+}
+
+void ReportAtExitStatistics() {
+ Decorator d;
+ Printf("%s", d.Warning());
+ Printf("MemorySanitizer: %d warnings reported.\n", msan_report_count);
+ Printf("%s", d.End());
+}
+
+
+} // namespace msan
diff --git a/lib/msan/tests/CMakeLists.txt b/lib/msan/tests/CMakeLists.txt
new file mode 100644
index 000000000000..d2a28b2cba5c
--- /dev/null
+++ b/lib/msan/tests/CMakeLists.txt
@@ -0,0 +1,166 @@
+include(CheckCXXCompilerFlag)
+include(CompilerRTCompile)
+include(CompilerRTLink)
+
+include_directories(..)
+include_directories(../..)
+
+# Instrumented libcxx sources and build flags.
+set(MSAN_LIBCXX_PATH ${LLVM_MAIN_SRC_DIR}/projects/libcxx)
+file(GLOB MSAN_LIBCXX_SOURCES ${MSAN_LIBCXX_PATH}/src/*.cpp)
+set(MSAN_LIBCXX_CFLAGS
+ -I${MSAN_LIBCXX_PATH}/include
+ -fsanitize=memory
+ -fsanitize-memory-track-origins
+ -fPIC
+ -Wno-\#warnings
+ -g
+ -O2
+ -std=c++0x
+ -fstrict-aliasing
+ -fno-exceptions
+ -nostdinc++
+ -fno-omit-frame-pointer
+ -mno-omit-leaf-frame-pointer)
+set(MSAN_LIBCXX_LINK_FLAGS
+ -nodefaultlibs
+ -lpthread
+ -lrt
+ -lc
+ -lstdc++
+ -fsanitize=memory)
+
+# Unittest sources and build flags.
+set(MSAN_UNITTEST_SOURCE msan_test.cc)
+set(MSAN_UNITTEST_HEADERS msandr_test_so.h)
+set(MSANDR_UNITTEST_SOURCE msandr_test_so.cc)
+set(MSAN_UNITTEST_COMMON_CFLAGS
+ -I${MSAN_LIBCXX_PATH}/include
+ ${COMPILER_RT_GTEST_INCLUDE_CFLAGS}
+ -I${COMPILER_RT_SOURCE_DIR}/include
+ -I${COMPILER_RT_SOURCE_DIR}/lib
+ -I${COMPILER_RT_SOURCE_DIR}/lib/msan
+ -std=c++0x
+ -stdlib=libc++
+ -fPIE
+ -g
+ -O2
+ -fno-exceptions
+ -fno-omit-frame-pointer
+ -mno-omit-leaf-frame-pointer
+)
+set(MSAN_UNITTEST_INSTRUMENTED_CFLAGS
+ ${MSAN_UNITTEST_COMMON_CFLAGS}
+ -fsanitize=memory
+ -fsanitize-memory-track-origins
+ -mllvm -msan-keep-going=1
+)
+set(MSAN_UNITTEST_LINK_FLAGS
+ -fsanitize=memory
+ -pie
+ -ldl
+ # FIXME: we build libcxx without cxxabi and need libstdc++ to provide it.
+ -lstdc++
+)
+
+# Compile source for the given architecture, using compiler
+# options in ${ARGN}, and add it to the object list.
+macro(msan_compile obj_list source arch)
+ get_filename_component(basename ${source} NAME)
+ set(output_obj "${basename}.${arch}.o")
+ get_target_flags_for_arch(${arch} TARGET_CFLAGS)
+ clang_compile(${output_obj} ${source}
+ CFLAGS ${ARGN} ${TARGET_CFLAGS}
+ DEPS gtest ${MSAN_RUNTIME_LIBRARIES} ${MSAN_UNITTEST_HEADERS})
+ list(APPEND ${obj_list} ${output_obj})
+endmacro()
+
+macro(msan_link_shared so_list so_name arch)
+ parse_arguments(SOURCE "OBJECTS;LINKFLAGS;DEPS" "" ${ARGN})
+ get_unittest_directory(OUTPUT_DIR)
+ file(MAKE_DIRECTORY ${OUTPUT_DIR})
+ set(output_so "${OUTPUT_DIR}/${so_name}.${arch}.so")
+ get_target_flags_for_arch(${arch} TARGET_LINKFLAGS)
+ clang_link_shared(${output_so}
+ OBJECTS ${SOURCE_OBJECTS}
+ LINKFLAGS ${TARGET_LINKFLAGS} ${SOURCE_LINKFLAGS}
+ DEPS ${SOURCE_DEPS})
+ list(APPEND ${so_list} ${output_so})
+endmacro()
+
+# Link MSan unit test for a given architecture from a set
+# of objects in ${ARGN}.
+macro(add_msan_test test_suite test_name arch)
+ get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS)
+ get_unittest_directory(OUTPUT_DIR)
+ add_compiler_rt_test(${test_suite} ${test_name}
+ OBJECTS ${ARGN}
+ DEPS ${MSAN_RUNTIME_LIBRARIES} ${ARGN}
+ LINK_FLAGS ${MSAN_UNITTEST_LINK_FLAGS}
+ ${TARGET_LINK_FLAGS}
+ "-Wl,-rpath=${OUTPUT_DIR}")
+endmacro()
+
+# Main MemorySanitizer unit tests.
+add_custom_target(MsanUnitTests)
+set_target_properties(MsanUnitTests PROPERTIES FOLDER "MSan unit tests")
+
+# Adds MSan unit tests and benchmarks for architecture.
+macro(add_msan_tests_for_arch arch)
+ # Build gtest instrumented with MSan.
+ set(MSAN_INST_GTEST)
+ msan_compile(MSAN_INST_GTEST ${COMPILER_RT_GTEST_SOURCE} ${arch}
+ ${MSAN_UNITTEST_INSTRUMENTED_CFLAGS})
+
+ # Build libcxx instrumented with MSan.
+ set(MSAN_INST_LIBCXX_OBJECTS)
+ foreach(SOURCE ${MSAN_LIBCXX_SOURCES})
+ msan_compile(MSAN_INST_LIBCXX_OBJECTS ${SOURCE} ${arch}
+ ${MSAN_LIBCXX_CFLAGS})
+ endforeach(SOURCE)
+
+ set(MSAN_INST_LIBCXX)
+ msan_link_shared(MSAN_INST_LIBCXX "libcxx" ${arch}
+ OBJECTS ${MSAN_INST_LIBCXX_OBJECTS}
+ LINKFLAGS ${MSAN_LIBCXX_LINK_FLAGS}
+ DEPS ${MSAN_INST_LIBCXX_OBJECTS} ${MSAN_RUNTIME_LIBRARIES})
+
+ # Instrumented tests.
+ set(MSAN_INST_TEST_OBJECTS)
+ msan_compile(MSAN_INST_TEST_OBJECTS ${MSAN_UNITTEST_SOURCE} ${arch}
+ ${MSAN_UNITTEST_INSTRUMENTED_CFLAGS})
+
+ # Uninstrumented shared object for MSanDR tests.
+ set(MSANDR_TEST_OBJECTS)
+ msan_compile(MSANDR_TEST_OBJECTS ${MSANDR_UNITTEST_SOURCE} ${arch}
+ ${MSAN_UNITTEST_COMMON_CFLAGS})
+
+ # Uninstrumented shared library tests.
+ set(MSANDR_TEST_SO)
+ msan_link_shared(MSANDR_TEST_SO "libmsandr_test" ${arch}
+ OBJECTS ${MSANDR_TEST_OBJECTS}
+ DEPS ${MSANDR_TEST_OBJECTS} ${MSAN_RUNTIME_LIBRARIES})
+
+ # Link everything together.
+ add_msan_test(MsanUnitTests "Msan-${arch}-Test" ${arch}
+ ${MSAN_INST_TEST_OBJECTS} ${MSAN_INST_GTEST}
+ ${MSAN_INST_LIBCXX} ${MSANDR_TEST_SO})
+endmacro()
+
+if(COMPILER_RT_CAN_EXECUTE_TESTS AND EXISTS ${MSAN_LIBCXX_PATH}/)
+ if(CAN_TARGET_x86_64)
+ add_msan_tests_for_arch(x86_64)
+ endif()
+
+ # Run unittests as a part of lit testsuite.
+ configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+
+ add_lit_testsuite(check-msan "Running MemorySanitizer unittests"
+ ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS MsanUnitTests
+ )
+ set_target_properties(check-msan PROPERTIES FOLDER "MemorySanitizer unittests")
+endif()
diff --git a/lib/msan/tests/lit.cfg b/lib/msan/tests/lit.cfg
new file mode 100644
index 000000000000..38aa1380443f
--- /dev/null
+++ b/lib/msan/tests/lit.cfg
@@ -0,0 +1,29 @@
+# -*- Python -*-
+
+import os
+
+def get_required_attr(config, attr_name):
+ attr_value = getattr(config, attr_name, None)
+ if not attr_value:
+ lit.fatal("No attribute %r in test configuration! You may need to run "
+ "tests from your build directory or add this attribute "
+ "to lit.site.cfg " % attr_name)
+ return attr_value
+
+# Setup attributes common for all compiler-rt projects.
+llvm_src_root = get_required_attr(config, 'llvm_src_root')
+compiler_rt_lit_unit_cfg = os.path.join(llvm_src_root, "projects",
+ "compiler-rt", "lib",
+ "lit.common.unit.cfg")
+lit.load_config(config, compiler_rt_lit_unit_cfg)
+
+# Setup config name.
+config.name = 'MemorySanitizer'
+
+# Setup test source and exec root. For unit tests, we define
+# it as build directory with sanitizer_common unit tests.
+llvm_obj_root = get_required_attr(config, "llvm_obj_root")
+config.test_exec_root = os.path.join(llvm_obj_root, "projects",
+ "compiler-rt", "lib",
+ "msan", "tests")
+config.test_source_root = config.test_exec_root
diff --git a/lib/msan/tests/lit.site.cfg.in b/lib/msan/tests/lit.site.cfg.in
new file mode 100644
index 000000000000..bb9a28d6a6cb
--- /dev/null
+++ b/lib/msan/tests/lit.site.cfg.in
@@ -0,0 +1,9 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+config.build_type = "@CMAKE_BUILD_TYPE@"
+config.llvm_obj_root = "@LLVM_BINARY_DIR@"
+config.llvm_src_root = "@LLVM_SOURCE_DIR@"
+
+# Let the main config do the real work.
+lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg")
diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc
new file mode 100644
index 000000000000..1ee7a27d56b1
--- /dev/null
+++ b/lib/msan/tests/msan_test.cc
@@ -0,0 +1,1675 @@
+//===-- msan_test.cc ------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// MemorySanitizer unit tests.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer/msan_interface.h"
+#include "msandr_test_so.h"
+#include "gtest/gtest.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+#include <wchar.h>
+
+#include <dlfcn.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <sys/mman.h>
+#include <sys/vfs.h>
+
+#if defined(__i386__) || defined(__x86_64__)
+# include <emmintrin.h>
+# define MSAN_HAS_M128 1
+#else
+# define MSAN_HAS_M128 0
+#endif
+
+typedef unsigned char U1;
+typedef unsigned short U2; // NOLINT
+typedef unsigned int U4;
+typedef unsigned long long U8; // NOLINT
+typedef signed char S1;
+typedef signed short S2; // NOLINT
+typedef signed int S4;
+typedef signed long long S8; // NOLINT
+#define NOINLINE __attribute__((noinline))
+#define INLINE __attribute__((always_inline))
+
+
+#define EXPECT_POISONED(action) \
+ do { \
+ __msan_set_expect_umr(1); \
+ action; \
+ __msan_set_expect_umr(0); \
+ } while (0)
+
+#define EXPECT_POISONED_O(action, origin) \
+ do { \
+ __msan_set_expect_umr(1); \
+ action; \
+ __msan_set_expect_umr(0); \
+ if (TrackingOrigins()) \
+ EXPECT_EQ(origin, __msan_get_origin_tls()); \
+ } while (0)
+
+#define EXPECT_POISONED_S(action, stack_origin) \
+ do { \
+ __msan_set_expect_umr(1); \
+ action; \
+ __msan_set_expect_umr(0); \
+ u32 id = __msan_get_origin_tls(); \
+ const char *str = __msan_get_origin_descr_if_stack(id); \
+ if (!str || strcmp(str, stack_origin)) { \
+ fprintf(stderr, "EXPECT_POISONED_S: id=%u %s, %s", \
+ id, stack_origin, str); \
+ EXPECT_EQ(1, 0); \
+ } \
+ } while (0)
+
+
+static U8 poisoned_array[100];
+template<class T>
+T *GetPoisoned(int i = 0, T val = 0) {
+ T *res = (T*)&poisoned_array[i];
+ *res = val;
+ __msan_poison(&poisoned_array[i], sizeof(T));
+ return res;
+}
+
+template<class T>
+T *GetPoisonedO(int i, u32 origin, T val = 0) {
+ T *res = (T*)&poisoned_array[i];
+ *res = val;
+ __msan_poison(&poisoned_array[i], sizeof(T));
+ __msan_set_origin(&poisoned_array[i], sizeof(T), origin);
+ return res;
+}
+
+// This function returns its parameter but in such a way that compiler
+// can not prove it.
+template<class T>
+NOINLINE
+static T Ident(T t) {
+ volatile T ret = t;
+ return ret;
+}
+
+static bool TrackingOrigins() {
+ S8 x;
+ __msan_set_origin(&x, sizeof(x), 0x1234);
+ u32 origin = __msan_get_origin(&x);
+ __msan_set_origin(&x, sizeof(x), 0);
+ return origin == 0x1234;
+}
+
+template<class T> NOINLINE T ReturnPoisoned() { return *GetPoisoned<T>(); }
+
+static volatile S1 v_s1;
+static volatile S2 v_s2;
+static volatile S4 v_s4;
+static volatile S8 v_s8;
+static volatile U1 v_u1;
+static volatile U2 v_u2;
+static volatile U4 v_u4;
+static volatile U8 v_u8;
+static void* volatile v_p;
+static volatile double v_d;
+static volatile int g_one = 1;
+static volatile int g_zero = 0;
+static volatile int g_0 = 0;
+static volatile int g_1 = 1;
+
+#if MSAN_HAS_M128
+static volatile __m128i v_m128;
+#endif
+
+S4 a_s4[100];
+S8 a_s8[100];
+
+TEST(MemorySanitizer, NegativeTest1) {
+ S4 *x = GetPoisoned<S4>();
+ if (g_one)
+ *x = 0;
+ v_s4 = *x;
+}
+
+TEST(MemorySanitizer, PositiveTest1) {
+ // Load to store.
+ EXPECT_POISONED(v_s1 = *GetPoisoned<S1>());
+ EXPECT_POISONED(v_s2 = *GetPoisoned<S2>());
+ EXPECT_POISONED(v_s4 = *GetPoisoned<S4>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<S8>());
+
+ // S->S conversions.
+ EXPECT_POISONED(v_s2 = *GetPoisoned<S1>());
+ EXPECT_POISONED(v_s4 = *GetPoisoned<S1>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<S1>());
+
+ EXPECT_POISONED(v_s1 = *GetPoisoned<S2>());
+ EXPECT_POISONED(v_s4 = *GetPoisoned<S2>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<S2>());
+
+ EXPECT_POISONED(v_s1 = *GetPoisoned<S4>());
+ EXPECT_POISONED(v_s2 = *GetPoisoned<S4>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<S4>());
+
+ EXPECT_POISONED(v_s1 = *GetPoisoned<S8>());
+ EXPECT_POISONED(v_s2 = *GetPoisoned<S8>());
+ EXPECT_POISONED(v_s4 = *GetPoisoned<S8>());
+
+ // ZExt
+ EXPECT_POISONED(v_s2 = *GetPoisoned<U1>());
+ EXPECT_POISONED(v_s4 = *GetPoisoned<U1>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<U1>());
+ EXPECT_POISONED(v_s4 = *GetPoisoned<U2>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<U2>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<U4>());
+
+ // Unary ops.
+ EXPECT_POISONED(v_s4 = - *GetPoisoned<S4>());
+
+ EXPECT_POISONED(a_s4[g_zero] = 100 / *GetPoisoned<S4>(0, 1));
+
+
+ a_s4[g_zero] = 1 - *GetPoisoned<S4>();
+ a_s4[g_zero] = 1 + *GetPoisoned<S4>();
+}
+
+TEST(MemorySanitizer, Phi1) {
+ S4 c;
+ if (g_one) {
+ c = *GetPoisoned<S4>();
+ } else {
+ __msan_break_optimization(0);
+ c = 0;
+ }
+ EXPECT_POISONED(v_s4 = c);
+}
+
+TEST(MemorySanitizer, Phi2) {
+ S4 i = *GetPoisoned<S4>();
+ S4 n = g_one;
+ EXPECT_POISONED(for (; i < g_one; i++););
+ EXPECT_POISONED(v_s4 = i);
+}
+
+NOINLINE void Arg1ExpectUMR(S4 a1) { EXPECT_POISONED(v_s4 = a1); }
+NOINLINE void Arg2ExpectUMR(S4 a1, S4 a2) { EXPECT_POISONED(v_s4 = a2); }
+NOINLINE void Arg3ExpectUMR(S1 a1, S4 a2, S8 a3) { EXPECT_POISONED(v_s8 = a3); }
+
+TEST(MemorySanitizer, ArgTest) {
+ Arg1ExpectUMR(*GetPoisoned<S4>());
+ Arg2ExpectUMR(0, *GetPoisoned<S4>());
+ Arg3ExpectUMR(0, 1, *GetPoisoned<S8>());
+}
+
+
+TEST(MemorySanitizer, CallAndRet) {
+ if (!__msan_has_dynamic_component()) return;
+ ReturnPoisoned<S1>();
+ ReturnPoisoned<S2>();
+ ReturnPoisoned<S4>();
+ ReturnPoisoned<S8>();
+
+ EXPECT_POISONED(v_s1 = ReturnPoisoned<S1>());
+ EXPECT_POISONED(v_s2 = ReturnPoisoned<S2>());
+ EXPECT_POISONED(v_s4 = ReturnPoisoned<S4>());
+ EXPECT_POISONED(v_s8 = ReturnPoisoned<S8>());
+}
+
+// malloc() in the following test may be optimized to produce a compile-time
+// undef value. Check that we trap on the volatile assignment anyway.
+TEST(MemorySanitizer, DISABLED_MallocNoIdent) {
+ S4 *x = (int*)malloc(sizeof(S4));
+ EXPECT_POISONED(v_s4 = *x);
+ free(x);
+}
+
+TEST(MemorySanitizer, Malloc) {
+ S4 *x = (int*)Ident(malloc(sizeof(S4)));
+ EXPECT_POISONED(v_s4 = *x);
+ free(x);
+}
+
+TEST(MemorySanitizer, Realloc) {
+ S4 *x = (int*)Ident(realloc(0, sizeof(S4)));
+ EXPECT_POISONED(v_s4 = x[0]);
+ x[0] = 1;
+ x = (int*)Ident(realloc(x, 2 * sizeof(S4)));
+ v_s4 = x[0]; // Ok, was inited before.
+ EXPECT_POISONED(v_s4 = x[1]);
+ x = (int*)Ident(realloc(x, 3 * sizeof(S4)));
+ v_s4 = x[0]; // Ok, was inited before.
+ EXPECT_POISONED(v_s4 = x[2]);
+ EXPECT_POISONED(v_s4 = x[1]);
+ x[2] = 1; // Init this here. Check that after realloc it is poisoned again.
+ x = (int*)Ident(realloc(x, 2 * sizeof(S4)));
+ v_s4 = x[0]; // Ok, was inited before.
+ EXPECT_POISONED(v_s4 = x[1]);
+ x = (int*)Ident(realloc(x, 3 * sizeof(S4)));
+ EXPECT_POISONED(v_s4 = x[1]);
+ EXPECT_POISONED(v_s4 = x[2]);
+ free(x);
+}
+
+TEST(MemorySanitizer, Calloc) {
+ S4 *x = (int*)Ident(calloc(1, sizeof(S4)));
+ v_s4 = *x; // Should not be poisoned.
+ // EXPECT_EQ(0, *x);
+ free(x);
+}
+
+TEST(MemorySanitizer, AndOr) {
+ U4 *p = GetPoisoned<U4>();
+ // We poison two bytes in the midle of a 4-byte word to make the test
+ // correct regardless of endianness.
+ ((U1*)p)[1] = 0;
+ ((U1*)p)[2] = 0xff;
+ v_u4 = *p & 0x00ffff00;
+ v_u4 = *p & 0x00ff0000;
+ v_u4 = *p & 0x0000ff00;
+ EXPECT_POISONED(v_u4 = *p & 0xff000000);
+ EXPECT_POISONED(v_u4 = *p & 0x000000ff);
+ EXPECT_POISONED(v_u4 = *p & 0x0000ffff);
+ EXPECT_POISONED(v_u4 = *p & 0xffff0000);
+
+ v_u4 = *p | 0xff0000ff;
+ v_u4 = *p | 0xff00ffff;
+ v_u4 = *p | 0xffff00ff;
+ EXPECT_POISONED(v_u4 = *p | 0xff000000);
+ EXPECT_POISONED(v_u4 = *p | 0x000000ff);
+ EXPECT_POISONED(v_u4 = *p | 0x0000ffff);
+ EXPECT_POISONED(v_u4 = *p | 0xffff0000);
+
+ EXPECT_POISONED(v_u4 = *GetPoisoned<bool>() & *GetPoisoned<bool>());
+}
+
+template<class T>
+static void testNot(T value, T shadow) {
+ __msan_partial_poison(&value, &shadow, sizeof(T));
+ volatile bool v_T = !value;
+}
+
+TEST(MemorySanitizer, Not) {
+ testNot<U4>(0x0, 0x0);
+ testNot<U4>(0xFFFFFFFF, 0x0);
+ EXPECT_POISONED(testNot<U4>(0xFFFFFFFF, 0xFFFFFFFF));
+ testNot<U4>(0xFF000000, 0x0FFFFFFF);
+ testNot<U4>(0xFF000000, 0x00FFFFFF);
+ testNot<U4>(0xFF000000, 0x0000FFFF);
+ testNot<U4>(0xFF000000, 0x00000000);
+ EXPECT_POISONED(testNot<U4>(0xFF000000, 0xFF000000));
+ testNot<U4>(0xFF800000, 0xFF000000);
+ EXPECT_POISONED(testNot<U4>(0x00008000, 0x00008000));
+
+ testNot<U1>(0x0, 0x0);
+ testNot<U1>(0xFF, 0xFE);
+ testNot<U1>(0xFF, 0x0);
+ EXPECT_POISONED(testNot<U1>(0xFF, 0xFF));
+
+ EXPECT_POISONED(testNot<void*>((void*)0xFFFFFF, (void*)(-1)));
+ testNot<void*>((void*)0xFFFFFF, (void*)(-2));
+}
+
+TEST(MemorySanitizer, Shift) {
+ U4 *up = GetPoisoned<U4>();
+ ((U1*)up)[0] = 0;
+ ((U1*)up)[3] = 0xff;
+ v_u4 = *up >> 30;
+ v_u4 = *up >> 24;
+ EXPECT_POISONED(v_u4 = *up >> 23);
+ EXPECT_POISONED(v_u4 = *up >> 10);
+
+ v_u4 = *up << 30;
+ v_u4 = *up << 24;
+ EXPECT_POISONED(v_u4 = *up << 23);
+ EXPECT_POISONED(v_u4 = *up << 10);
+
+ S4 *sp = (S4*)up;
+ v_s4 = *sp >> 30;
+ v_s4 = *sp >> 24;
+ EXPECT_POISONED(v_s4 = *sp >> 23);
+ EXPECT_POISONED(v_s4 = *sp >> 10);
+
+ sp = GetPoisoned<S4>();
+ ((S1*)sp)[1] = 0;
+ ((S1*)sp)[2] = 0;
+ EXPECT_POISONED(v_s4 = *sp >> 31);
+
+ v_s4 = 100;
+ EXPECT_POISONED(v_s4 = v_s4 >> *GetPoisoned<S4>());
+ v_u4 = 100;
+ EXPECT_POISONED(v_u4 = v_u4 >> *GetPoisoned<S4>());
+ v_u4 = 100;
+ EXPECT_POISONED(v_u4 = v_u4 << *GetPoisoned<S4>());
+}
+
+NOINLINE static int GetPoisonedZero() {
+ int *zero = new int;
+ *zero = 0;
+ __msan_poison(zero, sizeof(*zero));
+ int res = *zero;
+ delete zero;
+ return res;
+}
+
+TEST(MemorySanitizer, LoadFromDirtyAddress) {
+ int *a = new int;
+ *a = 0;
+ EXPECT_POISONED(__msan_break_optimization((void*)(U8)a[GetPoisonedZero()]));
+ delete a;
+}
+
+TEST(MemorySanitizer, StoreToDirtyAddress) {
+ int *a = new int;
+ EXPECT_POISONED(a[GetPoisonedZero()] = 0);
+ __msan_break_optimization(a);
+ delete a;
+}
+
+
+NOINLINE void StackTestFunc() {
+ S4 p4;
+ S4 ok4 = 1;
+ S2 p2;
+ S2 ok2 = 1;
+ S1 p1;
+ S1 ok1 = 1;
+ __msan_break_optimization(&p4);
+ __msan_break_optimization(&ok4);
+ __msan_break_optimization(&p2);
+ __msan_break_optimization(&ok2);
+ __msan_break_optimization(&p1);
+ __msan_break_optimization(&ok1);
+
+ EXPECT_POISONED(v_s4 = p4);
+ EXPECT_POISONED(v_s2 = p2);
+ EXPECT_POISONED(v_s1 = p1);
+ v_s1 = ok1;
+ v_s2 = ok2;
+ v_s4 = ok4;
+}
+
+TEST(MemorySanitizer, StackTest) {
+ StackTestFunc();
+}
+
+NOINLINE void StackStressFunc() {
+ int foo[10000];
+ __msan_break_optimization(foo);
+}
+
+TEST(MemorySanitizer, DISABLED_StackStressTest) {
+ for (int i = 0; i < 1000000; i++)
+ StackStressFunc();
+}
+
+template<class T>
+void TestFloatingPoint() {
+ static volatile T v;
+ static T g[100];
+ __msan_break_optimization(&g);
+ T *x = GetPoisoned<T>();
+ T *y = GetPoisoned<T>(1);
+ EXPECT_POISONED(v = *x);
+ EXPECT_POISONED(v_s8 = *x);
+ EXPECT_POISONED(v_s4 = *x);
+ g[0] = *x;
+ g[1] = *x + *y;
+ g[2] = *x - *y;
+ g[3] = *x * *y;
+}
+
+TEST(MemorySanitizer, FloatingPointTest) {
+ TestFloatingPoint<float>();
+ TestFloatingPoint<double>();
+}
+
+TEST(MemorySanitizer, DynMem) {
+ S4 x = 0;
+ S4 *y = GetPoisoned<S4>();
+ memcpy(y, &x, g_one * sizeof(S4));
+ v_s4 = *y;
+}
+
+static char *DynRetTestStr;
+
+TEST(MemorySanitizer, DynRet) {
+ if (!__msan_has_dynamic_component()) return;
+ ReturnPoisoned<S8>();
+ v_s4 = clearenv();
+}
+
+
+TEST(MemorySanitizer, DynRet1) {
+ if (!__msan_has_dynamic_component()) return;
+ ReturnPoisoned<S8>();
+}
+
+struct LargeStruct {
+ S4 x[10];
+};
+
+NOINLINE
+LargeStruct LargeRetTest() {
+ LargeStruct res;
+ res.x[0] = *GetPoisoned<S4>();
+ res.x[1] = *GetPoisoned<S4>();
+ res.x[2] = *GetPoisoned<S4>();
+ res.x[3] = *GetPoisoned<S4>();
+ res.x[4] = *GetPoisoned<S4>();
+ res.x[5] = *GetPoisoned<S4>();
+ res.x[6] = *GetPoisoned<S4>();
+ res.x[7] = *GetPoisoned<S4>();
+ res.x[8] = *GetPoisoned<S4>();
+ res.x[9] = *GetPoisoned<S4>();
+ return res;
+}
+
+TEST(MemorySanitizer, LargeRet) {
+ LargeStruct a = LargeRetTest();
+ EXPECT_POISONED(v_s4 = a.x[0]);
+ EXPECT_POISONED(v_s4 = a.x[9]);
+}
+
+TEST(MemorySanitizer, fread) {
+ char *x = new char[32];
+ FILE *f = fopen("/proc/self/stat", "r");
+ assert(f);
+ fread(x, 1, 32, f);
+ v_s1 = x[0];
+ v_s1 = x[16];
+ v_s1 = x[31];
+ fclose(f);
+ delete x;
+}
+
+TEST(MemorySanitizer, read) {
+ char *x = new char[32];
+ int fd = open("/proc/self/stat", O_RDONLY);
+ assert(fd > 0);
+ int sz = read(fd, x, 32);
+ assert(sz == 32);
+ v_s1 = x[0];
+ v_s1 = x[16];
+ v_s1 = x[31];
+ close(fd);
+ delete x;
+}
+
+TEST(MemorySanitizer, pread) {
+ char *x = new char[32];
+ int fd = open("/proc/self/stat", O_RDONLY);
+ assert(fd > 0);
+ int sz = pread(fd, x, 32, 0);
+ assert(sz == 32);
+ v_s1 = x[0];
+ v_s1 = x[16];
+ v_s1 = x[31];
+ close(fd);
+ delete x;
+}
+
+// FIXME: fails now.
+TEST(MemorySanitizer, DISABLED_ioctl) {
+ struct winsize ws;
+ EXPECT_EQ(ioctl(2, TIOCGWINSZ, &ws), 0);
+ v_s4 = ws.ws_col;
+}
+
+TEST(MemorySanitizer, readlink) {
+ char *x = new char[1000];
+ readlink("/proc/self/exe", x, 1000);
+ v_s1 = x[0];
+ delete [] x;
+}
+
+
+TEST(MemorySanitizer, stat) {
+ struct stat* st = new struct stat;
+ int res = stat("/proc/self/stat", st);
+ assert(!res);
+ v_u8 = st->st_dev;
+ v_u8 = st->st_mode;
+ v_u8 = st->st_size;
+}
+
+TEST(MemorySanitizer, statfs) {
+ struct statfs* st = new struct statfs;
+ int res = statfs("/", st);
+ assert(!res);
+ v_u8 = st->f_type;
+ v_u8 = st->f_bfree;
+ v_u8 = st->f_namelen;
+}
+
+TEST(MemorySanitizer, pipe) {
+ int* pipefd = new int[2];
+ int res = pipe(pipefd);
+ assert(!res);
+ v_u8 = pipefd[0];
+ v_u8 = pipefd[1];
+ close(pipefd[0]);
+ close(pipefd[1]);
+}
+
+TEST(MemorySanitizer, getcwd) {
+ char path[PATH_MAX + 1];
+ char* res = getcwd(path, sizeof(path));
+ assert(res);
+ v_s1 = path[0];
+}
+
+TEST(MemorySanitizer, realpath) {
+ const char* relpath = ".";
+ char path[PATH_MAX + 1];
+ char* res = realpath(relpath, path);
+ assert(res);
+ v_s1 = path[0];
+}
+
+TEST(MemorySanitizer, memcpy) {
+ char* x = new char[2];
+ char* y = new char[2];
+ x[0] = 1;
+ x[1] = *GetPoisoned<char>();
+ memcpy(y, x, 2);
+ v_s4 = y[0];
+ EXPECT_POISONED(v_s4 = y[1]);
+}
+
+TEST(MemorySanitizer, memmove) {
+ char* x = new char[2];
+ char* y = new char[2];
+ x[0] = 1;
+ x[1] = *GetPoisoned<char>();
+ memmove(y, x, 2);
+ v_s4 = y[0];
+ EXPECT_POISONED(v_s4 = y[1]);
+}
+
+TEST(MemorySanitizer, strdup) {
+ char *x = strdup("zzz");
+ v_s1 = *x;
+ free(x);
+}
+
+template<class T, int size>
+void TestOverlapMemmove() {
+ T *x = new T[size];
+ assert(size >= 3);
+ x[2] = 0;
+ memmove(x, x + 1, (size - 1) * sizeof(T));
+ v_s8 = x[1];
+ if (!__msan_has_dynamic_component()) {
+ // FIXME: under DR we will lose this information
+ // because accesses in memmove will unpoisin the shadow.
+ // We need to use our own memove implementation instead of libc's.
+ EXPECT_POISONED(v_s8 = x[0]);
+ EXPECT_POISONED(v_s8 = x[2]);
+ }
+ delete [] x;
+}
+
+TEST(MemorySanitizer, overlap_memmove) {
+ TestOverlapMemmove<U1, 10>();
+ TestOverlapMemmove<U1, 1000>();
+ TestOverlapMemmove<U8, 4>();
+ TestOverlapMemmove<U8, 1000>();
+}
+
+TEST(MemorySanitizer, strcpy) { // NOLINT
+ char* x = new char[3];
+ char* y = new char[3];
+ x[0] = 'a';
+ x[1] = *GetPoisoned<char>(1, 1);
+ x[2] = 0;
+ strcpy(y, x); // NOLINT
+ v_s4 = y[0];
+ EXPECT_POISONED(v_s4 = y[1]);
+ v_s4 = y[2];
+}
+
+TEST(MemorySanitizer, strncpy) { // NOLINT
+ char* x = new char[3];
+ char* y = new char[3];
+ x[0] = 'a';
+ x[1] = *GetPoisoned<char>(1, 1);
+ x[2] = 0;
+ strncpy(y, x, 2); // NOLINT
+ v_s4 = y[0];
+ EXPECT_POISONED(v_s4 = y[1]);
+ EXPECT_POISONED(v_s4 = y[2]);
+}
+
+TEST(MemorySanitizer, strtol) {
+ char *e;
+ assert(1 == strtol("1", &e, 10));
+ v_s8 = (S8) e;
+}
+
+TEST(MemorySanitizer, strtoll) {
+ char *e;
+ assert(1 == strtoll("1", &e, 10));
+ v_s8 = (S8) e;
+}
+
+TEST(MemorySanitizer, strtoul) {
+ char *e;
+ assert(1 == strtoul("1", &e, 10));
+ v_s8 = (S8) e;
+}
+
+TEST(MemorySanitizer, strtoull) {
+ char *e;
+ assert(1 == strtoull("1", &e, 10));
+ v_s8 = (S8) e;
+}
+
+TEST(MemorySanitizer, strtod) {
+ char *e;
+ assert(0 != strtod("1.5", &e));
+ v_s8 = (S8) e;
+}
+
+TEST(MemorySanitizer, strtof) {
+ char *e;
+ assert(0 != strtof("1.5", &e));
+ v_s8 = (S8) e;
+}
+
+TEST(MemorySanitizer, strtold) {
+ char *e;
+ assert(0 != strtold("1.5", &e));
+ v_s8 = (S8) e;
+}
+
+TEST(MemorySanitizer, sprintf) { // NOLINT
+ char buff[10];
+ __msan_break_optimization(buff);
+ EXPECT_POISONED(v_s1 = buff[0]);
+ int res = sprintf(buff, "%d", 1234567); // NOLINT
+ assert(res == 7);
+ assert(buff[0] == '1');
+ assert(buff[1] == '2');
+ assert(buff[2] == '3');
+ assert(buff[6] == '7');
+ assert(buff[7] == 0);
+ EXPECT_POISONED(v_s1 = buff[8]);
+}
+
+TEST(MemorySanitizer, snprintf) {
+ char buff[10];
+ __msan_break_optimization(buff);
+ EXPECT_POISONED(v_s1 = buff[0]);
+ int res = snprintf(buff, sizeof(buff), "%d", 1234567);
+ assert(res == 7);
+ assert(buff[0] == '1');
+ assert(buff[1] == '2');
+ assert(buff[2] == '3');
+ assert(buff[6] == '7');
+ assert(buff[7] == 0);
+ EXPECT_POISONED(v_s1 = buff[8]);
+}
+
+TEST(MemorySanitizer, swprintf) {
+ wchar_t buff[10];
+ assert(sizeof(wchar_t) == 4);
+ __msan_break_optimization(buff);
+ EXPECT_POISONED(v_s1 = buff[0]);
+ int res = swprintf(buff, 9, L"%d", 1234567);
+ assert(res == 7);
+ assert(buff[0] == '1');
+ assert(buff[1] == '2');
+ assert(buff[2] == '3');
+ assert(buff[6] == '7');
+ assert(buff[7] == 0);
+ EXPECT_POISONED(v_s4 = buff[8]);
+}
+
+TEST(MemorySanitizer, wcstombs) {
+ const wchar_t *x = L"abc";
+ char buff[10];
+ int res = wcstombs(buff, x, 4);
+ EXPECT_EQ(res, 3);
+ EXPECT_EQ(buff[0], 'a');
+ EXPECT_EQ(buff[1], 'b');
+ EXPECT_EQ(buff[2], 'c');
+}
+
+TEST(MemorySanitizer, gettimeofday) {
+ struct timeval tv;
+ struct timezone tz;
+ __msan_break_optimization(&tv);
+ __msan_break_optimization(&tz);
+ assert(sizeof(tv) == 16);
+ assert(sizeof(tz) == 8);
+ EXPECT_POISONED(v_s8 = tv.tv_sec);
+ EXPECT_POISONED(v_s8 = tv.tv_usec);
+ EXPECT_POISONED(v_s4 = tz.tz_minuteswest);
+ EXPECT_POISONED(v_s4 = tz.tz_dsttime);
+ assert(0 == gettimeofday(&tv, &tz));
+ v_s8 = tv.tv_sec;
+ v_s8 = tv.tv_usec;
+ v_s4 = tz.tz_minuteswest;
+ v_s4 = tz.tz_dsttime;
+}
+
+TEST(MemorySanitizer, mmap) {
+ const int size = 4096;
+ void *p1, *p2;
+ p1 = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ __msan_poison(p1, size);
+ munmap(p1, size);
+ for (int i = 0; i < 1000; i++) {
+ p2 = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (p2 == p1)
+ break;
+ else
+ munmap(p2, size);
+ }
+ if (p1 == p2) {
+ v_s1 = *(char*)p2;
+ munmap(p2, size);
+ }
+}
+
+// FIXME: enable and add ecvt.
+// FIXME: check why msandr does nt handle fcvt.
+TEST(MemorySanitizer, fcvt) {
+ int a, b;
+ __msan_break_optimization(&a);
+ __msan_break_optimization(&b);
+ EXPECT_POISONED(v_s4 = a);
+ EXPECT_POISONED(v_s4 = b);
+ char *str = fcvt(12345.6789, 10, &a, &b);
+ v_s4 = a;
+ v_s4 = b;
+}
+
+TEST(MemorySanitizer, LoadUnpoisoned) {
+ S8 s = *GetPoisoned<S8>();
+ EXPECT_POISONED(v_s8 = s);
+ S8 safe = *GetPoisoned<S8>();
+ __msan_load_unpoisoned(&s, sizeof(s), &safe);
+ v_s8 = safe;
+}
+
+struct StructWithDtor {
+ ~StructWithDtor();
+};
+
+NOINLINE StructWithDtor::~StructWithDtor() {
+ __msan_break_optimization(0);
+}
+
+NOINLINE void ExpectGood(int a) { v_s4 = a; }
+NOINLINE void ExpectPoisoned(int a) {
+ EXPECT_POISONED(v_s4 = a);
+}
+
+TEST(MemorySanitizer, Invoke) {
+ StructWithDtor s; // Will cause the calls to become invokes.
+ ExpectGood(0);
+ ExpectPoisoned(*GetPoisoned<int>());
+ ExpectGood(0);
+ ExpectPoisoned(*GetPoisoned<int>());
+ EXPECT_POISONED(v_s4 = ReturnPoisoned<S4>());
+}
+
+TEST(MemorySanitizer, ptrtoint) {
+ // Test that shadow is propagated through pointer-to-integer conversion.
+ void* p = (void*)0xABCD;
+ __msan_poison(((char*)&p) + 1, sizeof(p));
+ v_u1 = (((uptr)p) & 0xFF) == 0;
+
+ void* q = (void*)0xABCD;
+ __msan_poison(&q, sizeof(q) - 1);
+ EXPECT_POISONED(v_u1 = (((uptr)q) & 0xFF) == 0);
+}
+
+static void vaargsfn2(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_d = va_arg(vl, double));
+ va_end(vl);
+}
+
+static void vaargsfn(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ // The following call will overwrite __msan_param_tls.
+ // Checks after it test that arg shadow was somehow saved across the call.
+ vaargsfn2(1, 2, 3, 4, *GetPoisoned<double>());
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgTest) {
+ int* x = GetPoisoned<int>();
+ int* y = GetPoisoned<int>(4);
+ vaargsfn(1, 13, *x, 42, *y);
+}
+
+static void vaargsfn_many(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgManyTest) {
+ int* x = GetPoisoned<int>();
+ int* y = GetPoisoned<int>(4);
+ vaargsfn_many(1, 2, *x, 3, 4, 5, 6, 7, 8, 9, *y);
+}
+
+static void vaargsfn_pass2(va_list vl) {
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+}
+
+static void vaargsfn_pass(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ vaargsfn_pass2(vl);
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgPass) {
+ int* x = GetPoisoned<int>();
+ int* y = GetPoisoned<int>(4);
+ vaargsfn_pass(1, *x, 2, 3, *y);
+}
+
+static void vaargsfn_copy2(va_list vl) {
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+}
+
+static void vaargsfn_copy(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ va_list vl2;
+ va_copy(vl2, vl);
+ vaargsfn_copy2(vl2);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgCopy) {
+ int* x = GetPoisoned<int>();
+ int* y = GetPoisoned<int>(4);
+ vaargsfn_copy(1, 2, *x, 3, *y);
+}
+
+static void vaargsfn_ptr(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_p = va_arg(vl, int*);
+ EXPECT_POISONED(v_p = va_arg(vl, int*));
+ v_p = va_arg(vl, int*);
+ EXPECT_POISONED(v_p = va_arg(vl, double*));
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgPtr) {
+ int** x = GetPoisoned<int*>();
+ double** y = GetPoisoned<double*>(8);
+ int z;
+ vaargsfn_ptr(1, &z, *x, &z, *y);
+}
+
+static void vaargsfn_overflow(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+
+ v_d = va_arg(vl, double);
+ v_d = va_arg(vl, double);
+ v_d = va_arg(vl, double);
+ EXPECT_POISONED(v_d = va_arg(vl, double));
+ v_d = va_arg(vl, double);
+ EXPECT_POISONED(v_p = va_arg(vl, int*));
+ v_d = va_arg(vl, double);
+ v_d = va_arg(vl, double);
+
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ EXPECT_POISONED(v_d = va_arg(vl, double));
+ EXPECT_POISONED(v_p = va_arg(vl, int*));
+
+ v_s4 = va_arg(vl, int);
+ v_d = va_arg(vl, double);
+ v_p = va_arg(vl, int*);
+
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ EXPECT_POISONED(v_d = va_arg(vl, double));
+ EXPECT_POISONED(v_p = va_arg(vl, int*));
+
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgOverflow) {
+ int* x = GetPoisoned<int>();
+ double* y = GetPoisoned<double>(8);
+ int** p = GetPoisoned<int*>(16);
+ int z;
+ vaargsfn_overflow(1,
+ 1, 2, *x, 4, 5, 6,
+ 1.1, 2.2, 3.3, *y, 5.5, *p, 7.7, 8.8,
+ // the following args will overflow for sure
+ *x, *y, *p,
+ 7, 9.9, &z,
+ *x, *y, *p);
+}
+
+static void vaargsfn_tlsoverwrite2(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_s4 = va_arg(vl, int);
+ va_end(vl);
+}
+
+static void vaargsfn_tlsoverwrite(int guard, ...) {
+ // This call will overwrite TLS contents unless it's backed up somewhere.
+ vaargsfn_tlsoverwrite2(2, 42);
+ va_list vl;
+ va_start(vl, guard);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgTLSOverwrite) {
+ int* x = GetPoisoned<int>();
+ vaargsfn_tlsoverwrite(1, *x);
+}
+
+struct StructByVal {
+ int a, b, c, d, e, f;
+};
+
+NOINLINE void StructByValTestFunc(struct StructByVal s) {
+ v_s4 = s.a;
+ EXPECT_POISONED(v_s4 = s.b);
+ v_s4 = s.c;
+ EXPECT_POISONED(v_s4 = s.d);
+ v_s4 = s.e;
+ EXPECT_POISONED(v_s4 = s.f);
+}
+
+NOINLINE void StructByValTestFunc1(struct StructByVal s) {
+ StructByValTestFunc(s);
+}
+
+NOINLINE void StructByValTestFunc2(int z, struct StructByVal s) {
+ StructByValTestFunc(s);
+}
+
+TEST(MemorySanitizer, StructByVal) {
+ // Large aggregates are passed as "byval" pointer argument in LLVM.
+ struct StructByVal s;
+ s.a = 1;
+ s.b = *GetPoisoned<int>();
+ s.c = 2;
+ s.d = *GetPoisoned<int>();
+ s.e = 3;
+ s.f = *GetPoisoned<int>();
+ StructByValTestFunc(s);
+ StructByValTestFunc1(s);
+ StructByValTestFunc2(0, s);
+}
+
+
+#if MSAN_HAS_M128
+NOINLINE __m128i m128Eq(__m128i *a, __m128i *b) { return *a == *b; }
+NOINLINE __m128i m128Lt(__m128i *a, __m128i *b) { return *a < *b; }
+TEST(MemorySanitizer, m128) {
+ __m128i a = _mm_set1_epi16(0x1234);
+ __m128i b = _mm_set1_epi16(0x7890);
+ v_m128 = m128Eq(&a, &b);
+ v_m128 = m128Lt(&a, &b);
+}
+// FIXME: add more tests for __m128i.
+#endif // MSAN_HAS_M128
+
+// We should not complain when copying this poisoned hole.
+struct StructWithHole {
+ U4 a;
+ // 4-byte hole.
+ U8 b;
+};
+
+NOINLINE StructWithHole ReturnStructWithHole() {
+ StructWithHole res;
+ __msan_poison(&res, sizeof(res));
+ res.a = 1;
+ res.b = 2;
+ return res;
+}
+
+TEST(MemorySanitizer, StructWithHole) {
+ StructWithHole a = ReturnStructWithHole();
+ __msan_break_optimization(&a);
+}
+
+template <class T>
+NOINLINE T ReturnStruct() {
+ T res;
+ __msan_poison(&res, sizeof(res));
+ res.a = 1;
+ return res;
+}
+
+template <class T>
+NOINLINE void TestReturnStruct() {
+ T s1 = ReturnStruct<T>();
+ v_s4 = s1.a;
+ EXPECT_POISONED(v_s4 = s1.b);
+}
+
+struct SSS1 {
+ int a, b, c;
+};
+struct SSS2 {
+ int b, a, c;
+};
+struct SSS3 {
+ int b, c, a;
+};
+struct SSS4 {
+ int c, b, a;
+};
+
+struct SSS5 {
+ int a;
+ float b;
+};
+struct SSS6 {
+ int a;
+ double b;
+};
+struct SSS7 {
+ S8 b;
+ int a;
+};
+struct SSS8 {
+ S2 b;
+ S8 a;
+};
+
+TEST(MemorySanitizer, IntStruct3) {
+ TestReturnStruct<SSS1>();
+ TestReturnStruct<SSS2>();
+ TestReturnStruct<SSS3>();
+ TestReturnStruct<SSS4>();
+ TestReturnStruct<SSS5>();
+ TestReturnStruct<SSS6>();
+ TestReturnStruct<SSS7>();
+ TestReturnStruct<SSS8>();
+}
+
+struct LongStruct {
+ U1 a1, b1;
+ U2 a2, b2;
+ U4 a4, b4;
+ U8 a8, b8;
+};
+
+NOINLINE LongStruct ReturnLongStruct1() {
+ LongStruct res;
+ __msan_poison(&res, sizeof(res));
+ res.a1 = res.a2 = res.a4 = res.a8 = 111;
+ // leaves b1, .., b8 poisoned.
+ return res;
+}
+
+NOINLINE LongStruct ReturnLongStruct2() {
+ LongStruct res;
+ __msan_poison(&res, sizeof(res));
+ res.b1 = res.b2 = res.b4 = res.b8 = 111;
+ // leaves a1, .., a8 poisoned.
+ return res;
+}
+
+TEST(MemorySanitizer, LongStruct) {
+ LongStruct s1 = ReturnLongStruct1();
+ __msan_print_shadow(&s1, sizeof(s1));
+ v_u1 = s1.a1;
+ v_u2 = s1.a2;
+ v_u4 = s1.a4;
+ v_u8 = s1.a8;
+
+ EXPECT_POISONED(v_u1 = s1.b1);
+ EXPECT_POISONED(v_u2 = s1.b2);
+ EXPECT_POISONED(v_u4 = s1.b4);
+ EXPECT_POISONED(v_u8 = s1.b8);
+
+ LongStruct s2 = ReturnLongStruct2();
+ __msan_print_shadow(&s2, sizeof(s2));
+ v_u1 = s2.b1;
+ v_u2 = s2.b2;
+ v_u4 = s2.b4;
+ v_u8 = s2.b8;
+
+ EXPECT_POISONED(v_u1 = s2.a1);
+ EXPECT_POISONED(v_u2 = s2.a2);
+ EXPECT_POISONED(v_u4 = s2.a4);
+ EXPECT_POISONED(v_u8 = s2.a8);
+}
+
+TEST(MemorySanitizer, getrlimit) {
+ struct rlimit limit;
+ __msan_poison(&limit, sizeof(limit));
+ int result = getrlimit(RLIMIT_DATA, &limit);
+ assert(result == 0);
+ volatile rlim_t t;
+ t = limit.rlim_cur;
+ t = limit.rlim_max;
+}
+
+TEST(MemorySanitizer, getrusage) {
+ struct rusage usage;
+ __msan_poison(&usage, sizeof(usage));
+ int result = getrusage(RUSAGE_SELF, &usage);
+ assert(result == 0);
+ volatile struct timeval t;
+ v_u8 = usage.ru_utime.tv_sec;
+ v_u8 = usage.ru_utime.tv_usec;
+ v_u8 = usage.ru_stime.tv_sec;
+ v_u8 = usage.ru_stime.tv_usec;
+ v_s8 = usage.ru_maxrss;
+ v_s8 = usage.ru_minflt;
+ v_s8 = usage.ru_majflt;
+ v_s8 = usage.ru_inblock;
+ v_s8 = usage.ru_oublock;
+ v_s8 = usage.ru_nvcsw;
+ v_s8 = usage.ru_nivcsw;
+}
+
+static void dladdr_testfn() {}
+
+TEST(MemorySanitizer, dladdr) {
+ Dl_info info;
+ __msan_poison(&info, sizeof(info));
+ int result = dladdr((const void*)dladdr_testfn, &info);
+ assert(result != 0);
+ v_u8 = (unsigned long)info.dli_fname;
+ if (info.dli_fname)
+ v_u8 = strlen(info.dli_fname);
+ v_u8 = (unsigned long)info.dli_fbase;
+ v_u8 = (unsigned long)info.dli_sname;
+ if (info.dli_sname)
+ v_u8 = strlen(info.dli_sname);
+ v_u8 = (unsigned long)info.dli_saddr;
+}
+
+TEST(MemorySanitizer, scanf) {
+ const char *input = "42 hello";
+ int* d = new int;
+ char* s = new char[7];
+ int res = sscanf(input, "%d %5s", d, s);
+ printf("res %d\n", res);
+ assert(res == 2);
+ v_s4 = *d;
+ v_u1 = s[0];
+ v_u1 = s[1];
+ v_u1 = s[2];
+ v_u1 = s[3];
+ v_u1 = s[4];
+ v_u1 = s[5];
+ EXPECT_POISONED(v_u1 = s[6]);
+ delete s;
+ delete d;
+}
+
+static void* SimpleThread_threadfn(void* data) {
+ return new int;
+}
+
+TEST(MemorySanitizer, SimpleThread) {
+ pthread_t t;
+ void* p;
+ int res = pthread_create(&t, NULL, SimpleThread_threadfn, NULL);
+ assert(!res);
+ res = pthread_join(t, &p);
+ assert(!res);
+ if (!__msan_has_dynamic_component()) // FIXME: intercept pthread_join (?).
+ __msan_unpoison(&p, sizeof(p));
+ delete (int*)p;
+}
+
+TEST(MemorySanitizer, uname) {
+ struct utsname u;
+ int res = uname(&u);
+ assert(!res);
+ v_u8 = strlen(u.sysname);
+ v_u8 = strlen(u.nodename);
+ v_u8 = strlen(u.release);
+ v_u8 = strlen(u.version);
+ v_u8 = strlen(u.machine);
+}
+
+template<class T>
+static void testSlt(T value, T shadow) {
+ __msan_partial_poison(&value, &shadow, sizeof(T));
+ volatile bool zzz = true;
+ // This "|| zzz" trick somehow makes LLVM emit "icmp slt" instead of
+ // a shift-and-trunc to get at the highest bit.
+ volatile bool v_T = value < 0 || zzz;
+}
+
+TEST(MemorySanitizer, SignedCompareWithZero) {
+ testSlt<S4>(0xF, 0xF);
+ testSlt<S4>(0xF, 0xFF);
+ testSlt<S4>(0xF, 0xFFFFFF);
+ testSlt<S4>(0xF, 0x7FFFFFF);
+ EXPECT_POISONED(testSlt<S4>(0xF, 0x80FFFFFF));
+ EXPECT_POISONED(testSlt<S4>(0xF, 0xFFFFFFFF));
+}
+
+extern "C" {
+NOINLINE void ZZZZZZZZZZZZZZ() {
+ __msan_break_optimization(0);
+
+ // v_s1 = ReturnPoisoned<S1>();
+ // a_s8[g_zero] = *GetPoisoned<S8>() - 1;
+ // v_s4 = a_s4[g_zero];
+ __msan_break_optimization(0);
+}
+}
+
+TEST(MemorySanitizer, ZZZTest) {
+ ZZZZZZZZZZZZZZ();
+}
+
+TEST(MemorySanitizerDr, StoreInDSOTest) {
+ if (!__msan_has_dynamic_component()) return;
+ char* s = new char[10];
+ dso_memfill(s, 9);
+ v_s1 = s[5];
+ EXPECT_POISONED(v_s1 = s[9]);
+}
+
+int return_poisoned_int() {
+ return ReturnPoisoned<U8>();
+}
+
+TEST(MemorySanitizerDr, ReturnFromDSOTest) {
+ if (!__msan_has_dynamic_component()) return;
+ v_u8 = dso_callfn(return_poisoned_int);
+}
+
+NOINLINE int TrashParamTLS(long long x, long long y, long long z) { //NOLINT
+ EXPECT_POISONED(v_s8 = x);
+ EXPECT_POISONED(v_s8 = y);
+ EXPECT_POISONED(v_s8 = z);
+ return 0;
+}
+
+static int CheckParamTLS(long long x, long long y, long long z) { //NOLINT
+ v_s8 = x;
+ v_s8 = y;
+ v_s8 = z;
+ return 0;
+}
+
+TEST(MemorySanitizerDr, CallFromDSOTest) {
+ if (!__msan_has_dynamic_component()) return;
+ S8* x = GetPoisoned<S8>();
+ S8* y = GetPoisoned<S8>();
+ S8* z = GetPoisoned<S8>();
+ v_s4 = TrashParamTLS(*x, *y, *z);
+ v_u8 = dso_callfn1(CheckParamTLS);
+}
+
+static void StackStoreInDSOFn(int* x, int* y) {
+ v_s4 = *x;
+ v_s4 = *y;
+}
+
+TEST(MemorySanitizerDr, StackStoreInDSOTest) {
+ if (!__msan_has_dynamic_component()) return;
+ dso_stack_store(StackStoreInDSOFn, 1);
+}
+
+TEST(MemorySanitizerOrigins, SetGet) {
+ EXPECT_EQ(TrackingOrigins(), __msan_get_track_origins());
+ if (!TrackingOrigins()) return;
+ int x;
+ __msan_set_origin(&x, sizeof(x), 1234);
+ EXPECT_EQ(1234, __msan_get_origin(&x));
+ __msan_set_origin(&x, sizeof(x), 5678);
+ EXPECT_EQ(5678, __msan_get_origin(&x));
+ __msan_set_origin(&x, sizeof(x), 0);
+ EXPECT_EQ(0, __msan_get_origin(&x));
+}
+
+namespace {
+struct S {
+ U4 dummy;
+ U2 a;
+ U2 b;
+};
+
+// http://code.google.com/p/memory-sanitizer/issues/detail?id=6
+TEST(MemorySanitizerOrigins, DISABLED_InitializedStoreDoesNotChangeOrigin) {
+ if (!TrackingOrigins()) return;
+
+ S s;
+ u32 origin = rand(); // NOLINT
+ s.a = *GetPoisonedO<U2>(0, origin);
+ EXPECT_EQ(origin, __msan_get_origin(&s.a));
+ EXPECT_EQ(origin, __msan_get_origin(&s.b));
+
+ s.b = 42;
+ EXPECT_EQ(origin, __msan_get_origin(&s.a));
+ EXPECT_EQ(origin, __msan_get_origin(&s.b));
+}
+} // namespace
+
+template<class T, class BinaryOp>
+INLINE
+void BinaryOpOriginTest(BinaryOp op) {
+ u32 ox = rand(); //NOLINT
+ u32 oy = rand(); //NOLINT
+ T *x = GetPoisonedO<T>(0, ox, 0);
+ T *y = GetPoisonedO<T>(1, oy, 0);
+ T *z = GetPoisonedO<T>(2, 0, 0);
+
+ *z = op(*x, *y);
+ u32 origin = __msan_get_origin(z);
+ EXPECT_POISONED_O(v_s8 = *z, origin);
+ EXPECT_EQ(true, origin == ox || origin == oy);
+
+ // y is poisoned, x is not.
+ *x = 10101;
+ *y = *GetPoisonedO<T>(1, oy);
+ __msan_break_optimization(x);
+ __msan_set_origin(z, sizeof(*z), 0);
+ *z = op(*x, *y);
+ EXPECT_POISONED_O(v_s8 = *z, oy);
+ EXPECT_EQ(__msan_get_origin(z), oy);
+
+ // x is poisoned, y is not.
+ *x = *GetPoisonedO<T>(0, ox);
+ *y = 10101010;
+ __msan_break_optimization(y);
+ __msan_set_origin(z, sizeof(*z), 0);
+ *z = op(*x, *y);
+ EXPECT_POISONED_O(v_s8 = *z, ox);
+ EXPECT_EQ(__msan_get_origin(z), ox);
+}
+
+template<class T> INLINE T XOR(const T &a, const T&b) { return a ^ b; }
+template<class T> INLINE T ADD(const T &a, const T&b) { return a + b; }
+template<class T> INLINE T SUB(const T &a, const T&b) { return a - b; }
+template<class T> INLINE T MUL(const T &a, const T&b) { return a * b; }
+template<class T> INLINE T AND(const T &a, const T&b) { return a & b; }
+template<class T> INLINE T OR (const T &a, const T&b) { return a | b; }
+
+TEST(MemorySanitizerOrigins, BinaryOp) {
+ if (!TrackingOrigins()) return;
+ BinaryOpOriginTest<S8>(XOR<S8>);
+ BinaryOpOriginTest<U8>(ADD<U8>);
+ BinaryOpOriginTest<S4>(SUB<S4>);
+ BinaryOpOriginTest<S4>(MUL<S4>);
+ BinaryOpOriginTest<U4>(OR<U4>);
+ BinaryOpOriginTest<U4>(AND<U4>);
+ BinaryOpOriginTest<double>(ADD<U4>);
+ BinaryOpOriginTest<float>(ADD<S4>);
+ BinaryOpOriginTest<double>(ADD<double>);
+ BinaryOpOriginTest<float>(ADD<double>);
+}
+
+TEST(MemorySanitizerOrigins, Unary) {
+ if (!TrackingOrigins()) return;
+ EXPECT_POISONED_O(v_s8 = *GetPoisonedO<S8>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s4 = *GetPoisonedO<S8>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s2 = *GetPoisonedO<S8>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s1 = *GetPoisonedO<S8>(0, __LINE__), __LINE__);
+
+ EXPECT_POISONED_O(v_s8 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s4 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s2 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s1 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+
+ EXPECT_POISONED_O(v_s8 = *GetPoisonedO<U4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s4 = *GetPoisonedO<U4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s2 = *GetPoisonedO<U4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s1 = *GetPoisonedO<U4>(0, __LINE__), __LINE__);
+
+ EXPECT_POISONED_O(v_u8 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_u4 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_u2 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_u1 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+
+ EXPECT_POISONED_O(v_p = (void*)*GetPoisonedO<S8>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_u8 = (U8)*GetPoisonedO<void*>(0, __LINE__), __LINE__);
+}
+
+TEST(MemorySanitizerOrigins, EQ) {
+ if (!TrackingOrigins()) return;
+ EXPECT_POISONED_O(v_u1 = *GetPoisonedO<S4>(0, __LINE__) <= 11, __LINE__);
+ EXPECT_POISONED_O(v_u1 = *GetPoisonedO<S4>(0, __LINE__) == 11, __LINE__);
+ EXPECT_POISONED_O(v_u1 = *GetPoisonedO<float>(0, __LINE__) == 1.1, __LINE__);
+}
+
+TEST(MemorySanitizerOrigins, DIV) {
+ if (!TrackingOrigins()) return;
+ EXPECT_POISONED_O(v_u8 = *GetPoisonedO<U8>(0, __LINE__) / 100, __LINE__);
+ EXPECT_POISONED_O(v_s4 = 100 / *GetPoisonedO<S4>(0, __LINE__, 1), __LINE__);
+}
+
+TEST(MemorySanitizerOrigins, SHIFT) {
+ if (!TrackingOrigins()) return;
+ EXPECT_POISONED_O(v_u8 = *GetPoisonedO<U8>(0, __LINE__) >> 10, __LINE__);
+ EXPECT_POISONED_O(v_s8 = *GetPoisonedO<S8>(0, __LINE__) >> 10, __LINE__);
+ EXPECT_POISONED_O(v_s8 = *GetPoisonedO<S8>(0, __LINE__) << 10, __LINE__);
+ EXPECT_POISONED_O(v_u8 = 10U << *GetPoisonedO<U8>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s8 = -10 >> *GetPoisonedO<S8>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s8 = -10 << *GetPoisonedO<S8>(0, __LINE__), __LINE__);
+}
+
+template<class T, int N>
+void MemCpyTest() {
+ int ox = __LINE__;
+ T *x = new T[N];
+ T *y = new T[N];
+ T *z = new T[N];
+ __msan_poison(x, N * sizeof(T));
+ __msan_set_origin(x, N * sizeof(T), ox);
+ __msan_set_origin(y, N * sizeof(T), 777777);
+ __msan_set_origin(z, N * sizeof(T), 888888);
+ v_p = x;
+ memcpy(y, v_p, N * sizeof(T));
+ EXPECT_POISONED_O(v_s1 = y[0], ox);
+ EXPECT_POISONED_O(v_s1 = y[N/2], ox);
+ EXPECT_POISONED_O(v_s1 = y[N-1], ox);
+ v_p = x;
+ memmove(z, v_p, N * sizeof(T));
+ EXPECT_POISONED_O(v_s1 = z[0], ox);
+ EXPECT_POISONED_O(v_s1 = z[N/2], ox);
+ EXPECT_POISONED_O(v_s1 = z[N-1], ox);
+}
+
+TEST(MemorySanitizerOrigins, LargeMemCpy) {
+ if (!TrackingOrigins()) return;
+ MemCpyTest<U1, 10000>();
+ MemCpyTest<U8, 10000>();
+}
+
+TEST(MemorySanitizerOrigins, SmallMemCpy) {
+ if (!TrackingOrigins()) return;
+ MemCpyTest<U8, 1>();
+ MemCpyTest<U8, 2>();
+ MemCpyTest<U8, 3>();
+}
+
+TEST(MemorySanitizerOrigins, Select) {
+ if (!TrackingOrigins()) return;
+ v_s8 = g_one ? 1 : *GetPoisonedO<S4>(0, __LINE__);
+ EXPECT_POISONED_O(v_s8 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ S4 x;
+ __msan_break_optimization(&x);
+ x = g_1 ? *GetPoisonedO<S4>(0, __LINE__) : 0;
+
+ EXPECT_POISONED_O(v_s8 = g_1 ? *GetPoisonedO<S4>(0, __LINE__) : 1, __LINE__);
+ EXPECT_POISONED_O(v_s8 = g_0 ? 1 : *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+}
+
+extern "C"
+NOINLINE void AllocaTOTest() {
+ int ar[100];
+ __msan_break_optimization(ar);
+ v_s8 = ar[10];
+ // fprintf(stderr, "Descr: %s\n",
+ // __msan_get_origin_descr_if_stack(__msan_get_origin_tls()));
+}
+
+TEST(MemorySanitizerOrigins, Alloca) {
+ if (!TrackingOrigins()) return;
+ EXPECT_POISONED_S(AllocaTOTest(), "ar@AllocaTOTest");
+ EXPECT_POISONED_S(AllocaTOTest(), "ar@AllocaTOTest");
+ EXPECT_POISONED_S(AllocaTOTest(), "ar@AllocaTOTest");
+ EXPECT_POISONED_S(AllocaTOTest(), "ar@AllocaTOTest");
+}
+
+// FIXME: replace with a lit-like test.
+TEST(MemorySanitizerOrigins, DISABLED_AllocaDeath) {
+ if (!TrackingOrigins()) return;
+ EXPECT_DEATH(AllocaTOTest(), "ORIGIN: stack allocation: ar@AllocaTOTest");
+}
+
+NOINLINE int RetvalOriginTest(u32 origin) {
+ int *a = new int;
+ __msan_break_optimization(a);
+ __msan_set_origin(a, sizeof(*a), origin);
+ int res = *a;
+ delete a;
+ return res;
+}
+
+TEST(MemorySanitizerOrigins, Retval) {
+ if (!TrackingOrigins()) return;
+ EXPECT_POISONED_O(v_s4 = RetvalOriginTest(__LINE__), __LINE__);
+}
+
+NOINLINE void ParamOriginTest(int param, u32 origin) {
+ EXPECT_POISONED_O(v_s4 = param, origin);
+}
+
+TEST(MemorySanitizerOrigins, Param) {
+ if (!TrackingOrigins()) return;
+ int *a = new int;
+ u32 origin = __LINE__;
+ __msan_break_optimization(a);
+ __msan_set_origin(a, sizeof(*a), origin);
+ ParamOriginTest(*a, origin);
+ delete a;
+}
+
+TEST(MemorySanitizerOrigins, Invoke) {
+ if (!TrackingOrigins()) return;
+ StructWithDtor s; // Will cause the calls to become invokes.
+ EXPECT_POISONED_O(v_s4 = RetvalOriginTest(__LINE__), __LINE__);
+}
+
+TEST(MemorySanitizerOrigins, strlen) {
+ S8 alignment;
+ __msan_break_optimization(&alignment);
+ char x[4] = {'a', 'b', 0, 0};
+ __msan_poison(&x[2], 1);
+ u32 origin = __LINE__;
+ __msan_set_origin(x, sizeof(x), origin);
+ EXPECT_POISONED_O(v_s4 = strlen(x), origin);
+}
+
+TEST(MemorySanitizerOrigins, wcslen) {
+ wchar_t w[3] = {'a', 'b', 0};
+ u32 origin = __LINE__;
+ __msan_set_origin(w, sizeof(w), origin);
+ __msan_poison(&w[2], sizeof(wchar_t));
+ EXPECT_POISONED_O(v_s4 = wcslen(w), origin);
+}
+
+#if MSAN_HAS_M128
+TEST(MemorySanitizerOrigins, StoreIntrinsic) {
+ __m128 x, y;
+ u32 origin = __LINE__;
+ __msan_set_origin(&x, sizeof(x), origin);
+ __msan_poison(&x, sizeof(x));
+ __builtin_ia32_storeups((float*)&y, x);
+ EXPECT_POISONED_O(v_m128 = y, origin);
+}
+#endif
+
+NOINLINE void RecursiveMalloc(int depth) {
+ static int count;
+ count++;
+ if ((count % (1024 * 1024)) == 0)
+ printf("RecursiveMalloc: %d\n", count);
+ int *x1 = new int;
+ int *x2 = new int;
+ __msan_break_optimization(x1);
+ __msan_break_optimization(x2);
+ if (depth > 0) {
+ RecursiveMalloc(depth-1);
+ RecursiveMalloc(depth-1);
+ }
+ delete x1;
+ delete x2;
+}
+
+TEST(MemorySanitizerStress, DISABLED_MallocStackTrace) {
+ RecursiveMalloc(22);
+}
+
+int main(int argc, char **argv) {
+ __msan_set_poison_in_malloc(1);
+ testing::InitGoogleTest(&argc, argv);
+ int res = RUN_ALL_TESTS();
+ return res;
+}
diff --git a/lib/msan/tests/msandr_test_so.cc b/lib/msan/tests/msandr_test_so.cc
new file mode 100644
index 000000000000..fddd8ded8964
--- /dev/null
+++ b/lib/msan/tests/msandr_test_so.cc
@@ -0,0 +1,36 @@
+//===-- msandr_test_so.cc ------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// MemorySanitizer unit tests.
+//===----------------------------------------------------------------------===//
+
+#include "msandr_test_so.h"
+
+void dso_memfill(char* s, unsigned n) {
+ for (unsigned i = 0; i < n; ++i)
+ s[i] = i;
+}
+
+int dso_callfn(int (*fn)(void)) {
+ volatile int x = fn();
+ return x;
+}
+
+int dso_callfn1(int (*fn)(long long, long long, long long)) { //NOLINT
+ volatile int x = fn(1, 2, 3);
+ return x;
+}
+
+int dso_stack_store(void (*fn)(int*, int*), int x) {
+ int y = x + 1;
+ fn(&x, &y);
+ return y;
+}
diff --git a/lib/msan/tests/msandr_test_so.h b/lib/msan/tests/msandr_test_so.h
new file mode 100644
index 000000000000..6119542ee50e
--- /dev/null
+++ b/lib/msan/tests/msandr_test_so.h
@@ -0,0 +1,23 @@
+//===-- msandr_test_so.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// MemorySanitizer unit tests.
+//===----------------------------------------------------------------------===//
+
+#ifndef MSANDR_MSANDR_TEST_SO_H
+#define MSANDR_MSANDR_TEST_SO_H
+
+void dso_memfill(char* s, unsigned n);
+int dso_callfn(int (*fn)(void));
+int dso_callfn1(int (*fn)(long long, long long, long long)); //NOLINT
+int dso_stack_store(void (*fn)(int*, int*), int x);
+
+#endif
diff --git a/lib/profile/GCDAProfiling.c b/lib/profile/GCDAProfiling.c
index 8f92a915484b..7c52a1740999 100644
--- a/lib/profile/GCDAProfiling.c
+++ b/lib/profile/GCDAProfiling.c
@@ -66,6 +66,24 @@ static void write_string(const char *s) {
fwrite("\0\0\0\0", 4 - (strlen(s) % 4), 1, output_file);
}
+static uint32_t read_int32() {
+ uint32_t tmp;
+
+ if (fread(&tmp, 1, 4, output_file) != 4)
+ return (uint32_t)-1;
+
+ return tmp;
+}
+
+static uint64_t read_int64() {
+ uint64_t tmp;
+
+ if (fread(&tmp, 1, 8, output_file) != 8)
+ return (uint64_t)-1;
+
+ return tmp;
+}
+
static char *mangle_filename(const char *orig_filename) {
char *filename = 0;
int prefix_len = 0;
@@ -129,15 +147,23 @@ static void recursive_mkdir(char *filename) {
*/
void llvm_gcda_start_file(const char *orig_filename) {
char *filename = mangle_filename(orig_filename);
- output_file = fopen(filename, "w+b");
+
+ /* Try just opening the file. */
+ output_file = fopen(filename, "r+b");
if (!output_file) {
- recursive_mkdir(filename);
+ /* Try opening the file, creating it if necessary. */
output_file = fopen(filename, "w+b");
if (!output_file) {
- fprintf(stderr, "profiling:%s: cannot open\n", filename);
- free(filename);
- return;
+ /* Try creating the directories first then opening the file. */
+ recursive_mkdir(filename);
+ output_file = fopen(filename, "w+b");
+ if (!output_file) {
+ /* Bah! It's hopeless. */
+ fprintf(stderr, "profiling:%s: cannot open\n", filename);
+ free(filename);
+ return;
+ }
}
}
@@ -148,11 +174,11 @@ void llvm_gcda_start_file(const char *orig_filename) {
fwrite("adcg*404MVLL", 12, 1, output_file);
#endif
+ free(filename);
+
#ifdef DEBUG_GCDAPROFILING
- printf("llvmgcda: [%s]\n", orig_filename);
+ fprintf(stderr, "llvmgcda: [%s]\n", orig_filename);
#endif
-
- free(filename);
}
/* Given an array of pointers to counters (counters), increment the n-th one,
@@ -175,14 +201,14 @@ void llvm_gcda_increment_indirect_counter(uint32_t *predecessor,
#ifdef DEBUG_GCDAPROFILING
else
fprintf(stderr,
- "llvmgcda: increment_indirect_counter counters=%x, pred=%u\n",
- state_table_row, *predecessor);
+ "llvmgcda: increment_indirect_counter counters=%08llx, pred=%u\n",
+ *counter, *predecessor);
#endif
}
void llvm_gcda_emit_function(uint32_t ident, const char *function_name) {
#ifdef DEBUG_GCDAPROFILING
- printf("llvmgcda: function id=%x\n", ident);
+ fprintf(stderr, "llvmgcda: function id=0x%08x\n", ident);
#endif
if (!output_file) return;
@@ -197,18 +223,51 @@ void llvm_gcda_emit_function(uint32_t ident, const char *function_name) {
void llvm_gcda_emit_arcs(uint32_t num_counters, uint64_t *counters) {
uint32_t i;
+ uint64_t *old_ctrs = NULL;
+ uint32_t val = 0;
+ long pos = 0;
- /* Counter #1 (arcs) tag */
if (!output_file) return;
+
+ pos = ftell(output_file);
+ val = read_int32();
+
+ if (val != (uint32_t)-1) {
+ /* There are counters present in the file. Merge them. */
+ uint32_t j;
+
+ if (val != 0x01a10000) {
+ fprintf(stderr, "profiling: invalid magic number (0x%08x)\n", val);
+ return;
+ }
+
+ val = read_int32();
+ if (val == (uint32_t)-1 || val / 2 != num_counters) {
+ fprintf(stderr, "profiling: invalid number of counters (%d)\n", val);
+ return;
+ }
+
+ old_ctrs = malloc(sizeof(uint64_t) * num_counters);
+
+ for (j = 0; j < num_counters; ++j)
+ old_ctrs[j] = read_int64();
+ }
+
+ /* Reset for writing. */
+ fseek(output_file, pos, SEEK_SET);
+
+ /* Counter #1 (arcs) tag */
fwrite("\0\0\xa1\1", 4, 1, output_file);
write_int32(num_counters * 2);
for (i = 0; i < num_counters; ++i)
- write_int64(counters[i]);
+ write_int64(counters[i] + (old_ctrs ? old_ctrs[i] : 0));
+
+ free(old_ctrs);
#ifdef DEBUG_GCDAPROFILING
- printf("llvmgcda: %u arcs\n", num_counters);
+ fprintf(stderr, "llvmgcda: %u arcs\n", num_counters);
for (i = 0; i < num_counters; ++i)
- printf("llvmgcda: %llu\n", (unsigned long long)counters[i]);
+ fprintf(stderr, "llvmgcda: %llu\n", (unsigned long long)counters[i]);
#endif
}
@@ -220,6 +279,6 @@ void llvm_gcda_end_file() {
output_file = NULL;
#ifdef DEBUG_GCDAPROFILING
- printf("llvmgcda: -----\n");
+ fprintf(stderr, "llvmgcda: -----\n");
#endif
}
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index d797a56dabd9..ee0e1237c1a9 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -10,26 +10,68 @@ set(SANITIZER_SOURCES
sanitizer_mac.cc
sanitizer_posix.cc
sanitizer_printf.cc
+ sanitizer_stackdepot.cc
+ sanitizer_stacktrace.cc
sanitizer_symbolizer.cc
+ sanitizer_symbolizer_itanium.cc
+ sanitizer_symbolizer_linux.cc
+ sanitizer_symbolizer_mac.cc
+ sanitizer_symbolizer_win.cc
sanitizer_win.cc
)
-set(SANITIZER_CFLAGS "-fPIC -fno-exceptions -funwind-tables -fvisibility=hidden")
+# Explicitly list all sanitizer_common headers. Not all of these are
+# included in sanitizer_common source files, but we need to depend on
+# headers when building our custom unit tests.
+set(SANITIZER_HEADERS
+ sanitizer_allocator.h
+ sanitizer_atomic_clang.h
+ sanitizer_atomic_msvc.h
+ sanitizer_atomic.h
+ sanitizer_common.h
+ sanitizer_common_interceptors.inc
+ sanitizer_common_interceptors_scanf.inc
+ sanitizer_flags.h
+ sanitizer_internal_defs.h
+ sanitizer_lfstack.h
+ sanitizer_libc.h
+ sanitizer_list.h
+ sanitizer_mutex.h
+ sanitizer_placement_new.h
+ sanitizer_platform_interceptors.h
+ sanitizer_procmaps.h
+ sanitizer_quarantine.h
+ sanitizer_report_decorator.h
+ sanitizer_stackdepot.h
+ sanitizer_stacktrace.h
+ sanitizer_symbolizer.h
+ )
-set(SANITIZER_COMMON_DEFINITIONS
- SANITIZER_HAS_EXCEPTIONS=1)
+set(SANITIZER_CFLAGS ${SANITIZER_COMMON_CFLAGS})
-if(CAN_TARGET_X86_64)
- add_library(RTSanitizerCommon.x86_64 OBJECT ${SANITIZER_SOURCES})
- set_property(TARGET RTSanitizerCommon.x86_64 PROPERTY COMPILE_FLAGS
- "${SANITIZER_CFLAGS} ${TARGET_X86_64_CFLAGS}")
- set_property(TARGET RTSanitizerCommon.x86_64 APPEND PROPERTY COMPILE_DEFINITIONS
- ${SANITIZER_COMMON_DEFINITIONS})
+set(SANITIZER_RUNTIME_LIBRARIES)
+if(APPLE)
+ # Build universal binary on APPLE.
+ add_library(RTSanitizerCommon.osx OBJECT ${SANITIZER_SOURCES})
+ set_target_compile_flags(RTSanitizerCommon.osx ${SANITIZER_CFLAGS})
+ set_target_properties(RTSanitizerCommon.osx PROPERTIES
+ OSX_ARCHITECTURES "${SANITIZER_COMMON_SUPPORTED_ARCH}")
+ list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.osx)
+elseif(ANDROID)
+ add_library(RTSanitizerCommon.arm.android OBJECT ${SANITIZER_SOURCES})
+ set_target_compile_flags(RTSanitizerCommon.arm.android
+ ${SANITIZER_CFLAGS})
+ list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.arm.android)
+else()
+ # Otherwise, build separate libraries for each target.
+ foreach(arch ${SANITIZER_COMMON_SUPPORTED_ARCH})
+ add_compiler_rt_object_library(RTSanitizerCommon ${arch}
+ SOURCES ${SANITIZER_SOURCES} CFLAGS ${SANITIZER_CFLAGS})
+ list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.${arch})
+ endforeach()
endif()
-if(CAN_TARGET_I386)
- add_library(RTSanitizerCommon.i386 OBJECT ${SANITIZER_SOURCES})
- set_property(TARGET RTSanitizerCommon.i386 PROPERTY COMPILE_FLAGS
- "${SANITIZER_CFLAGS} ${TARGET_I386_CFLAGS}")
- set_property(TARGET RTSanitizerCommon.i386 APPEND PROPERTY COMPILE_DEFINITIONS
- ${SANITIZER_COMMON_DEFINITIONS})
+
+# Unit tests for common sanitizer runtime.
+if(LLVM_INCLUDE_TESTS)
+ add_subdirectory(tests)
endif()
diff --git a/lib/sanitizer_common/sanitizer_allocator.cc b/lib/sanitizer_common/sanitizer_allocator.cc
index 816fddf1c5ad..b13a7c6c14c0 100644
--- a/lib/sanitizer_common/sanitizer_allocator.cc
+++ b/lib/sanitizer_common/sanitizer_allocator.cc
@@ -15,16 +15,16 @@
// FIXME: We should probably use more low-level allocator that would
// mmap some pages and split them into chunks to fulfill requests.
-#ifdef __linux__
+#if defined(__linux__) && !defined(__ANDROID__)
extern "C" void *__libc_malloc(__sanitizer::uptr size);
extern "C" void __libc_free(void *ptr);
# define LIBC_MALLOC __libc_malloc
# define LIBC_FREE __libc_free
-#else // __linux__
+#else // __linux__ && !ANDROID
# include <stdlib.h>
# define LIBC_MALLOC malloc
# define LIBC_FREE free
-#endif // __linux__
+#endif // __linux__ && !ANDROID
namespace __sanitizer {
@@ -49,11 +49,30 @@ void InternalFree(void *addr) {
LIBC_FREE(addr);
}
-void *InternalAllocBlock(void *p) {
- CHECK_NE(p, (void*)0);
- u64 *pp = (u64*)((uptr)p & ~0x7);
- for (; pp[0] != kBlockMagic; pp--) {}
- return pp + 1;
+// LowLevelAllocator
+static LowLevelAllocateCallback low_level_alloc_callback;
+
+void *LowLevelAllocator::Allocate(uptr size) {
+ // Align allocation size.
+ size = RoundUpTo(size, 8);
+ if (allocated_end_ - allocated_current_ < (sptr)size) {
+ uptr size_to_allocate = Max(size, GetPageSizeCached());
+ allocated_current_ =
+ (char*)MmapOrDie(size_to_allocate, __FUNCTION__);
+ allocated_end_ = allocated_current_ + size_to_allocate;
+ if (low_level_alloc_callback) {
+ low_level_alloc_callback((uptr)allocated_current_,
+ size_to_allocate);
+ }
+ }
+ CHECK(allocated_end_ - allocated_current_ >= (sptr)size);
+ void *res = allocated_current_;
+ allocated_current_ += size;
+ return res;
+}
+
+void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) {
+ low_level_alloc_callback = callback;
}
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_allocator.h b/lib/sanitizer_common/sanitizer_allocator.h
new file mode 100644
index 000000000000..ad89c3c870dc
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_allocator.h
@@ -0,0 +1,989 @@
+//===-- sanitizer_allocator.h -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Specialized memory allocator for ThreadSanitizer, MemorySanitizer, etc.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ALLOCATOR_H
+#define SANITIZER_ALLOCATOR_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_list.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_lfstack.h"
+
+namespace __sanitizer {
+
+// SizeClassMap maps allocation sizes into size classes and back.
+// Class 0 corresponds to size 0.
+// Classes 1 - 16 correspond to sizes 8 - 128 (size = class_id * 8).
+// Next 8 classes: 128 + i * 16 (i = 1 to 8).
+// Next 8 classes: 256 + i * 32 (i = 1 to 8).
+// ...
+// Next 8 classes: 2^k + i * 2^(k-3) (i = 1 to 8).
+// Last class corresponds to kMaxSize = 1 << kMaxSizeLog.
+//
+// This structure of the size class map gives us:
+// - Efficient table-free class-to-size and size-to-class functions.
+// - Difference between two consequent size classes is betweed 12% and 6%
+//
+// This class also gives a hint to a thread-caching allocator about the amount
+// of chunks that need to be cached per-thread:
+// - kMaxNumCached is the maximal number of chunks per size class.
+// - (1 << kMaxBytesCachedLog) is the maximal number of bytes per size class.
+//
+// Part of output of SizeClassMap::Print():
+// c00 => s: 0 diff: +0 00% l 0 cached: 0 0; id 0
+// c01 => s: 8 diff: +8 00% l 3 cached: 256 2048; id 1
+// c02 => s: 16 diff: +8 100% l 4 cached: 256 4096; id 2
+// ...
+// c07 => s: 56 diff: +8 16% l 5 cached: 256 14336; id 7
+//
+// c08 => s: 64 diff: +8 14% l 6 cached: 256 16384; id 8
+// ...
+// c15 => s: 120 diff: +8 07% l 6 cached: 256 30720; id 15
+//
+// c16 => s: 128 diff: +8 06% l 7 cached: 256 32768; id 16
+// c17 => s: 144 diff: +16 12% l 7 cached: 227 32688; id 17
+// ...
+// c23 => s: 240 diff: +16 07% l 7 cached: 136 32640; id 23
+//
+// c24 => s: 256 diff: +16 06% l 8 cached: 128 32768; id 24
+// c25 => s: 288 diff: +32 12% l 8 cached: 113 32544; id 25
+// ...
+// c31 => s: 480 diff: +32 07% l 8 cached: 68 32640; id 31
+//
+// c32 => s: 512 diff: +32 06% l 9 cached: 64 32768; id 32
+
+
+template <uptr kMaxSizeLog, uptr kMaxNumCachedT, uptr kMaxBytesCachedLog,
+ uptr kMinBatchClassT>
+class SizeClassMap {
+ static const uptr kMinSizeLog = 3;
+ static const uptr kMidSizeLog = kMinSizeLog + 4;
+ static const uptr kMinSize = 1 << kMinSizeLog;
+ static const uptr kMidSize = 1 << kMidSizeLog;
+ static const uptr kMidClass = kMidSize / kMinSize;
+ static const uptr S = 3;
+ static const uptr M = (1 << S) - 1;
+
+ public:
+ static const uptr kMaxNumCached = kMaxNumCachedT;
+ struct TransferBatch {
+ TransferBatch *next;
+ uptr count;
+ void *batch[kMaxNumCached];
+ };
+
+ static const uptr kMinBatchClass = kMinBatchClassT;
+ static const uptr kMaxSize = 1 << kMaxSizeLog;
+ static const uptr kNumClasses =
+ kMidClass + ((kMaxSizeLog - kMidSizeLog) << S) + 1;
+ COMPILER_CHECK(kNumClasses >= 32 && kNumClasses <= 256);
+ static const uptr kNumClassesRounded =
+ kNumClasses == 32 ? 32 :
+ kNumClasses <= 64 ? 64 :
+ kNumClasses <= 128 ? 128 : 256;
+
+ static uptr Size(uptr class_id) {
+ if (class_id <= kMidClass)
+ return kMinSize * class_id;
+ class_id -= kMidClass;
+ uptr t = kMidSize << (class_id >> S);
+ return t + (t >> S) * (class_id & M);
+ }
+
+ static uptr ClassID(uptr size) {
+ if (size <= kMidSize)
+ return (size + kMinSize - 1) >> kMinSizeLog;
+ if (size > kMaxSize) return 0;
+ uptr l = SANITIZER_WORDSIZE - 1 - __builtin_clzl(size);
+ uptr hbits = (size >> (l - S)) & M;
+ uptr lbits = size & ((1 << (l - S)) - 1);
+ uptr l1 = l - kMidSizeLog;
+ return kMidClass + (l1 << S) + hbits + (lbits > 0);
+ }
+
+ static uptr MaxCached(uptr class_id) {
+ if (class_id == 0) return 0;
+ uptr n = (1UL << kMaxBytesCachedLog) / Size(class_id);
+ return Max(1UL, Min(kMaxNumCached, n));
+ }
+
+ static void Print() {
+ uptr prev_s = 0;
+ uptr total_cached = 0;
+ for (uptr i = 0; i < kNumClasses; i++) {
+ uptr s = Size(i);
+ if (s >= kMidSize / 2 && (s & (s - 1)) == 0)
+ Printf("\n");
+ uptr d = s - prev_s;
+ uptr p = prev_s ? (d * 100 / prev_s) : 0;
+ uptr l = SANITIZER_WORDSIZE - 1 - __builtin_clzl(s);
+ uptr cached = MaxCached(i) * s;
+ Printf("c%02zd => s: %zd diff: +%zd %02zd%% l %zd "
+ "cached: %zd %zd; id %zd\n",
+ i, Size(i), d, p, l, MaxCached(i), cached, ClassID(s));
+ total_cached += cached;
+ prev_s = s;
+ }
+ Printf("Total cached: %zd\n", total_cached);
+ }
+
+ static void Validate() {
+ for (uptr c = 1; c < kNumClasses; c++) {
+ // Printf("Validate: c%zd\n", c);
+ uptr s = Size(c);
+ CHECK_EQ(ClassID(s), c);
+ if (c != kNumClasses - 1)
+ CHECK_EQ(ClassID(s + 1), c + 1);
+ CHECK_EQ(ClassID(s - 1), c);
+ if (c)
+ CHECK_GT(Size(c), Size(c-1));
+ }
+ CHECK_EQ(ClassID(kMaxSize + 1), 0);
+
+ for (uptr s = 1; s <= kMaxSize; s++) {
+ uptr c = ClassID(s);
+ // Printf("s%zd => c%zd\n", s, c);
+ CHECK_LT(c, kNumClasses);
+ CHECK_GE(Size(c), s);
+ if (c > 0)
+ CHECK_LT(Size(c-1), s);
+ }
+
+ // TransferBatch for kMinBatchClass must fit into the block itself.
+ const uptr batch_size = sizeof(TransferBatch)
+ - sizeof(void*) // NOLINT
+ * (kMaxNumCached - MaxCached(kMinBatchClass));
+ CHECK_LE(batch_size, Size(kMinBatchClass));
+ // TransferBatch for kMinBatchClass-1 must not fit into the block itself.
+ const uptr batch_size1 = sizeof(TransferBatch)
+ - sizeof(void*) // NOLINT
+ * (kMaxNumCached - MaxCached(kMinBatchClass - 1));
+ CHECK_GT(batch_size1, Size(kMinBatchClass - 1));
+ }
+};
+
+typedef SizeClassMap<17, 256, 16, FIRST_32_SECOND_64(33, 36)>
+ DefaultSizeClassMap;
+typedef SizeClassMap<17, 64, 14, FIRST_32_SECOND_64(25, 28)>
+ CompactSizeClassMap;
+template<class SizeClassAllocator> struct SizeClassAllocatorLocalCache;
+
+// Allocators call these callbacks on mmap/munmap.
+struct NoOpMapUnmapCallback {
+ void OnMap(uptr p, uptr size) const { }
+ void OnUnmap(uptr p, uptr size) const { }
+};
+
+// SizeClassAllocator64 -- allocator for 64-bit address space.
+//
+// Space: a portion of address space of kSpaceSize bytes starting at
+// a fixed address (kSpaceBeg). Both constants are powers of two and
+// kSpaceBeg is kSpaceSize-aligned.
+// At the beginning the entire space is mprotect-ed, then small parts of it
+// are mapped on demand.
+//
+// Region: a part of Space dedicated to a single size class.
+// There are kNumClasses Regions of equal size.
+//
+// UserChunk: a piece of memory returned to user.
+// MetaChunk: kMetadataSize bytes of metadata associated with a UserChunk.
+//
+// A Region looks like this:
+// UserChunk1 ... UserChunkN <gap> MetaChunkN ... MetaChunk1
+template <const uptr kSpaceBeg, const uptr kSpaceSize,
+ const uptr kMetadataSize, class SizeClassMap,
+ class MapUnmapCallback = NoOpMapUnmapCallback>
+class SizeClassAllocator64 {
+ public:
+ typedef typename SizeClassMap::TransferBatch Batch;
+ typedef SizeClassAllocator64<kSpaceBeg, kSpaceSize, kMetadataSize,
+ SizeClassMap, MapUnmapCallback> ThisT;
+ typedef SizeClassAllocatorLocalCache<ThisT> AllocatorCache;
+
+ void Init() {
+ CHECK_EQ(kSpaceBeg,
+ reinterpret_cast<uptr>(Mprotect(kSpaceBeg, kSpaceSize)));
+ MapWithCallback(kSpaceEnd, AdditionalSize());
+ }
+
+ void MapWithCallback(uptr beg, uptr size) {
+ CHECK_EQ(beg, reinterpret_cast<uptr>(MmapFixedOrDie(beg, size)));
+ MapUnmapCallback().OnMap(beg, size);
+ }
+
+ void UnmapWithCallback(uptr beg, uptr size) {
+ MapUnmapCallback().OnUnmap(beg, size);
+ UnmapOrDie(reinterpret_cast<void *>(beg), size);
+ }
+
+ static bool CanAllocate(uptr size, uptr alignment) {
+ return size <= SizeClassMap::kMaxSize &&
+ alignment <= SizeClassMap::kMaxSize;
+ }
+
+ Batch *NOINLINE AllocateBatch(AllocatorCache *c, uptr class_id) {
+ CHECK_LT(class_id, kNumClasses);
+ RegionInfo *region = GetRegionInfo(class_id);
+ Batch *b = region->free_list.Pop();
+ if (b == 0)
+ b = PopulateFreeList(c, class_id, region);
+ region->n_allocated += b->count;
+ return b;
+ }
+
+ void NOINLINE DeallocateBatch(uptr class_id, Batch *b) {
+ RegionInfo *region = GetRegionInfo(class_id);
+ region->free_list.Push(b);
+ region->n_freed += b->count;
+ }
+
+ static bool PointerIsMine(void *p) {
+ return reinterpret_cast<uptr>(p) / kSpaceSize == kSpaceBeg / kSpaceSize;
+ }
+
+ static uptr GetSizeClass(void *p) {
+ return (reinterpret_cast<uptr>(p) / kRegionSize) % kNumClassesRounded;
+ }
+
+ void *GetBlockBegin(void *p) {
+ uptr class_id = GetSizeClass(p);
+ uptr size = SizeClassMap::Size(class_id);
+ uptr chunk_idx = GetChunkIdx((uptr)p, size);
+ uptr reg_beg = (uptr)p & ~(kRegionSize - 1);
+ uptr beg = chunk_idx * size;
+ uptr next_beg = beg + size;
+ RegionInfo *region = GetRegionInfo(class_id);
+ if (region->mapped_user >= next_beg)
+ return reinterpret_cast<void*>(reg_beg + beg);
+ return 0;
+ }
+
+ static uptr GetActuallyAllocatedSize(void *p) {
+ CHECK(PointerIsMine(p));
+ return SizeClassMap::Size(GetSizeClass(p));
+ }
+
+ uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); }
+
+ void *GetMetaData(void *p) {
+ uptr class_id = GetSizeClass(p);
+ uptr size = SizeClassMap::Size(class_id);
+ uptr chunk_idx = GetChunkIdx(reinterpret_cast<uptr>(p), size);
+ return reinterpret_cast<void*>(kSpaceBeg + (kRegionSize * (class_id + 1)) -
+ (1 + chunk_idx) * kMetadataSize);
+ }
+
+ uptr TotalMemoryUsed() {
+ uptr res = 0;
+ for (uptr i = 0; i < kNumClasses; i++)
+ res += GetRegionInfo(i)->allocated_user;
+ return res;
+ }
+
+ // Test-only.
+ void TestOnlyUnmap() {
+ UnmapWithCallback(kSpaceBeg, kSpaceSize + AdditionalSize());
+ }
+
+ void PrintStats() {
+ uptr total_mapped = 0;
+ uptr n_allocated = 0;
+ uptr n_freed = 0;
+ for (uptr class_id = 1; class_id < kNumClasses; class_id++) {
+ RegionInfo *region = GetRegionInfo(class_id);
+ total_mapped += region->mapped_user;
+ n_allocated += region->n_allocated;
+ n_freed += region->n_freed;
+ }
+ Printf("Stats: SizeClassAllocator64: %zdM mapped in %zd allocations; "
+ "remains %zd\n",
+ total_mapped >> 20, n_allocated, n_allocated - n_freed);
+ for (uptr class_id = 1; class_id < kNumClasses; class_id++) {
+ RegionInfo *region = GetRegionInfo(class_id);
+ if (region->mapped_user == 0) continue;
+ Printf(" %02zd (%zd): total: %zd K allocs: %zd remains: %zd\n",
+ class_id,
+ SizeClassMap::Size(class_id),
+ region->mapped_user >> 10,
+ region->n_allocated,
+ region->n_allocated - region->n_freed);
+ }
+ }
+
+ typedef SizeClassMap SizeClassMapT;
+ static const uptr kNumClasses = SizeClassMap::kNumClasses;
+ static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded;
+
+ private:
+ static const uptr kRegionSize = kSpaceSize / kNumClassesRounded;
+ static const uptr kSpaceEnd = kSpaceBeg + kSpaceSize;
+ COMPILER_CHECK(kSpaceBeg % kSpaceSize == 0);
+ // kRegionSize must be >= 2^32.
+ COMPILER_CHECK((kRegionSize) >= (1ULL << (SANITIZER_WORDSIZE / 2)));
+ // Populate the free list with at most this number of bytes at once
+ // or with one element if its size is greater.
+ static const uptr kPopulateSize = 1 << 14;
+ // Call mmap for user memory with at least this size.
+ static const uptr kUserMapSize = 1 << 15;
+ // Call mmap for metadata memory with at least this size.
+ static const uptr kMetaMapSize = 1 << 16;
+
+ struct RegionInfo {
+ BlockingMutex mutex;
+ LFStack<Batch> free_list;
+ uptr allocated_user; // Bytes allocated for user memory.
+ uptr allocated_meta; // Bytes allocated for metadata.
+ uptr mapped_user; // Bytes mapped for user memory.
+ uptr mapped_meta; // Bytes mapped for metadata.
+ uptr n_allocated, n_freed; // Just stats.
+ };
+ COMPILER_CHECK(sizeof(RegionInfo) >= kCacheLineSize);
+
+ static uptr AdditionalSize() {
+ return RoundUpTo(sizeof(RegionInfo) * kNumClassesRounded,
+ GetPageSizeCached());
+ }
+
+ RegionInfo *GetRegionInfo(uptr class_id) {
+ CHECK_LT(class_id, kNumClasses);
+ RegionInfo *regions = reinterpret_cast<RegionInfo*>(kSpaceBeg + kSpaceSize);
+ return &regions[class_id];
+ }
+
+ static uptr GetChunkIdx(uptr chunk, uptr size) {
+ u32 offset = chunk % kRegionSize;
+ // Here we divide by a non-constant. This is costly.
+ // We require that kRegionSize is at least 2^32 so that offset is 32-bit.
+ // We save 2x by using 32-bit div, but may need to use a 256-way switch.
+ return offset / (u32)size;
+ }
+
+ Batch *NOINLINE PopulateFreeList(AllocatorCache *c, uptr class_id,
+ RegionInfo *region) {
+ BlockingMutexLock l(&region->mutex);
+ Batch *b = region->free_list.Pop();
+ if (b)
+ return b;
+ uptr size = SizeClassMap::Size(class_id);
+ uptr count = size < kPopulateSize ? SizeClassMap::MaxCached(class_id) : 1;
+ uptr beg_idx = region->allocated_user;
+ uptr end_idx = beg_idx + count * size;
+ uptr region_beg = kSpaceBeg + kRegionSize * class_id;
+ if (end_idx + size > region->mapped_user) {
+ // Do the mmap for the user memory.
+ uptr map_size = kUserMapSize;
+ while (end_idx + size > region->mapped_user + map_size)
+ map_size += kUserMapSize;
+ CHECK_GE(region->mapped_user + map_size, end_idx);
+ MapWithCallback(region_beg + region->mapped_user, map_size);
+ region->mapped_user += map_size;
+ }
+ uptr total_count = (region->mapped_user - beg_idx - size)
+ / size / count * count;
+ region->allocated_meta += total_count * kMetadataSize;
+ if (region->allocated_meta > region->mapped_meta) {
+ uptr map_size = kMetaMapSize;
+ while (region->allocated_meta > region->mapped_meta + map_size)
+ map_size += kMetaMapSize;
+ // Do the mmap for the metadata.
+ CHECK_GE(region->mapped_meta + map_size, region->allocated_meta);
+ MapWithCallback(region_beg + kRegionSize -
+ region->mapped_meta - map_size, map_size);
+ region->mapped_meta += map_size;
+ }
+ CHECK_LE(region->allocated_meta, region->mapped_meta);
+ if (region->allocated_user + region->allocated_meta > kRegionSize) {
+ Printf("Out of memory. Dying.\n");
+ Printf("The process has exhausted %zuMB for size class %zu.\n",
+ kRegionSize / 1024 / 1024, size);
+ Die();
+ }
+ for (;;) {
+ if (class_id < SizeClassMap::kMinBatchClass)
+ b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch)));
+ else
+ b = (Batch*)(region_beg + beg_idx);
+ b->count = count;
+ for (uptr i = 0; i < count; i++)
+ b->batch[i] = (void*)(region_beg + beg_idx + i * size);
+ region->allocated_user += count * size;
+ CHECK_LE(region->allocated_user, region->mapped_user);
+ beg_idx += count * size;
+ if (beg_idx + count * size + size > region->mapped_user)
+ break;
+ region->free_list.Push(b);
+ }
+ return b;
+ }
+};
+
+// SizeClassAllocator32 -- allocator for 32-bit address space.
+// This allocator can theoretically be used on 64-bit arch, but there it is less
+// efficient than SizeClassAllocator64.
+//
+// [kSpaceBeg, kSpaceBeg + kSpaceSize) is the range of addresses which can
+// be returned by MmapOrDie().
+//
+// Region:
+// a result of a single call to MmapAlignedOrDie(kRegionSize, kRegionSize).
+// Since the regions are aligned by kRegionSize, there are exactly
+// kNumPossibleRegions possible regions in the address space and so we keep
+// an u8 array possible_regions[kNumPossibleRegions] to store the size classes.
+// 0 size class means the region is not used by the allocator.
+//
+// One Region is used to allocate chunks of a single size class.
+// A Region looks like this:
+// UserChunk1 .. UserChunkN <gap> MetaChunkN .. MetaChunk1
+//
+// In order to avoid false sharing the objects of this class should be
+// chache-line aligned.
+template <const uptr kSpaceBeg, const u64 kSpaceSize,
+ const uptr kMetadataSize, class SizeClassMap,
+ class MapUnmapCallback = NoOpMapUnmapCallback>
+class SizeClassAllocator32 {
+ public:
+ typedef typename SizeClassMap::TransferBatch Batch;
+ typedef SizeClassAllocator32<kSpaceBeg, kSpaceSize, kMetadataSize,
+ SizeClassMap, MapUnmapCallback> ThisT;
+ typedef SizeClassAllocatorLocalCache<ThisT> AllocatorCache;
+
+ void Init() {
+ state_ = reinterpret_cast<State *>(MapWithCallback(sizeof(State)));
+ }
+
+ void *MapWithCallback(uptr size) {
+ size = RoundUpTo(size, GetPageSizeCached());
+ void *res = MmapOrDie(size, "SizeClassAllocator32");
+ MapUnmapCallback().OnMap((uptr)res, size);
+ return res;
+ }
+ void UnmapWithCallback(uptr beg, uptr size) {
+ MapUnmapCallback().OnUnmap(beg, size);
+ UnmapOrDie(reinterpret_cast<void *>(beg), size);
+ }
+
+ static bool CanAllocate(uptr size, uptr alignment) {
+ return size <= SizeClassMap::kMaxSize &&
+ alignment <= SizeClassMap::kMaxSize;
+ }
+
+ void *GetMetaData(void *p) {
+ CHECK(PointerIsMine(p));
+ uptr mem = reinterpret_cast<uptr>(p);
+ uptr beg = ComputeRegionBeg(mem);
+ uptr size = SizeClassMap::Size(GetSizeClass(p));
+ u32 offset = mem - beg;
+ uptr n = offset / (u32)size; // 32-bit division
+ uptr meta = (beg + kRegionSize) - (n + 1) * kMetadataSize;
+ return reinterpret_cast<void*>(meta);
+ }
+
+ Batch *NOINLINE AllocateBatch(AllocatorCache *c, uptr class_id) {
+ CHECK_LT(class_id, kNumClasses);
+ SizeClassInfo *sci = GetSizeClassInfo(class_id);
+ SpinMutexLock l(&sci->mutex);
+ if (sci->free_list.empty())
+ PopulateFreeList(c, sci, class_id);
+ CHECK(!sci->free_list.empty());
+ Batch *b = sci->free_list.front();
+ sci->free_list.pop_front();
+ return b;
+ }
+
+ void NOINLINE DeallocateBatch(uptr class_id, Batch *b) {
+ CHECK_LT(class_id, kNumClasses);
+ SizeClassInfo *sci = GetSizeClassInfo(class_id);
+ SpinMutexLock l(&sci->mutex);
+ sci->free_list.push_front(b);
+ }
+
+ bool PointerIsMine(void *p) {
+ return GetSizeClass(p) != 0;
+ }
+
+ uptr GetSizeClass(void *p) {
+ return state_->possible_regions[ComputeRegionId(reinterpret_cast<uptr>(p))];
+ }
+
+ void *GetBlockBegin(void *p) {
+ CHECK(PointerIsMine(p));
+ uptr mem = reinterpret_cast<uptr>(p);
+ uptr beg = ComputeRegionBeg(mem);
+ uptr size = SizeClassMap::Size(GetSizeClass(p));
+ u32 offset = mem - beg;
+ u32 n = offset / (u32)size; // 32-bit division
+ uptr res = beg + (n * (u32)size);
+ return reinterpret_cast<void*>(res);
+ }
+
+ uptr GetActuallyAllocatedSize(void *p) {
+ CHECK(PointerIsMine(p));
+ return SizeClassMap::Size(GetSizeClass(p));
+ }
+
+ uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); }
+
+ uptr TotalMemoryUsed() {
+ // No need to lock here.
+ uptr res = 0;
+ for (uptr i = 0; i < kNumPossibleRegions; i++)
+ if (state_->possible_regions[i])
+ res += kRegionSize;
+ return res;
+ }
+
+ void TestOnlyUnmap() {
+ for (uptr i = 0; i < kNumPossibleRegions; i++)
+ if (state_->possible_regions[i])
+ UnmapWithCallback((i * kRegionSize), kRegionSize);
+ UnmapWithCallback(reinterpret_cast<uptr>(state_), sizeof(State));
+ }
+
+ void PrintStats() {
+ }
+
+ typedef SizeClassMap SizeClassMapT;
+ static const uptr kNumClasses = SizeClassMap::kNumClasses;
+
+ private:
+ static const uptr kRegionSizeLog = SANITIZER_WORDSIZE == 64 ? 24 : 20;
+ static const uptr kRegionSize = 1 << kRegionSizeLog;
+ static const uptr kNumPossibleRegions = kSpaceSize / kRegionSize;
+
+ struct SizeClassInfo {
+ SpinMutex mutex;
+ IntrusiveList<Batch> free_list;
+ char padding[kCacheLineSize - sizeof(uptr) - sizeof(IntrusiveList<Batch>)];
+ };
+ COMPILER_CHECK(sizeof(SizeClassInfo) == kCacheLineSize);
+
+ uptr ComputeRegionId(uptr mem) {
+ uptr res = mem >> kRegionSizeLog;
+ CHECK_LT(res, kNumPossibleRegions);
+ return res;
+ }
+
+ uptr ComputeRegionBeg(uptr mem) {
+ return mem & ~(kRegionSize - 1);
+ }
+
+ uptr AllocateRegion(uptr class_id) {
+ CHECK_LT(class_id, kNumClasses);
+ uptr res = reinterpret_cast<uptr>(MmapAlignedOrDie(kRegionSize, kRegionSize,
+ "SizeClassAllocator32"));
+ MapUnmapCallback().OnMap(res, kRegionSize);
+ CHECK_EQ(0U, (res & (kRegionSize - 1)));
+ CHECK_EQ(0U, state_->possible_regions[ComputeRegionId(res)]);
+ state_->possible_regions[ComputeRegionId(res)] = class_id;
+ return res;
+ }
+
+ SizeClassInfo *GetSizeClassInfo(uptr class_id) {
+ CHECK_LT(class_id, kNumClasses);
+ return &state_->size_class_info_array[class_id];
+ }
+
+ void PopulateFreeList(AllocatorCache *c, SizeClassInfo *sci, uptr class_id) {
+ uptr size = SizeClassMap::Size(class_id);
+ uptr reg = AllocateRegion(class_id);
+ uptr n_chunks = kRegionSize / (size + kMetadataSize);
+ uptr max_count = SizeClassMap::MaxCached(class_id);
+ Batch *b = 0;
+ for (uptr i = reg; i < reg + n_chunks * size; i += size) {
+ if (b == 0) {
+ if (class_id < SizeClassMap::kMinBatchClass)
+ b = (Batch*)c->Allocate(this, SizeClassMap::ClassID(sizeof(Batch)));
+ else
+ b = (Batch*)i;
+ b->count = 0;
+ }
+ b->batch[b->count++] = (void*)i;
+ if (b->count == max_count) {
+ sci->free_list.push_back(b);
+ b = 0;
+ }
+ }
+ if (b)
+ sci->free_list.push_back(b);
+ }
+
+ struct State {
+ u8 possible_regions[kNumPossibleRegions];
+ SizeClassInfo size_class_info_array[kNumClasses];
+ };
+ State *state_;
+};
+
+// Objects of this type should be used as local caches for SizeClassAllocator64
+// or SizeClassAllocator32. Since the typical use of this class is to have one
+// object per thread in TLS, is has to be POD.
+template<class SizeClassAllocator>
+struct SizeClassAllocatorLocalCache {
+ typedef SizeClassAllocator Allocator;
+ static const uptr kNumClasses = SizeClassAllocator::kNumClasses;
+
+ // Don't need to call Init if the object is a global (i.e. zero-initialized).
+ void Init() {
+ internal_memset(this, 0, sizeof(*this));
+ }
+
+ void *Allocate(SizeClassAllocator *allocator, uptr class_id) {
+ CHECK_NE(class_id, 0UL);
+ CHECK_LT(class_id, kNumClasses);
+ PerClass *c = &per_class_[class_id];
+ if (UNLIKELY(c->count == 0))
+ Refill(allocator, class_id);
+ void *res = c->batch[--c->count];
+ PREFETCH(c->batch[c->count - 1]);
+ return res;
+ }
+
+ void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) {
+ CHECK_NE(class_id, 0UL);
+ CHECK_LT(class_id, kNumClasses);
+ PerClass *c = &per_class_[class_id];
+ if (UNLIKELY(c->count == c->max_count))
+ Drain(allocator, class_id);
+ c->batch[c->count++] = p;
+ }
+
+ void Drain(SizeClassAllocator *allocator) {
+ for (uptr class_id = 0; class_id < kNumClasses; class_id++) {
+ PerClass *c = &per_class_[class_id];
+ while (c->count > 0)
+ Drain(allocator, class_id);
+ }
+ }
+
+ // private:
+ typedef typename SizeClassAllocator::SizeClassMapT SizeClassMap;
+ typedef typename SizeClassMap::TransferBatch Batch;
+ struct PerClass {
+ uptr count;
+ uptr max_count;
+ void *batch[2 * SizeClassMap::kMaxNumCached];
+ };
+ PerClass per_class_[kNumClasses];
+
+ void InitCache() {
+ if (per_class_[0].max_count)
+ return;
+ for (uptr i = 0; i < kNumClasses; i++) {
+ PerClass *c = &per_class_[i];
+ c->max_count = 2 * SizeClassMap::MaxCached(i);
+ }
+ }
+
+ void NOINLINE Refill(SizeClassAllocator *allocator, uptr class_id) {
+ InitCache();
+ PerClass *c = &per_class_[class_id];
+ Batch *b = allocator->AllocateBatch(this, class_id);
+ for (uptr i = 0; i < b->count; i++)
+ c->batch[i] = b->batch[i];
+ c->count = b->count;
+ if (class_id < SizeClassMap::kMinBatchClass)
+ Deallocate(allocator, SizeClassMap::ClassID(sizeof(Batch)), b);
+ }
+
+ void NOINLINE Drain(SizeClassAllocator *allocator, uptr class_id) {
+ InitCache();
+ PerClass *c = &per_class_[class_id];
+ Batch *b;
+ if (class_id < SizeClassMap::kMinBatchClass)
+ b = (Batch*)Allocate(allocator, SizeClassMap::ClassID(sizeof(Batch)));
+ else
+ b = (Batch*)c->batch[0];
+ uptr cnt = Min(c->max_count / 2, c->count);
+ for (uptr i = 0; i < cnt; i++) {
+ b->batch[i] = c->batch[i];
+ c->batch[i] = c->batch[i + c->max_count / 2];
+ }
+ b->count = cnt;
+ c->count -= cnt;
+ allocator->DeallocateBatch(class_id, b);
+ }
+};
+
+// This class can (de)allocate only large chunks of memory using mmap/unmap.
+// The main purpose of this allocator is to cover large and rare allocation
+// sizes not covered by more efficient allocators (e.g. SizeClassAllocator64).
+template <class MapUnmapCallback = NoOpMapUnmapCallback>
+class LargeMmapAllocator {
+ public:
+ void Init() {
+ internal_memset(this, 0, sizeof(*this));
+ page_size_ = GetPageSizeCached();
+ }
+
+ void *Allocate(uptr size, uptr alignment) {
+ CHECK(IsPowerOfTwo(alignment));
+ uptr map_size = RoundUpMapSize(size);
+ if (alignment > page_size_)
+ map_size += alignment;
+ if (map_size < size) return 0; // Overflow.
+ uptr map_beg = reinterpret_cast<uptr>(
+ MmapOrDie(map_size, "LargeMmapAllocator"));
+ MapUnmapCallback().OnMap(map_beg, map_size);
+ uptr map_end = map_beg + map_size;
+ uptr res = map_beg + page_size_;
+ if (res & (alignment - 1)) // Align.
+ res += alignment - (res & (alignment - 1));
+ CHECK_EQ(0, res & (alignment - 1));
+ CHECK_LE(res + size, map_end);
+ Header *h = GetHeader(res);
+ h->size = size;
+ h->map_beg = map_beg;
+ h->map_size = map_size;
+ uptr size_log = SANITIZER_WORDSIZE - __builtin_clzl(map_size) - 1;
+ CHECK_LT(size_log, ARRAY_SIZE(stats.by_size_log));
+ {
+ SpinMutexLock l(&mutex_);
+ uptr idx = n_chunks_++;
+ CHECK_LT(idx, kMaxNumChunks);
+ h->chunk_idx = idx;
+ chunks_[idx] = h;
+ stats.n_allocs++;
+ stats.currently_allocated += map_size;
+ stats.max_allocated = Max(stats.max_allocated, stats.currently_allocated);
+ stats.by_size_log[size_log]++;
+ }
+ return reinterpret_cast<void*>(res);
+ }
+
+ void Deallocate(void *p) {
+ Header *h = GetHeader(p);
+ {
+ SpinMutexLock l(&mutex_);
+ uptr idx = h->chunk_idx;
+ CHECK_EQ(chunks_[idx], h);
+ CHECK_LT(idx, n_chunks_);
+ chunks_[idx] = chunks_[n_chunks_ - 1];
+ chunks_[idx]->chunk_idx = idx;
+ n_chunks_--;
+ stats.n_frees++;
+ stats.currently_allocated -= h->map_size;
+ }
+ MapUnmapCallback().OnUnmap(h->map_beg, h->map_size);
+ UnmapOrDie(reinterpret_cast<void*>(h->map_beg), h->map_size);
+ }
+
+ uptr TotalMemoryUsed() {
+ SpinMutexLock l(&mutex_);
+ uptr res = 0;
+ for (uptr i = 0; i < n_chunks_; i++) {
+ Header *h = chunks_[i];
+ CHECK_EQ(h->chunk_idx, i);
+ res += RoundUpMapSize(h->size);
+ }
+ return res;
+ }
+
+ bool PointerIsMine(void *p) {
+ return GetBlockBegin(p) != 0;
+ }
+
+ uptr GetActuallyAllocatedSize(void *p) {
+ return RoundUpTo(GetHeader(p)->size, page_size_);
+ }
+
+ // At least page_size_/2 metadata bytes is available.
+ void *GetMetaData(void *p) {
+ // Too slow: CHECK_EQ(p, GetBlockBegin(p));
+ CHECK(IsAligned(reinterpret_cast<uptr>(p), page_size_));
+ return GetHeader(p) + 1;
+ }
+
+ void *GetBlockBegin(void *ptr) {
+ uptr p = reinterpret_cast<uptr>(ptr);
+ SpinMutexLock l(&mutex_);
+ uptr nearest_chunk = 0;
+ // Cache-friendly linear search.
+ for (uptr i = 0; i < n_chunks_; i++) {
+ uptr ch = reinterpret_cast<uptr>(chunks_[i]);
+ if (p < ch) continue; // p is at left to this chunk, skip it.
+ if (p - ch < p - nearest_chunk)
+ nearest_chunk = ch;
+ }
+ if (!nearest_chunk)
+ return 0;
+ Header *h = reinterpret_cast<Header *>(nearest_chunk);
+ CHECK_GE(nearest_chunk, h->map_beg);
+ CHECK_LT(nearest_chunk, h->map_beg + h->map_size);
+ CHECK_LE(nearest_chunk, p);
+ if (h->map_beg + h->map_size < p)
+ return 0;
+ return GetUser(h);
+ }
+
+ void PrintStats() {
+ Printf("Stats: LargeMmapAllocator: allocated %zd times, "
+ "remains %zd (%zd K) max %zd M; by size logs: ",
+ stats.n_allocs, stats.n_allocs - stats.n_frees,
+ stats.currently_allocated >> 10, stats.max_allocated >> 20);
+ for (uptr i = 0; i < ARRAY_SIZE(stats.by_size_log); i++) {
+ uptr c = stats.by_size_log[i];
+ if (!c) continue;
+ Printf("%zd:%zd; ", i, c);
+ }
+ Printf("\n");
+ }
+
+ private:
+ static const int kMaxNumChunks = 1 << FIRST_32_SECOND_64(15, 18);
+ struct Header {
+ uptr map_beg;
+ uptr map_size;
+ uptr size;
+ uptr chunk_idx;
+ };
+
+ Header *GetHeader(uptr p) {
+ CHECK_EQ(p % page_size_, 0);
+ return reinterpret_cast<Header*>(p - page_size_);
+ }
+ Header *GetHeader(void *p) { return GetHeader(reinterpret_cast<uptr>(p)); }
+
+ void *GetUser(Header *h) {
+ CHECK_EQ((uptr)h % page_size_, 0);
+ return reinterpret_cast<void*>(reinterpret_cast<uptr>(h) + page_size_);
+ }
+
+ uptr RoundUpMapSize(uptr size) {
+ return RoundUpTo(size, page_size_) + page_size_;
+ }
+
+ uptr page_size_;
+ Header *chunks_[kMaxNumChunks];
+ uptr n_chunks_;
+ struct Stats {
+ uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64];
+ } stats;
+ SpinMutex mutex_;
+};
+
+// This class implements a complete memory allocator by using two
+// internal allocators:
+// PrimaryAllocator is efficient, but may not allocate some sizes (alignments).
+// When allocating 2^x bytes it should return 2^x aligned chunk.
+// PrimaryAllocator is used via a local AllocatorCache.
+// SecondaryAllocator can allocate anything, but is not efficient.
+template <class PrimaryAllocator, class AllocatorCache,
+ class SecondaryAllocator> // NOLINT
+class CombinedAllocator {
+ public:
+ void Init() {
+ primary_.Init();
+ secondary_.Init();
+ }
+
+ void *Allocate(AllocatorCache *cache, uptr size, uptr alignment,
+ bool cleared = false) {
+ // Returning 0 on malloc(0) may break a lot of code.
+ if (size == 0)
+ size = 1;
+ if (size + alignment < size)
+ return 0;
+ if (alignment > 8)
+ size = RoundUpTo(size, alignment);
+ void *res;
+ if (primary_.CanAllocate(size, alignment))
+ res = cache->Allocate(&primary_, primary_.ClassID(size));
+ else
+ res = secondary_.Allocate(size, alignment);
+ if (alignment > 8)
+ CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0);
+ if (cleared && res)
+ internal_memset(res, 0, size);
+ return res;
+ }
+
+ void Deallocate(AllocatorCache *cache, void *p) {
+ if (!p) return;
+ if (primary_.PointerIsMine(p))
+ cache->Deallocate(&primary_, primary_.GetSizeClass(p), p);
+ else
+ secondary_.Deallocate(p);
+ }
+
+ void *Reallocate(AllocatorCache *cache, void *p, uptr new_size,
+ uptr alignment) {
+ if (!p)
+ return Allocate(cache, new_size, alignment);
+ if (!new_size) {
+ Deallocate(cache, p);
+ return 0;
+ }
+ CHECK(PointerIsMine(p));
+ uptr old_size = GetActuallyAllocatedSize(p);
+ uptr memcpy_size = Min(new_size, old_size);
+ void *new_p = Allocate(cache, new_size, alignment);
+ if (new_p)
+ internal_memcpy(new_p, p, memcpy_size);
+ Deallocate(cache, p);
+ return new_p;
+ }
+
+ bool PointerIsMine(void *p) {
+ if (primary_.PointerIsMine(p))
+ return true;
+ return secondary_.PointerIsMine(p);
+ }
+
+ bool FromPrimary(void *p) {
+ return primary_.PointerIsMine(p);
+ }
+
+ void *GetMetaData(void *p) {
+ if (primary_.PointerIsMine(p))
+ return primary_.GetMetaData(p);
+ return secondary_.GetMetaData(p);
+ }
+
+ void *GetBlockBegin(void *p) {
+ if (primary_.PointerIsMine(p))
+ return primary_.GetBlockBegin(p);
+ return secondary_.GetBlockBegin(p);
+ }
+
+ uptr GetActuallyAllocatedSize(void *p) {
+ if (primary_.PointerIsMine(p))
+ return primary_.GetActuallyAllocatedSize(p);
+ return secondary_.GetActuallyAllocatedSize(p);
+ }
+
+ uptr TotalMemoryUsed() {
+ return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed();
+ }
+
+ void TestOnlyUnmap() { primary_.TestOnlyUnmap(); }
+
+ void SwallowCache(AllocatorCache *cache) {
+ cache->Drain(&primary_);
+ }
+
+ void PrintStats() {
+ primary_.PrintStats();
+ secondary_.PrintStats();
+ }
+
+ private:
+ PrimaryAllocator primary_;
+ SecondaryAllocator secondary_;
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_ALLOCATOR_H
+
diff --git a/lib/sanitizer_common/sanitizer_allocator64.h b/lib/sanitizer_common/sanitizer_allocator64.h
deleted file mode 100644
index eb79a128c1cc..000000000000
--- a/lib/sanitizer_common/sanitizer_allocator64.h
+++ /dev/null
@@ -1,488 +0,0 @@
-//===-- sanitizer_allocator64.h ---------------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// Specialized allocator which works only in 64-bit address space.
-// To be used by ThreadSanitizer, MemorySanitizer and possibly other tools.
-// The main feature of this allocator is that the header is located far away
-// from the user memory region, so that the tool does not use extra shadow
-// for the header.
-//
-// Status: not yet ready.
-//===----------------------------------------------------------------------===//
-#ifndef SANITIZER_ALLOCATOR_H
-#define SANITIZER_ALLOCATOR_H
-
-#include "sanitizer_common.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_list.h"
-#include "sanitizer_mutex.h"
-
-namespace __sanitizer {
-
-// Maps size class id to size and back.
-class DefaultSizeClassMap {
- private:
- // Here we use a spline composed of 5 polynomials of oder 1.
- // The first size class is l0, then the classes go with step s0
- // untill they reach l1, after which they go with step s1 and so on.
- // Steps should be powers of two for cheap division.
- // The size of the last size class should be a power of two.
- // There should be at most 256 size classes.
- static const uptr l0 = 1 << 4;
- static const uptr l1 = 1 << 9;
- static const uptr l2 = 1 << 12;
- static const uptr l3 = 1 << 15;
- static const uptr l4 = 1 << 18;
- static const uptr l5 = 1 << 21;
-
- static const uptr s0 = 1 << 4;
- static const uptr s1 = 1 << 6;
- static const uptr s2 = 1 << 9;
- static const uptr s3 = 1 << 12;
- static const uptr s4 = 1 << 15;
-
- static const uptr u0 = 0 + (l1 - l0) / s0;
- static const uptr u1 = u0 + (l2 - l1) / s1;
- static const uptr u2 = u1 + (l3 - l2) / s2;
- static const uptr u3 = u2 + (l4 - l3) / s3;
- static const uptr u4 = u3 + (l5 - l4) / s4;
-
- public:
- static const uptr kNumClasses = u4 + 1;
- static const uptr kMaxSize = l5;
- static const uptr kMinSize = l0;
-
- COMPILER_CHECK(kNumClasses <= 256);
- COMPILER_CHECK((kMaxSize & (kMaxSize - 1)) == 0);
-
- static uptr Size(uptr class_id) {
- if (class_id <= u0) return l0 + s0 * (class_id - 0);
- if (class_id <= u1) return l1 + s1 * (class_id - u0);
- if (class_id <= u2) return l2 + s2 * (class_id - u1);
- if (class_id <= u3) return l3 + s3 * (class_id - u2);
- if (class_id <= u4) return l4 + s4 * (class_id - u3);
- return 0;
- }
- static uptr ClassID(uptr size) {
- if (size <= l1) return 0 + (size - l0 + s0 - 1) / s0;
- if (size <= l2) return u0 + (size - l1 + s1 - 1) / s1;
- if (size <= l3) return u1 + (size - l2 + s2 - 1) / s2;
- if (size <= l4) return u2 + (size - l3 + s3 - 1) / s3;
- if (size <= l5) return u3 + (size - l4 + s4 - 1) / s4;
- return 0;
- }
-};
-
-struct AllocatorListNode {
- AllocatorListNode *next;
-};
-
-typedef IntrusiveList<AllocatorListNode> AllocatorFreeList;
-
-
-// Space: a portion of address space of kSpaceSize bytes starting at
-// a fixed address (kSpaceBeg). Both constants are powers of two and
-// kSpaceBeg is kSpaceSize-aligned.
-//
-// Region: a part of Space dedicated to a single size class.
-// There are kNumClasses Regions of equal size.
-//
-// UserChunk: a piece of memory returned to user.
-// MetaChunk: kMetadataSize bytes of metadata associated with a UserChunk.
-//
-// A Region looks like this:
-// UserChunk1 ... UserChunkN <gap> MetaChunkN ... MetaChunk1
-template <const uptr kSpaceBeg, const uptr kSpaceSize,
- const uptr kMetadataSize, class SizeClassMap>
-class SizeClassAllocator64 {
- public:
- void Init() {
- CHECK_EQ(AllocBeg(), reinterpret_cast<uptr>(MmapFixedNoReserve(
- AllocBeg(), AllocSize())));
- }
-
- bool CanAllocate(uptr size, uptr alignment) {
- return size <= SizeClassMap::kMaxSize &&
- alignment <= SizeClassMap::kMaxSize;
- }
-
- void *Allocate(uptr size, uptr alignment) {
- CHECK(CanAllocate(size, alignment));
- return AllocateBySizeClass(SizeClassMap::ClassID(size));
- }
-
- void Deallocate(void *p) {
- CHECK(PointerIsMine(p));
- DeallocateBySizeClass(p, GetSizeClass(p));
- }
-
- // Allocate several chunks of the given class_id.
- void BulkAllocate(uptr class_id, AllocatorFreeList *free_list) {
- CHECK_LT(class_id, kNumClasses);
- RegionInfo *region = GetRegionInfo(class_id);
- SpinMutexLock l(&region->mutex);
- if (region->free_list.empty()) {
- PopulateFreeList(class_id, region);
- }
- CHECK(!region->free_list.empty());
- // Just take as many chunks as we have in the free list now.
- // FIXME: this might be too much.
- free_list->append_front(&region->free_list);
- CHECK(region->free_list.empty());
- }
-
- // Swallow the entire free_list for the given class_id.
- void BulkDeallocate(uptr class_id, AllocatorFreeList *free_list) {
- CHECK_LT(class_id, kNumClasses);
- RegionInfo *region = GetRegionInfo(class_id);
- SpinMutexLock l(&region->mutex);
- region->free_list.append_front(free_list);
- }
-
- bool PointerIsMine(void *p) {
- return reinterpret_cast<uptr>(p) / kSpaceSize == kSpaceBeg / kSpaceSize;
- }
- uptr GetSizeClass(void *p) {
- return (reinterpret_cast<uptr>(p) / kRegionSize) % kNumClasses;
- }
-
- uptr GetActuallyAllocatedSize(void *p) {
- CHECK(PointerIsMine(p));
- return SizeClassMap::Size(GetSizeClass(p));
- }
-
- uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); }
-
- void *GetMetaData(void *p) {
- uptr class_id = GetSizeClass(p);
- uptr chunk_idx = GetChunkIdx(reinterpret_cast<uptr>(p), class_id);
- return reinterpret_cast<void*>(kSpaceBeg + (kRegionSize * (class_id + 1)) -
- (1 + chunk_idx) * kMetadataSize);
- }
-
- uptr TotalMemoryUsed() {
- uptr res = 0;
- for (uptr i = 0; i < kNumClasses; i++)
- res += GetRegionInfo(i)->allocated_user;
- return res;
- }
-
- // Test-only.
- void TestOnlyUnmap() {
- UnmapOrDie(reinterpret_cast<void*>(AllocBeg()), AllocSize());
- }
-
- static const uptr kNumClasses = 256; // Power of two <= 256
-
- private:
- COMPILER_CHECK(kNumClasses <= SizeClassMap::kNumClasses);
- static const uptr kRegionSize = kSpaceSize / kNumClasses;
- COMPILER_CHECK((kRegionSize >> 32) > 0); // kRegionSize must be >= 2^32.
- // Populate the free list with at most this number of bytes at once
- // or with one element if its size is greater.
- static const uptr kPopulateSize = 1 << 18;
-
- struct RegionInfo {
- SpinMutex mutex;
- AllocatorFreeList free_list;
- uptr allocated_user; // Bytes allocated for user memory.
- uptr allocated_meta; // Bytes allocated for metadata.
- char padding[kCacheLineSize - 3 * sizeof(uptr) - sizeof(AllocatorFreeList)];
- };
- COMPILER_CHECK(sizeof(RegionInfo) == kCacheLineSize);
-
- uptr AdditionalSize() {
- uptr res = sizeof(RegionInfo) * kNumClasses;
- CHECK_EQ(res % kPageSize, 0);
- return res;
- }
- uptr AllocBeg() { return kSpaceBeg - AdditionalSize(); }
- uptr AllocSize() { return kSpaceSize + AdditionalSize(); }
-
- RegionInfo *GetRegionInfo(uptr class_id) {
- CHECK_LT(class_id, kNumClasses);
- RegionInfo *regions = reinterpret_cast<RegionInfo*>(kSpaceBeg);
- return &regions[-1 - class_id];
- }
-
- uptr GetChunkIdx(uptr chunk, uptr class_id) {
- u32 offset = chunk % kRegionSize;
- // Here we divide by a non-constant. This is costly.
- // We require that kRegionSize is at least 2^32 so that offset is 32-bit.
- // We save 2x by using 32-bit div, but may need to use a 256-way switch.
- return offset / (u32)SizeClassMap::Size(class_id);
- }
-
- void PopulateFreeList(uptr class_id, RegionInfo *region) {
- uptr size = SizeClassMap::Size(class_id);
- uptr beg_idx = region->allocated_user;
- uptr end_idx = beg_idx + kPopulateSize;
- region->free_list.clear();
- uptr region_beg = kSpaceBeg + kRegionSize * class_id;
- uptr idx = beg_idx;
- uptr i = 0;
- do { // do-while loop because we need to put at least one item.
- uptr p = region_beg + idx;
- region->free_list.push_front(reinterpret_cast<AllocatorListNode*>(p));
- idx += size;
- i++;
- } while (idx < end_idx);
- region->allocated_user += idx - beg_idx;
- region->allocated_meta += i * kMetadataSize;
- CHECK_LT(region->allocated_user + region->allocated_meta, kRegionSize);
- }
-
- void *AllocateBySizeClass(uptr class_id) {
- CHECK_LT(class_id, kNumClasses);
- RegionInfo *region = GetRegionInfo(class_id);
- SpinMutexLock l(&region->mutex);
- if (region->free_list.empty()) {
- PopulateFreeList(class_id, region);
- }
- CHECK(!region->free_list.empty());
- AllocatorListNode *node = region->free_list.front();
- region->free_list.pop_front();
- return reinterpret_cast<void*>(node);
- }
-
- void DeallocateBySizeClass(void *p, uptr class_id) {
- RegionInfo *region = GetRegionInfo(class_id);
- SpinMutexLock l(&region->mutex);
- region->free_list.push_front(reinterpret_cast<AllocatorListNode*>(p));
- }
-};
-
-// Objects of this type should be used as local caches for SizeClassAllocator64.
-// Since the typical use of this class is to have one object per thread in TLS,
-// is has to be POD.
-template<const uptr kNumClasses, class SizeClassAllocator>
-struct SizeClassAllocatorLocalCache {
- // Don't need to call Init if the object is a global (i.e. zero-initialized).
- void Init() {
- internal_memset(this, 0, sizeof(*this));
- }
-
- void *Allocate(SizeClassAllocator *allocator, uptr class_id) {
- CHECK_LT(class_id, kNumClasses);
- AllocatorFreeList *free_list = &free_lists_[class_id];
- if (free_list->empty())
- allocator->BulkAllocate(class_id, free_list);
- CHECK(!free_list->empty());
- void *res = free_list->front();
- free_list->pop_front();
- return res;
- }
-
- void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) {
- CHECK_LT(class_id, kNumClasses);
- free_lists_[class_id].push_front(reinterpret_cast<AllocatorListNode*>(p));
- }
-
- void Drain(SizeClassAllocator *allocator) {
- for (uptr i = 0; i < kNumClasses; i++) {
- allocator->BulkDeallocate(i, &free_lists_[i]);
- CHECK(free_lists_[i].empty());
- }
- }
-
- // private:
- AllocatorFreeList free_lists_[kNumClasses];
-};
-
-// This class can (de)allocate only large chunks of memory using mmap/unmap.
-// The main purpose of this allocator is to cover large and rare allocation
-// sizes not covered by more efficient allocators (e.g. SizeClassAllocator64).
-// The result is always page-aligned.
-class LargeMmapAllocator {
- public:
- void Init() {
- internal_memset(this, 0, sizeof(*this));
- }
- void *Allocate(uptr size, uptr alignment) {
- CHECK_LE(alignment, kPageSize); // Not implemented. Do we need it?
- uptr map_size = RoundUpMapSize(size);
- void *map = MmapOrDie(map_size, "LargeMmapAllocator");
- void *res = reinterpret_cast<void*>(reinterpret_cast<uptr>(map)
- + kPageSize);
- Header *h = GetHeader(res);
- h->size = size;
- {
- SpinMutexLock l(&mutex_);
- h->next = list_;
- h->prev = 0;
- if (list_)
- list_->prev = h;
- list_ = h;
- }
- return res;
- }
-
- void Deallocate(void *p) {
- Header *h = GetHeader(p);
- uptr map_size = RoundUpMapSize(h->size);
- {
- SpinMutexLock l(&mutex_);
- Header *prev = h->prev;
- Header *next = h->next;
- if (prev)
- prev->next = next;
- if (next)
- next->prev = prev;
- if (h == list_)
- list_ = next;
- }
- UnmapOrDie(h, map_size);
- }
-
- uptr TotalMemoryUsed() {
- SpinMutexLock l(&mutex_);
- uptr res = 0;
- for (Header *l = list_; l; l = l->next) {
- res += RoundUpMapSize(l->size);
- }
- return res;
- }
-
- bool PointerIsMine(void *p) {
- // Fast check.
- if ((reinterpret_cast<uptr>(p) % kPageSize) != 0) return false;
- SpinMutexLock l(&mutex_);
- for (Header *l = list_; l; l = l->next) {
- if (GetUser(l) == p) return true;
- }
- return false;
- }
-
- uptr GetActuallyAllocatedSize(void *p) {
- return RoundUpMapSize(GetHeader(p)->size) - kPageSize;
- }
-
- // At least kPageSize/2 metadata bytes is available.
- void *GetMetaData(void *p) {
- return GetHeader(p) + 1;
- }
-
- private:
- struct Header {
- uptr size;
- Header *next;
- Header *prev;
- };
-
- Header *GetHeader(void *p) {
- return reinterpret_cast<Header*>(reinterpret_cast<uptr>(p) - kPageSize);
- }
-
- void *GetUser(Header *h) {
- return reinterpret_cast<void*>(reinterpret_cast<uptr>(h) + kPageSize);
- }
-
- uptr RoundUpMapSize(uptr size) {
- return RoundUpTo(size, kPageSize) + kPageSize;
- }
-
- Header *list_;
- SpinMutex mutex_;
-};
-
-// This class implements a complete memory allocator by using two
-// internal allocators:
-// PrimaryAllocator is efficient, but may not allocate some sizes (alignments).
-// When allocating 2^x bytes it should return 2^x aligned chunk.
-// PrimaryAllocator is used via a local AllocatorCache.
-// SecondaryAllocator can allocate anything, but is not efficient.
-template <class PrimaryAllocator, class AllocatorCache,
- class SecondaryAllocator> // NOLINT
-class CombinedAllocator {
- public:
- void Init() {
- primary_.Init();
- secondary_.Init();
- }
-
- void *Allocate(AllocatorCache *cache, uptr size, uptr alignment,
- bool cleared = false) {
- // Returning 0 on malloc(0) may break a lot of code.
- if (size == 0) size = 1;
- if (alignment > 8)
- size = RoundUpTo(size, alignment);
- void *res;
- if (primary_.CanAllocate(size, alignment))
- res = cache->Allocate(&primary_, primary_.ClassID(size));
- else
- res = secondary_.Allocate(size, alignment);
- if (alignment > 8)
- CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0);
- if (cleared)
- internal_memset(res, 0, size);
- return res;
- }
-
- void Deallocate(AllocatorCache *cache, void *p) {
- if (!p) return;
- if (primary_.PointerIsMine(p))
- cache->Deallocate(&primary_, primary_.GetSizeClass(p), p);
- else
- secondary_.Deallocate(p);
- }
-
- void *Reallocate(AllocatorCache *cache, void *p, uptr new_size,
- uptr alignment) {
- if (!p)
- return Allocate(cache, new_size, alignment);
- if (!new_size) {
- Deallocate(cache, p);
- return 0;
- }
- CHECK(PointerIsMine(p));
- uptr old_size = GetActuallyAllocatedSize(p);
- uptr memcpy_size = Min(new_size, old_size);
- void *new_p = Allocate(cache, new_size, alignment);
- if (new_p)
- internal_memcpy(new_p, p, memcpy_size);
- Deallocate(cache, p);
- return new_p;
- }
-
- bool PointerIsMine(void *p) {
- if (primary_.PointerIsMine(p))
- return true;
- return secondary_.PointerIsMine(p);
- }
-
- void *GetMetaData(void *p) {
- if (primary_.PointerIsMine(p))
- return primary_.GetMetaData(p);
- return secondary_.GetMetaData(p);
- }
-
- uptr GetActuallyAllocatedSize(void *p) {
- if (primary_.PointerIsMine(p))
- return primary_.GetActuallyAllocatedSize(p);
- return secondary_.GetActuallyAllocatedSize(p);
- }
-
- uptr TotalMemoryUsed() {
- return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed();
- }
-
- void TestOnlyUnmap() { primary_.TestOnlyUnmap(); }
-
- void SwallowCache(AllocatorCache *cache) {
- cache->Drain(&primary_);
- }
-
- private:
- PrimaryAllocator primary_;
- SecondaryAllocator secondary_;
-};
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_ALLOCATOR_H
diff --git a/lib/sanitizer_common/sanitizer_atomic_clang.h b/lib/sanitizer_common/sanitizer_atomic_clang.h
index af7044165a61..7f73df3bd455 100644
--- a/lib/sanitizer_common/sanitizer_atomic_clang.h
+++ b/lib/sanitizer_common/sanitizer_atomic_clang.h
@@ -41,6 +41,7 @@ INLINE typename T::Type atomic_load(
| memory_order_acquire | memory_order_seq_cst));
DCHECK(!((uptr)a % sizeof(*a)));
typename T::Type v;
+ // FIXME(dvyukov): 64-bit load is not atomic on 32-bits.
if (mo == memory_order_relaxed) {
v = a->val_dont_use;
} else {
@@ -56,6 +57,7 @@ INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
DCHECK(mo & (memory_order_relaxed | memory_order_release
| memory_order_seq_cst));
DCHECK(!((uptr)a % sizeof(*a)));
+ // FIXME(dvyukov): 64-bit store is not atomic on 32-bits.
if (mo == memory_order_relaxed) {
a->val_dont_use = v;
} else {
diff --git a/lib/sanitizer_common/sanitizer_atomic_msvc.h b/lib/sanitizer_common/sanitizer_atomic_msvc.h
index 2a15b59a3442..58a6a20ec9c5 100644
--- a/lib/sanitizer_common/sanitizer_atomic_msvc.h
+++ b/lib/sanitizer_common/sanitizer_atomic_msvc.h
@@ -25,6 +25,31 @@ extern "C" long _InterlockedExchangeAdd( // NOLINT
long volatile * Addend, long Value); // NOLINT
#pragma intrinsic(_InterlockedExchangeAdd)
+#ifdef _WIN64
+extern "C" void *_InterlockedCompareExchangePointer(
+ void *volatile *Destination,
+ void *Exchange, void *Comparand);
+#pragma intrinsic(_InterlockedCompareExchangePointer)
+#else
+// There's no _InterlockedCompareExchangePointer intrinsic on x86,
+// so call _InterlockedCompareExchange instead.
+extern "C"
+long __cdecl _InterlockedCompareExchange( // NOLINT
+ long volatile *Destination, // NOLINT
+ long Exchange, long Comparand); // NOLINT
+#pragma intrinsic(_InterlockedCompareExchange)
+
+inline static void *_InterlockedCompareExchangePointer(
+ void *volatile *Destination,
+ void *Exchange, void *Comparand) {
+ return reinterpret_cast<void*>(
+ _InterlockedCompareExchange(
+ reinterpret_cast<long volatile*>(Destination), // NOLINT
+ reinterpret_cast<long>(Exchange), // NOLINT
+ reinterpret_cast<long>(Comparand))); // NOLINT
+}
+#endif
+
namespace __sanitizer {
INLINE void atomic_signal_fence(memory_order) {
@@ -47,6 +72,7 @@ INLINE typename T::Type atomic_load(
| memory_order_acquire | memory_order_seq_cst));
DCHECK(!((uptr)a % sizeof(*a)));
typename T::Type v;
+ // FIXME(dvyukov): 64-bit load is not atomic on 32-bits.
if (mo == memory_order_relaxed) {
v = a->val_dont_use;
} else {
@@ -62,6 +88,7 @@ INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
DCHECK(mo & (memory_order_relaxed | memory_order_release
| memory_order_seq_cst));
DCHECK(!((uptr)a % sizeof(*a)));
+ // FIXME(dvyukov): 64-bit store is not atomic on 32-bits.
if (mo == memory_order_relaxed) {
a->val_dont_use = v;
} else {
@@ -107,6 +134,27 @@ INLINE u16 atomic_exchange(volatile atomic_uint16_t *a,
return v;
}
+INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a,
+ uptr *cmp,
+ uptr xchg,
+ memory_order mo) {
+ uptr cmpv = *cmp;
+ uptr prev = (uptr)_InterlockedCompareExchangePointer(
+ (void*volatile*)&a->val_dont_use, (void*)xchg, (void*)cmpv);
+ if (prev == cmpv)
+ return true;
+ *cmp = prev;
+ return false;
+}
+
+template<typename T>
+INLINE bool atomic_compare_exchange_weak(volatile T *a,
+ typename T::Type *cmp,
+ typename T::Type xchg,
+ memory_order mo) {
+ return atomic_compare_exchange_strong(a, cmp, xchg, mo);
+}
+
} // namespace __sanitizer
#endif // SANITIZER_ATOMIC_CLANG_H
diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc
index 6dd1ff91726e..4a8d9a749bf8 100644
--- a/lib/sanitizer_common/sanitizer_common.cc
+++ b/lib/sanitizer_common/sanitizer_common.cc
@@ -16,18 +16,90 @@
namespace __sanitizer {
+uptr GetPageSizeCached() {
+ static uptr PageSize;
+ if (!PageSize)
+ PageSize = GetPageSize();
+ return PageSize;
+}
+
+static bool log_to_file = false; // Set to true by __sanitizer_set_report_path
+
+// By default, dump to stderr. If |log_to_file| is true and |report_fd_pid|
+// isn't equal to the current PID, try to obtain file descriptor by opening
+// file "report_path_prefix.<PID>".
+static fd_t report_fd = kStderrFd;
+static char report_path_prefix[4096]; // Set via __sanitizer_set_report_path.
+// PID of process that opened |report_fd|. If a fork() occurs, the PID of the
+// child thread will be different from |report_fd_pid|.
+static int report_fd_pid = 0;
+
+static void (*DieCallback)(void);
+void SetDieCallback(void (*callback)(void)) {
+ DieCallback = callback;
+}
+
+void NORETURN Die() {
+ if (DieCallback) {
+ DieCallback();
+ }
+ Exit(1);
+}
+
+static CheckFailedCallbackType CheckFailedCallback;
+void SetCheckFailedCallback(CheckFailedCallbackType callback) {
+ CheckFailedCallback = callback;
+}
+
+void NORETURN CheckFailed(const char *file, int line, const char *cond,
+ u64 v1, u64 v2) {
+ if (CheckFailedCallback) {
+ CheckFailedCallback(file, line, cond, v1, v2);
+ }
+ Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond,
+ v1, v2);
+ Die();
+}
+
+static void MaybeOpenReportFile() {
+ if (!log_to_file || (report_fd_pid == GetPid())) return;
+ char report_path_full[4096];
+ internal_snprintf(report_path_full, sizeof(report_path_full),
+ "%s.%d", report_path_prefix, GetPid());
+ fd_t fd = internal_open(report_path_full, true);
+ if (fd == kInvalidFd) {
+ report_fd = kStderrFd;
+ log_to_file = false;
+ Report("ERROR: Can't open file: %s\n", report_path_full);
+ Die();
+ }
+ if (report_fd != kInvalidFd) {
+ // We're in the child. Close the parent's log.
+ internal_close(report_fd);
+ }
+ report_fd = fd;
+ report_fd_pid = GetPid();
+}
+
+bool PrintsToTty() {
+ MaybeOpenReportFile();
+ return internal_isatty(report_fd);
+}
+
void RawWrite(const char *buffer) {
static const char *kRawWriteError = "RawWrite can't output requested buffer!";
uptr length = (uptr)internal_strlen(buffer);
- if (length != internal_write(2, buffer, length)) {
- internal_write(2, kRawWriteError, internal_strlen(kRawWriteError));
+ MaybeOpenReportFile();
+ if (length != internal_write(report_fd, buffer, length)) {
+ internal_write(report_fd, kRawWriteError, internal_strlen(kRawWriteError));
Die();
}
}
uptr ReadFileToBuffer(const char *file_name, char **buff,
uptr *buff_size, uptr max_len) {
- const uptr kMinFileLen = kPageSize;
+ uptr PageSize = GetPageSizeCached();
+ uptr kMinFileLen = PageSize;
uptr read_len = 0;
*buff = 0;
*buff_size = 0;
@@ -41,8 +113,8 @@ uptr ReadFileToBuffer(const char *file_name, char **buff,
// Read up to one page at a time.
read_len = 0;
bool reached_eof = false;
- while (read_len + kPageSize <= size) {
- uptr just_read = internal_read(fd, *buff + read_len, kPageSize);
+ while (read_len + PageSize <= size) {
+ uptr just_read = internal_read(fd, *buff + read_len, PageSize);
if (just_read == 0) {
reached_eof = true;
break;
@@ -97,4 +169,57 @@ void SortArray(uptr *array, uptr size) {
}
}
+// We want to map a chunk of address space aligned to 'alignment'.
+// We do it by maping a bit more and then unmaping redundant pieces.
+// We probably can do it with fewer syscalls in some OS-dependent way.
+void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) {
+// uptr PageSize = GetPageSizeCached();
+ CHECK(IsPowerOfTwo(size));
+ CHECK(IsPowerOfTwo(alignment));
+ uptr map_size = size + alignment;
+ uptr map_res = (uptr)MmapOrDie(map_size, mem_type);
+ uptr map_end = map_res + map_size;
+ uptr res = map_res;
+ if (res & (alignment - 1)) // Not aligned.
+ res = (map_res + alignment) & ~(alignment - 1);
+ uptr end = res + size;
+ if (res != map_res)
+ UnmapOrDie((void*)map_res, res - map_res);
+ if (end != map_end)
+ UnmapOrDie((void*)end, map_end - end);
+ return (void*)res;
+}
+
} // namespace __sanitizer
+
+using namespace __sanitizer; // NOLINT
+
+extern "C" {
+void __sanitizer_set_report_path(const char *path) {
+ if (!path) return;
+ uptr len = internal_strlen(path);
+ if (len > sizeof(report_path_prefix) - 100) {
+ Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
+ path[0], path[1], path[2], path[3],
+ path[4], path[5], path[6], path[7]);
+ Die();
+ }
+ internal_strncpy(report_path_prefix, path, sizeof(report_path_prefix));
+ report_path_prefix[len] = '\0';
+ report_fd = kInvalidFd;
+ log_to_file = true;
+}
+
+void __sanitizer_set_report_fd(int fd) {
+ if (report_fd != kStdoutFd &&
+ report_fd != kStderrFd &&
+ report_fd != kInvalidFd)
+ internal_close(report_fd);
+ report_fd = fd;
+}
+
+void NOINLINE __sanitizer_sandbox_on_notify(void *reserved) {
+ (void)reserved;
+ PrepareForSandboxing();
+}
+} // extern "C"
diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h
index 4c7c1e9d86ed..1d002398c785 100644
--- a/lib/sanitizer_common/sanitizer_common.h
+++ b/lib/sanitizer_common/sanitizer_common.h
@@ -21,19 +21,21 @@
namespace __sanitizer {
// Constants.
-const uptr kWordSize = __WORDSIZE / 8;
+const uptr kWordSize = SANITIZER_WORDSIZE / 8;
const uptr kWordSizeInBits = 8 * kWordSize;
-const uptr kPageSizeBits = 12;
-const uptr kPageSize = 1UL << kPageSizeBits;
-const uptr kCacheLineSize = 64;
-#ifndef _WIN32
-const uptr kMmapGranularity = kPageSize;
+
+#if defined(__powerpc__) || defined(__powerpc64__)
+const uptr kCacheLineSize = 128;
#else
-const uptr kMmapGranularity = 1UL << 16;
+const uptr kCacheLineSize = 64;
#endif
+uptr GetPageSize();
+uptr GetPageSizeCached();
+uptr GetMmapGranularity();
// Threads
int GetPid();
+uptr GetTid();
uptr GetThreadSelf();
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
uptr *stack_bottom);
@@ -42,21 +44,66 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
void *MmapOrDie(uptr size, const char *mem_type);
void UnmapOrDie(void *addr, uptr size);
void *MmapFixedNoReserve(uptr fixed_addr, uptr size);
+void *MmapFixedOrDie(uptr fixed_addr, uptr size);
void *Mprotect(uptr fixed_addr, uptr size);
+// Map aligned chunk of address space; size and alignment are powers of two.
+void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type);
// Used to check if we can map shadow memory to a fixed location.
bool MemoryRangeIsAvailable(uptr range_start, uptr range_end);
+void FlushUnneededShadowMemory(uptr addr, uptr size);
// Internal allocator
void *InternalAlloc(uptr size);
void InternalFree(void *p);
-// Given the pointer p into a valid allocated block,
-// returns a pointer to the beginning of the block.
-void *InternalAllocBlock(void *p);
+
+// InternalScopedBuffer can be used instead of large stack arrays to
+// keep frame size low.
+// FIXME: use InternalAlloc instead of MmapOrDie once
+// InternalAlloc is made libc-free.
+template<typename T>
+class InternalScopedBuffer {
+ public:
+ explicit InternalScopedBuffer(uptr cnt) {
+ cnt_ = cnt;
+ ptr_ = (T*)MmapOrDie(cnt * sizeof(T), "InternalScopedBuffer");
+ }
+ ~InternalScopedBuffer() {
+ UnmapOrDie(ptr_, cnt_ * sizeof(T));
+ }
+ T &operator[](uptr i) { return ptr_[i]; }
+ T *data() { return ptr_; }
+ uptr size() { return cnt_ * sizeof(T); }
+
+ private:
+ T *ptr_;
+ uptr cnt_;
+ // Disallow evil constructors.
+ InternalScopedBuffer(const InternalScopedBuffer&);
+ void operator=(const InternalScopedBuffer&);
+};
+
+// Simple low-level (mmap-based) allocator for internal use. Doesn't have
+// constructor, so all instances of LowLevelAllocator should be
+// linker initialized.
+class LowLevelAllocator {
+ public:
+ // Requires an external lock.
+ void *Allocate(uptr size);
+ private:
+ char *allocated_end_;
+ char *allocated_current_;
+};
+typedef void (*LowLevelAllocateCallback)(uptr ptr, uptr size);
+// Allows to register tool-specific callbacks for LowLevelAllocator.
+// Passing NULL removes the callback.
+void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback);
// IO
void RawWrite(const char *buffer);
+bool PrintsToTty();
void Printf(const char *format, ...);
void Report(const char *format, ...);
+void SetPrintfAndReportCallback(void (*callback)(const char *));
// Opens the file 'file_name" and reads up to 'max_len' bytes.
// The resulting buffer is mmaped and stored in '*buff'.
@@ -69,19 +116,44 @@ uptr ReadFileToBuffer(const char *file_name, char **buff,
// in '*buff_size'.
void *MapFileToMemory(const char *file_name, uptr *buff_size);
+// OS
+void DisableCoreDumper();
+void DumpProcessMap();
+bool FileExists(const char *filename);
const char *GetEnv(const char *name);
const char *GetPwd();
+void ReExec();
+bool StackSizeIsUnlimited();
+void SetStackSizeLimitInBytes(uptr limit);
+void PrepareForSandboxing();
// Other
-void DisableCoreDumper();
-void DumpProcessMap();
void SleepForSeconds(int seconds);
void SleepForMillis(int millis);
-void NORETURN Exit(int exitcode);
-void NORETURN Abort();
int Atexit(void (*function)(void));
void SortArray(uptr *array, uptr size);
+// Exit
+void NORETURN Abort();
+void NORETURN Exit(int exitcode);
+void NORETURN Die();
+void NORETURN SANITIZER_INTERFACE_ATTRIBUTE
+CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2);
+
+// Set the name of the current thread to 'name', return true on succees.
+// The name may be truncated to a system-dependent limit.
+bool SanitizerSetThreadName(const char *name);
+// Get the name of the current thread (no more than max_len bytes),
+// return true on succees. name should have space for at least max_len+1 bytes.
+bool SanitizerGetThreadName(char *name, int max_len);
+
+// Specific tools may override behavior of "Die" and "CheckFailed" functions
+// to do tool-specific job.
+void SetDieCallback(void (*callback)(void));
+typedef void (*CheckFailedCallbackType)(const char *, int, const char *,
+ u64, u64);
+void SetCheckFailedCallback(CheckFailedCallbackType callback);
+
// Math
INLINE bool IsPowerOfTwo(uptr x) {
return (x & (x - 1)) == 0;
@@ -90,6 +162,12 @@ INLINE uptr RoundUpTo(uptr size, uptr boundary) {
CHECK(IsPowerOfTwo(boundary));
return (size + boundary - 1) & ~(boundary - 1);
}
+INLINE uptr RoundDownTo(uptr x, uptr boundary) {
+ return x & ~(boundary - 1);
+}
+INLINE bool IsAligned(uptr a, uptr alignment) {
+ return (a & (alignment - 1)) == 0;
+}
// Don't use std::min, std::max or std::swap, to minimize dependency
// on libstdc++.
template<class T> T Min(T a, T b) { return a < b ? a : b; }
@@ -112,7 +190,7 @@ INLINE int ToLower(int c) {
return (c >= 'A' && c <= 'Z') ? (c + 'a' - 'A') : c;
}
-#if __WORDSIZE == 64
+#if SANITIZER_WORDSIZE == 64
# define FIRST_32_SECOND_64(a, b) (b)
#else
# define FIRST_32_SECOND_64(a, b) (a)
diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc
new file mode 100644
index 000000000000..8bc2e8b5c292
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -0,0 +1,224 @@
+//===-- sanitizer_common_interceptors.inc -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Common function interceptors for tools like AddressSanitizer,
+// ThreadSanitizer, MemorySanitizer, etc.
+//
+// This file should be included into the tool's interceptor file,
+// which has to define it's own macros:
+// COMMON_INTERCEPTOR_ENTER
+// COMMON_INTERCEPTOR_READ_RANGE
+// COMMON_INTERCEPTOR_WRITE_RANGE
+// COMMON_INTERCEPTOR_FD_ACQUIRE
+// COMMON_INTERCEPTOR_FD_RELEASE
+// COMMON_INTERCEPTOR_SET_THREAD_NAME
+//===----------------------------------------------------------------------===//
+#include "interception/interception.h"
+#include "sanitizer_platform_interceptors.h"
+
+#include <stdarg.h>
+
+#if SANITIZER_INTERCEPT_READ
+INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, read, fd, ptr, count);
+ SSIZE_T res = REAL(read)(fd, ptr, count);
+ if (res > 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
+ if (res >= 0 && fd >= 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ return res;
+}
+# define INIT_READ INTERCEPT_FUNCTION(read)
+#else
+# define INIT_READ
+#endif
+
+#if SANITIZER_INTERCEPT_PREAD
+INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pread, fd, ptr, count, offset);
+ SSIZE_T res = REAL(pread)(fd, ptr, count, offset);
+ if (res > 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
+ if (res >= 0 && fd >= 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ return res;
+}
+# define INIT_PREAD INTERCEPT_FUNCTION(pread)
+#else
+# define INIT_PREAD
+#endif
+
+#if SANITIZER_INTERCEPT_PREAD64
+INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pread64, fd, ptr, count, offset);
+ SSIZE_T res = REAL(pread64)(fd, ptr, count, offset);
+ if (res > 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res);
+ if (res >= 0 && fd >= 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ return res;
+}
+# define INIT_PREAD64 INTERCEPT_FUNCTION(pread64)
+#else
+# define INIT_PREAD64
+#endif
+
+#if SANITIZER_INTERCEPT_WRITE
+INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count);
+ if (fd >= 0)
+ COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
+ SSIZE_T res = REAL(write)(fd, ptr, count);
+ if (res > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
+ return res;
+}
+# define INIT_WRITE INTERCEPT_FUNCTION(write)
+#else
+# define INIT_WRITE
+#endif
+
+#if SANITIZER_INTERCEPT_PWRITE
+INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count);
+ if (fd >= 0)
+ COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
+ SSIZE_T res = REAL(pwrite)(fd, ptr, count);
+ if (res > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
+ return res;
+}
+# define INIT_PWRITE INTERCEPT_FUNCTION(pwrite)
+#else
+# define INIT_PWRITE
+#endif
+
+#if SANITIZER_INTERCEPT_PWRITE64
+INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count);
+ if (fd >= 0)
+ COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd);
+ SSIZE_T res = REAL(pwrite64)(fd, ptr, count);
+ if (res > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res);
+ return res;
+}
+# define INIT_PWRITE64 INTERCEPT_FUNCTION(pwrite64)
+#else
+# define INIT_PWRITE64
+#endif
+
+#if SANITIZER_INTERCEPT_PRCTL
+INTERCEPTOR(int, prctl, int option,
+ unsigned long arg2, unsigned long arg3, // NOLINT
+ unsigned long arg4, unsigned long arg5) { // NOLINT
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5);
+ static const int PR_SET_NAME = 15;
+ int res = REAL(prctl(option, arg2, arg3, arg4, arg5));
+ if (option == PR_SET_NAME) {
+ char buff[16];
+ internal_strncpy(buff, (char*)arg2, 15);
+ buff[15] = 0;
+ COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff);
+ }
+ return res;
+}
+# define INIT_PRCTL INTERCEPT_FUNCTION(prctl)
+#else
+# define INIT_PRCTL
+#endif // SANITIZER_INTERCEPT_PRCTL
+
+
+#if SANITIZER_INTERCEPT_SCANF
+
+#include "sanitizer_common_interceptors_scanf.inc"
+
+INTERCEPTOR(int, vscanf, const char *format, va_list ap) { // NOLINT
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, vscanf, format, ap);
+ scanf_common(ctx, format, ap);
+ int res = REAL(vscanf)(format, ap); // NOLINT
+ return res;
+}
+
+INTERCEPTOR(int, vsscanf, const char *str, const char *format, // NOLINT
+ va_list ap) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, vsscanf, str, format, ap);
+ scanf_common(ctx, format, ap);
+ int res = REAL(vsscanf)(str, format, ap); // NOLINT
+ // FIXME: read of str
+ return res;
+}
+
+INTERCEPTOR(int, vfscanf, void *stream, const char *format, // NOLINT
+ va_list ap) {
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, vfscanf, stream, format, ap);
+ scanf_common(ctx, format, ap);
+ int res = REAL(vfscanf)(stream, format, ap); // NOLINT
+ return res;
+}
+
+INTERCEPTOR(int, scanf, const char *format, ...) { // NOLINT
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, scanf, format);
+ va_list ap;
+ va_start(ap, format);
+ int res = vscanf(format, ap); // NOLINT
+ va_end(ap);
+ return res;
+}
+
+INTERCEPTOR(int, fscanf, void* stream, const char *format, ...) { // NOLINT
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fscanf, stream, format);
+ va_list ap;
+ va_start(ap, format);
+ int res = vfscanf(stream, format, ap); // NOLINT
+ va_end(ap);
+ return res;
+}
+
+INTERCEPTOR(int, sscanf, const char *str, const char *format, ...) { // NOLINT
+ void* ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sscanf, str, format); // NOLINT
+ va_list ap;
+ va_start(ap, format);
+ int res = vsscanf(str, format, ap); // NOLINT
+ va_end(ap);
+ return res;
+}
+
+#define INIT_SCANF \
+ INTERCEPT_FUNCTION(scanf); \
+ INTERCEPT_FUNCTION(sscanf); /* NOLINT */ \
+ INTERCEPT_FUNCTION(fscanf); \
+ INTERCEPT_FUNCTION(vscanf); \
+ INTERCEPT_FUNCTION(vsscanf); \
+ INTERCEPT_FUNCTION(vfscanf)
+
+#else
+#define INIT_SCANF
+#endif
+
+#define SANITIZER_COMMON_INTERCEPTORS_INIT \
+ INIT_READ; \
+ INIT_PREAD; \
+ INIT_PREAD64; \
+ INIT_PRCTL; \
+ INIT_WRITE; \
+ INIT_SCANF;
diff --git a/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc b/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc
new file mode 100644
index 000000000000..63d67a7115ec
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_common_interceptors_scanf.inc
@@ -0,0 +1,142 @@
+//===-- sanitizer_common_interceptors_scanf.inc -----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Scanf implementation for use in *Sanitizer interceptors.
+//
+//===----------------------------------------------------------------------===//
+#include <stdarg.h>
+
+struct ScanfSpec {
+ char c;
+ unsigned size;
+};
+
+// One-letter specs.
+static const ScanfSpec scanf_specs[] = {
+ {'p', sizeof(void *)},
+ {'e', sizeof(float)},
+ {'E', sizeof(float)},
+ {'a', sizeof(float)},
+ {'f', sizeof(float)},
+ {'g', sizeof(float)},
+ {'d', sizeof(int)},
+ {'i', sizeof(int)},
+ {'o', sizeof(int)},
+ {'u', sizeof(int)},
+ {'x', sizeof(int)},
+ {'X', sizeof(int)},
+ {'n', sizeof(int)},
+ {'t', sizeof(PTRDIFF_T)},
+ {'z', sizeof(SIZE_T)},
+ {'j', sizeof(INTMAX_T)},
+ {'h', sizeof(short)}
+};
+
+static const unsigned scanf_specs_cnt =
+ sizeof(scanf_specs) / sizeof(scanf_specs[0]);
+
+// %ll?, %L?, %q? specs
+static const ScanfSpec scanf_llspecs[] = {
+ {'e', sizeof(long double)},
+ {'f', sizeof(long double)},
+ {'g', sizeof(long double)},
+ {'d', sizeof(long long)},
+ {'i', sizeof(long long)},
+ {'o', sizeof(long long)},
+ {'u', sizeof(long long)},
+ {'x', sizeof(long long)}
+};
+
+static const unsigned scanf_llspecs_cnt =
+ sizeof(scanf_llspecs) / sizeof(scanf_llspecs[0]);
+
+// %l? specs
+static const ScanfSpec scanf_lspecs[] = {
+ {'e', sizeof(double)},
+ {'f', sizeof(double)},
+ {'g', sizeof(double)},
+ {'d', sizeof(long)},
+ {'i', sizeof(long)},
+ {'o', sizeof(long)},
+ {'u', sizeof(long)},
+ {'x', sizeof(long)},
+ {'X', sizeof(long)},
+};
+
+static const unsigned scanf_lspecs_cnt =
+ sizeof(scanf_lspecs) / sizeof(scanf_lspecs[0]);
+
+static unsigned match_spec(const struct ScanfSpec *spec, unsigned n, char c) {
+ for (unsigned i = 0; i < n; ++i)
+ if (spec[i].c == c)
+ return spec[i].size;
+ return 0;
+}
+
+static void scanf_common(void *ctx, const char *format, va_list ap_const) {
+ va_list aq;
+ va_copy(aq, ap_const);
+
+ const char *p = format;
+ unsigned size;
+
+ while (*p) {
+ if (*p != '%') {
+ ++p;
+ continue;
+ }
+ ++p;
+ if (*p == '*' || *p == '%' || *p == 0) {
+ ++p;
+ continue;
+ }
+ if (*p == '0' || (*p >= '1' && *p <= '9')) {
+ size = internal_atoll(p);
+ // +1 for the \0 at the end
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size + 1);
+ ++p;
+ continue;
+ }
+
+ if (*p == 'L' || *p == 'q') {
+ ++p;
+ size = match_spec(scanf_llspecs, scanf_llspecs_cnt, *p);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size);
+ continue;
+ }
+
+ if (*p == 'l') {
+ ++p;
+ if (*p == 'l') {
+ ++p;
+ size = match_spec(scanf_llspecs, scanf_llspecs_cnt, *p);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size);
+ continue;
+ } else {
+ size = match_spec(scanf_lspecs, scanf_lspecs_cnt, *p);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size);
+ continue;
+ }
+ }
+
+ if (*p == 'h' && *(p + 1) == 'h') {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), sizeof(char));
+ p += 2;
+ continue;
+ }
+
+ size = match_spec(scanf_specs, scanf_specs_cnt, *p);
+ if (size) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, va_arg(aq, void *), size);
+ ++p;
+ continue;
+ }
+ }
+ va_end(aq);
+}
diff --git a/lib/sanitizer_common/sanitizer_flags.cc b/lib/sanitizer_common/sanitizer_flags.cc
index cdeeb78d7a5d..eca910c08090 100644
--- a/lib/sanitizer_common/sanitizer_flags.cc
+++ b/lib/sanitizer_common/sanitizer_flags.cc
@@ -18,13 +18,14 @@
namespace __sanitizer {
-static char *GetFlagValue(const char *env, const char *name) {
+static bool GetFlagValue(const char *env, const char *name,
+ const char **value, int *value_length) {
if (env == 0)
- return 0;
+ return false;
const char *pos = internal_strstr(env, name);
const char *end;
if (pos == 0)
- return 0;
+ return false;
pos += internal_strlen(name);
if (pos[0] != '=') {
end = pos;
@@ -42,41 +43,55 @@ static char *GetFlagValue(const char *env, const char *name) {
if (end == 0)
end = pos + internal_strlen(pos);
}
- int len = end - pos;
- char *f = (char*)InternalAlloc(len + 1);
- internal_memcpy(f, pos, len);
- f[len] = '\0';
- return f;
+ *value = pos;
+ *value_length = end - pos;
+ return true;
+}
+
+static bool StartsWith(const char *flag, int flag_length, const char *value) {
+ if (!flag || !value)
+ return false;
+ int value_length = internal_strlen(value);
+ return (flag_length >= value_length) &&
+ (0 == internal_strncmp(flag, value, value_length));
}
void ParseFlag(const char *env, bool *flag, const char *name) {
- char *val = GetFlagValue(env, name);
- if (val == 0)
+ const char *value;
+ int value_length;
+ if (!GetFlagValue(env, name, &value, &value_length))
return;
- if (0 == internal_strcmp(val, "0") ||
- 0 == internal_strcmp(val, "no") ||
- 0 == internal_strcmp(val, "false"))
+ if (StartsWith(value, value_length, "0") ||
+ StartsWith(value, value_length, "no") ||
+ StartsWith(value, value_length, "false"))
*flag = false;
- if (0 == internal_strcmp(val, "1") ||
- 0 == internal_strcmp(val, "yes") ||
- 0 == internal_strcmp(val, "true"))
+ if (StartsWith(value, value_length, "1") ||
+ StartsWith(value, value_length, "yes") ||
+ StartsWith(value, value_length, "true"))
*flag = true;
- InternalFree(val);
}
void ParseFlag(const char *env, int *flag, const char *name) {
- char *val = GetFlagValue(env, name);
- if (val == 0)
+ const char *value;
+ int value_length;
+ if (!GetFlagValue(env, name, &value, &value_length))
return;
- *flag = internal_atoll(val);
- InternalFree(val);
+ *flag = internal_atoll(value);
}
+static LowLevelAllocator allocator_for_flags;
+
void ParseFlag(const char *env, const char **flag, const char *name) {
- const char *val = GetFlagValue(env, name);
- if (val == 0)
+ const char *value;
+ int value_length;
+ if (!GetFlagValue(env, name, &value, &value_length))
return;
- *flag = val;
+ // Copy the flag value. Don't use locks here, as flags are parsed at
+ // tool startup.
+ char *value_copy = (char*)(allocator_for_flags.Allocate(value_length + 1));
+ internal_memcpy(value_copy, value, value_length);
+ value_copy[value_length] = '\0';
+ *flag = value_copy;
}
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h
index b8cf61fad84a..7ff27338192a 100644
--- a/lib/sanitizer_common/sanitizer_internal_defs.h
+++ b/lib/sanitizer_common/sanitizer_internal_defs.h
@@ -13,7 +13,7 @@
#ifndef SANITIZER_DEFS_H
#define SANITIZER_DEFS_H
-#include "sanitizer_interface_defs.h"
+#include "sanitizer/common_interface_defs.h"
using namespace __sanitizer; // NOLINT
// ----------- ATTENTION -------------
// This header should NOT include any other headers to avoid portability issues.
@@ -24,8 +24,7 @@ using namespace __sanitizer; // NOLINT
#define WEAK SANITIZER_WEAK_ATTRIBUTE
// Platform-specific defs.
-#if defined(_WIN32)
-typedef unsigned long DWORD; // NOLINT
+#if defined(_MSC_VER)
# define ALWAYS_INLINE __declspec(forceinline)
// FIXME(timurrrr): do we need this on Windows?
# define ALIAS(x)
@@ -35,7 +34,12 @@ typedef unsigned long DWORD; // NOLINT
# define NORETURN __declspec(noreturn)
# define THREADLOCAL __declspec(thread)
# define NOTHROW
-#else // _WIN32
+# define LIKELY(x) (x)
+# define UNLIKELY(x) (x)
+# define UNUSED
+# define USED
+# define PREFETCH(x) /* _mm_prefetch(x, _MM_HINT_NTA) */
+#else // _MSC_VER
# define ALWAYS_INLINE __attribute__((always_inline))
# define ALIAS(x) __attribute__((alias(x)))
# define ALIGNED(x) __attribute__((aligned(x)))
@@ -43,22 +47,21 @@ typedef unsigned long DWORD; // NOLINT
# define NOINLINE __attribute__((noinline))
# define NORETURN __attribute__((noreturn))
# define THREADLOCAL __thread
-# ifdef __cplusplus
-# define NOTHROW throw()
-# else
-# define NOTHROW __attribute__((__nothrow__))
-#endif
-#endif // _WIN32
-
-// We have no equivalent of these on Windows.
-#ifndef _WIN32
+# define NOTHROW throw()
# define LIKELY(x) __builtin_expect(!!(x), 1)
# define UNLIKELY(x) __builtin_expect(!!(x), 0)
# define UNUSED __attribute__((unused))
# define USED __attribute__((used))
-#endif
+# if defined(__i386__) || defined(__x86_64__)
+// __builtin_prefetch(x) generates prefetchnt0 on x86
+# define PREFETCH(x) __asm__("prefetchnta (%0)" : : "r" (x))
+# else
+# define PREFETCH(x) __builtin_prefetch(x)
+# endif
+#endif // _MSC_VER
#if defined(_WIN32)
+typedef unsigned long DWORD; // NOLINT
typedef DWORD thread_return_t;
# define THREAD_CALLING_CONV __stdcall
#else // _WIN32
@@ -67,15 +70,11 @@ typedef void* thread_return_t;
#endif // _WIN32
typedef thread_return_t (THREAD_CALLING_CONV *thread_callback_t)(void* arg);
-// If __WORDSIZE was undefined by the platform, define it in terms of the
-// compiler built-ins __LP64__ and _WIN64.
-#ifndef __WORDSIZE
-# if __LP64__ || defined(_WIN64)
-# define __WORDSIZE 64
-# else
-# define __WORDSIZE 32
-# endif
-#endif // __WORDSIZE
+#if __LP64__ || defined(_WIN64)
+# define SANITIZER_WORDSIZE 64
+#else
+# define SANITIZER_WORDSIZE 32
+#endif
// NOTE: Functions below must be defined in each run-time.
namespace __sanitizer {
@@ -130,23 +129,32 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond,
#define DCHECK_GE(a, b)
#endif
-#define UNIMPLEMENTED() CHECK("unimplemented" && 0)
+#define UNREACHABLE(msg) do { \
+ CHECK(0 && msg); \
+ Die(); \
+} while (0)
+
+#define UNIMPLEMENTED() UNREACHABLE("unimplemented")
#define COMPILER_CHECK(pred) IMPL_COMPILER_ASSERT(pred, __LINE__)
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
#define IMPL_PASTE(a, b) a##b
#define IMPL_COMPILER_ASSERT(pred, line) \
- typedef char IMPL_PASTE(assertion_failed_##_, line)[2*(int)(pred)-1];
+ typedef char IMPL_PASTE(assertion_failed_##_, line)[2*(int)(pred)-1]
// Limits for integral types. We have to redefine it in case we don't
// have stdint.h (like in Visual Studio 9).
-#if __WORDSIZE == 64
+#undef __INT64_C
+#undef __UINT64_C
+#if SANITIZER_WORDSIZE == 64
# define __INT64_C(c) c ## L
# define __UINT64_C(c) c ## UL
#else
# define __INT64_C(c) c ## LL
# define __UINT64_C(c) c ## ULL
-#endif // __WORDSIZE == 64
+#endif // SANITIZER_WORDSIZE == 64
#undef INT32_MIN
#define INT32_MIN (-2147483647-1)
#undef INT32_MAX
@@ -160,4 +168,25 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond,
#undef UINT64_MAX
#define UINT64_MAX (__UINT64_C(18446744073709551615))
+enum LinkerInitialized { LINKER_INITIALIZED = 0 };
+
+#if !defined(_MSC_VER) || defined(__clang__)
+# define GET_CALLER_PC() (uptr)__builtin_return_address(0)
+# define GET_CURRENT_FRAME() (uptr)__builtin_frame_address(0)
+#else
+extern "C" void* _ReturnAddress(void);
+# pragma intrinsic(_ReturnAddress)
+# define GET_CALLER_PC() (uptr)_ReturnAddress()
+// CaptureStackBackTrace doesn't need to know BP on Windows.
+// FIXME: This macro is still used when printing error reports though it's not
+// clear if the BP value is needed in the ASan reports on Windows.
+# define GET_CURRENT_FRAME() (uptr)0xDEADBEEF
+#endif
+
+#define HANDLE_EINTR(res, f) { \
+ do { \
+ res = (f); \
+ } while (res == -1 && errno == EINTR); \
+ }
+
#endif // SANITIZER_DEFS_H
diff --git a/lib/sanitizer_common/sanitizer_lfstack.h b/lib/sanitizer_common/sanitizer_lfstack.h
new file mode 100644
index 000000000000..c26e45db8f89
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_lfstack.h
@@ -0,0 +1,73 @@
+//===-- sanitizer_lfstack.h -=-----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Lock-free stack.
+// Uses 32/17 bits as ABA-counter on 32/64-bit platforms.
+// The memory passed to Push() must not be ever munmap'ed.
+// The type T must contain T *next field.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_LFSTACK_H
+#define SANITIZER_LFSTACK_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_common.h"
+#include "sanitizer_atomic.h"
+
+namespace __sanitizer {
+
+template<typename T>
+struct LFStack {
+ void Clear() {
+ atomic_store(&head_, 0, memory_order_relaxed);
+ }
+
+ bool Empty() const {
+ return (atomic_load(&head_, memory_order_relaxed) & kPtrMask) == 0;
+ }
+
+ void Push(T *p) {
+ u64 cmp = atomic_load(&head_, memory_order_relaxed);
+ for (;;) {
+ u64 cnt = (cmp & kCounterMask) + kCounterInc;
+ u64 xch = (u64)(uptr)p | cnt;
+ p->next = (T*)(uptr)(cmp & kPtrMask);
+ if (atomic_compare_exchange_weak(&head_, &cmp, xch,
+ memory_order_release))
+ break;
+ }
+ }
+
+ T *Pop() {
+ u64 cmp = atomic_load(&head_, memory_order_acquire);
+ for (;;) {
+ T *cur = (T*)(uptr)(cmp & kPtrMask);
+ if (cur == 0)
+ return 0;
+ T *nxt = cur->next;
+ u64 cnt = (cmp & kCounterMask);
+ u64 xch = (u64)(uptr)nxt | cnt;
+ if (atomic_compare_exchange_weak(&head_, &cmp, xch,
+ memory_order_acquire))
+ return cur;
+ }
+ }
+
+ // private:
+ static const int kCounterBits = FIRST_32_SECOND_64(32, 17);
+ static const u64 kPtrMask = ((u64)-1) >> kCounterBits;
+ static const u64 kCounterMask = ~kPtrMask;
+ static const u64 kCounterInc = kPtrMask + 1;
+
+ atomic_uint64_t head_;
+};
+}
+
+#endif // #ifndef SANITIZER_LFSTACK_H
diff --git a/lib/sanitizer_common/sanitizer_libc.cc b/lib/sanitizer_common/sanitizer_libc.cc
index c4332423bf3a..349be35012dd 100644
--- a/lib/sanitizer_common/sanitizer_libc.cc
+++ b/lib/sanitizer_common/sanitizer_libc.cc
@@ -44,6 +44,23 @@ void *internal_memcpy(void *dest, const void *src, uptr n) {
return dest;
}
+void *internal_memmove(void *dest, const void *src, uptr n) {
+ char *d = (char*)dest;
+ char *s = (char*)src;
+ sptr i, signed_n = (sptr)n;
+ CHECK_GE(signed_n, 0);
+ if (d < s) {
+ for (i = 0; i < signed_n; ++i)
+ d[i] = s[i];
+ } else {
+ if (d > s && signed_n > 0)
+ for (i = signed_n - 1; i >= 0 ; --i) {
+ d[i] = s[i];
+ }
+ }
+ return dest;
+}
+
void *internal_memset(void* s, int c, uptr n) {
// The next line prevents Clang from making a call to memset() instead of the
// loop below.
@@ -56,6 +73,15 @@ void *internal_memset(void* s, int c, uptr n) {
return s;
}
+uptr internal_strcspn(const char *s, const char *reject) {
+ uptr i;
+ for (i = 0; s[i]; i++) {
+ if (internal_strchr(reject, s[i]) != 0)
+ return i;
+ }
+ return i;
+}
+
char* internal_strdup(const char *s) {
uptr len = internal_strlen(s);
char *s2 = (char*)InternalAlloc(len + 1);
@@ -179,4 +205,23 @@ s64 internal_simple_strtoll(const char *nptr, char **endptr, int base) {
}
}
+bool mem_is_zero(const char *beg, uptr size) {
+ CHECK_LE(size, 1UL << FIRST_32_SECOND_64(30, 40)); // Sanity check.
+ const char *end = beg + size;
+ uptr *aligned_beg = (uptr *)RoundUpTo((uptr)beg, sizeof(uptr));
+ uptr *aligned_end = (uptr *)RoundDownTo((uptr)end, sizeof(uptr));
+ uptr all = 0;
+ // Prologue.
+ for (const char *mem = beg; mem < (char*)aligned_beg && mem < end; mem++)
+ all |= *mem;
+ // Aligned loop.
+ for (; aligned_beg < aligned_end; aligned_beg++)
+ all |= *aligned_beg;
+ // Epilogue.
+ if ((char*)aligned_end >= beg)
+ for (const char *mem = (char*)aligned_end; mem < end; mem++)
+ all |= *mem;
+ return all == 0;
+}
+
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_libc.h b/lib/sanitizer_common/sanitizer_libc.h
index 8da4286cef73..aa052c654d39 100644
--- a/lib/sanitizer_common/sanitizer_libc.h
+++ b/lib/sanitizer_common/sanitizer_libc.h
@@ -18,7 +18,7 @@
// ----------- ATTENTION -------------
// This header should NOT include any other headers from sanitizer runtime.
-#include "sanitizer_interface_defs.h"
+#include "sanitizer/common_interface_defs.h"
namespace __sanitizer {
@@ -29,10 +29,12 @@ s64 internal_atoll(const char *nptr);
void *internal_memchr(const void *s, int c, uptr n);
int internal_memcmp(const void* s1, const void* s2, uptr n);
void *internal_memcpy(void *dest, const void *src, uptr n);
+void *internal_memmove(void *dest, const void *src, uptr n);
// Should not be used in performance-critical places.
void *internal_memset(void *s, int c, uptr n);
char* internal_strchr(const char *s, int c);
int internal_strcmp(const char *s1, const char *s2);
+uptr internal_strcspn(const char *s, const char *reject);
char *internal_strdup(const char *s);
uptr internal_strlen(const char *s);
char *internal_strncat(char *dst, const char *src, uptr n);
@@ -45,6 +47,11 @@ char *internal_strstr(const char *haystack, const char *needle);
// Works only for base=10 and doesn't set errno.
s64 internal_simple_strtoll(const char *nptr, char **endptr, int base);
+// Return true if all bytes in [mem, mem+size) are zero.
+// Optimized for the case when the result is true.
+bool mem_is_zero(const char *mem, uptr size);
+
+
// Memory
void *internal_mmap(void *addr, uptr length, int prot, int flags,
int fd, u64 offset);
@@ -53,12 +60,17 @@ int internal_munmap(void *addr, uptr length);
// I/O
typedef int fd_t;
const fd_t kInvalidFd = -1;
+const fd_t kStdinFd = 0;
+const fd_t kStdoutFd = 1;
+const fd_t kStderrFd = 2;
int internal_close(fd_t fd);
+int internal_isatty(fd_t fd);
fd_t internal_open(const char *filename, bool write);
uptr internal_read(fd_t fd, void *buf, uptr count);
uptr internal_write(fd_t fd, const void *buf, uptr count);
uptr internal_filesize(fd_t fd); // -1 on error.
int internal_dup2(int oldfd, int newfd);
+uptr internal_readlink(const char *path, char *buf, uptr bufsize);
int internal_snprintf(char *buffer, uptr length, const char *format, ...);
// Threading
diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc
index 70e2eb346183..8b9ba38ca777 100644
--- a/lib/sanitizer_common/sanitizer_linux.cc
+++ b/lib/sanitizer_common/sanitizer_linux.cc
@@ -16,13 +16,12 @@
#include "sanitizer_common.h"
#include "sanitizer_internal_defs.h"
#include "sanitizer_libc.h"
+#include "sanitizer_mutex.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_procmaps.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_stacktrace.h"
-#include <elf.h>
#include <fcntl.h>
-#include <link.h>
#include <pthread.h>
#include <sched.h>
#include <sys/mman.h>
@@ -32,13 +31,26 @@
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
+#include <unwind.h>
+#include <errno.h>
+#include <sys/prctl.h>
+#include <linux/futex.h>
+
+// Are we using 32-bit or 64-bit syscalls?
+// x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32
+// but it still needs to use 64-bit syscalls.
+#if defined(__x86_64__) || SANITIZER_WORDSIZE == 64
+# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1
+#else
+# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0
+#endif
namespace __sanitizer {
// --------------- sanitizer_libc.h
void *internal_mmap(void *addr, uptr length, int prot, int flags,
int fd, u64 offset) {
-#if __WORDSIZE == 64
+#if SANITIZER_LINUX_USES_64BIT_SYSCALLS
return (void *)syscall(__NR_mmap, addr, length, prot, flags, fd, offset);
#else
return (void *)syscall(__NR_mmap2, addr, length, prot, flags, fd, offset);
@@ -59,15 +71,19 @@ fd_t internal_open(const char *filename, bool write) {
}
uptr internal_read(fd_t fd, void *buf, uptr count) {
- return (uptr)syscall(__NR_read, fd, buf, count);
+ sptr res;
+ HANDLE_EINTR(res, (sptr)syscall(__NR_read, fd, buf, count));
+ return res;
}
uptr internal_write(fd_t fd, const void *buf, uptr count) {
- return (uptr)syscall(__NR_write, fd, buf, count);
+ sptr res;
+ HANDLE_EINTR(res, (sptr)syscall(__NR_write, fd, buf, count));
+ return res;
}
uptr internal_filesize(fd_t fd) {
-#if __WORDSIZE == 64
+#if SANITIZER_LINUX_USES_64BIT_SYSCALLS
struct stat st;
if (syscall(__NR_fstat, fd, &st))
return -1;
@@ -83,11 +99,33 @@ int internal_dup2(int oldfd, int newfd) {
return syscall(__NR_dup2, oldfd, newfd);
}
+uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
+ return (uptr)syscall(__NR_readlink, path, buf, bufsize);
+}
+
int internal_sched_yield() {
return syscall(__NR_sched_yield);
}
// ----------------- sanitizer_common.h
+bool FileExists(const char *filename) {
+#if SANITIZER_LINUX_USES_64BIT_SYSCALLS
+ struct stat st;
+ if (syscall(__NR_stat, filename, &st))
+ return false;
+#else
+ struct stat64 st;
+ if (syscall(__NR_stat64, filename, &st))
+ return false;
+#endif
+ // Sanity check: filename is a regular file.
+ return S_ISREG(st.st_mode);
+}
+
+uptr GetTid() {
+ return syscall(__NR_gettid);
+}
+
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
uptr *stack_bottom) {
static const uptr kMaxThreadStackSize = 256 * (1 << 20); // 256M
@@ -99,7 +137,7 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
// Find the mapping that contains a stack variable.
- ProcessMaps proc_maps;
+ MemoryMappingLayout proc_maps;
uptr start, end, offset;
uptr prev_end = 0;
while (proc_maps.Next(&start, &end, &offset, 0, 0)) {
@@ -163,102 +201,96 @@ const char *GetEnv(const char *name) {
return 0; // Not found.
}
-// ------------------ sanitizer_symbolizer.h
-typedef ElfW(Ehdr) Elf_Ehdr;
-typedef ElfW(Shdr) Elf_Shdr;
-typedef ElfW(Phdr) Elf_Phdr;
-
-bool FindDWARFSection(uptr object_file_addr, const char *section_name,
- DWARFSection *section) {
- Elf_Ehdr *exe = (Elf_Ehdr*)object_file_addr;
- Elf_Shdr *sections = (Elf_Shdr*)(object_file_addr + exe->e_shoff);
- uptr section_names = object_file_addr +
- sections[exe->e_shstrndx].sh_offset;
- for (int i = 0; i < exe->e_shnum; i++) {
- Elf_Shdr *current_section = &sections[i];
- const char *current_name = (const char*)section_names +
- current_section->sh_name;
- if (IsFullNameOfDWARFSection(current_name, section_name)) {
- section->data = (const char*)object_file_addr +
- current_section->sh_offset;
- section->size = current_section->sh_size;
- return true;
+static void ReadNullSepFileToArray(const char *path, char ***arr,
+ int arr_size) {
+ char *buff;
+ uptr buff_size = 0;
+ *arr = (char **)MmapOrDie(arr_size * sizeof(char *), "NullSepFileArray");
+ ReadFileToBuffer(path, &buff, &buff_size, 1024 * 1024);
+ (*arr)[0] = buff;
+ int count, i;
+ for (count = 1, i = 1; ; i++) {
+ if (buff[i] == 0) {
+ if (buff[i+1] == 0) break;
+ (*arr)[count] = &buff[i+1];
+ CHECK_LE(count, arr_size - 1); // FIXME: make this more flexible.
+ count++;
}
}
- return false;
+ (*arr)[count] = 0;
}
-#ifdef ANDROID
-uptr GetListOfModules(ModuleDIContext *modules, uptr max_modules) {
- UNIMPLEMENTED();
+void ReExec() {
+ static const int kMaxArgv = 100, kMaxEnvp = 1000;
+ char **argv, **envp;
+ ReadNullSepFileToArray("/proc/self/cmdline", &argv, kMaxArgv);
+ ReadNullSepFileToArray("/proc/self/environ", &envp, kMaxEnvp);
+ execve(argv[0], argv, envp);
}
-#else // ANDROID
-struct DlIteratePhdrData {
- ModuleDIContext *modules;
- uptr current_n;
- uptr max_n;
-};
-static const uptr kMaxPathLength = 512;
-
-static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
- DlIteratePhdrData *data = (DlIteratePhdrData*)arg;
- if (data->current_n == data->max_n)
- return 0;
- char *module_name = 0;
- if (data->current_n == 0) {
- // First module is the binary itself.
- module_name = (char*)InternalAlloc(kMaxPathLength);
- uptr module_name_len = readlink("/proc/self/exe",
- module_name, kMaxPathLength);
- CHECK_NE(module_name_len, (uptr)-1);
- CHECK_LT(module_name_len, kMaxPathLength);
- module_name[module_name_len] = '\0';
- } else if (info->dlpi_name) {
- module_name = internal_strdup(info->dlpi_name);
- }
- if (module_name == 0 || module_name[0] == '\0')
- return 0;
- void *mem = &data->modules[data->current_n];
- ModuleDIContext *cur_module = new(mem) ModuleDIContext(module_name,
- info->dlpi_addr);
- data->current_n++;
- for (int i = 0; i < info->dlpi_phnum; i++) {
- const Elf_Phdr *phdr = &info->dlpi_phdr[i];
- if (phdr->p_type == PT_LOAD) {
- uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
- uptr cur_end = cur_beg + phdr->p_memsz;
- cur_module->addAddressRange(cur_beg, cur_end);
- }
+void PrepareForSandboxing() {
+ // Some kinds of sandboxes may forbid filesystem access, so we won't be able
+ // to read the file mappings from /proc/self/maps. Luckily, neither the
+ // process will be able to load additional libraries, so it's fine to use the
+ // cached mappings.
+ MemoryMappingLayout::CacheMemoryMappings();
+}
+
+// ----------------- sanitizer_procmaps.h
+// Linker initialized.
+ProcSelfMapsBuff MemoryMappingLayout::cached_proc_self_maps_;
+StaticSpinMutex MemoryMappingLayout::cache_lock_; // Linker initialized.
+
+MemoryMappingLayout::MemoryMappingLayout() {
+ proc_self_maps_.len =
+ ReadFileToBuffer("/proc/self/maps", &proc_self_maps_.data,
+ &proc_self_maps_.mmaped_size, 1 << 26);
+ if (proc_self_maps_.mmaped_size == 0) {
+ LoadFromCache();
+ CHECK_GT(proc_self_maps_.len, 0);
}
- InternalFree(module_name);
- return 0;
+ // internal_write(2, proc_self_maps_.data, proc_self_maps_.len);
+ Reset();
+ // FIXME: in the future we may want to cache the mappings on demand only.
+ CacheMemoryMappings();
}
-uptr GetListOfModules(ModuleDIContext *modules, uptr max_modules) {
- CHECK(modules);
- DlIteratePhdrData data = {modules, 0, max_modules};
- dl_iterate_phdr(dl_iterate_phdr_cb, &data);
- return data.current_n;
+MemoryMappingLayout::~MemoryMappingLayout() {
+ // Only unmap the buffer if it is different from the cached one. Otherwise
+ // it will be unmapped when the cache is refreshed.
+ if (proc_self_maps_.data != cached_proc_self_maps_.data) {
+ UnmapOrDie(proc_self_maps_.data, proc_self_maps_.mmaped_size);
+ }
}
-#endif // ANDROID
-// ----------------- sanitizer_procmaps.h
-ProcessMaps::ProcessMaps() {
- proc_self_maps_buff_len_ =
- ReadFileToBuffer("/proc/self/maps", &proc_self_maps_buff_,
- &proc_self_maps_buff_mmaped_size_, 1 << 26);
- CHECK_GT(proc_self_maps_buff_len_, 0);
- // internal_write(2, proc_self_maps_buff_, proc_self_maps_buff_len_);
- Reset();
+void MemoryMappingLayout::Reset() {
+ current_ = proc_self_maps_.data;
}
-ProcessMaps::~ProcessMaps() {
- UnmapOrDie(proc_self_maps_buff_, proc_self_maps_buff_mmaped_size_);
+// static
+void MemoryMappingLayout::CacheMemoryMappings() {
+ SpinMutexLock l(&cache_lock_);
+ // Don't invalidate the cache if the mappings are unavailable.
+ ProcSelfMapsBuff old_proc_self_maps;
+ old_proc_self_maps = cached_proc_self_maps_;
+ cached_proc_self_maps_.len =
+ ReadFileToBuffer("/proc/self/maps", &cached_proc_self_maps_.data,
+ &cached_proc_self_maps_.mmaped_size, 1 << 26);
+ if (cached_proc_self_maps_.mmaped_size == 0) {
+ cached_proc_self_maps_ = old_proc_self_maps;
+ } else {
+ if (old_proc_self_maps.mmaped_size) {
+ UnmapOrDie(old_proc_self_maps.data,
+ old_proc_self_maps.mmaped_size);
+ }
+ }
}
-void ProcessMaps::Reset() {
- current_ = proc_self_maps_buff_;
+void MemoryMappingLayout::LoadFromCache() {
+ SpinMutexLock l(&cache_lock_);
+ if (cached_proc_self_maps_.data) {
+ proc_self_maps_ = cached_proc_self_maps_;
+ }
}
// Parse a hex value in str and update str.
@@ -290,9 +322,9 @@ static bool IsDecimal(char c) {
return c >= '0' && c <= '9';
}
-bool ProcessMaps::Next(uptr *start, uptr *end, uptr *offset,
- char filename[], uptr filename_size) {
- char *last = proc_self_maps_buff_ + proc_self_maps_buff_len_;
+bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
+ char filename[], uptr filename_size) {
+ char *last = proc_self_maps_.data + proc_self_maps_.len;
if (current_ >= last) return false;
uptr dummy;
if (!start) start = &dummy;
@@ -336,13 +368,116 @@ bool ProcessMaps::Next(uptr *start, uptr *end, uptr *offset,
return true;
}
-// Gets the object name and the offset by walking ProcessMaps.
-bool ProcessMaps::GetObjectNameAndOffset(uptr addr, uptr *offset,
- char filename[],
- uptr filename_size) {
+// Gets the object name and the offset by walking MemoryMappingLayout.
+bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset,
+ char filename[],
+ uptr filename_size) {
return IterateForObjectNameAndOffset(addr, offset, filename, filename_size);
}
+bool SanitizerSetThreadName(const char *name) {
+#ifdef PR_SET_NAME
+ return 0 == prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); // NOLINT
+#else
+ return false;
+#endif
+}
+
+bool SanitizerGetThreadName(char *name, int max_len) {
+#ifdef PR_GET_NAME
+ char buff[17];
+ if (prctl(PR_GET_NAME, (unsigned long)buff, 0, 0, 0)) // NOLINT
+ return false;
+ internal_strncpy(name, buff, max_len);
+ name[max_len] = 0;
+ return true;
+#else
+ return false;
+#endif
+}
+
+#ifndef SANITIZER_GO
+//------------------------- SlowUnwindStack -----------------------------------
+#ifdef __arm__
+#define UNWIND_STOP _URC_END_OF_STACK
+#define UNWIND_CONTINUE _URC_NO_REASON
+#else
+#define UNWIND_STOP _URC_NORMAL_STOP
+#define UNWIND_CONTINUE _URC_NO_REASON
+#endif
+
+uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
+#ifdef __arm__
+ uptr val;
+ _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
+ 15 /* r15 = PC */, _UVRSD_UINT32, &val);
+ CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
+ // Clear the Thumb bit.
+ return val & ~(uptr)1;
+#else
+ return _Unwind_GetIP(ctx);
+#endif
+}
+
+_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
+ StackTrace *b = (StackTrace*)param;
+ CHECK(b->size < b->max_size);
+ uptr pc = Unwind_GetIP(ctx);
+ b->trace[b->size++] = pc;
+ if (b->size == b->max_size) return UNWIND_STOP;
+ return UNWIND_CONTINUE;
+}
+
+static bool MatchPc(uptr cur_pc, uptr trace_pc) {
+ return cur_pc - trace_pc <= 64 || trace_pc - cur_pc <= 64;
+}
+
+void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
+ this->size = 0;
+ this->max_size = max_depth;
+ if (max_depth > 1) {
+ _Unwind_Backtrace(Unwind_Trace, this);
+ // We need to pop a few frames so that pc is on top.
+ // trace[0] belongs to the current function so we always pop it.
+ int to_pop = 1;
+ /**/ if (size > 1 && MatchPc(pc, trace[1])) to_pop = 1;
+ else if (size > 2 && MatchPc(pc, trace[2])) to_pop = 2;
+ else if (size > 3 && MatchPc(pc, trace[3])) to_pop = 3;
+ else if (size > 4 && MatchPc(pc, trace[4])) to_pop = 4;
+ else if (size > 5 && MatchPc(pc, trace[5])) to_pop = 5;
+ this->PopStackFrames(to_pop);
+ }
+ this->trace[0] = pc;
+}
+
+#endif // #ifndef SANITIZER_GO
+
+enum MutexState {
+ MtxUnlocked = 0,
+ MtxLocked = 1,
+ MtxSleeping = 2
+};
+
+BlockingMutex::BlockingMutex(LinkerInitialized) {
+ CHECK_EQ(owner_, 0);
+}
+
+void BlockingMutex::Lock() {
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+ if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
+ return;
+ while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked)
+ syscall(__NR_futex, m, FUTEX_WAIT, MtxSleeping, 0, 0, 0);
+}
+
+void BlockingMutex::Unlock() {
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+ u32 v = atomic_exchange(m, MtxUnlocked, memory_order_relaxed);
+ CHECK_NE(v, MtxUnlocked);
+ if (v == MtxSleeping)
+ syscall(__NR_futex, m, FUTEX_WAKE, 1, 0, 0, 0);
+}
+
} // namespace __sanitizer
#endif // __linux__
diff --git a/lib/sanitizer_common/sanitizer_list.h b/lib/sanitizer_common/sanitizer_list.h
index ef98eee12317..f61d28f3d900 100644
--- a/lib/sanitizer_common/sanitizer_list.h
+++ b/lib/sanitizer_common/sanitizer_list.h
@@ -72,6 +72,8 @@ struct IntrusiveList {
void append_front(IntrusiveList<Item> *l) {
CHECK_NE(this, l);
+ if (l->empty())
+ return;
if (empty()) {
*this = *l;
} else if (!l->empty()) {
@@ -84,6 +86,8 @@ struct IntrusiveList {
void append_back(IntrusiveList<Item> *l) {
CHECK_NE(this, l);
+ if (l->empty())
+ return;
if (empty()) {
*this = *l;
} else {
diff --git a/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc
index e64c2debb882..c4b8e4c2bcf2 100644
--- a/lib/sanitizer_common/sanitizer_mac.cc
+++ b/lib/sanitizer_common/sanitizer_mac.cc
@@ -18,7 +18,6 @@
#include "sanitizer_internal_defs.h"
#include "sanitizer_libc.h"
#include "sanitizer_procmaps.h"
-#include "sanitizer_symbolizer.h"
#include <crt_externs.h> // for _NSGetEnviron
#include <fcntl.h>
@@ -31,6 +30,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <libkern/OSAtomic.h>
namespace __sanitizer {
@@ -62,7 +62,7 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) {
}
uptr internal_filesize(fd_t fd) {
- struct stat st = {};
+ struct stat st;
if (fstat(fd, &st))
return -1;
return (uptr)st.st_size;
@@ -72,11 +72,27 @@ int internal_dup2(int oldfd, int newfd) {
return dup2(oldfd, newfd);
}
+uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
+ return readlink(path, buf, bufsize);
+}
+
int internal_sched_yield() {
return sched_yield();
}
// ----------------- sanitizer_common.h
+bool FileExists(const char *filename) {
+ struct stat st;
+ if (stat(filename, &st))
+ return false;
+ // Sanity check: filename is a regular file.
+ return S_ISREG(st.st_mode);
+}
+
+uptr GetTid() {
+ return reinterpret_cast<uptr>(pthread_self());
+}
+
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
uptr *stack_bottom) {
CHECK(stack_top);
@@ -107,25 +123,21 @@ const char *GetEnv(const char *name) {
return 0;
}
-// ------------------ sanitizer_symbolizer.h
-bool FindDWARFSection(uptr object_file_addr, const char *section_name,
- DWARFSection *section) {
+void ReExec() {
UNIMPLEMENTED();
- return false;
}
-uptr GetListOfModules(ModuleDIContext *modules, uptr max_modules) {
- UNIMPLEMENTED();
- return 0;
-};
+void PrepareForSandboxing() {
+ // Nothing here for now.
+}
// ----------------- sanitizer_procmaps.h
-ProcessMaps::ProcessMaps() {
+MemoryMappingLayout::MemoryMappingLayout() {
Reset();
}
-ProcessMaps::~ProcessMaps() {
+MemoryMappingLayout::~MemoryMappingLayout() {
}
// More information about Mach-O headers can be found in mach-o/loader.h
@@ -142,15 +154,26 @@ ProcessMaps::~ProcessMaps() {
// Because these fields are taken from the images as is, one needs to add
// _dyld_get_image_vmaddr_slide() to get the actual addresses at runtime.
-void ProcessMaps::Reset() {
+void MemoryMappingLayout::Reset() {
// Count down from the top.
// TODO(glider): as per man 3 dyld, iterating over the headers with
// _dyld_image_count is thread-unsafe. We need to register callbacks for
- // adding and removing images which will invalidate the ProcessMaps state.
+ // adding and removing images which will invalidate the MemoryMappingLayout
+ // state.
current_image_ = _dyld_image_count();
current_load_cmd_count_ = -1;
current_load_cmd_addr_ = 0;
current_magic_ = 0;
+ current_filetype_ = 0;
+}
+
+// static
+void MemoryMappingLayout::CacheMemoryMappings() {
+ // No-op on Mac for now.
+}
+
+void MemoryMappingLayout::LoadFromCache() {
+ // No-op on Mac for now.
}
// Next and NextSegmentLoad were inspired by base/sysinfo.cc in
@@ -161,7 +184,7 @@ void ProcessMaps::Reset() {
// segment.
// Note that the segment addresses are not necessarily sorted.
template<u32 kLCSegment, typename SegmentCommand>
-bool ProcessMaps::NextSegmentLoad(
+bool MemoryMappingLayout::NextSegmentLoad(
uptr *start, uptr *end, uptr *offset,
char filename[], uptr filename_size) {
const char* lc = current_load_cmd_addr_;
@@ -171,7 +194,13 @@ bool ProcessMaps::NextSegmentLoad(
const SegmentCommand* sc = (const SegmentCommand *)lc;
if (start) *start = sc->vmaddr + dlloff;
if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
- if (offset) *offset = sc->fileoff;
+ if (offset) {
+ if (current_filetype_ == /*MH_EXECUTE*/ 0x2) {
+ *offset = sc->vmaddr;
+ } else {
+ *offset = sc->fileoff;
+ }
+ }
if (filename) {
internal_strncpy(filename, _dyld_get_image_name(current_image_),
filename_size);
@@ -181,8 +210,8 @@ bool ProcessMaps::NextSegmentLoad(
return false;
}
-bool ProcessMaps::Next(uptr *start, uptr *end, uptr *offset,
- char filename[], uptr filename_size) {
+bool MemoryMappingLayout::Next(uptr *start, uptr *end, uptr *offset,
+ char filename[], uptr filename_size) {
for (; current_image_ >= 0; current_image_--) {
const mach_header* hdr = _dyld_get_image_header(current_image_);
if (!hdr) continue;
@@ -190,6 +219,7 @@ bool ProcessMaps::Next(uptr *start, uptr *end, uptr *offset,
// Set up for this image;
current_load_cmd_count_ = hdr->ncmds;
current_magic_ = hdr->magic;
+ current_filetype_ = hdr->filetype;
switch (current_magic_) {
#ifdef MH_MAGIC_64
case MH_MAGIC_64: {
@@ -232,12 +262,31 @@ bool ProcessMaps::Next(uptr *start, uptr *end, uptr *offset,
return false;
}
-bool ProcessMaps::GetObjectNameAndOffset(uptr addr, uptr *offset,
- char filename[],
- uptr filename_size) {
+bool MemoryMappingLayout::GetObjectNameAndOffset(uptr addr, uptr *offset,
+ char filename[],
+ uptr filename_size) {
return IterateForObjectNameAndOffset(addr, offset, filename, filename_size);
}
+BlockingMutex::BlockingMutex(LinkerInitialized) {
+ // We assume that OS_SPINLOCK_INIT is zero
+}
+
+void BlockingMutex::Lock() {
+ CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_));
+ CHECK(OS_SPINLOCK_INIT == 0);
+ CHECK(owner_ != (uptr)pthread_self());
+ OSSpinLockLock((OSSpinLock*)&opaque_storage_);
+ CHECK(!owner_);
+ owner_ = (uptr)pthread_self();
+}
+
+void BlockingMutex::Unlock() {
+ CHECK(owner_ == (uptr)pthread_self());
+ owner_ = 0;
+ OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
+}
+
} // namespace __sanitizer
#endif // __APPLE__
diff --git a/lib/sanitizer_common/sanitizer_mutex.h b/lib/sanitizer_common/sanitizer_mutex.h
index ca3e2f9a4839..56438fce471c 100644
--- a/lib/sanitizer_common/sanitizer_mutex.h
+++ b/lib/sanitizer_common/sanitizer_mutex.h
@@ -20,18 +20,22 @@
namespace __sanitizer {
-class SpinMutex {
+class StaticSpinMutex {
public:
- SpinMutex() {
+ void Init() {
atomic_store(&state_, 0, memory_order_relaxed);
}
void Lock() {
- if (atomic_exchange(&state_, 1, memory_order_acquire) == 0)
+ if (TryLock())
return;
LockSlow();
}
+ bool TryLock() {
+ return atomic_exchange(&state_, 1, memory_order_acquire) == 0;
+ }
+
void Unlock() {
atomic_store(&state_, 0, memory_order_release);
}
@@ -50,11 +54,29 @@ class SpinMutex {
return;
}
}
+};
+class SpinMutex : public StaticSpinMutex {
+ public:
+ SpinMutex() {
+ Init();
+ }
+
+ private:
SpinMutex(const SpinMutex&);
void operator=(const SpinMutex&);
};
+class BlockingMutex {
+ public:
+ explicit BlockingMutex(LinkerInitialized);
+ void Lock();
+ void Unlock();
+ private:
+ uptr opaque_storage_[10];
+ uptr owner_; // for debugging
+};
+
template<typename MutexType>
class GenericScopedLock {
public:
@@ -93,7 +115,8 @@ class GenericScopedReadLock {
void operator=(const GenericScopedReadLock&);
};
-typedef GenericScopedLock<SpinMutex> SpinMutexLock;
+typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;
+typedef GenericScopedLock<BlockingMutex> BlockingMutexLock;
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_placement_new.h b/lib/sanitizer_common/sanitizer_placement_new.h
index f133a6ffe513..c0b85e1c1717 100644
--- a/lib/sanitizer_common/sanitizer_placement_new.h
+++ b/lib/sanitizer_common/sanitizer_placement_new.h
@@ -19,7 +19,7 @@
#include "sanitizer_internal_defs.h"
namespace __sanitizer {
-#if (__WORDSIZE == 64) || defined(__APPLE__)
+#if (SANITIZER_WORDSIZE == 64) || defined(__APPLE__)
typedef uptr operator_new_ptr_type;
#else
typedef u32 operator_new_ptr_type;
diff --git a/lib/sanitizer_common/sanitizer_platform_interceptors.h b/lib/sanitizer_common/sanitizer_platform_interceptors.h
new file mode 100644
index 000000000000..abd41fe8c997
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_platform_interceptors.h
@@ -0,0 +1,38 @@
+//===-- sanitizer_platform_interceptors.h -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines macro telling whether sanitizer tools can/should intercept
+// given library functions on a given platform.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_internal_defs.h"
+
+#if !defined(_WIN32)
+# define SI_NOT_WINDOWS 1
+#else
+# define SI_NOT_WINDOWS 0
+#endif
+
+#if defined(__linux__) && !defined(ANDROID)
+# define SI_LINUX_NOT_ANDROID 1
+#else
+# define SI_LINUX_NOT_ANDROID 0
+#endif
+
+# define SANITIZER_INTERCEPT_READ SI_NOT_WINDOWS
+# define SANITIZER_INTERCEPT_PREAD SI_NOT_WINDOWS
+# define SANITIZER_INTERCEPT_WRITE SI_NOT_WINDOWS
+# define SANITIZER_INTERCEPT_PWRITE SI_NOT_WINDOWS
+
+# define SANITIZER_INTERCEPT_PREAD64 SI_LINUX_NOT_ANDROID
+# define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID
+# define SANITIZER_INTERCEPT_PRCTL SI_LINUX_NOT_ANDROID
+
+# define SANITIZER_INTERCEPT_SCANF 1
diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc
index 4caee3ba68c0..32657838600d 100644
--- a/lib/sanitizer_common/sanitizer_posix.cc
+++ b/lib/sanitizer_common/sanitizer_posix.cc
@@ -17,10 +17,12 @@
#include "sanitizer_libc.h"
#include "sanitizer_procmaps.h"
+#include <errno.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/time.h>
@@ -30,6 +32,13 @@
namespace __sanitizer {
// ------------- sanitizer_common.h
+uptr GetPageSize() {
+ return sysconf(_SC_PAGESIZE);
+}
+
+uptr GetMmapGranularity() {
+ return GetPageSize();
+}
int GetPid() {
return getpid();
@@ -40,13 +49,22 @@ uptr GetThreadSelf() {
}
void *MmapOrDie(uptr size, const char *mem_type) {
- size = RoundUpTo(size, kPageSize);
+ size = RoundUpTo(size, GetPageSizeCached());
void *res = internal_mmap(0, size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0);
if (res == (void*)-1) {
- Report("ERROR: Failed to allocate 0x%zx (%zd) bytes of %s\n",
- size, size, mem_type);
+ static int recursion_count;
+ if (recursion_count) {
+ // The Report() and CHECK calls below may call mmap recursively and fail.
+ // If we went into recursion, just die.
+ RawWrite("AddressSanitizer is unable to mmap\n");
+ Die();
+ }
+ recursion_count++;
+ Report("ERROR: Failed to allocate 0x%zx (%zd) bytes of %s: %s\n",
+ size, size, mem_type, strerror(errno));
+ DumpProcessMap();
CHECK("unable to mmap" && 0);
}
return res;
@@ -63,10 +81,31 @@ void UnmapOrDie(void *addr, uptr size) {
}
void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
- return internal_mmap((void*)fixed_addr, size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
- -1, 0);
+ uptr PageSize = GetPageSizeCached();
+ void *p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),
+ RoundUpTo(size, PageSize),
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
+ -1, 0);
+ if (p == (void*)-1)
+ Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n",
+ size, size, fixed_addr, errno);
+ return p;
+}
+
+void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
+ uptr PageSize = GetPageSizeCached();
+ void *p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),
+ RoundUpTo(size, PageSize),
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED,
+ -1, 0);
+ if (p == (void*)-1) {
+ Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at address %p (%d)\n",
+ size, size, fixed_addr, errno);
+ CHECK("unable to mmap" && 0);
+ }
+ return p;
}
void *Mprotect(uptr fixed_addr, uptr size) {
@@ -76,13 +115,17 @@ void *Mprotect(uptr fixed_addr, uptr size) {
-1, 0);
}
+void FlushUnneededShadowMemory(uptr addr, uptr size) {
+ madvise((void*)addr, size, MADV_DONTNEED);
+}
+
void *MapFileToMemory(const char *file_name, uptr *buff_size) {
fd_t fd = internal_open(file_name, false);
CHECK_NE(fd, kInvalidFd);
uptr fsize = internal_filesize(fd);
CHECK_NE(fsize, (uptr)-1);
CHECK_GT(fsize, 0);
- *buff_size = RoundUpTo(fsize, kPageSize);
+ *buff_size = RoundUpTo(fsize, GetPageSizeCached());
void *map = internal_mmap(0, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0);
return (map == MAP_FAILED) ? 0 : map;
}
@@ -100,7 +143,7 @@ static inline bool IntervalsAreSeparate(uptr start1, uptr end1,
// several worker threads on Mac, which aren't expected to map big chunks of
// memory).
bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
- ProcessMaps procmaps;
+ MemoryMappingLayout procmaps;
uptr start, end;
while (procmaps.Next(&start, &end,
/*offset*/0, /*filename*/0, /*filename_size*/0)) {
@@ -111,7 +154,7 @@ bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
}
void DumpProcessMap() {
- ProcessMaps proc_maps;
+ MemoryMappingLayout proc_maps;
uptr start, end;
const sptr kBufSize = 4095;
char *filename = (char*)MmapOrDie(kBufSize, __FUNCTION__);
@@ -135,6 +178,23 @@ void DisableCoreDumper() {
setrlimit(RLIMIT_CORE, &nocore);
}
+bool StackSizeIsUnlimited() {
+ struct rlimit rlim;
+ CHECK_EQ(0, getrlimit(RLIMIT_STACK, &rlim));
+ return (rlim.rlim_cur == (uptr)-1);
+}
+
+void SetStackSizeLimitInBytes(uptr limit) {
+ struct rlimit rlim;
+ rlim.rlim_cur = limit;
+ rlim.rlim_max = limit;
+ if (setrlimit(RLIMIT_STACK, &rlim)) {
+ Report("setrlimit() failed %d\n", errno);
+ Die();
+ }
+ CHECK(!StackSizeIsUnlimited());
+}
+
void SleepForSeconds(int seconds) {
sleep(seconds);
}
@@ -159,6 +219,10 @@ int Atexit(void (*function)(void)) {
#endif
}
+int internal_isatty(fd_t fd) {
+ return isatty(fd);
+}
+
} // namespace __sanitizer
#endif // __linux__ || __APPLE_
diff --git a/lib/sanitizer_common/sanitizer_printf.cc b/lib/sanitizer_common/sanitizer_printf.cc
index 7b70c3aae23d..2393e8f2b87b 100644
--- a/lib/sanitizer_common/sanitizer_printf.cc
+++ b/lib/sanitizer_common/sanitizer_printf.cc
@@ -45,7 +45,12 @@ static int AppendUnsigned(char **buff, const char *buff_end, u64 num,
num_buffer[pos++] = num % base;
num /= base;
} while (num > 0);
- while (pos < minimal_num_length) num_buffer[pos++] = 0;
+ if (pos < minimal_num_length) {
+ // Make sure compiler doesn't insert call to memset here.
+ internal_memset(&num_buffer[pos], 0,
+ sizeof(num_buffer[0]) * (minimal_num_length - pos));
+ pos = minimal_num_length;
+ }
int result = 0;
while (pos-- > 0) {
uptr digit = num_buffer[pos];
@@ -55,13 +60,16 @@ static int AppendUnsigned(char **buff, const char *buff_end, u64 num,
return result;
}
-static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num) {
+static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num,
+ u8 minimal_num_length) {
int result = 0;
if (num < 0) {
result += AppendChar(buff, buff_end, '-');
num = -num;
+ if (minimal_num_length)
+ --minimal_num_length;
}
- result += AppendUnsigned(buff, buff_end, (u64)num, 10, 0);
+ result += AppendUnsigned(buff, buff_end, (u64)num, 10, minimal_num_length);
return result;
}
@@ -79,14 +87,14 @@ static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {
int result = 0;
result += AppendString(buff, buff_end, "0x");
result += AppendUnsigned(buff, buff_end, ptr_value, 16,
- (__WORDSIZE == 64) ? 12 : 8);
+ (SANITIZER_WORDSIZE == 64) ? 12 : 8);
return result;
}
int VSNPrintf(char *buff, int buff_length,
const char *format, va_list args) {
- static const char *kPrintfFormatsHelp = "Supported Printf formats: "
- "%%[z]{d,u,x}; %%p; %%s\n";
+ static const char *kPrintfFormatsHelp =
+ "Supported Printf formats: %(0[0-9]*)?(z|ll)?{d,u,x}; %p; %s; %c\n";
RAW_CHECK(format);
RAW_CHECK(buff_length > 0);
const char *buff_end = &buff[buff_length - 1];
@@ -98,37 +106,55 @@ int VSNPrintf(char *buff, int buff_length,
continue;
}
cur++;
+ bool have_width = (*cur == '0');
+ int width = 0;
+ if (have_width) {
+ while (*cur >= '0' && *cur <= '9') {
+ have_width = true;
+ width = width * 10 + *cur++ - '0';
+ }
+ }
bool have_z = (*cur == 'z');
cur += have_z;
+ bool have_ll = !have_z && (cur[0] == 'l' && cur[1] == 'l');
+ cur += have_ll * 2;
s64 dval;
u64 uval;
+ bool have_flags = have_width | have_z | have_ll;
switch (*cur) {
case 'd': {
- dval = have_z ? va_arg(args, sptr)
- : va_arg(args, int);
- result += AppendSignedDecimal(&buff, buff_end, dval);
+ dval = have_ll ? va_arg(args, s64)
+ : have_z ? va_arg(args, sptr)
+ : va_arg(args, int);
+ result += AppendSignedDecimal(&buff, buff_end, dval, width);
break;
}
case 'u':
case 'x': {
- uval = have_z ? va_arg(args, uptr)
- : va_arg(args, unsigned);
+ uval = have_ll ? va_arg(args, u64)
+ : have_z ? va_arg(args, uptr)
+ : va_arg(args, unsigned);
result += AppendUnsigned(&buff, buff_end, uval,
- (*cur == 'u') ? 10 : 16, 0);
+ (*cur == 'u') ? 10 : 16, width);
break;
}
case 'p': {
- RAW_CHECK_MSG(!have_z, kPrintfFormatsHelp);
+ RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
result += AppendPointer(&buff, buff_end, va_arg(args, uptr));
break;
}
case 's': {
- RAW_CHECK_MSG(!have_z, kPrintfFormatsHelp);
+ RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
result += AppendString(&buff, buff_end, va_arg(args, char*));
break;
}
+ case 'c': {
+ RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
+ result += AppendChar(&buff, buff_end, va_arg(args, int));
+ break;
+ }
case '%' : {
- RAW_CHECK_MSG(!have_z, kPrintfFormatsHelp);
+ RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
result += AppendChar(&buff, buff_end, '%');
break;
}
@@ -142,16 +168,22 @@ int VSNPrintf(char *buff, int buff_length,
return result;
}
+static void (*PrintfAndReportCallback)(const char *);
+void SetPrintfAndReportCallback(void (*callback)(const char *)) {
+ PrintfAndReportCallback = callback;
+}
+
void Printf(const char *format, ...) {
- const int kLen = 1024 * 4;
- char *buffer = (char*)MmapOrDie(kLen, __FUNCTION__);
+ const int kLen = 16 * 1024;
+ InternalScopedBuffer<char> buffer(kLen);
va_list args;
va_start(args, format);
- int needed_length = VSNPrintf(buffer, kLen, format, args);
+ int needed_length = VSNPrintf(buffer.data(), kLen, format, args);
va_end(args);
RAW_CHECK_MSG(needed_length < kLen, "Buffer in Printf is too short!\n");
- RawWrite(buffer);
- UnmapOrDie(buffer, kLen);
+ RawWrite(buffer.data());
+ if (PrintfAndReportCallback)
+ PrintfAndReportCallback(buffer.data());
}
// Writes at most "length" symbols to "buffer" (including trailing '\0').
@@ -168,18 +200,20 @@ int internal_snprintf(char *buffer, uptr length, const char *format, ...) {
// Like Printf, but prints the current PID before the output string.
void Report(const char *format, ...) {
- const int kLen = 1024 * 4;
- char *buffer = (char*)MmapOrDie(kLen, __FUNCTION__);
- int needed_length = internal_snprintf(buffer, kLen, "==%d== ", GetPid());
+ const int kLen = 16 * 1024;
+ InternalScopedBuffer<char> buffer(kLen);
+ int needed_length = internal_snprintf(buffer.data(),
+ kLen, "==%d== ", GetPid());
RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
va_list args;
va_start(args, format);
- needed_length += VSNPrintf(buffer + needed_length, kLen - needed_length,
- format, args);
+ needed_length += VSNPrintf(buffer.data() + needed_length,
+ kLen - needed_length, format, args);
va_end(args);
RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
- RawWrite(buffer);
- UnmapOrDie(buffer, kLen);
+ RawWrite(buffer.data());
+ if (PrintfAndReportCallback)
+ PrintfAndReportCallback(buffer.data());
}
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_procmaps.h b/lib/sanitizer_common/sanitizer_procmaps.h
index e7f9cac6cf6c..1b8ea7aff165 100644
--- a/lib/sanitizer_common/sanitizer_procmaps.h
+++ b/lib/sanitizer_common/sanitizer_procmaps.h
@@ -15,12 +15,32 @@
#define SANITIZER_PROCMAPS_H
#include "sanitizer_internal_defs.h"
+#include "sanitizer_mutex.h"
namespace __sanitizer {
-class ProcessMaps {
+#ifdef _WIN32
+class MemoryMappingLayout {
public:
- ProcessMaps();
+ MemoryMappingLayout() {}
+ bool GetObjectNameAndOffset(uptr addr, uptr *offset,
+ char filename[], uptr filename_size) {
+ UNIMPLEMENTED();
+ }
+};
+
+#else // _WIN32
+#if defined(__linux__)
+struct ProcSelfMapsBuff {
+ char *data;
+ uptr mmaped_size;
+ uptr len;
+};
+#endif // defined(__linux__)
+
+class MemoryMappingLayout {
+ public:
+ MemoryMappingLayout();
bool Next(uptr *start, uptr *end, uptr *offset,
char filename[], uptr filename_size);
void Reset();
@@ -28,9 +48,14 @@ class ProcessMaps {
// address 'addr'. Returns true on success.
bool GetObjectNameAndOffset(uptr addr, uptr *offset,
char filename[], uptr filename_size);
- ~ProcessMaps();
+ // In some cases, e.g. when running under a sandbox on Linux, ASan is unable
+ // to obtain the memory mappings. It should fall back to pre-cached data
+ // instead of aborting.
+ static void CacheMemoryMappings();
+ ~MemoryMappingLayout();
private:
+ void LoadFromCache();
// Default implementation of GetObjectNameAndOffset.
// Quite slow, because it iterates through the whole process map for each
// lookup.
@@ -61,22 +86,27 @@ class ProcessMaps {
return false;
}
-#if defined __linux__
- char *proc_self_maps_buff_;
- uptr proc_self_maps_buff_mmaped_size_;
- uptr proc_self_maps_buff_len_;
+# if defined __linux__
+ ProcSelfMapsBuff proc_self_maps_;
char *current_;
-#elif defined __APPLE__
+
+ // Static mappings cache.
+ static ProcSelfMapsBuff cached_proc_self_maps_;
+ static StaticSpinMutex cache_lock_; // protects cached_proc_self_maps_.
+# elif defined __APPLE__
template<u32 kLCSegment, typename SegmentCommand>
bool NextSegmentLoad(uptr *start, uptr *end, uptr *offset,
char filename[], uptr filename_size);
int current_image_;
u32 current_magic_;
+ u32 current_filetype_;
int current_load_cmd_count_;
char *current_load_cmd_addr_;
-#endif
+# endif
};
+#endif // _WIN32
+
} // namespace __sanitizer
#endif // SANITIZER_PROCMAPS_H
diff --git a/lib/sanitizer_common/sanitizer_quarantine.h b/lib/sanitizer_common/sanitizer_quarantine.h
new file mode 100644
index 000000000000..ec90d2d6871b
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_quarantine.h
@@ -0,0 +1,172 @@
+//===-- sanitizer_quarantine.h ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Memory quarantine for AddressSanitizer and potentially other tools.
+// Quarantine caches some specified amount of memory in per-thread caches,
+// then evicts to global FIFO queue. When the queue reaches specified threshold,
+// oldest memory is recycled.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_QUARANTINE_H
+#define SANITIZER_QUARANTINE_H
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_list.h"
+
+namespace __sanitizer {
+
+template<typename Node> class QuarantineCache;
+
+struct QuarantineBatch {
+ static const uptr kSize = 1024;
+ QuarantineBatch *next;
+ uptr size;
+ uptr count;
+ void *batch[kSize];
+};
+
+// The callback interface is:
+// void Callback::Recycle(Node *ptr);
+// void *cb.Allocate(uptr size);
+// void cb.Deallocate(void *ptr);
+template<typename Callback, typename Node>
+class Quarantine {
+ public:
+ typedef QuarantineCache<Callback> Cache;
+
+ explicit Quarantine(LinkerInitialized)
+ : cache_(LINKER_INITIALIZED) {
+ }
+
+ void Init(uptr size, uptr cache_size) {
+ max_size_ = size;
+ min_size_ = size / 10 * 9; // 90% of max size.
+ max_cache_size_ = cache_size;
+ }
+
+ void Put(Cache *c, Callback cb, Node *ptr, uptr size) {
+ c->Enqueue(cb, ptr, size);
+ if (c->Size() > max_cache_size_)
+ Drain(c, cb);
+ }
+
+ void NOINLINE Drain(Cache *c, Callback cb) {
+ {
+ SpinMutexLock l(&cache_mutex_);
+ cache_.Transfer(c);
+ }
+ if (cache_.Size() > max_size_ && recycle_mutex_.TryLock())
+ Recycle(cb);
+ }
+
+ private:
+ // Read-only data.
+ char pad0_[kCacheLineSize];
+ uptr max_size_;
+ uptr min_size_;
+ uptr max_cache_size_;
+ char pad1_[kCacheLineSize];
+ SpinMutex cache_mutex_;
+ SpinMutex recycle_mutex_;
+ Cache cache_;
+ char pad2_[kCacheLineSize];
+
+ void NOINLINE Recycle(Callback cb) {
+ Cache tmp;
+ {
+ SpinMutexLock l(&cache_mutex_);
+ while (cache_.Size() > min_size_) {
+ QuarantineBatch *b = cache_.DequeueBatch();
+ tmp.EnqueueBatch(b);
+ }
+ }
+ recycle_mutex_.Unlock();
+ DoRecycle(&tmp, cb);
+ }
+
+ void NOINLINE DoRecycle(Cache *c, Callback cb) {
+ while (QuarantineBatch *b = c->DequeueBatch()) {
+ const uptr kPrefetch = 16;
+ for (uptr i = 0; i < kPrefetch; i++)
+ PREFETCH(b->batch[i]);
+ for (uptr i = 0; i < b->count; i++) {
+ PREFETCH(b->batch[i + kPrefetch]);
+ cb.Recycle((Node*)b->batch[i]);
+ }
+ cb.Deallocate(b);
+ }
+ }
+};
+
+// Per-thread cache of memory blocks.
+template<typename Callback>
+class QuarantineCache {
+ public:
+ explicit QuarantineCache(LinkerInitialized) {
+ }
+
+ QuarantineCache()
+ : size_() {
+ list_.clear();
+ }
+
+ uptr Size() const {
+ return atomic_load(&size_, memory_order_relaxed);
+ }
+
+ void Enqueue(Callback cb, void *ptr, uptr size) {
+ if (list_.empty() || list_.back()->count == QuarantineBatch::kSize)
+ AllocBatch(cb);
+ QuarantineBatch *b = list_.back();
+ b->batch[b->count++] = ptr;
+ b->size += size;
+ SizeAdd(size);
+ }
+
+ void Transfer(QuarantineCache *c) {
+ list_.append_back(&c->list_);
+ SizeAdd(c->Size());
+ atomic_store(&c->size_, 0, memory_order_relaxed);
+ }
+
+ void EnqueueBatch(QuarantineBatch *b) {
+ list_.push_back(b);
+ SizeAdd(b->size);
+ }
+
+ QuarantineBatch *DequeueBatch() {
+ if (list_.empty())
+ return 0;
+ QuarantineBatch *b = list_.front();
+ list_.pop_front();
+ SizeAdd(-b->size);
+ return b;
+ }
+
+ private:
+ IntrusiveList<QuarantineBatch> list_;
+ atomic_uintptr_t size_;
+
+ void SizeAdd(uptr add) {
+ atomic_store(&size_, Size() + add, memory_order_relaxed);
+ }
+
+ QuarantineBatch *NOINLINE AllocBatch(Callback cb) {
+ QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b));
+ b->count = 0;
+ b->size = 0;
+ list_.push_back(b);
+ return b;
+ }
+};
+}
+
+#endif // #ifndef SANITIZER_QUARANTINE_H
diff --git a/lib/sanitizer_common/sanitizer_report_decorator.h b/lib/sanitizer_common/sanitizer_report_decorator.h
new file mode 100644
index 000000000000..50a3ee572fdb
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_report_decorator.h
@@ -0,0 +1,37 @@
+//===-- sanitizer_report_decorator.h ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Tags to decorate the sanitizer reports.
+// Currently supported tags:
+// * None.
+// * ANSI color sequences.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_ALLOCATOR_H
+#define SANITIZER_ALLOCATOR_H
+
+namespace __sanitizer {
+class AnsiColorDecorator {
+ public:
+ explicit AnsiColorDecorator(bool use_ansi_colors) : ansi_(use_ansi_colors) { }
+ const char *Black() { return ansi_ ? "\033[1m\033[30m" : ""; }
+ const char *Red() { return ansi_ ? "\033[1m\033[31m" : ""; }
+ const char *Green() { return ansi_ ? "\033[1m\033[32m" : ""; }
+ const char *Yellow() { return ansi_ ? "\033[1m\033[33m" : ""; }
+ const char *Blue() { return ansi_ ? "\033[1m\033[34m" : ""; }
+ const char *Magenta() { return ansi_ ? "\033[1m\033[35m" : ""; }
+ const char *Cyan() { return ansi_ ? "\033[1m\033[36m" : ""; }
+ const char *White() { return ansi_ ? "\033[1m\033[37m" : ""; }
+ const char *Default() { return ansi_ ? "\033[1m\033[0m" : ""; }
+ private:
+ bool ansi_;
+};
+} // namespace __sanitizer
+#endif // SANITIZER_ALLOCATOR_H
diff --git a/lib/sanitizer_common/sanitizer_stackdepot.cc b/lib/sanitizer_common/sanitizer_stackdepot.cc
new file mode 100644
index 000000000000..08e5238325e5
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_stackdepot.cc
@@ -0,0 +1,204 @@
+//===-- sanitizer_stackdepot.cc -------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_stackdepot.h"
+#include "sanitizer_common.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_atomic.h"
+
+namespace __sanitizer {
+
+const int kTabSize = 1024 * 1024; // Hash table size.
+const int kPartBits = 8;
+const int kPartShift = sizeof(u32) * 8 - kPartBits - 1;
+const int kPartCount = 1 << kPartBits; // Number of subparts in the table.
+const int kPartSize = kTabSize / kPartCount;
+const int kMaxId = 1 << kPartShift;
+
+struct StackDesc {
+ StackDesc *link;
+ u32 id;
+ u32 hash;
+ uptr size;
+ uptr stack[1]; // [size]
+};
+
+static struct {
+ StaticSpinMutex mtx; // Protects alloc of new blocks for region allocator.
+ atomic_uintptr_t region_pos; // Region allocator for StackDesc's.
+ atomic_uintptr_t region_end;
+ atomic_uintptr_t tab[kTabSize]; // Hash table of StackDesc's.
+ atomic_uint32_t seq[kPartCount]; // Unique id generators.
+} depot;
+
+static StackDepotStats stats;
+
+StackDepotStats *StackDepotGetStats() {
+ return &stats;
+}
+
+static u32 hash(const uptr *stack, uptr size) {
+ // murmur2
+ const u32 m = 0x5bd1e995;
+ const u32 seed = 0x9747b28c;
+ const u32 r = 24;
+ u32 h = seed ^ (size * sizeof(uptr));
+ for (uptr i = 0; i < size; i++) {
+ u32 k = stack[i];
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+ h *= m;
+ h ^= k;
+ }
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+ return h;
+}
+
+static StackDesc *tryallocDesc(uptr memsz) {
+ // Optimisic lock-free allocation, essentially try to bump the region ptr.
+ for (;;) {
+ uptr cmp = atomic_load(&depot.region_pos, memory_order_acquire);
+ uptr end = atomic_load(&depot.region_end, memory_order_acquire);
+ if (cmp == 0 || cmp + memsz > end)
+ return 0;
+ if (atomic_compare_exchange_weak(
+ &depot.region_pos, &cmp, cmp + memsz,
+ memory_order_acquire))
+ return (StackDesc*)cmp;
+ }
+}
+
+static StackDesc *allocDesc(uptr size) {
+ // First, try to allocate optimisitically.
+ uptr memsz = sizeof(StackDesc) + (size - 1) * sizeof(uptr);
+ StackDesc *s = tryallocDesc(memsz);
+ if (s)
+ return s;
+ // If failed, lock, retry and alloc new superblock.
+ SpinMutexLock l(&depot.mtx);
+ for (;;) {
+ s = tryallocDesc(memsz);
+ if (s)
+ return s;
+ atomic_store(&depot.region_pos, 0, memory_order_relaxed);
+ uptr allocsz = 64 * 1024;
+ if (allocsz < memsz)
+ allocsz = memsz;
+ uptr mem = (uptr)MmapOrDie(allocsz, "stack depot");
+ stats.mapped += allocsz;
+ atomic_store(&depot.region_end, mem + allocsz, memory_order_release);
+ atomic_store(&depot.region_pos, mem, memory_order_release);
+ }
+}
+
+static u32 find(StackDesc *s, const uptr *stack, uptr size, u32 hash) {
+ // Searches linked list s for the stack, returns its id.
+ for (; s; s = s->link) {
+ if (s->hash == hash && s->size == size) {
+ uptr i = 0;
+ for (; i < size; i++) {
+ if (stack[i] != s->stack[i])
+ break;
+ }
+ if (i == size)
+ return s->id;
+ }
+ }
+ return 0;
+}
+
+static StackDesc *lock(atomic_uintptr_t *p) {
+ // Uses the pointer lsb as mutex.
+ for (int i = 0;; i++) {
+ uptr cmp = atomic_load(p, memory_order_relaxed);
+ if ((cmp & 1) == 0
+ && atomic_compare_exchange_weak(p, &cmp, cmp | 1,
+ memory_order_acquire))
+ return (StackDesc*)cmp;
+ if (i < 10)
+ proc_yield(10);
+ else
+ internal_sched_yield();
+ }
+}
+
+static void unlock(atomic_uintptr_t *p, StackDesc *s) {
+ DCHECK_EQ((uptr)s & 1, 0);
+ atomic_store(p, (uptr)s, memory_order_release);
+}
+
+u32 StackDepotPut(const uptr *stack, uptr size) {
+ if (stack == 0 || size == 0)
+ return 0;
+ uptr h = hash(stack, size);
+ atomic_uintptr_t *p = &depot.tab[h % kTabSize];
+ uptr v = atomic_load(p, memory_order_consume);
+ StackDesc *s = (StackDesc*)(v & ~1);
+ // First, try to find the existing stack.
+ u32 id = find(s, stack, size, h);
+ if (id)
+ return id;
+ // If failed, lock, retry and insert new.
+ StackDesc *s2 = lock(p);
+ if (s2 != s) {
+ id = find(s2, stack, size, h);
+ if (id) {
+ unlock(p, s2);
+ return id;
+ }
+ }
+ uptr part = (h % kTabSize) / kPartSize;
+ id = atomic_fetch_add(&depot.seq[part], 1, memory_order_relaxed) + 1;
+ stats.n_uniq_ids++;
+ CHECK_LT(id, kMaxId);
+ id |= part << kPartShift;
+ CHECK_NE(id, 0);
+ CHECK_EQ(id & (1u << 31), 0);
+ s = allocDesc(size);
+ s->id = id;
+ s->hash = h;
+ s->size = size;
+ internal_memcpy(s->stack, stack, size * sizeof(uptr));
+ s->link = s2;
+ unlock(p, s);
+ return id;
+}
+
+const uptr *StackDepotGet(u32 id, uptr *size) {
+ if (id == 0)
+ return 0;
+ CHECK_EQ(id & (1u << 31), 0);
+ // High kPartBits contain part id, so we need to scan at most kPartSize lists.
+ uptr part = id >> kPartShift;
+ for (int i = 0; i != kPartSize; i++) {
+ uptr idx = part * kPartSize + i;
+ CHECK_LT(idx, kTabSize);
+ atomic_uintptr_t *p = &depot.tab[idx];
+ uptr v = atomic_load(p, memory_order_consume);
+ StackDesc *s = (StackDesc*)(v & ~1);
+ for (; s; s = s->link) {
+ if (s->id == id) {
+ *size = s->size;
+ return s->stack;
+ }
+ }
+ }
+ *size = 0;
+ return 0;
+}
+
+} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_stackdepot.h b/lib/sanitizer_common/sanitizer_stackdepot.h
new file mode 100644
index 000000000000..49e6669dd203
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_stackdepot.h
@@ -0,0 +1,36 @@
+//===-- sanitizer_stackdepot.h ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_STACKDEPOT_H
+#define SANITIZER_STACKDEPOT_H
+
+#include "sanitizer/common_interface_defs.h"
+
+namespace __sanitizer {
+
+// StackDepot efficiently stores huge amounts of stack traces.
+
+// Maps stack trace to an unique id.
+u32 StackDepotPut(const uptr *stack, uptr size);
+// Retrieves a stored stack trace by the id.
+const uptr *StackDepotGet(u32 id, uptr *size);
+
+struct StackDepotStats {
+ uptr n_uniq_ids;
+ uptr mapped;
+};
+
+StackDepotStats *StackDepotGetStats();
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_STACKDEPOT_H
diff --git a/lib/sanitizer_common/sanitizer_stacktrace.cc b/lib/sanitizer_common/sanitizer_stacktrace.cc
new file mode 100644
index 000000000000..109a674e45b3
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_stacktrace.cc
@@ -0,0 +1,262 @@
+//===-- sanitizer_stacktrace.cc -------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common.h"
+#include "sanitizer_procmaps.h"
+#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+static const char *StripPathPrefix(const char *filepath,
+ const char *strip_file_prefix) {
+ if (filepath == internal_strstr(filepath, strip_file_prefix))
+ return filepath + internal_strlen(strip_file_prefix);
+ return filepath;
+}
+
+// ----------------------- StackTrace ----------------------------- {{{1
+uptr StackTrace::GetPreviousInstructionPc(uptr pc) {
+#ifdef __arm__
+ // Cancel Thumb bit.
+ pc = pc & (~1);
+#endif
+#if defined(__powerpc__) || defined(__powerpc64__)
+ // PCs are always 4 byte aligned.
+ return pc - 4;
+#elif defined(__sparc__)
+ return pc - 8;
+#else
+ return pc - 1;
+#endif
+}
+
+static void PrintStackFramePrefix(uptr frame_num, uptr pc) {
+ Printf(" #%zu 0x%zx", frame_num, pc);
+}
+
+static void PrintSourceLocation(const char *file, int line, int column,
+ const char *strip_file_prefix) {
+ CHECK(file);
+ Printf(" %s", StripPathPrefix(file, strip_file_prefix));
+ if (line > 0) {
+ Printf(":%d", line);
+ if (column > 0)
+ Printf(":%d", column);
+ }
+}
+
+static void PrintModuleAndOffset(const char *module, uptr offset,
+ const char *strip_file_prefix) {
+ Printf(" (%s+0x%zx)", StripPathPrefix(module, strip_file_prefix), offset);
+}
+
+void StackTrace::PrintStack(const uptr *addr, uptr size,
+ bool symbolize, const char *strip_file_prefix,
+ SymbolizeCallback symbolize_callback ) {
+ MemoryMappingLayout proc_maps;
+ InternalScopedBuffer<char> buff(GetPageSizeCached() * 2);
+ InternalScopedBuffer<AddressInfo> addr_frames(64);
+ uptr frame_num = 0;
+ for (uptr i = 0; i < size && addr[i]; i++) {
+ // PCs in stack traces are actually the return addresses, that is,
+ // addresses of the next instructions after the call.
+ uptr pc = GetPreviousInstructionPc(addr[i]);
+ uptr addr_frames_num = 0; // The number of stack frames for current
+ // instruction address.
+ if (symbolize_callback) {
+ if (symbolize_callback((void*)pc, buff.data(), buff.size())) {
+ addr_frames_num = 1;
+ PrintStackFramePrefix(frame_num, pc);
+ // We can't know anything about the string returned by external
+ // symbolizer, but if it starts with filename, try to strip path prefix
+ // from it.
+ Printf(" %s\n", StripPathPrefix(buff.data(), strip_file_prefix));
+ frame_num++;
+ }
+ }
+ if (symbolize && addr_frames_num == 0) {
+ // Use our own (online) symbolizer, if necessary.
+ addr_frames_num = SymbolizeCode(pc, addr_frames.data(),
+ addr_frames.size());
+ for (uptr j = 0; j < addr_frames_num; j++) {
+ AddressInfo &info = addr_frames[j];
+ PrintStackFramePrefix(frame_num, pc);
+ if (info.function) {
+ Printf(" in %s", info.function);
+ }
+ if (info.file) {
+ PrintSourceLocation(info.file, info.line, info.column,
+ strip_file_prefix);
+ } else if (info.module) {
+ PrintModuleAndOffset(info.module, info.module_offset,
+ strip_file_prefix);
+ }
+ Printf("\n");
+ info.Clear();
+ frame_num++;
+ }
+ }
+ if (addr_frames_num == 0) {
+ // If online symbolization failed, try to output at least module and
+ // offset for instruction.
+ PrintStackFramePrefix(frame_num, pc);
+ uptr offset;
+ if (proc_maps.GetObjectNameAndOffset(pc, &offset,
+ buff.data(), buff.size())) {
+ PrintModuleAndOffset(buff.data(), offset, strip_file_prefix);
+ }
+ Printf("\n");
+ frame_num++;
+ }
+ }
+}
+
+uptr StackTrace::GetCurrentPc() {
+ return GET_CALLER_PC();
+}
+
+void StackTrace::FastUnwindStack(uptr pc, uptr bp,
+ uptr stack_top, uptr stack_bottom) {
+ CHECK(size == 0 && trace[0] == pc);
+ size = 1;
+ uhwptr *frame = (uhwptr *)bp;
+ uhwptr *prev_frame = frame;
+ while (frame >= prev_frame &&
+ frame < (uhwptr *)stack_top - 2 &&
+ frame > (uhwptr *)stack_bottom &&
+ size < max_size) {
+ uhwptr pc1 = frame[1];
+ if (pc1 != pc) {
+ trace[size++] = (uptr) pc1;
+ }
+ prev_frame = frame;
+ frame = (uhwptr *)frame[0];
+ }
+}
+
+void StackTrace::PopStackFrames(uptr count) {
+ CHECK(size >= count);
+ size -= count;
+ for (uptr i = 0; i < size; i++) {
+ trace[i] = trace[i + count];
+ }
+}
+
+// On 32-bits we don't compress stack traces.
+// On 64-bits we compress stack traces: if a given pc differes slightly from
+// the previous one, we record a 31-bit offset instead of the full pc.
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr StackTrace::CompressStack(StackTrace *stack, u32 *compressed, uptr size) {
+#if SANITIZER_WORDSIZE == 32
+ // Don't compress, just copy.
+ uptr res = 0;
+ for (uptr i = 0; i < stack->size && i < size; i++) {
+ compressed[i] = stack->trace[i];
+ res++;
+ }
+ if (stack->size < size)
+ compressed[stack->size] = 0;
+#else // 64 bits, compress.
+ uptr prev_pc = 0;
+ const uptr kMaxOffset = (1ULL << 30) - 1;
+ uptr c_index = 0;
+ uptr res = 0;
+ for (uptr i = 0, n = stack->size; i < n; i++) {
+ uptr pc = stack->trace[i];
+ if (!pc) break;
+ if ((s64)pc < 0) break;
+ // Printf("C pc[%zu] %zx\n", i, pc);
+ if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) {
+ uptr offset = (s64)(pc - prev_pc);
+ offset |= (1U << 31);
+ if (c_index >= size) break;
+ // Printf("C co[%zu] offset %zx\n", i, offset);
+ compressed[c_index++] = offset;
+ } else {
+ uptr hi = pc >> 32;
+ uptr lo = (pc << 32) >> 32;
+ CHECK_EQ((hi & (1 << 31)), 0);
+ if (c_index + 1 >= size) break;
+ // Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo);
+ compressed[c_index++] = hi;
+ compressed[c_index++] = lo;
+ }
+ res++;
+ prev_pc = pc;
+ }
+ if (c_index < size)
+ compressed[c_index] = 0;
+ if (c_index + 1 < size)
+ compressed[c_index + 1] = 0;
+#endif // SANITIZER_WORDSIZE
+
+ // debug-only code
+#if 0
+ StackTrace check_stack;
+ UncompressStack(&check_stack, compressed, size);
+ if (res < check_stack.size) {
+ Printf("res %zu check_stack.size %zu; c_size %zu\n", res,
+ check_stack.size, size);
+ }
+ // |res| may be greater than check_stack.size, because
+ // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames.
+ CHECK(res >= check_stack.size);
+ CHECK_EQ(0, REAL(memcmp)(check_stack.trace, stack->trace,
+ check_stack.size * sizeof(uptr)));
+#endif
+
+ return res;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void StackTrace::UncompressStack(StackTrace *stack,
+ u32 *compressed, uptr size) {
+#if SANITIZER_WORDSIZE == 32
+ // Don't uncompress, just copy.
+ stack->size = 0;
+ for (uptr i = 0; i < size && i < kStackTraceMax; i++) {
+ if (!compressed[i]) break;
+ stack->size++;
+ stack->trace[i] = compressed[i];
+ }
+#else // 64 bits, uncompress
+ uptr prev_pc = 0;
+ stack->size = 0;
+ for (uptr i = 0; i < size && stack->size < kStackTraceMax; i++) {
+ u32 x = compressed[i];
+ uptr pc = 0;
+ if (x & (1U << 31)) {
+ // Printf("U co[%zu] offset: %x\n", i, x);
+ // this is an offset
+ s32 offset = x;
+ offset = (offset << 1) >> 1; // remove the 31-byte and sign-extend.
+ pc = prev_pc + offset;
+ CHECK(pc);
+ } else {
+ // CHECK(i + 1 < size);
+ if (i + 1 >= size) break;
+ uptr hi = x;
+ uptr lo = compressed[i+1];
+ // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo);
+ i++;
+ pc = (hi << 32) | lo;
+ if (!pc) break;
+ }
+ // Printf("U pc[%zu] %zx\n", stack->size, pc);
+ stack->trace[stack->size++] = pc;
+ prev_pc = pc;
+ }
+#endif // SANITIZER_WORDSIZE
+}
+
+} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_stacktrace.h b/lib/sanitizer_common/sanitizer_stacktrace.h
new file mode 100644
index 000000000000..597d24fd067f
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_stacktrace.h
@@ -0,0 +1,79 @@
+//===-- sanitizer_stacktrace.h ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_STACKTRACE_H
+#define SANITIZER_STACKTRACE_H
+
+#include "sanitizer_internal_defs.h"
+
+namespace __sanitizer {
+
+static const uptr kStackTraceMax = 256;
+
+struct StackTrace {
+ typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer,
+ int out_size);
+ uptr size;
+ uptr max_size;
+ uptr trace[kStackTraceMax];
+ static void PrintStack(const uptr *addr, uptr size,
+ bool symbolize, const char *strip_file_prefix,
+ SymbolizeCallback symbolize_callback);
+ void CopyTo(uptr *dst, uptr dst_size) {
+ for (uptr i = 0; i < size && i < dst_size; i++)
+ dst[i] = trace[i];
+ for (uptr i = size; i < dst_size; i++)
+ dst[i] = 0;
+ }
+
+ void CopyFrom(uptr *src, uptr src_size) {
+ size = src_size;
+ if (size > kStackTraceMax) size = kStackTraceMax;
+ for (uptr i = 0; i < size; i++) {
+ trace[i] = src[i];
+ }
+ }
+
+ void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom);
+ void SlowUnwindStack(uptr pc, uptr max_depth);
+
+ void PopStackFrames(uptr count);
+
+ static uptr GetCurrentPc();
+ static uptr GetPreviousInstructionPc(uptr pc);
+
+ static uptr CompressStack(StackTrace *stack,
+ u32 *compressed, uptr size);
+ static void UncompressStack(StackTrace *stack,
+ u32 *compressed, uptr size);
+};
+
+} // namespace __sanitizer
+
+// Use this macro if you want to print stack trace with the caller
+// of the current function in the top frame.
+#define GET_CALLER_PC_BP_SP \
+ uptr bp = GET_CURRENT_FRAME(); \
+ uptr pc = GET_CALLER_PC(); \
+ uptr local_stack; \
+ uptr sp = (uptr)&local_stack
+
+// Use this macro if you want to print stack trace with the current
+// function in the top frame.
+#define GET_CURRENT_PC_BP_SP \
+ uptr bp = GET_CURRENT_FRAME(); \
+ uptr pc = StackTrace::GetCurrentPc(); \
+ uptr local_stack; \
+ uptr sp = (uptr)&local_stack
+
+
+#endif // SANITIZER_STACKTRACE_H
diff --git a/lib/sanitizer_common/sanitizer_symbolizer.cc b/lib/sanitizer_common/sanitizer_symbolizer.cc
index 85eb0764f19c..a1d95ae0e0b2 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer.cc
@@ -7,9 +7,8 @@
//
//===----------------------------------------------------------------------===//
//
-// This is a stub for LLVM-based symbolizer.
// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries. See sanitizer.h for details.
+// run-time libraries. See sanitizer_symbolizer.h for details.
//===----------------------------------------------------------------------===//
#include "sanitizer_common.h"
@@ -19,18 +18,6 @@
namespace __sanitizer {
-bool IsFullNameOfDWARFSection(const char *full_name, const char *short_name) {
- // Skip "__DWARF," prefix.
- if (0 == internal_strncmp(full_name, "__DWARF,", 8)) {
- full_name += 8;
- }
- // Skip . and _ prefices.
- while (*full_name == '.' || *full_name == '_') {
- full_name++;
- }
- return 0 == internal_strcmp(full_name, short_name);
-}
-
void AddressInfo::Clear() {
InternalFree(module);
InternalFree(function);
@@ -38,28 +25,20 @@ void AddressInfo::Clear() {
internal_memset(this, 0, sizeof(AddressInfo));
}
-ModuleDIContext::ModuleDIContext(const char *module_name, uptr base_address) {
+LoadedModule::LoadedModule(const char *module_name, uptr base_address) {
full_name_ = internal_strdup(module_name);
- short_name_ = internal_strrchr(module_name, '/');
- if (short_name_ == 0) {
- short_name_ = full_name_;
- } else {
- short_name_++;
- }
base_address_ = base_address;
n_ranges_ = 0;
- mapped_addr_ = 0;
- mapped_size_ = 0;
}
-void ModuleDIContext::addAddressRange(uptr beg, uptr end) {
+void LoadedModule::addAddressRange(uptr beg, uptr end) {
CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges);
ranges_[n_ranges_].beg = beg;
ranges_[n_ranges_].end = end;
n_ranges_++;
}
-bool ModuleDIContext::containsAddress(uptr address) const {
+bool LoadedModule::containsAddress(uptr address) const {
for (uptr i = 0; i < n_ranges_; i++) {
if (ranges_[i].beg <= address && address < ranges_[i].end)
return true;
@@ -67,56 +46,256 @@ bool ModuleDIContext::containsAddress(uptr address) const {
return false;
}
-void ModuleDIContext::getAddressInfo(AddressInfo *info) {
- info->module = internal_strdup(full_name_);
- info->module_offset = info->address - base_address_;
- if (mapped_addr_ == 0)
- CreateDIContext();
- // FIXME: Use the actual debug info context here.
- info->function = 0;
- info->file = 0;
- info->line = 0;
- info->column = 0;
+// Extracts the prefix of "str" that consists of any characters not
+// present in "delims" string, and copies this prefix to "result", allocating
+// space for it.
+// Returns a pointer to "str" after skipping extracted prefix and first
+// delimiter char.
+static const char *ExtractToken(const char *str, const char *delims,
+ char **result) {
+ uptr prefix_len = internal_strcspn(str, delims);
+ *result = (char*)InternalAlloc(prefix_len + 1);
+ internal_memcpy(*result, str, prefix_len);
+ (*result)[prefix_len] = '\0';
+ const char *prefix_end = str + prefix_len;
+ if (*prefix_end != '\0') prefix_end++;
+ return prefix_end;
+}
+
+// Same as ExtractToken, but converts extracted token to integer.
+static const char *ExtractInt(const char *str, const char *delims,
+ int *result) {
+ char *buff;
+ const char *ret = ExtractToken(str, delims, &buff);
+ if (buff != 0) {
+ *result = (int)internal_atoll(buff);
+ }
+ InternalFree(buff);
+ return ret;
}
-void ModuleDIContext::CreateDIContext() {
- mapped_addr_ = (uptr)MapFileToMemory(full_name_, &mapped_size_);
- CHECK(mapped_addr_);
- DWARFSection debug_info;
- DWARFSection debug_abbrev;
- DWARFSection debug_line;
- DWARFSection debug_aranges;
- DWARFSection debug_str;
- FindDWARFSection(mapped_addr_, "debug_info", &debug_info);
- FindDWARFSection(mapped_addr_, "debug_abbrev", &debug_abbrev);
- FindDWARFSection(mapped_addr_, "debug_line", &debug_line);
- FindDWARFSection(mapped_addr_, "debug_aranges", &debug_aranges);
- FindDWARFSection(mapped_addr_, "debug_str", &debug_str);
- // FIXME: Construct actual debug info context using mapped_addr,
- // mapped_size and pointers to DWARF sections in memory.
+static const char *ExtractUptr(const char *str, const char *delims,
+ uptr *result) {
+ char *buff;
+ const char *ret = ExtractToken(str, delims, &buff);
+ if (buff != 0) {
+ *result = (uptr)internal_atoll(buff);
+ }
+ InternalFree(buff);
+ return ret;
}
+// ExternalSymbolizer encapsulates communication between the tool and
+// external symbolizer program, running in a different subprocess,
+// For now we assume the following protocol:
+// For each request of the form
+// <module_name> <module_offset>
+// passed to STDIN, external symbolizer prints to STDOUT response:
+// <function_name>
+// <file_name>:<line_number>:<column_number>
+// <function_name>
+// <file_name>:<line_number>:<column_number>
+// ...
+// <empty line>
+class ExternalSymbolizer {
+ public:
+ ExternalSymbolizer(const char *path, int input_fd, int output_fd)
+ : path_(path),
+ input_fd_(input_fd),
+ output_fd_(output_fd),
+ times_restarted_(0) {
+ CHECK(path_);
+ CHECK_NE(input_fd_, kInvalidFd);
+ CHECK_NE(output_fd_, kInvalidFd);
+ }
+
+ char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
+ CHECK(module_name);
+ internal_snprintf(buffer_, kBufferSize, "%s%s 0x%zx\n",
+ is_data ? "DATA " : "", module_name, module_offset);
+ if (!writeToSymbolizer(buffer_, internal_strlen(buffer_)))
+ return 0;
+ if (!readFromSymbolizer(buffer_, kBufferSize))
+ return 0;
+ return buffer_;
+ }
+
+ bool Restart() {
+ if (times_restarted_ >= kMaxTimesRestarted) return false;
+ times_restarted_++;
+ internal_close(input_fd_);
+ internal_close(output_fd_);
+ return StartSymbolizerSubprocess(path_, &input_fd_, &output_fd_);
+ }
+
+ private:
+ bool readFromSymbolizer(char *buffer, uptr max_length) {
+ if (max_length == 0)
+ return true;
+ uptr read_len = 0;
+ while (true) {
+ uptr just_read = internal_read(input_fd_, buffer + read_len,
+ max_length - read_len);
+ // We can't read 0 bytes, as we don't expect external symbolizer to close
+ // its stdout.
+ if (just_read == 0 || just_read == (uptr)-1) {
+ Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
+ return false;
+ }
+ read_len += just_read;
+ // Empty line marks the end of symbolizer output.
+ if (read_len >= 2 && buffer[read_len - 1] == '\n' &&
+ buffer[read_len - 2] == '\n') {
+ break;
+ }
+ }
+ return true;
+ }
+
+ bool writeToSymbolizer(const char *buffer, uptr length) {
+ if (length == 0)
+ return true;
+ uptr write_len = internal_write(output_fd_, buffer, length);
+ if (write_len == 0 || write_len == (uptr)-1) {
+ Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
+ return false;
+ }
+ return true;
+ }
+
+ const char *path_;
+ int input_fd_;
+ int output_fd_;
+
+ static const uptr kBufferSize = 16 * 1024;
+ char buffer_[kBufferSize];
+
+ static const uptr kMaxTimesRestarted = 5;
+ uptr times_restarted_;
+};
+
+static LowLevelAllocator symbolizer_allocator; // Linker initialized.
+
class Symbolizer {
public:
uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames) {
if (max_frames == 0)
return 0;
- AddressInfo *info = &frames[0];
- info->Clear();
- info->address = addr;
- ModuleDIContext *module = FindModuleForAddress(addr);
- if (module) {
- module->getAddressInfo(info);
+ LoadedModule *module = FindModuleForAddress(addr);
+ if (module == 0)
+ return 0;
+ const char *module_name = module->full_name();
+ uptr module_offset = addr - module->base_address();
+ const char *str = SendCommand(false, module_name, module_offset);
+ if (str == 0) {
+ // External symbolizer was not initialized or failed. Fill only data
+ // about module name and offset.
+ AddressInfo *info = &frames[0];
+ info->Clear();
+ info->FillAddressAndModuleInfo(addr, module_name, module_offset);
return 1;
}
- return 0;
+ uptr frame_id = 0;
+ for (frame_id = 0; frame_id < max_frames; frame_id++) {
+ AddressInfo *info = &frames[frame_id];
+ char *function_name = 0;
+ str = ExtractToken(str, "\n", &function_name);
+ CHECK(function_name);
+ if (function_name[0] == '\0') {
+ // There are no more frames.
+ break;
+ }
+ info->Clear();
+ info->FillAddressAndModuleInfo(addr, module_name, module_offset);
+ info->function = function_name;
+ // Parse <file>:<line>:<column> buffer.
+ char *file_line_info = 0;
+ str = ExtractToken(str, "\n", &file_line_info);
+ CHECK(file_line_info);
+ const char *line_info = ExtractToken(file_line_info, ":", &info->file);
+ line_info = ExtractInt(line_info, ":", &info->line);
+ line_info = ExtractInt(line_info, "", &info->column);
+ InternalFree(file_line_info);
+
+ // Functions and filenames can be "??", in which case we write 0
+ // to address info to mark that names are unknown.
+ if (0 == internal_strcmp(info->function, "??")) {
+ InternalFree(info->function);
+ info->function = 0;
+ }
+ if (0 == internal_strcmp(info->file, "??")) {
+ InternalFree(info->file);
+ info->file = 0;
+ }
+ }
+ if (frame_id == 0) {
+ // Make sure we return at least one frame.
+ AddressInfo *info = &frames[0];
+ info->Clear();
+ info->FillAddressAndModuleInfo(addr, module_name, module_offset);
+ frame_id = 1;
+ }
+ return frame_id;
+ }
+
+ bool SymbolizeData(uptr addr, DataInfo *info) {
+ LoadedModule *module = FindModuleForAddress(addr);
+ if (module == 0)
+ return false;
+ const char *module_name = module->full_name();
+ uptr module_offset = addr - module->base_address();
+ internal_memset(info, 0, sizeof(*info));
+ info->address = addr;
+ info->module = internal_strdup(module_name);
+ info->module_offset = module_offset;
+ const char *str = SendCommand(true, module_name, module_offset);
+ if (str == 0)
+ return true;
+ str = ExtractToken(str, "\n", &info->name);
+ str = ExtractUptr(str, " ", &info->start);
+ str = ExtractUptr(str, "\n", &info->size);
+ info->start += module->base_address();
+ return true;
+ }
+
+ bool InitializeExternalSymbolizer(const char *path_to_symbolizer) {
+ int input_fd, output_fd;
+ if (!StartSymbolizerSubprocess(path_to_symbolizer, &input_fd, &output_fd))
+ return false;
+ void *mem = symbolizer_allocator.Allocate(sizeof(ExternalSymbolizer));
+ external_symbolizer_ = new(mem) ExternalSymbolizer(path_to_symbolizer,
+ input_fd, output_fd);
+ return true;
}
private:
- ModuleDIContext *FindModuleForAddress(uptr address) {
+ char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
+ if (external_symbolizer_ == 0) {
+ ReportExternalSymbolizerError(
+ "WARNING: Trying to symbolize code, but external "
+ "symbolizer is not initialized!\n");
+ return 0;
+ }
+ for (;;) {
+ char *reply = external_symbolizer_->SendCommand(is_data, module_name,
+ module_offset);
+ if (reply)
+ return reply;
+ // Try to restart symbolizer subprocess. If we don't succeed, forget
+ // about it and don't try to use it later.
+ if (!external_symbolizer_->Restart()) {
+ ReportExternalSymbolizerError(
+ "WARNING: Failed to use and restart external symbolizer!\n");
+ external_symbolizer_ = 0;
+ return 0;
+ }
+ }
+ }
+
+ LoadedModule *FindModuleForAddress(uptr address) {
if (modules_ == 0) {
- modules_ = (ModuleDIContext*)InternalAlloc(
- kMaxNumberOfModuleContexts * sizeof(ModuleDIContext));
+ modules_ = (LoadedModule*)(symbolizer_allocator.Allocate(
+ kMaxNumberOfModuleContexts * sizeof(LoadedModule)));
CHECK(modules_);
n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts);
CHECK_GT(n_modules_, 0);
@@ -129,10 +308,22 @@ class Symbolizer {
}
return 0;
}
- static const uptr kMaxNumberOfModuleContexts = 4096;
- // Array of module debug info contexts is leaked.
- ModuleDIContext *modules_;
+ void ReportExternalSymbolizerError(const char *msg) {
+ // Don't use atomics here for now, as SymbolizeCode can't be called
+ // from multiple threads anyway.
+ static bool reported;
+ if (!reported) {
+ Report(msg);
+ reported = true;
+ }
+ }
+
+ // 16K loaded modules should be enough for everyone.
+ static const uptr kMaxNumberOfModuleContexts = 1 << 14;
+ LoadedModule *modules_; // Array of module descriptions is leaked.
uptr n_modules_;
+
+ ExternalSymbolizer *external_symbolizer_; // Leaked.
};
static Symbolizer symbolizer; // Linker initialized.
@@ -141,4 +332,12 @@ uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames) {
return symbolizer.SymbolizeCode(address, frames, max_frames);
}
+bool SymbolizeData(uptr address, DataInfo *info) {
+ return symbolizer.SymbolizeData(address, info);
+}
+
+bool InitializeExternalSymbolizer(const char *path_to_symbolizer) {
+ return symbolizer.InitializeExternalSymbolizer(path_to_symbolizer);
+}
+
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_symbolizer.h b/lib/sanitizer_common/sanitizer_symbolizer.h
index c813e8088d7e..c26d621ea065 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer.h
+++ b/lib/sanitizer_common/sanitizer_symbolizer.h
@@ -44,6 +44,22 @@ struct AddressInfo {
}
// Deletes all strings and sets all fields to zero.
void Clear();
+
+ void FillAddressAndModuleInfo(uptr addr, const char *mod_name,
+ uptr mod_offset) {
+ address = addr;
+ module = internal_strdup(mod_name);
+ module_offset = mod_offset;
+ }
+};
+
+struct DataInfo {
+ uptr address;
+ char *module;
+ uptr module_offset;
+ char *name;
+ uptr start;
+ uptr size;
};
// Fills at most "max_frames" elements of "frames" with descriptions
@@ -51,49 +67,45 @@ struct AddressInfo {
// of descriptions actually filled.
// This function should NOT be called from two threads simultaneously.
uptr SymbolizeCode(uptr address, AddressInfo *frames, uptr max_frames);
+bool SymbolizeData(uptr address, DataInfo *info);
-// Debug info routines
-struct DWARFSection {
- const char *data;
- uptr size;
- DWARFSection() {
- data = 0;
- size = 0;
- }
-};
-// Returns true on success.
-bool FindDWARFSection(uptr object_file_addr, const char *section_name,
- DWARFSection *section);
-bool IsFullNameOfDWARFSection(const char *full_name, const char *short_name);
+// Attempts to demangle the provided C++ mangled name.
+const char *Demangle(const char *Name);
-class ModuleDIContext {
+// Starts external symbolizer program in a subprocess. Sanitizer communicates
+// with external symbolizer via pipes.
+bool InitializeExternalSymbolizer(const char *path_to_symbolizer);
+
+class LoadedModule {
public:
- ModuleDIContext(const char *module_name, uptr base_address);
+ LoadedModule(const char *module_name, uptr base_address);
void addAddressRange(uptr beg, uptr end);
bool containsAddress(uptr address) const;
- void getAddressInfo(AddressInfo *info);
const char *full_name() const { return full_name_; }
+ uptr base_address() const { return base_address_; }
private:
- void CreateDIContext();
-
struct AddressRange {
uptr beg;
uptr end;
};
char *full_name_;
- char *short_name_;
uptr base_address_;
- static const uptr kMaxNumberOfAddressRanges = 8;
+ static const uptr kMaxNumberOfAddressRanges = 6;
AddressRange ranges_[kMaxNumberOfAddressRanges];
uptr n_ranges_;
- uptr mapped_addr_;
- uptr mapped_size_;
};
-// OS-dependent function that gets the linked list of all loaded modules.
-uptr GetListOfModules(ModuleDIContext *modules, uptr max_modules);
+// Creates external symbolizer connected via pipe, user should write
+// to output_fd and read from input_fd.
+bool StartSymbolizerSubprocess(const char *path_to_symbolizer,
+ int *input_fd, int *output_fd);
+
+// OS-dependent function that fills array with descriptions of at most
+// "max_modules" currently loaded modules. Returns the number of
+// initialized modules.
+uptr GetListOfModules(LoadedModule *modules, uptr max_modules);
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_itanium.cc b/lib/sanitizer_common/sanitizer_symbolizer_itanium.cc
new file mode 100644
index 000000000000..438629492923
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_itanium.cc
@@ -0,0 +1,42 @@
+//===-- sanitizer_symbolizer_itanium.cc -----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between the sanitizer run-time libraries.
+// Itanium C++ ABI-specific implementation of symbolizer parts.
+//===----------------------------------------------------------------------===//
+#if defined(__APPLE__) || defined(__linux__)
+
+#include "sanitizer_symbolizer.h"
+
+#include <stdlib.h>
+
+// C++ demangling function, as required by Itanium C++ ABI. This is weak,
+// because we do not require a C++ ABI library to be linked to a program
+// using sanitizers; if it's not present, we'll just use the mangled name.
+namespace __cxxabiv1 {
+ extern "C" char *__cxa_demangle(const char *mangled, char *buffer,
+ size_t *length, int *status)
+ SANITIZER_WEAK_ATTRIBUTE;
+}
+
+const char *__sanitizer::Demangle(const char *MangledName) {
+ // FIXME: __cxa_demangle aggressively insists on allocating memory.
+ // There's not much we can do about that, short of providing our
+ // own demangler (libc++abi's implementation could be adapted so that
+ // it does not allocate). For now, we just call it anyway, and we leak
+ // the returned value.
+ if (__cxxabiv1::__cxa_demangle)
+ if (const char *Demangled =
+ __cxxabiv1::__cxa_demangle(MangledName, 0, 0, 0))
+ return Demangled;
+
+ return MangledName;
+}
+
+#endif // __APPLE__ || __linux__
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_linux.cc b/lib/sanitizer_common/sanitizer_symbolizer_linux.cc
new file mode 100644
index 000000000000..4bd3dc8826ef
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_linux.cc
@@ -0,0 +1,182 @@
+//===-- sanitizer_symbolizer_linux.cc -------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+// Linux-specific implementation of symbolizer parts.
+//===----------------------------------------------------------------------===//
+#ifdef __linux__
+#include "sanitizer_common.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_symbolizer.h"
+
+#include <elf.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#if !defined(__ANDROID__) && !defined(ANDROID)
+#include <link.h>
+#endif
+
+namespace __sanitizer {
+
+static const int kSymbolizerStartupTimeMillis = 10;
+
+bool StartSymbolizerSubprocess(const char *path_to_symbolizer,
+ int *input_fd, int *output_fd) {
+ if (!FileExists(path_to_symbolizer)) {
+ Report("WARNING: invalid path to external symbolizer!\n");
+ return false;
+ }
+
+ int *infd = NULL;
+ int *outfd = NULL;
+ // The client program may close its stdin and/or stdout and/or stderr
+ // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
+ // In this case the communication between the forked processes may be
+ // broken if either the parent or the child tries to close or duplicate
+ // these descriptors. The loop below produces two pairs of file
+ // descriptors, each greater than 2 (stderr).
+ int sock_pair[5][2];
+ for (int i = 0; i < 5; i++) {
+ if (pipe(sock_pair[i]) == -1) {
+ for (int j = 0; j < i; j++) {
+ internal_close(sock_pair[j][0]);
+ internal_close(sock_pair[j][1]);
+ }
+ Report("WARNING: Can't create a socket pair to start "
+ "external symbolizer (errno: %d)\n", errno);
+ return false;
+ } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) {
+ if (infd == NULL) {
+ infd = sock_pair[i];
+ } else {
+ outfd = sock_pair[i];
+ for (int j = 0; j < i; j++) {
+ if (sock_pair[j] == infd) continue;
+ internal_close(sock_pair[j][0]);
+ internal_close(sock_pair[j][1]);
+ }
+ break;
+ }
+ }
+ }
+ CHECK(infd);
+ CHECK(outfd);
+
+ int pid = fork();
+ if (pid == -1) {
+ // Fork() failed.
+ internal_close(infd[0]);
+ internal_close(infd[1]);
+ internal_close(outfd[0]);
+ internal_close(outfd[1]);
+ Report("WARNING: failed to fork external symbolizer "
+ " (errno: %d)\n", errno);
+ return false;
+ } else if (pid == 0) {
+ // Child subprocess.
+ internal_close(STDOUT_FILENO);
+ internal_close(STDIN_FILENO);
+ internal_dup2(outfd[0], STDIN_FILENO);
+ internal_dup2(infd[1], STDOUT_FILENO);
+ internal_close(outfd[0]);
+ internal_close(outfd[1]);
+ internal_close(infd[0]);
+ internal_close(infd[1]);
+ for (int fd = getdtablesize(); fd > 2; fd--)
+ internal_close(fd);
+ execl(path_to_symbolizer, path_to_symbolizer, (char*)0);
+ Exit(1);
+ }
+
+ // Continue execution in parent process.
+ internal_close(outfd[0]);
+ internal_close(infd[1]);
+ *input_fd = infd[0];
+ *output_fd = outfd[1];
+
+ // Check that symbolizer subprocess started successfully.
+ int pid_status;
+ SleepForMillis(kSymbolizerStartupTimeMillis);
+ int exited_pid = waitpid(pid, &pid_status, WNOHANG);
+ if (exited_pid != 0) {
+ // Either waitpid failed, or child has already exited.
+ Report("WARNING: external symbolizer didn't start up correctly!\n");
+ return false;
+ }
+
+ return true;
+}
+
+#if defined(__ANDROID__) || defined(ANDROID)
+uptr GetListOfModules(LoadedModule *modules, uptr max_modules) {
+ UNIMPLEMENTED();
+}
+#else // ANDROID
+typedef ElfW(Phdr) Elf_Phdr;
+
+struct DlIteratePhdrData {
+ LoadedModule *modules;
+ uptr current_n;
+ uptr max_n;
+};
+
+static const uptr kMaxPathLength = 512;
+
+static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
+ DlIteratePhdrData *data = (DlIteratePhdrData*)arg;
+ if (data->current_n == data->max_n)
+ return 0;
+ InternalScopedBuffer<char> module_name(kMaxPathLength);
+ module_name.data()[0] = '\0';
+ if (data->current_n == 0) {
+ // First module is the binary itself.
+ uptr module_name_len = internal_readlink(
+ "/proc/self/exe", module_name.data(), module_name.size());
+ CHECK_NE(module_name_len, (uptr)-1);
+ CHECK_LT(module_name_len, module_name.size());
+ module_name[module_name_len] = '\0';
+ } else if (info->dlpi_name) {
+ internal_strncpy(module_name.data(), info->dlpi_name, module_name.size());
+ }
+ if (module_name.data()[0] == '\0')
+ return 0;
+ void *mem = &data->modules[data->current_n];
+ LoadedModule *cur_module = new(mem) LoadedModule(module_name.data(),
+ info->dlpi_addr);
+ data->current_n++;
+ for (int i = 0; i < info->dlpi_phnum; i++) {
+ const Elf_Phdr *phdr = &info->dlpi_phdr[i];
+ if (phdr->p_type == PT_LOAD) {
+ uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
+ uptr cur_end = cur_beg + phdr->p_memsz;
+ cur_module->addAddressRange(cur_beg, cur_end);
+ }
+ }
+ return 0;
+}
+
+uptr GetListOfModules(LoadedModule *modules, uptr max_modules) {
+ CHECK(modules);
+ DlIteratePhdrData data = {modules, 0, max_modules};
+ dl_iterate_phdr(dl_iterate_phdr_cb, &data);
+ return data.current_n;
+}
+#endif // ANDROID
+
+} // namespace __sanitizer
+
+#endif // __linux__
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_mac.cc b/lib/sanitizer_common/sanitizer_symbolizer_mac.cc
new file mode 100644
index 000000000000..23993607e77b
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_mac.cc
@@ -0,0 +1,31 @@
+//===-- sanitizer_symbolizer_mac.cc ---------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+// Mac-specific implementation of symbolizer parts.
+//===----------------------------------------------------------------------===//
+#ifdef __APPLE__
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+
+bool StartSymbolizerSubprocess(const char *path_to_symbolizer,
+ int *input_fd, int *output_fd) {
+ UNIMPLEMENTED();
+}
+
+uptr GetListOfModules(LoadedModule *modules, uptr max_modules) {
+ UNIMPLEMENTED();
+}
+
+} // namespace __sanitizer
+
+#endif // __APPLE__
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_win.cc b/lib/sanitizer_common/sanitizer_symbolizer_win.cc
new file mode 100644
index 000000000000..f1b6a02a6f9a
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_win.cc
@@ -0,0 +1,37 @@
+//===-- sanitizer_symbolizer_win.cc ---------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+// Windows-specific implementation of symbolizer parts.
+//===----------------------------------------------------------------------===//
+#ifdef _WIN32
+#include <windows.h>
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+
+bool StartSymbolizerSubprocess(const char *path_to_symbolizer,
+ int *input_fd, int *output_fd) {
+ UNIMPLEMENTED();
+}
+
+uptr GetListOfModules(LoadedModule *modules, uptr max_modules) {
+ UNIMPLEMENTED();
+};
+
+const char *Demangle(const char *MangledName) {
+ return MangledName;
+}
+
+} // namespace __sanitizer
+
+#endif // _WIN32
diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc
index c68a1fee4068..2ae37af8847c 100644
--- a/lib/sanitizer_common/sanitizer_win.cc
+++ b/lib/sanitizer_common/sanitizer_win.cc
@@ -12,15 +12,32 @@
// sanitizer_libc.h.
//===----------------------------------------------------------------------===//
#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#define NOGDI
+#include <stdlib.h>
+#include <io.h>
#include <windows.h>
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_mutex.h"
namespace __sanitizer {
// --------------------- sanitizer_common.h
+uptr GetPageSize() {
+ return 1U << 14; // FIXME: is this configurable?
+}
+
+uptr GetMmapGranularity() {
+ return 1U << 16; // FIXME: is this configurable?
+}
+
+bool FileExists(const char *filename) {
+ UNIMPLEMENTED();
+}
+
int GetPid() {
return GetProcessId(GetCurrentProcess());
}
@@ -42,7 +59,6 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
*stack_bottom = (uptr)mbi.AllocationBase;
}
-
void *MmapOrDie(uptr size, const char *mem_type) {
void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (rv == 0) {
@@ -62,8 +78,18 @@ void UnmapOrDie(void *addr, uptr size) {
}
void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
- return VirtualAlloc((LPVOID)fixed_addr, size,
- MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+ // FIXME: is this really "NoReserve"? On Win32 this does not matter much,
+ // but on Win64 it does.
+ void *p = VirtualAlloc((LPVOID)fixed_addr, size,
+ MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+ if (p == 0)
+ Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at %p (%d)\n",
+ size, size, fixed_addr, GetLastError());
+ return p;
+}
+
+void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
+ return MmapFixedNoReserve(fixed_addr, size);
}
void *Mprotect(uptr fixed_addr, uptr size) {
@@ -98,7 +124,6 @@ const char *GetEnv(const char *name) {
const char *GetPwd() {
UNIMPLEMENTED();
- return 0;
}
void DumpProcessMap() {
@@ -109,6 +134,22 @@ void DisableCoreDumper() {
UNIMPLEMENTED();
}
+void ReExec() {
+ UNIMPLEMENTED();
+}
+
+void PrepareForSandboxing() {
+ // Nothing here for now.
+}
+
+bool StackSizeIsUnlimited() {
+ UNIMPLEMENTED();
+}
+
+void SetStackSizeLimitInBytes(uptr limit) {
+ UNIMPLEMENTED();
+}
+
void SleepForSeconds(int seconds) {
Sleep(seconds * 1000);
}
@@ -126,50 +167,40 @@ void Abort() {
_exit(-1); // abort is not NORETURN on Windows.
}
+#ifndef SANITIZER_GO
int Atexit(void (*function)(void)) {
return atexit(function);
}
-
-// ------------------ sanitizer_symbolizer.h
-bool FindDWARFSection(uptr object_file_addr, const char *section_name,
- DWARFSection *section) {
- UNIMPLEMENTED();
- return false;
-}
-
-uptr GetListOfModules(ModuleDIContext *modules, uptr max_modules) {
- UNIMPLEMENTED();
-};
+#endif
// ------------------ sanitizer_libc.h
void *internal_mmap(void *addr, uptr length, int prot, int flags,
int fd, u64 offset) {
UNIMPLEMENTED();
- return 0;
}
int internal_munmap(void *addr, uptr length) {
UNIMPLEMENTED();
- return 0;
}
int internal_close(fd_t fd) {
UNIMPLEMENTED();
- return 0;
+}
+
+int internal_isatty(fd_t fd) {
+ return _isatty(fd);
}
fd_t internal_open(const char *filename, bool write) {
UNIMPLEMENTED();
- return 0;
}
uptr internal_read(fd_t fd, void *buf, uptr count) {
UNIMPLEMENTED();
- return 0;
}
uptr internal_write(fd_t fd, const void *buf, uptr count) {
- if (fd != 2)
+ if (fd != kStderrFd)
UNIMPLEMENTED();
HANDLE err = GetStdHandle(STD_ERROR_HANDLE);
if (err == 0)
@@ -182,19 +213,57 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) {
uptr internal_filesize(fd_t fd) {
UNIMPLEMENTED();
- return 0;
}
int internal_dup2(int oldfd, int newfd) {
UNIMPLEMENTED();
- return 0;
}
-int internal_sched_yield() {
+uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
UNIMPLEMENTED();
+}
+
+int internal_sched_yield() {
+ Sleep(0);
return 0;
}
+// ---------------------- BlockingMutex ---------------- {{{1
+enum LockState {
+ LOCK_UNINITIALIZED = 0,
+ LOCK_READY = -1,
+};
+
+BlockingMutex::BlockingMutex(LinkerInitialized li) {
+ // FIXME: see comments in BlockingMutex::Lock() for the details.
+ CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);
+
+ CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
+ InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+ owner_ = LOCK_READY;
+}
+
+void BlockingMutex::Lock() {
+ if (owner_ == LOCK_UNINITIALIZED) {
+ // FIXME: hm, global BlockingMutex objects are not initialized?!?
+ // This might be a side effect of the clang+cl+link Frankenbuild...
+ new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1));
+
+ // FIXME: If it turns out the linker doesn't invoke our
+ // constructors, we should probably manually Lock/Unlock all the global
+ // locks while we're starting in one thread to avoid double-init races.
+ }
+ EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+ CHECK(owner_ == LOCK_READY);
+ owner_ = GetThreadSelf();
+}
+
+void BlockingMutex::Unlock() {
+ CHECK(owner_ == GetThreadSelf());
+ owner_ = LOCK_READY;
+ LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
+}
+
} // namespace __sanitizer
#endif // _WIN32
diff --git a/lib/sanitizer_common/scripts/check_lint.sh b/lib/sanitizer_common/scripts/check_lint.sh
new file mode 100755
index 000000000000..e65794df0ce7
--- /dev/null
+++ b/lib/sanitizer_common/scripts/check_lint.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+
+set -e
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+
+# Guess path to LLVM_CHECKOUT if not provided
+if [ "${LLVM_CHECKOUT}" == "" ]; then
+ LLVM_CHECKOUT="${SCRIPT_DIR}/../../../../../"
+ echo "LLVM Checkout: ${LLVM_CHECKOUT}"
+fi
+
+# Cpplint setup
+cd ${SCRIPT_DIR}
+if [ ! -d cpplint ]; then
+ svn co -r83 http://google-styleguide.googlecode.com/svn/trunk/cpplint cpplint
+fi
+CPPLINT=${SCRIPT_DIR}/cpplint/cpplint.py
+
+# Filters
+# TODO: remove some of these filters
+ASAN_RTL_LINT_FILTER=-readability/casting,-readability/check,-build/include,-build/header_guard,-build/class,-legal/copyright,-build/namespaces
+ASAN_TEST_LINT_FILTER=-readability/casting,-build/include,-legal/copyright,-whitespace/newline,-runtime/sizeof,-runtime/int,-runtime/printf,-build/header_guard
+ASAN_LIT_TEST_LINT_FILTER=${ASAN_TEST_LINT_FILTER},-whitespace/line_length
+TSAN_RTL_LINT_FILTER=-legal/copyright,-build/include,-readability/casting,-build/header_guard,-build/namespaces
+TSAN_TEST_LINT_FILTER=${TSAN_RTL_LINT_FILTER},-runtime/threadsafe_fn,-runtime/int
+TSAN_LIT_TEST_LINT_FILTER=${TSAN_TEST_LINT_FILTER},-whitespace/line_length
+MSAN_RTL_LINT_FILTER=-legal/copyright,-build/include,-readability/casting,-build/header_guard,-build/namespaces
+TSAN_RTL_INC_LINT_FILTER=${TSAN_TEST_LINT_FILTER},-runtime/sizeof
+
+cd ${LLVM_CHECKOUT}
+
+# LLVM Instrumentation
+LLVM_INSTRUMENTATION=lib/Transforms/Instrumentation
+LLVM_LINT_FILTER=-,+whitespace
+${CPPLINT} --filter=${LLVM_LINT_FILTER} ${LLVM_INSTRUMENTATION}/*Sanitizer.cpp \
+ ${LLVM_INSTRUMENTATION}/BlackList.*
+
+COMPILER_RT=projects/compiler-rt
+
+# Headers
+SANITIZER_INCLUDES=${COMPILER_RT}/include/sanitizer
+${CPPLINT} --filter=${TSAN_RTL_LINT_FILTER} ${SANITIZER_INCLUDES}/*.h
+
+# Sanitizer_common
+COMMON_RTL=${COMPILER_RT}/lib/sanitizer_common
+${CPPLINT} --filter=${ASAN_RTL_LINT_FILTER} ${COMMON_RTL}/*.{cc,h}
+${CPPLINT} --filter=${TSAN_RTL_LINT_FILTER} ${COMMON_RTL}/tests/*.cc
+
+# Interception
+INTERCEPTION=${COMPILER_RT}/lib/interception
+${CPPLINT} --filter=${ASAN_RTL_LINT_FILTER} ${INTERCEPTION}/*.{cc,h}
+
+# ASan
+ASAN_RTL=${COMPILER_RT}/lib/asan
+${CPPLINT} --filter=${ASAN_RTL_LINT_FILTER} ${ASAN_RTL}/*.{cc,h}
+${CPPLINT} --filter=${ASAN_TEST_LINT_FILTER} ${ASAN_RTL}/tests/*.{cc,h}
+${CPPLINT} --filter=${ASAN_LIT_TEST_LINT_FILTER} ${ASAN_RTL}/lit_tests/*.cc \
+ ${ASAN_RTL}/lit_tests/*/*.cc \
+
+# TSan
+TSAN_RTL=${COMPILER_RT}/lib/tsan
+${CPPLINT} --filter=${TSAN_RTL_LINT_FILTER} ${TSAN_RTL}/rtl/*.{cc,h}
+${CPPLINT} --filter=${TSAN_TEST_LINT_FILTER} ${TSAN_RTL}/tests/rtl/*.{cc,h} \
+ ${TSAN_RTL}/tests/unit/*.cc
+${CPPLINT} --filter=${TSAN_LIT_TEST_LINT_FILTER} ${TSAN_RTL}/lit_tests/*.cc
+
+# MSan
+MSAN_RTL=${COMPILER_RT}/lib/msan
+${CPPLINT} --filter=${MSAN_RTL_LINT_FILTER} ${MSAN_RTL}/*.{cc,h}
+
+set +e
+
+# Misc files
+FILES=${COMMON_RTL}/*.inc
+for FILE in $FILES; do
+ TMPFILE=$(mktemp -u ${FILE}.XXXXX).cc
+ echo "Checking $FILE"
+ cp -f $FILE $TMPFILE && \
+ ${CPPLINT} --filter=${TSAN_RTL_INC_LINT_FILTER} $TMPFILE
+ rm $TMPFILE
+done
diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt
new file mode 100644
index 000000000000..f83a89cbe37c
--- /dev/null
+++ b/lib/sanitizer_common/tests/CMakeLists.txt
@@ -0,0 +1,138 @@
+include(CompilerRTCompile)
+
+set(SANITIZER_UNITTESTS
+ sanitizer_allocator_test.cc
+ sanitizer_common_test.cc
+ sanitizer_flags_test.cc
+ sanitizer_libc_test.cc
+ sanitizer_list_test.cc
+ sanitizer_mutex_test.cc
+ sanitizer_printf_test.cc
+ sanitizer_scanf_interceptor_test.cc
+ sanitizer_stackdepot_test.cc
+ sanitizer_test_main.cc
+ )
+
+set(SANITIZER_TEST_HEADERS)
+foreach(header ${SANITIZER_HEADERS})
+ list(APPEND SANITIZER_TEST_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../${header})
+endforeach()
+
+include_directories(..)
+include_directories(../..)
+
+# Adds static library which contains sanitizer_common object file
+# (universal binary on Mac and arch-specific object files on Linux).
+macro(add_sanitizer_common_lib library)
+ add_library(${library} STATIC ${ARGN})
+ set_target_properties(${library} PROPERTIES
+ ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+endmacro()
+
+function(get_sanitizer_common_lib_for_arch arch lib lib_name)
+ if(APPLE)
+ set(tgt_name "RTSanitizerCommon.test.osx")
+ else()
+ set(tgt_name "RTSanitizerCommon.test.${arch}")
+ endif()
+ set(${lib} "${tgt_name}" PARENT_SCOPE)
+ set(${lib_name} "lib${tgt_name}.a" PARENT_SCOPE)
+endfunction()
+
+# Sanitizer_common unit tests testsuite.
+add_custom_target(SanitizerUnitTests)
+set_target_properties(SanitizerUnitTests PROPERTIES
+ FOLDER "Sanitizer unittests")
+
+# Adds sanitizer tests for architecture.
+macro(add_sanitizer_tests_for_arch arch)
+ get_target_flags_for_arch(${arch} TARGET_FLAGS)
+ set(SANITIZER_TEST_SOURCES ${SANITIZER_UNITTESTS}
+ ${COMPILER_RT_GTEST_SOURCE})
+ set(SANITIZER_TEST_CFLAGS ${COMPILER_RT_GTEST_INCLUDE_CFLAGS}
+ -I${COMPILER_RT_SOURCE_DIR}/include
+ -I${COMPILER_RT_SOURCE_DIR}/lib
+ -I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common
+ -O2 -g ${TARGET_FLAGS})
+ set(SANITIZER_TEST_LINK_FLAGS -lstdc++ -lpthread ${TARGET_FLAGS})
+ set(SANITIZER_TEST_OBJECTS)
+ foreach(source ${SANITIZER_TEST_SOURCES})
+ get_filename_component(basename ${source} NAME)
+ set(output_obj "${basename}.${arch}.o")
+ clang_compile(${output_obj} ${source}
+ CFLAGS ${SANITIZER_TEST_CFLAGS}
+ DEPS gtest ${SANITIZER_RUNTIME_LIBRARIES}
+ ${SANITIZER_TEST_HEADERS})
+ list(APPEND SANITIZER_TEST_OBJECTS ${output_obj})
+ endforeach()
+ get_sanitizer_common_lib_for_arch(${arch} SANITIZER_COMMON_LIB
+ SANITIZER_COMMON_LIB_NAME)
+ # Add unittest target.
+ set(SANITIZER_TEST_NAME "Sanitizer-${arch}-Test")
+ add_compiler_rt_test(SanitizerUnitTests ${SANITIZER_TEST_NAME}
+ OBJECTS ${SANITIZER_TEST_OBJECTS}
+ ${SANITIZER_COMMON_LIB_NAME}
+ DEPS ${SANITIZER_TEST_OBJECTS} ${SANITIZER_COMMON_LIB}
+ LINK_FLAGS ${SANITIZER_TEST_LINK_FLAGS})
+endmacro()
+
+if(COMPILER_RT_CAN_EXECUTE_TESTS)
+ # We use just-built clang to build sanitizer_common unittests, so we must
+ # be sure that produced binaries would work.
+ if(APPLE)
+ add_sanitizer_common_lib("RTSanitizerCommon.test.osx"
+ $<TARGET_OBJECTS:RTSanitizerCommon.osx>)
+ else()
+ if(CAN_TARGET_x86_64)
+ add_sanitizer_common_lib("RTSanitizerCommon.test.x86_64"
+ $<TARGET_OBJECTS:RTSanitizerCommon.x86_64>)
+ endif()
+ if(CAN_TARGET_i386)
+ add_sanitizer_common_lib("RTSanitizerCommon.test.i386"
+ $<TARGET_OBJECTS:RTSanitizerCommon.i386>)
+ endif()
+ endif()
+ if(CAN_TARGET_x86_64)
+ add_sanitizer_tests_for_arch(x86_64)
+ endif()
+ if(CAN_TARGET_i386)
+ add_sanitizer_tests_for_arch(i386)
+ endif()
+
+ # Run unittests as a part of lit testsuite.
+ configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+
+ add_lit_testsuite(check-sanitizer "Running sanitizer library unittests"
+ ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS SanitizerUnitTests
+ )
+ set_target_properties(check-sanitizer PROPERTIES FOLDER "Sanitizer unittests")
+endif()
+
+if(ANDROID)
+ # We assume that unit tests on Android are built in a build
+ # tree with fresh Clang as a host compiler.
+ add_executable(SanitizerTest
+ ${SANITIZER_UNITTESTS}
+ ${COMPILER_RT_GTEST_SOURCE}
+ $<TARGET_OBJECTS:RTSanitizerCommon.arm.android>
+ )
+ set_target_compile_flags(SanitizerTest
+ ${SANITIZER_COMMON_CFLAGS}
+ ${COMPILER_RT_GTEST_INCLUDE_CFLAGS}
+ -I${COMPILER_RT_SOURCE_DIR}/include
+ -I${COMPILER_RT_SOURCE_DIR}/lib
+ -I${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common
+ -O2 -g
+ )
+ # Setup correct output directory and link flags.
+ get_unittest_directory(OUTPUT_DIR)
+ set_target_properties(SanitizerTest PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
+ set_target_link_flags(SanitizerTest ${SANITIZER_TEST_LINK_FLAGS})
+ # Add unit test to test suite.
+ add_dependencies(SanitizerUnitTests SanitizerTest)
+endif()
diff --git a/lib/sanitizer_common/tests/lit.cfg b/lib/sanitizer_common/tests/lit.cfg
new file mode 100644
index 000000000000..d774753985ac
--- /dev/null
+++ b/lib/sanitizer_common/tests/lit.cfg
@@ -0,0 +1,29 @@
+# -*- Python -*-
+
+import os
+
+def get_required_attr(config, attr_name):
+ attr_value = getattr(config, attr_name, None)
+ if not attr_value:
+ lit.fatal("No attribute %r in test configuration! You may need to run "
+ "tests from your build directory or add this attribute "
+ "to lit.site.cfg " % attr_name)
+ return attr_value
+
+# Setup attributes common for all compiler-rt projects.
+llvm_src_root = get_required_attr(config, 'llvm_src_root')
+compiler_rt_lit_unit_cfg = os.path.join(llvm_src_root, "projects",
+ "compiler-rt", "lib",
+ "lit.common.unit.cfg")
+lit.load_config(config, compiler_rt_lit_unit_cfg)
+
+# Setup config name.
+config.name = 'SanitizerCommon-Unit'
+
+# Setup test source and exec root. For unit tests, we define
+# it as build directory with sanitizer_common unit tests.
+llvm_obj_root = get_required_attr(config, "llvm_obj_root")
+config.test_exec_root = os.path.join(llvm_obj_root, "projects",
+ "compiler-rt", "lib",
+ "sanitizer_common", "tests")
+config.test_source_root = config.test_exec_root
diff --git a/lib/sanitizer_common/tests/lit.site.cfg.in b/lib/sanitizer_common/tests/lit.site.cfg.in
new file mode 100644
index 000000000000..bb9a28d6a6cb
--- /dev/null
+++ b/lib/sanitizer_common/tests/lit.site.cfg.in
@@ -0,0 +1,9 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+config.build_type = "@CMAKE_BUILD_TYPE@"
+config.llvm_obj_root = "@LLVM_BINARY_DIR@"
+config.llvm_src_root = "@LLVM_SOURCE_DIR@"
+
+# Let the main config do the real work.
+lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg")
diff --git a/lib/sanitizer_common/tests/sanitizer_allocator64_test.cc b/lib/sanitizer_common/tests/sanitizer_allocator64_test.cc
deleted file mode 100644
index 1410f26ce84f..000000000000
--- a/lib/sanitizer_common/tests/sanitizer_allocator64_test.cc
+++ /dev/null
@@ -1,257 +0,0 @@
-//===-- sanitizer_allocator64_test.cc -------------------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// Tests for sanitizer_allocator64.h.
-//===----------------------------------------------------------------------===//
-#include "sanitizer_common/sanitizer_allocator64.h"
-#include "gtest/gtest.h"
-
-#include <algorithm>
-#include <vector>
-
-static const uptr kAllocatorSpace = 0x600000000000ULL;
-static const uptr kAllocatorSize = 0x10000000000; // 1T.
-
-typedef DefaultSizeClassMap SCMap;
-typedef
- SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 16, SCMap> Allocator;
-typedef SizeClassAllocatorLocalCache<Allocator::kNumClasses, Allocator>
- AllocatorCache;
-
-TEST(SanitizerCommon, DefaultSizeClassMap) {
-#if 0
- for (uptr i = 0; i < SCMap::kNumClasses; i++) {
- // printf("% 3ld: % 5ld (%4lx); ", i, SCMap::Size(i), SCMap::Size(i));
- printf("c%ld => %ld ", i, SCMap::Size(i));
- if ((i % 8) == 7)
- printf("\n");
- }
- printf("\n");
-#endif
-
- for (uptr c = 0; c < SCMap::kNumClasses; c++) {
- uptr s = SCMap::Size(c);
- CHECK_EQ(SCMap::ClassID(s), c);
- if (c != SCMap::kNumClasses - 1)
- CHECK_EQ(SCMap::ClassID(s + 1), c + 1);
- CHECK_EQ(SCMap::ClassID(s - 1), c);
- if (c)
- CHECK_GT(SCMap::Size(c), SCMap::Size(c-1));
- }
- CHECK_EQ(SCMap::ClassID(SCMap::kMaxSize + 1), 0);
-
- for (uptr s = 1; s <= SCMap::kMaxSize; s++) {
- uptr c = SCMap::ClassID(s);
- CHECK_LT(c, SCMap::kNumClasses);
- CHECK_GE(SCMap::Size(c), s);
- if (c > 0)
- CHECK_LT(SCMap::Size(c-1), s);
- }
-}
-
-TEST(SanitizerCommon, SizeClassAllocator64) {
- Allocator a;
- a.Init();
-
- static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000,
- 50000, 60000, 100000, 300000, 500000, 1000000, 2000000};
-
- std::vector<void *> allocated;
-
- uptr last_total_allocated = 0;
- for (int i = 0; i < 5; i++) {
- // Allocate a bunch of chunks.
- for (uptr s = 0; s < sizeof(sizes) /sizeof(sizes[0]); s++) {
- uptr size = sizes[s];
- // printf("s = %ld\n", size);
- uptr n_iter = std::max((uptr)2, 1000000 / size);
- for (uptr i = 0; i < n_iter; i++) {
- void *x = a.Allocate(size, 1);
- allocated.push_back(x);
- CHECK(a.PointerIsMine(x));
- CHECK_GE(a.GetActuallyAllocatedSize(x), size);
- uptr class_id = a.GetSizeClass(x);
- CHECK_EQ(class_id, SCMap::ClassID(size));
- uptr *metadata = reinterpret_cast<uptr*>(a.GetMetaData(x));
- metadata[0] = reinterpret_cast<uptr>(x) + 1;
- metadata[1] = 0xABCD;
- }
- }
- // Deallocate all.
- for (uptr i = 0; i < allocated.size(); i++) {
- void *x = allocated[i];
- uptr *metadata = reinterpret_cast<uptr*>(a.GetMetaData(x));
- CHECK_EQ(metadata[0], reinterpret_cast<uptr>(x) + 1);
- CHECK_EQ(metadata[1], 0xABCD);
- a.Deallocate(x);
- }
- allocated.clear();
- uptr total_allocated = a.TotalMemoryUsed();
- if (last_total_allocated == 0)
- last_total_allocated = total_allocated;
- CHECK_EQ(last_total_allocated, total_allocated);
- }
-
- a.TestOnlyUnmap();
-}
-
-
-TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) {
- Allocator a;
- a.Init();
- static volatile void *sink;
-
- const uptr kNumAllocs = 10000;
- void *allocated[kNumAllocs];
- for (uptr i = 0; i < kNumAllocs; i++) {
- uptr size = (i % 4096) + 1;
- void *x = a.Allocate(size, 1);
- allocated[i] = x;
- }
- // Get Metadata kNumAllocs^2 times.
- for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) {
- sink = a.GetMetaData(allocated[i % kNumAllocs]);
- }
- for (uptr i = 0; i < kNumAllocs; i++) {
- a.Deallocate(allocated[i]);
- }
-
- a.TestOnlyUnmap();
- (void)sink;
-}
-
-void FailInAssertionOnOOM() {
- Allocator a;
- a.Init();
- const uptr size = 1 << 20;
- for (int i = 0; i < 1000000; i++) {
- a.Allocate(size, 1);
- }
-
- a.TestOnlyUnmap();
-}
-
-TEST(SanitizerCommon, SizeClassAllocator64Overflow) {
- EXPECT_DEATH(FailInAssertionOnOOM(),
- "allocated_user.*allocated_meta.*kRegionSize");
-}
-
-TEST(SanitizerCommon, LargeMmapAllocator) {
- LargeMmapAllocator a;
- a.Init();
-
- static const int kNumAllocs = 100;
- void *allocated[kNumAllocs];
- static const uptr size = 1000;
- // Allocate some.
- for (int i = 0; i < kNumAllocs; i++) {
- allocated[i] = a.Allocate(size, 1);
- }
- // Deallocate all.
- CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs);
- for (int i = 0; i < kNumAllocs; i++) {
- void *p = allocated[i];
- CHECK(a.PointerIsMine(p));
- a.Deallocate(p);
- }
- // Check that non left.
- CHECK_EQ(a.TotalMemoryUsed(), 0);
-
- // Allocate some more, also add metadata.
- for (int i = 0; i < kNumAllocs; i++) {
- void *x = a.Allocate(size, 1);
- CHECK_GE(a.GetActuallyAllocatedSize(x), size);
- uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(x));
- *meta = i;
- allocated[i] = x;
- }
- CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs);
- // Deallocate all in reverse order.
- for (int i = 0; i < kNumAllocs; i++) {
- int idx = kNumAllocs - i - 1;
- void *p = allocated[idx];
- uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(p));
- CHECK_EQ(*meta, idx);
- CHECK(a.PointerIsMine(p));
- a.Deallocate(p);
- }
- CHECK_EQ(a.TotalMemoryUsed(), 0);
-}
-
-TEST(SanitizerCommon, CombinedAllocator) {
- typedef Allocator PrimaryAllocator;
- typedef LargeMmapAllocator SecondaryAllocator;
- typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
- SecondaryAllocator> Allocator;
-
- AllocatorCache cache;
- Allocator a;
- a.Init();
- cache.Init();
- const uptr kNumAllocs = 100000;
- const uptr kNumIter = 10;
- for (uptr iter = 0; iter < kNumIter; iter++) {
- std::vector<void*> allocated;
- for (uptr i = 0; i < kNumAllocs; i++) {
- uptr size = (i % (1 << 14)) + 1;
- if ((i % 1024) == 0)
- size = 1 << (10 + (i % 14));
- void *x = a.Allocate(&cache, size, 1);
- uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(x));
- CHECK_EQ(*meta, 0);
- *meta = size;
- allocated.push_back(x);
- }
-
- random_shuffle(allocated.begin(), allocated.end());
-
- for (uptr i = 0; i < kNumAllocs; i++) {
- void *x = allocated[i];
- uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(x));
- CHECK_NE(*meta, 0);
- CHECK(a.PointerIsMine(x));
- *meta = 0;
- a.Deallocate(&cache, x);
- }
- allocated.clear();
- a.SwallowCache(&cache);
- }
- a.TestOnlyUnmap();
-}
-
-static THREADLOCAL AllocatorCache static_allocator_cache;
-
-TEST(SanitizerCommon, SizeClassAllocatorLocalCache) {
- static_allocator_cache.Init();
-
- Allocator a;
- AllocatorCache cache;
-
- a.Init();
- cache.Init();
-
- const uptr kNumAllocs = 10000;
- const int kNumIter = 100;
- uptr saved_total = 0;
- for (int i = 0; i < kNumIter; i++) {
- void *allocated[kNumAllocs];
- for (uptr i = 0; i < kNumAllocs; i++) {
- allocated[i] = cache.Allocate(&a, 0);
- }
- for (uptr i = 0; i < kNumAllocs; i++) {
- cache.Deallocate(&a, 0, allocated[i]);
- }
- cache.Drain(&a);
- uptr total_allocated = a.TotalMemoryUsed();
- if (saved_total)
- CHECK_EQ(saved_total, total_allocated);
- saved_total = total_allocated;
- }
-
- a.TestOnlyUnmap();
-}
diff --git a/lib/sanitizer_common/tests/sanitizer_allocator64_testlib.cc b/lib/sanitizer_common/tests/sanitizer_allocator64_testlib.cc
deleted file mode 100644
index cff782342a6a..000000000000
--- a/lib/sanitizer_common/tests/sanitizer_allocator64_testlib.cc
+++ /dev/null
@@ -1,99 +0,0 @@
-//===-- sanitizer_allocator64_testlib.cc ----------------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// Malloc replacement library based on CombinedAllocator.
-// The primary purpose of this file is an end-to-end integration test
-// for CombinedAllocator.
-//===----------------------------------------------------------------------===//
-#include "sanitizer_common/sanitizer_allocator64.h"
-#include <stddef.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <assert.h>
-
-namespace {
-static const uptr kAllocatorSpace = 0x600000000000ULL;
-static const uptr kAllocatorSize = 0x10000000000; // 1T.
-
-typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 16,
- DefaultSizeClassMap> PrimaryAllocator;
-typedef SizeClassAllocatorLocalCache<PrimaryAllocator::kNumClasses,
- PrimaryAllocator> AllocatorCache;
-typedef LargeMmapAllocator SecondaryAllocator;
-typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
- SecondaryAllocator> Allocator;
-
-static THREADLOCAL AllocatorCache cache;
-static Allocator allocator;
-
-static int inited = 0;
-
-__attribute__((constructor))
-void Init() {
- if (inited) return;
- inited = true; // this must happen before any threads are created.
- allocator.Init();
-}
-
-} // namespace
-
-namespace __sanitizer {
-void NORETURN Die() {
- _exit(77);
-}
-void NORETURN CheckFailed(const char *file, int line, const char *cond,
- u64 v1, u64 v2) {
- fprintf(stderr, "CheckFailed: %s:%d %s (%lld %lld)\n",
- file, line, cond, v1, v2);
- Die();
-}
-}
-
-#if 1
-extern "C" {
-void *malloc(size_t size) {
- Init();
- assert(inited);
- return allocator.Allocate(&cache, size, 8);
-}
-
-void free(void *p) {
- assert(inited);
- allocator.Deallocate(&cache, p);
-}
-
-void *calloc(size_t nmemb, size_t size) {
- assert(inited);
- return allocator.Allocate(&cache, nmemb * size, 8, /*cleared=*/true);
-}
-
-void *realloc(void *p, size_t new_size) {
- assert(inited);
- return allocator.Reallocate(&cache, p, new_size, 8);
-}
-
-void *memalign() { assert(0); }
-
-int posix_memalign(void **memptr, size_t alignment, size_t size) {
- *memptr = allocator.Allocate(&cache, size, alignment);
- CHECK_EQ(((uptr)*memptr & (alignment - 1)), 0);
- return 0;
-}
-
-void *valloc(size_t size) {
- assert(inited);
- return allocator.Allocate(&cache, size, kPageSize);
-}
-
-void *pvalloc(size_t size) {
- assert(inited);
- if (size == 0) size = kPageSize;
- return allocator.Allocate(&cache, size, kPageSize);
-}
-}
-#endif
diff --git a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
index d6c7f56dc143..d67f4636ef4f 100644
--- a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
@@ -8,13 +8,465 @@
//===----------------------------------------------------------------------===//
//
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+// Tests for sanitizer_allocator.h.
//
//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_allocator.h"
#include "sanitizer_common/sanitizer_common.h"
+
+#include "sanitizer_test_utils.h"
+
#include "gtest/gtest.h"
+
#include <stdlib.h>
+#include <pthread.h>
+#include <algorithm>
+#include <vector>
+
+// Too slow for debug build
+#if TSAN_DEBUG == 0
+
+#if SANITIZER_WORDSIZE == 64
+static const uptr kAllocatorSpace = 0x700000000000ULL;
+static const uptr kAllocatorSize = 0x010000000000ULL; // 1T.
+static const u64 kAddressSpaceSize = 1ULL << 47;
+
+typedef SizeClassAllocator64<
+ kAllocatorSpace, kAllocatorSize, 16, DefaultSizeClassMap> Allocator64;
+
+typedef SizeClassAllocator64<
+ kAllocatorSpace, kAllocatorSize, 16, CompactSizeClassMap> Allocator64Compact;
+#else
+static const u64 kAddressSpaceSize = 1ULL << 32;
+#endif
+
+typedef SizeClassAllocator32<
+ 0, kAddressSpaceSize, 16, CompactSizeClassMap> Allocator32Compact;
+
+template <class SizeClassMap>
+void TestSizeClassMap() {
+ typedef SizeClassMap SCMap;
+ // SCMap::Print();
+ SCMap::Validate();
+}
+
+TEST(SanitizerCommon, DefaultSizeClassMap) {
+ TestSizeClassMap<DefaultSizeClassMap>();
+}
+
+TEST(SanitizerCommon, CompactSizeClassMap) {
+ TestSizeClassMap<CompactSizeClassMap>();
+}
-namespace __sanitizer {
+template <class Allocator>
+void TestSizeClassAllocator() {
+ Allocator *a = new Allocator;
+ a->Init();
+ SizeClassAllocatorLocalCache<Allocator> cache;
+ cache.Init();
+
+ static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000,
+ 50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000};
+
+ std::vector<void *> allocated;
+
+ uptr last_total_allocated = 0;
+ for (int i = 0; i < 3; i++) {
+ // Allocate a bunch of chunks.
+ for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) {
+ uptr size = sizes[s];
+ if (!a->CanAllocate(size, 1)) continue;
+ // printf("s = %ld\n", size);
+ uptr n_iter = std::max((uptr)6, 10000000 / size);
+ // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter);
+ for (uptr i = 0; i < n_iter; i++) {
+ uptr class_id0 = Allocator::SizeClassMapT::ClassID(size);
+ char *x = (char*)cache.Allocate(a, class_id0);
+ x[0] = 0;
+ x[size - 1] = 0;
+ x[size / 2] = 0;
+ allocated.push_back(x);
+ CHECK_EQ(x, a->GetBlockBegin(x));
+ CHECK_EQ(x, a->GetBlockBegin(x + size - 1));
+ CHECK(a->PointerIsMine(x));
+ CHECK(a->PointerIsMine(x + size - 1));
+ CHECK(a->PointerIsMine(x + size / 2));
+ CHECK_GE(a->GetActuallyAllocatedSize(x), size);
+ uptr class_id = a->GetSizeClass(x);
+ CHECK_EQ(class_id, Allocator::SizeClassMapT::ClassID(size));
+ uptr *metadata = reinterpret_cast<uptr*>(a->GetMetaData(x));
+ metadata[0] = reinterpret_cast<uptr>(x) + 1;
+ metadata[1] = 0xABCD;
+ }
+ }
+ // Deallocate all.
+ for (uptr i = 0; i < allocated.size(); i++) {
+ void *x = allocated[i];
+ uptr *metadata = reinterpret_cast<uptr*>(a->GetMetaData(x));
+ CHECK_EQ(metadata[0], reinterpret_cast<uptr>(x) + 1);
+ CHECK_EQ(metadata[1], 0xABCD);
+ cache.Deallocate(a, a->GetSizeClass(x), x);
+ }
+ allocated.clear();
+ uptr total_allocated = a->TotalMemoryUsed();
+ if (last_total_allocated == 0)
+ last_total_allocated = total_allocated;
+ CHECK_EQ(last_total_allocated, total_allocated);
+ }
+
+ a->TestOnlyUnmap();
+ delete a;
+}
+
+#if SANITIZER_WORDSIZE == 64
+TEST(SanitizerCommon, SizeClassAllocator64) {
+ TestSizeClassAllocator<Allocator64>();
+}
+
+TEST(SanitizerCommon, SizeClassAllocator64Compact) {
+ TestSizeClassAllocator<Allocator64Compact>();
+}
+#endif
+
+TEST(SanitizerCommon, SizeClassAllocator32Compact) {
+ TestSizeClassAllocator<Allocator32Compact>();
+}
+
+template <class Allocator>
+void SizeClassAllocatorMetadataStress() {
+ Allocator *a = new Allocator;
+ a->Init();
+ SizeClassAllocatorLocalCache<Allocator> cache;
+ cache.Init();
+ static volatile void *sink;
+
+ const uptr kNumAllocs = 10000;
+ void *allocated[kNumAllocs];
+ for (uptr i = 0; i < kNumAllocs; i++) {
+ void *x = cache.Allocate(a, 1 + i % 50);
+ allocated[i] = x;
+ }
+ // Get Metadata kNumAllocs^2 times.
+ for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) {
+ sink = a->GetMetaData(allocated[i % kNumAllocs]);
+ }
+ for (uptr i = 0; i < kNumAllocs; i++) {
+ cache.Deallocate(a, 1 + i % 50, allocated[i]);
+ }
+
+ a->TestOnlyUnmap();
+ (void)sink;
+ delete a;
+}
+
+#if SANITIZER_WORDSIZE == 64
+TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) {
+ SizeClassAllocatorMetadataStress<Allocator64>();
+}
+
+TEST(SanitizerCommon, SizeClassAllocator64CompactMetadataStress) {
+ SizeClassAllocatorMetadataStress<Allocator64Compact>();
+}
+#endif
+TEST(SanitizerCommon, SizeClassAllocator32CompactMetadataStress) {
+ SizeClassAllocatorMetadataStress<Allocator32Compact>();
+}
+
+struct TestMapUnmapCallback {
+ static int map_count, unmap_count;
+ void OnMap(uptr p, uptr size) const { map_count++; }
+ void OnUnmap(uptr p, uptr size) const { unmap_count++; }
+};
+int TestMapUnmapCallback::map_count;
+int TestMapUnmapCallback::unmap_count;
+
+#if SANITIZER_WORDSIZE == 64
+TEST(SanitizerCommon, SizeClassAllocator64MapUnmapCallback) {
+ TestMapUnmapCallback::map_count = 0;
+ TestMapUnmapCallback::unmap_count = 0;
+ typedef SizeClassAllocator64<
+ kAllocatorSpace, kAllocatorSize, 16, DefaultSizeClassMap,
+ TestMapUnmapCallback> Allocator64WithCallBack;
+ Allocator64WithCallBack *a = new Allocator64WithCallBack;
+ a->Init();
+ EXPECT_EQ(TestMapUnmapCallback::map_count, 1); // Allocator state.
+ SizeClassAllocatorLocalCache<Allocator64WithCallBack> cache;
+ cache.Init();
+ a->AllocateBatch(&cache, 64);
+ EXPECT_EQ(TestMapUnmapCallback::map_count, 3); // State + alloc + metadata.
+ a->TestOnlyUnmap();
+ EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); // The whole thing.
+ delete a;
+}
+#endif
+
+TEST(SanitizerCommon, SizeClassAllocator32MapUnmapCallback) {
+ TestMapUnmapCallback::map_count = 0;
+ TestMapUnmapCallback::unmap_count = 0;
+ typedef SizeClassAllocator32<
+ 0, kAddressSpaceSize, 16, CompactSizeClassMap,
+ TestMapUnmapCallback> Allocator32WithCallBack;
+ Allocator32WithCallBack *a = new Allocator32WithCallBack;
+ a->Init();
+ EXPECT_EQ(TestMapUnmapCallback::map_count, 1); // Allocator state.
+ SizeClassAllocatorLocalCache<Allocator32WithCallBack> cache;
+ cache.Init();
+ a->AllocateBatch(&cache, 64);
+ EXPECT_EQ(TestMapUnmapCallback::map_count, 2); // alloc.
+ a->TestOnlyUnmap();
+ EXPECT_EQ(TestMapUnmapCallback::unmap_count, 2); // The whole thing + alloc.
+ delete a;
+ // fprintf(stderr, "Map: %d Unmap: %d\n",
+ // TestMapUnmapCallback::map_count,
+ // TestMapUnmapCallback::unmap_count);
+}
+
+TEST(SanitizerCommon, LargeMmapAllocatorMapUnmapCallback) {
+ TestMapUnmapCallback::map_count = 0;
+ TestMapUnmapCallback::unmap_count = 0;
+ LargeMmapAllocator<TestMapUnmapCallback> a;
+ a.Init();
+ void *x = a.Allocate(1 << 20, 1);
+ EXPECT_EQ(TestMapUnmapCallback::map_count, 1);
+ a.Deallocate(x);
+ EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1);
+}
+
+template<class Allocator>
+void FailInAssertionOnOOM() {
+ Allocator a;
+ a.Init();
+ SizeClassAllocatorLocalCache<Allocator> cache;
+ cache.Init();
+ for (int i = 0; i < 1000000; i++) {
+ a.AllocateBatch(&cache, 64);
+ }
+
+ a.TestOnlyUnmap();
+}
+
+#if SANITIZER_WORDSIZE == 64
+TEST(SanitizerCommon, SizeClassAllocator64Overflow) {
+ EXPECT_DEATH(FailInAssertionOnOOM<Allocator64>(), "Out of memory");
+}
+#endif
+
+TEST(SanitizerCommon, LargeMmapAllocator) {
+ LargeMmapAllocator<> a;
+ a.Init();
+
+ static const int kNumAllocs = 1000;
+ char *allocated[kNumAllocs];
+ static const uptr size = 4000;
+ // Allocate some.
+ for (int i = 0; i < kNumAllocs; i++) {
+ allocated[i] = (char *)a.Allocate(size, 1);
+ CHECK(a.PointerIsMine(allocated[i]));
+ }
+ // Deallocate all.
+ CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs);
+ for (int i = 0; i < kNumAllocs; i++) {
+ char *p = allocated[i];
+ CHECK(a.PointerIsMine(p));
+ a.Deallocate(p);
+ }
+ // Check that non left.
+ CHECK_EQ(a.TotalMemoryUsed(), 0);
+
+ // Allocate some more, also add metadata.
+ for (int i = 0; i < kNumAllocs; i++) {
+ char *x = (char *)a.Allocate(size, 1);
+ CHECK_GE(a.GetActuallyAllocatedSize(x), size);
+ uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(x));
+ *meta = i;
+ allocated[i] = x;
+ }
+ for (int i = 0; i < kNumAllocs * kNumAllocs; i++) {
+ char *p = allocated[i % kNumAllocs];
+ CHECK(a.PointerIsMine(p));
+ CHECK(a.PointerIsMine(p + 2000));
+ }
+ CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs);
+ // Deallocate all in reverse order.
+ for (int i = 0; i < kNumAllocs; i++) {
+ int idx = kNumAllocs - i - 1;
+ char *p = allocated[idx];
+ uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(p));
+ CHECK_EQ(*meta, idx);
+ CHECK(a.PointerIsMine(p));
+ a.Deallocate(p);
+ }
+ CHECK_EQ(a.TotalMemoryUsed(), 0);
+
+ // Test alignments.
+ uptr max_alignment = SANITIZER_WORDSIZE == 64 ? (1 << 28) : (1 << 24);
+ for (uptr alignment = 8; alignment <= max_alignment; alignment *= 2) {
+ const uptr kNumAlignedAllocs = 100;
+ for (uptr i = 0; i < kNumAlignedAllocs; i++) {
+ uptr size = ((i % 10) + 1) * 4096;
+ char *p = allocated[i] = (char *)a.Allocate(size, alignment);
+ CHECK_EQ(p, a.GetBlockBegin(p));
+ CHECK_EQ(p, a.GetBlockBegin(p + size - 1));
+ CHECK_EQ(p, a.GetBlockBegin(p + size / 2));
+ CHECK_EQ(0, (uptr)allocated[i] % alignment);
+ p[0] = p[size - 1] = 0;
+ }
+ for (uptr i = 0; i < kNumAlignedAllocs; i++) {
+ a.Deallocate(allocated[i]);
+ }
+ }
+}
+
+template
+<class PrimaryAllocator, class SecondaryAllocator, class AllocatorCache>
+void TestCombinedAllocator() {
+ typedef
+ CombinedAllocator<PrimaryAllocator, AllocatorCache, SecondaryAllocator>
+ Allocator;
+ Allocator *a = new Allocator;
+ a->Init();
+
+ AllocatorCache cache;
+ cache.Init();
+
+ EXPECT_EQ(a->Allocate(&cache, -1, 1), (void*)0);
+ EXPECT_EQ(a->Allocate(&cache, -1, 1024), (void*)0);
+ EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1), (void*)0);
+ EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1024), (void*)0);
+ EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1023, 1024), (void*)0);
+
+ const uptr kNumAllocs = 100000;
+ const uptr kNumIter = 10;
+ for (uptr iter = 0; iter < kNumIter; iter++) {
+ std::vector<void*> allocated;
+ for (uptr i = 0; i < kNumAllocs; i++) {
+ uptr size = (i % (1 << 14)) + 1;
+ if ((i % 1024) == 0)
+ size = 1 << (10 + (i % 14));
+ void *x = a->Allocate(&cache, size, 1);
+ uptr *meta = reinterpret_cast<uptr*>(a->GetMetaData(x));
+ CHECK_EQ(*meta, 0);
+ *meta = size;
+ allocated.push_back(x);
+ }
+
+ random_shuffle(allocated.begin(), allocated.end());
+
+ for (uptr i = 0; i < kNumAllocs; i++) {
+ void *x = allocated[i];
+ uptr *meta = reinterpret_cast<uptr*>(a->GetMetaData(x));
+ CHECK_NE(*meta, 0);
+ CHECK(a->PointerIsMine(x));
+ *meta = 0;
+ a->Deallocate(&cache, x);
+ }
+ allocated.clear();
+ a->SwallowCache(&cache);
+ }
+ a->TestOnlyUnmap();
+}
+
+#if SANITIZER_WORDSIZE == 64
+TEST(SanitizerCommon, CombinedAllocator64) {
+ TestCombinedAllocator<Allocator64,
+ LargeMmapAllocator<>,
+ SizeClassAllocatorLocalCache<Allocator64> > ();
+}
+
+TEST(SanitizerCommon, CombinedAllocator64Compact) {
+ TestCombinedAllocator<Allocator64Compact,
+ LargeMmapAllocator<>,
+ SizeClassAllocatorLocalCache<Allocator64Compact> > ();
+}
+#endif
+
+TEST(SanitizerCommon, CombinedAllocator32Compact) {
+ TestCombinedAllocator<Allocator32Compact,
+ LargeMmapAllocator<>,
+ SizeClassAllocatorLocalCache<Allocator32Compact> > ();
+}
+
+template <class AllocatorCache>
+void TestSizeClassAllocatorLocalCache() {
+ static AllocatorCache static_allocator_cache;
+ static_allocator_cache.Init();
+ AllocatorCache cache;
+ typedef typename AllocatorCache::Allocator Allocator;
+ Allocator *a = new Allocator();
+
+ a->Init();
+ cache.Init();
+
+ const uptr kNumAllocs = 10000;
+ const int kNumIter = 100;
+ uptr saved_total = 0;
+ for (int class_id = 1; class_id <= 5; class_id++) {
+ for (int it = 0; it < kNumIter; it++) {
+ void *allocated[kNumAllocs];
+ for (uptr i = 0; i < kNumAllocs; i++) {
+ allocated[i] = cache.Allocate(a, class_id);
+ }
+ for (uptr i = 0; i < kNumAllocs; i++) {
+ cache.Deallocate(a, class_id, allocated[i]);
+ }
+ cache.Drain(a);
+ uptr total_allocated = a->TotalMemoryUsed();
+ if (it)
+ CHECK_EQ(saved_total, total_allocated);
+ saved_total = total_allocated;
+ }
+ }
+
+ a->TestOnlyUnmap();
+ delete a;
+}
+
+#if SANITIZER_WORDSIZE == 64
+TEST(SanitizerCommon, SizeClassAllocator64LocalCache) {
+ TestSizeClassAllocatorLocalCache<
+ SizeClassAllocatorLocalCache<Allocator64> >();
+}
+
+TEST(SanitizerCommon, SizeClassAllocator64CompactLocalCache) {
+ TestSizeClassAllocatorLocalCache<
+ SizeClassAllocatorLocalCache<Allocator64Compact> >();
+}
+#endif
+
+TEST(SanitizerCommon, SizeClassAllocator32CompactLocalCache) {
+ TestSizeClassAllocatorLocalCache<
+ SizeClassAllocatorLocalCache<Allocator32Compact> >();
+}
+
+#if SANITIZER_WORDSIZE == 64
+typedef SizeClassAllocatorLocalCache<Allocator64> AllocatorCache;
+static AllocatorCache static_allocator_cache;
+
+void *AllocatorLeakTestWorker(void *arg) {
+ typedef AllocatorCache::Allocator Allocator;
+ Allocator *a = (Allocator*)(arg);
+ static_allocator_cache.Allocate(a, 10);
+ static_allocator_cache.Drain(a);
+ return 0;
+}
+
+TEST(SanitizerCommon, AllocatorLeakTest) {
+ typedef AllocatorCache::Allocator Allocator;
+ Allocator a;
+ a.Init();
+ uptr total_used_memory = 0;
+ for (int i = 0; i < 100; i++) {
+ pthread_t t;
+ EXPECT_EQ(0, pthread_create(&t, 0, AllocatorLeakTestWorker, &a));
+ EXPECT_EQ(0, pthread_join(t, 0));
+ if (i == 0)
+ total_used_memory = a.TotalMemoryUsed();
+ EXPECT_EQ(a.TotalMemoryUsed(), total_used_memory);
+ }
+
+ a.TestOnlyUnmap();
+}
+#endif
TEST(Allocator, Basic) {
char *p = (char*)InternalAlloc(10);
@@ -22,14 +474,6 @@ TEST(Allocator, Basic) {
char *p2 = (char*)InternalAlloc(20);
EXPECT_NE(p2, (char*)0);
EXPECT_NE(p2, p);
- for (int i = 0; i < 10; i++) {
- p[i] = 42;
- EXPECT_EQ(p, InternalAllocBlock(p + i));
- }
- for (int i = 0; i < 20; i++) {
- ((char*)p2)[i] = 42;
- EXPECT_EQ(p2, InternalAllocBlock(p2 + i));
- }
InternalFree(p);
InternalFree(p2);
}
@@ -39,13 +483,9 @@ TEST(Allocator, Stress) {
char *ptrs[kCount];
unsigned rnd = 42;
for (int i = 0; i < kCount; i++) {
- uptr sz = rand_r(&rnd) % 1000;
+ uptr sz = my_rand_r(&rnd) % 1000;
char *p = (char*)InternalAlloc(sz);
EXPECT_NE(p, (char*)0);
- for (uptr j = 0; j < sz; j++) {
- p[j] = 42;
- EXPECT_EQ(p, InternalAllocBlock(p + j));
- }
ptrs[i] = p;
}
for (int i = 0; i < kCount; i++) {
@@ -53,4 +493,18 @@ TEST(Allocator, Stress) {
}
}
-} // namespace __sanitizer
+TEST(Allocator, ScopedBuffer) {
+ const int kSize = 512;
+ {
+ InternalScopedBuffer<int> int_buf(kSize);
+ EXPECT_EQ(sizeof(int) * kSize, int_buf.size()); // NOLINT
+ }
+ InternalScopedBuffer<char> char_buf(kSize);
+ EXPECT_EQ(sizeof(char) * kSize, char_buf.size()); // NOLINT
+ internal_memset(char_buf.data(), 'c', kSize);
+ for (int i = 0; i < kSize; i++) {
+ EXPECT_EQ('c', char_buf[i]);
+ }
+}
+
+#endif // #if TSAN_DEBUG==0
diff --git a/lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc b/lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc
new file mode 100644
index 000000000000..f6a944f68f5e
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_allocator_testlib.cc
@@ -0,0 +1,162 @@
+//===-- sanitizer_allocator_testlib.cc ------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Malloc replacement library based on CombinedAllocator.
+// The primary purpose of this file is an end-to-end integration test
+// for CombinedAllocator.
+//===----------------------------------------------------------------------===//
+/* Usage:
+clang++ -fno-exceptions -g -fPIC -I. -I../include -Isanitizer \
+ sanitizer_common/tests/sanitizer_allocator_testlib.cc \
+ sanitizer_common/sanitizer_*.cc -shared -lpthread -o testmalloc.so
+LD_PRELOAD=`pwd`/testmalloc.so /your/app
+*/
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <pthread.h>
+
+#ifndef SANITIZER_MALLOC_HOOK
+# define SANITIZER_MALLOC_HOOK(p, s)
+#endif
+
+#ifndef SANITIZER_FREE_HOOK
+# define SANITIZER_FREE_HOOK(p)
+#endif
+
+namespace {
+static const uptr kAllocatorSpace = 0x600000000000ULL;
+static const uptr kAllocatorSize = 0x10000000000ULL; // 1T.
+
+typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0,
+ CompactSizeClassMap> PrimaryAllocator;
+typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
+typedef LargeMmapAllocator<> SecondaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
+ SecondaryAllocator> Allocator;
+
+static Allocator allocator;
+static bool global_inited;
+static THREADLOCAL AllocatorCache cache;
+static THREADLOCAL bool thread_inited;
+static pthread_key_t pkey;
+
+static void thread_dtor(void *v) {
+ if ((uptr)v != 3) {
+ pthread_setspecific(pkey, (void*)((uptr)v + 1));
+ return;
+ }
+ allocator.SwallowCache(&cache);
+}
+
+static void NOINLINE thread_init() {
+ if (!global_inited) {
+ global_inited = true;
+ allocator.Init();
+ pthread_key_create(&pkey, thread_dtor);
+ }
+ thread_inited = true;
+ pthread_setspecific(pkey, (void*)1);
+ cache.Init();
+}
+} // namespace
+
+extern "C" {
+void *malloc(size_t size) {
+ if (UNLIKELY(!thread_inited))
+ thread_init();
+ void *p = allocator.Allocate(&cache, size, 8);
+ SANITIZER_MALLOC_HOOK(p, size);
+ return p;
+}
+
+void free(void *p) {
+ if (UNLIKELY(!thread_inited))
+ thread_init();
+ SANITIZER_FREE_HOOK(p);
+ allocator.Deallocate(&cache, p);
+}
+
+void *calloc(size_t nmemb, size_t size) {
+ if (UNLIKELY(!thread_inited))
+ thread_init();
+ size *= nmemb;
+ void *p = allocator.Allocate(&cache, size, 8, false);
+ memset(p, 0, size);
+ SANITIZER_MALLOC_HOOK(p, size);
+ return p;
+}
+
+void *realloc(void *p, size_t size) {
+ if (UNLIKELY(!thread_inited))
+ thread_init();
+ if (p) {
+ SANITIZER_FREE_HOOK(p);
+ }
+ p = allocator.Reallocate(&cache, p, size, 8);
+ if (p) {
+ SANITIZER_MALLOC_HOOK(p, size);
+ }
+ return p;
+}
+
+void *memalign(size_t alignment, size_t size) {
+ if (UNLIKELY(!thread_inited))
+ thread_init();
+ void *p = allocator.Allocate(&cache, size, alignment);
+ SANITIZER_MALLOC_HOOK(p, size);
+ return p;
+}
+
+int posix_memalign(void **memptr, size_t alignment, size_t size) {
+ if (UNLIKELY(!thread_inited))
+ thread_init();
+ *memptr = allocator.Allocate(&cache, size, alignment);
+ SANITIZER_MALLOC_HOOK(*memptr, size);
+ return 0;
+}
+
+void *valloc(size_t size) {
+ if (UNLIKELY(!thread_inited))
+ thread_init();
+ if (size == 0)
+ size = GetPageSizeCached();
+ void *p = allocator.Allocate(&cache, size, GetPageSizeCached());
+ SANITIZER_MALLOC_HOOK(p, size);
+ return p;
+}
+
+void cfree(void *p) ALIAS("free");
+void *pvalloc(size_t size) ALIAS("valloc");
+void *__libc_memalign(size_t alignment, size_t size) ALIAS("memalign");
+
+void malloc_usable_size() {
+}
+
+void mallinfo() {
+}
+
+void mallopt() {
+}
+} // extern "C"
+
+namespace std {
+ struct nothrow_t;
+}
+
+void *operator new(size_t size) ALIAS("malloc");
+void *operator new[](size_t size) ALIAS("malloc");
+void *operator new(size_t size, std::nothrow_t const&) ALIAS("malloc");
+void *operator new[](size_t size, std::nothrow_t const&) ALIAS("malloc");
+void operator delete(void *ptr) ALIAS("free");
+void operator delete[](void *ptr) ALIAS("free");
+void operator delete(void *ptr, std::nothrow_t const&) ALIAS("free");
+void operator delete[](void *ptr, std::nothrow_t const&) ALIAS("free");
diff --git a/lib/sanitizer_common/tests/sanitizer_common_test.cc b/lib/sanitizer_common/tests/sanitizer_common_test.cc
index 91570dcc99e0..01d8b5a87c01 100644
--- a/lib/sanitizer_common/tests/sanitizer_common_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_common_test.cc
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
#include "gtest/gtest.h"
namespace __sanitizer {
@@ -63,4 +64,36 @@ TEST(SanitizerCommon, SortTest) {
EXPECT_TRUE(IsSorted(array, 2));
}
+TEST(SanitizerCommon, MmapAlignedOrDie) {
+ uptr PageSize = GetPageSizeCached();
+ for (uptr size = 1; size <= 32; size *= 2) {
+ for (uptr alignment = 1; alignment <= 32; alignment *= 2) {
+ for (int iter = 0; iter < 100; iter++) {
+ uptr res = (uptr)MmapAlignedOrDie(
+ size * PageSize, alignment * PageSize, "MmapAlignedOrDieTest");
+ EXPECT_EQ(0U, res % (alignment * PageSize));
+ internal_memset((void*)res, 1, size * PageSize);
+ UnmapOrDie((void*)res, size * PageSize);
+ }
+ }
+ }
+}
+
+#ifdef __linux__
+TEST(SanitizerCommon, SanitizerSetThreadName) {
+ const char *names[] = {
+ "0123456789012",
+ "01234567890123",
+ "012345678901234", // Larger names will be truncated on linux.
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(names); i++) {
+ EXPECT_TRUE(SanitizerSetThreadName(names[i]));
+ char buff[100];
+ EXPECT_TRUE(SanitizerGetThreadName(buff, sizeof(buff) - 1));
+ EXPECT_EQ(0, internal_strcmp(buff, names[i]));
+ }
+}
+#endif
+
} // namespace sanitizer
diff --git a/lib/sanitizer_common/tests/sanitizer_flags_test.cc b/lib/sanitizer_common/tests/sanitizer_flags_test.cc
index 4b273e5b9cef..c0589f4d2e90 100644
--- a/lib/sanitizer_common/tests/sanitizer_flags_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_flags_test.cc
@@ -12,11 +12,9 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_libc.h"
#include "gtest/gtest.h"
-#include "tsan_rtl.h" // FIXME: break dependency from TSan runtime.
-using __tsan::ScopedInRtl;
-
#include <string.h>
namespace __sanitizer {
@@ -34,11 +32,10 @@ static void TestStrFlag(const char *start_value, const char *env,
const char *final_value) {
const char *flag = start_value;
ParseFlag(env, &flag, kFlagName);
- EXPECT_STREQ(final_value, flag);
+ EXPECT_EQ(internal_strcmp(final_value, flag), 0);
}
TEST(SanitizerCommon, BooleanFlags) {
- ScopedInRtl in_rtl;
TestFlag(true, "--flag_name", true);
TestFlag(false, "flag_name", false);
TestFlag(false, "--flag_name=1", true);
@@ -51,7 +48,6 @@ TEST(SanitizerCommon, BooleanFlags) {
}
TEST(SanitizerCommon, IntFlags) {
- ScopedInRtl in_rtl;
TestFlag(-11, 0, -11);
TestFlag(-11, "flag_name", 0);
TestFlag(-11, "--flag_name=", 0);
@@ -60,12 +56,12 @@ TEST(SanitizerCommon, IntFlags) {
}
TEST(SanitizerCommon, StrFlags) {
- ScopedInRtl in_rtl;
TestStrFlag("zzz", 0, "zzz");
TestStrFlag("zzz", "flag_name", "");
TestStrFlag("zzz", "--flag_name=", "");
TestStrFlag("", "--flag_name=abc", "abc");
TestStrFlag("", "--flag_name='abc zxc'", "abc zxc");
+ TestStrFlag("", "--flag_name='abc zxcc'", "abc zxcc");
TestStrFlag("", "--flag_name=\"abc qwe\" asd", "abc qwe");
}
diff --git a/lib/sanitizer_common/tests/sanitizer_libc_test.cc b/lib/sanitizer_common/tests/sanitizer_libc_test.cc
new file mode 100644
index 000000000000..b9d8414e0cbf
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_libc_test.cc
@@ -0,0 +1,42 @@
+//===-- sanitizer_libc_test.cc --------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Tests for sanitizer_libc.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_libc.h"
+#include "gtest/gtest.h"
+
+// A regression test for internal_memmove() implementation.
+TEST(SanitizerCommon, InternalMemmoveRegression) {
+ char src[] = "Hello World";
+ char *dest = src + 6;
+ __sanitizer::internal_memmove(dest, src, 5);
+ EXPECT_EQ(dest[0], src[0]);
+ EXPECT_EQ(dest[4], src[4]);
+}
+
+TEST(SanitizerCommon, mem_is_zero) {
+ size_t size = 128;
+ char *x = new char[size];
+ memset(x, 0, size);
+ for (size_t pos = 0; pos < size; pos++) {
+ x[pos] = 1;
+ for (size_t beg = 0; beg < size; beg++) {
+ for (size_t end = beg; end < size; end++) {
+ // fprintf(stderr, "pos %zd beg %zd end %zd \n", pos, beg, end);
+ if (beg <= pos && pos < end)
+ EXPECT_FALSE(__sanitizer::mem_is_zero(x + beg, end - beg));
+ else
+ EXPECT_TRUE(__sanitizer::mem_is_zero(x + beg, end - beg));
+ }
+ }
+ x[pos] = 0;
+ }
+ delete [] x;
+}
diff --git a/lib/sanitizer_common/tests/sanitizer_list_test.cc b/lib/sanitizer_common/tests/sanitizer_list_test.cc
index d328fbfdf92c..fbe53c0375c0 100644
--- a/lib/sanitizer_common/tests/sanitizer_list_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_list_test.cc
@@ -21,8 +21,7 @@ struct ListItem {
typedef IntrusiveList<ListItem> List;
-// Check that IntrusiveList can be made thread-local.
-static THREADLOCAL List static_list;
+static List static_list;
static void SetList(List *l, ListItem *x = 0,
ListItem *y = 0, ListItem *z = 0) {
@@ -154,4 +153,21 @@ TEST(SanitizerCommon, IntrusiveList) {
CHECK(l2.empty());
}
+TEST(SanitizerCommon, IntrusiveListAppendEmpty) {
+ ListItem i;
+ List l;
+ l.clear();
+ l.push_back(&i);
+ List l2;
+ l2.clear();
+ l.append_back(&l2);
+ CHECK_EQ(l.back(), &i);
+ CHECK_EQ(l.front(), &i);
+ CHECK_EQ(l.size(), 1);
+ l.append_front(&l2);
+ CHECK_EQ(l.back(), &i);
+ CHECK_EQ(l.front(), &i);
+ CHECK_EQ(l.size(), 1);
+}
+
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/tests/sanitizer_mutex_test.cc b/lib/sanitizer_common/tests/sanitizer_mutex_test.cc
new file mode 100644
index 000000000000..6bb2ae29a188
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_mutex_test.cc
@@ -0,0 +1,128 @@
+//===-- sanitizer_mutex_test.cc -------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "gtest/gtest.h"
+
+#include <string.h>
+
+namespace __sanitizer {
+
+template<typename MutexType>
+class TestData {
+ public:
+ explicit TestData(MutexType *mtx)
+ : mtx_(mtx) {
+ for (int i = 0; i < kSize; i++)
+ data_[i] = 0;
+ }
+
+ void Write() {
+ Lock l(mtx_);
+ T v0 = data_[0];
+ for (int i = 0; i < kSize; i++) {
+ CHECK_EQ(data_[i], v0);
+ data_[i]++;
+ }
+ }
+
+ void TryWrite() {
+ if (!mtx_->TryLock())
+ return;
+ T v0 = data_[0];
+ for (int i = 0; i < kSize; i++) {
+ CHECK_EQ(data_[i], v0);
+ data_[i]++;
+ }
+ mtx_->Unlock();
+ }
+
+ void Backoff() {
+ volatile T data[kSize] = {};
+ for (int i = 0; i < kSize; i++) {
+ data[i]++;
+ CHECK_EQ(data[i], 1);
+ }
+ }
+
+ private:
+ typedef GenericScopedLock<MutexType> Lock;
+ static const int kSize = 64;
+ typedef u64 T;
+ MutexType *mtx_;
+ char pad_[kCacheLineSize];
+ T data_[kSize];
+};
+
+const int kThreads = 8;
+const int kWriteRate = 1024;
+#if SANITIZER_DEBUG
+const int kIters = 16*1024;
+#else
+const int kIters = 64*1024;
+#endif
+
+template<typename MutexType>
+static void *lock_thread(void *param) {
+ TestData<MutexType> *data = (TestData<MutexType>*)param;
+ for (int i = 0; i < kIters; i++) {
+ data->Write();
+ data->Backoff();
+ }
+ return 0;
+}
+
+template<typename MutexType>
+static void *try_thread(void *param) {
+ TestData<MutexType> *data = (TestData<MutexType>*)param;
+ for (int i = 0; i < kIters; i++) {
+ data->TryWrite();
+ data->Backoff();
+ }
+ return 0;
+}
+
+TEST(SanitizerCommon, SpinMutex) {
+ SpinMutex mtx;
+ mtx.Init();
+ TestData<SpinMutex> data(&mtx);
+ pthread_t threads[kThreads];
+ for (int i = 0; i < kThreads; i++)
+ pthread_create(&threads[i], 0, lock_thread<SpinMutex>, &data);
+ for (int i = 0; i < kThreads; i++)
+ pthread_join(threads[i], 0);
+}
+
+TEST(SanitizerCommon, SpinMutexTry) {
+ SpinMutex mtx;
+ mtx.Init();
+ TestData<SpinMutex> data(&mtx);
+ pthread_t threads[kThreads];
+ for (int i = 0; i < kThreads; i++)
+ pthread_create(&threads[i], 0, try_thread<SpinMutex>, &data);
+ for (int i = 0; i < kThreads; i++)
+ pthread_join(threads[i], 0);
+}
+
+TEST(SanitizerCommon, BlockingMutex) {
+ u64 mtxmem[1024] = {};
+ BlockingMutex *mtx = new(mtxmem) BlockingMutex(LINKER_INITIALIZED);
+ TestData<BlockingMutex> data(mtx);
+ pthread_t threads[kThreads];
+ for (int i = 0; i < kThreads; i++)
+ pthread_create(&threads[i], 0, lock_thread<BlockingMutex>, &data);
+ for (int i = 0; i < kThreads; i++)
+ pthread_join(threads[i], 0);
+}
+
+} // namespace __sanitizer
diff --git a/lib/tsan/unit_tests/tsan_printf_test.cc b/lib/sanitizer_common/tests/sanitizer_printf_test.cc
index 0dfd1d2dfe42..b1889cd8794e 100644
--- a/lib/tsan/unit_tests/tsan_printf_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_printf_test.cc
@@ -1,4 +1,4 @@
-//===-- tsan_printf_test.cc -----------------------------------------------===//
+//===-- sanitizer_printf_test.cc ------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -7,16 +7,17 @@
//
//===----------------------------------------------------------------------===//
//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
+// Tests for sanitizer_printf.cc
//
//===----------------------------------------------------------------------===//
-#include "tsan_rtl.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
#include "gtest/gtest.h"
#include <string.h>
#include <limits.h>
-namespace __tsan {
+namespace __sanitizer {
TEST(Printf, Basic) {
char buf[1024];
@@ -27,15 +28,21 @@ TEST(Printf, Basic) {
(unsigned)10, (unsigned long)11, // NOLINT
(void*)0x123, "_string_");
EXPECT_EQ(len, strlen(buf));
- EXPECT_EQ(0, strcmp(buf, "a-1b-2c4294967292e5fahbq"
- "0x000000000123e_string_r"));
+ void *ptr;
+ if (sizeof(ptr) == 4) {
+ EXPECT_STREQ("a-1b-2c4294967292e5fahbq"
+ "0x00000123e_string_r", buf);
+ } else {
+ EXPECT_STREQ("a-1b-2c4294967292e5fahbq"
+ "0x000000000123e_string_r", buf);
+ }
}
TEST(Printf, OverflowStr) {
char buf[] = "123456789";
uptr len = internal_snprintf(buf, 4, "%s", "abcdef"); // NOLINT
EXPECT_EQ(len, (uptr)6);
- EXPECT_EQ(0, strcmp(buf, "abc"));
+ EXPECT_STREQ("abc", buf);
EXPECT_EQ(buf[3], 0);
EXPECT_EQ(buf[4], '5');
EXPECT_EQ(buf[5], '6');
@@ -48,7 +55,7 @@ TEST(Printf, OverflowStr) {
TEST(Printf, OverflowInt) {
char buf[] = "123456789";
internal_snprintf(buf, 4, "%d", -123456789); // NOLINT
- EXPECT_EQ(0, strcmp(buf, "-12"));
+ EXPECT_STREQ("-12", buf);
EXPECT_EQ(buf[3], 0);
EXPECT_EQ(buf[4], '5');
EXPECT_EQ(buf[5], '6');
@@ -60,8 +67,14 @@ TEST(Printf, OverflowInt) {
TEST(Printf, OverflowUint) {
char buf[] = "123456789";
- internal_snprintf(buf, 4, "a%zx", (unsigned long)0x123456789); // NOLINT
- EXPECT_EQ(0, strcmp(buf, "a12"));
+ uptr val;
+ if (sizeof(val) == 4) {
+ val = (uptr)0x12345678;
+ } else {
+ val = (uptr)0x123456789ULL;
+ }
+ internal_snprintf(buf, 4, "a%zx", val); // NOLINT
+ EXPECT_STREQ("a12", buf);
EXPECT_EQ(buf[3], 0);
EXPECT_EQ(buf[4], '5');
EXPECT_EQ(buf[5], '6');
@@ -73,8 +86,14 @@ TEST(Printf, OverflowUint) {
TEST(Printf, OverflowPtr) {
char buf[] = "123456789";
- internal_snprintf(buf, 4, "%p", (void*)0x123456789); // NOLINT
- EXPECT_EQ(0, strcmp(buf, "0x0"));
+ void *p;
+ if (sizeof(p) == 4) {
+ p = (void*)0x1234567;
+ } else {
+ p = (void*)0x123456789ULL;
+ }
+ internal_snprintf(buf, 4, "%p", p); // NOLINT
+ EXPECT_STREQ("0x0", buf);
EXPECT_EQ(buf[3], 0);
EXPECT_EQ(buf[4], '5');
EXPECT_EQ(buf[5], '6');
@@ -91,7 +110,7 @@ static void TestMinMax(const char *fmt, T min, T max) {
char buf2[1024];
snprintf(buf2, sizeof(buf2), fmt, min, max);
EXPECT_EQ(len, strlen(buf));
- EXPECT_EQ(0, strcmp(buf, buf2));
+ EXPECT_STREQ(buf2, buf);
}
TEST(Printf, MinMax) {
@@ -103,4 +122,4 @@ TEST(Printf, MinMax) {
TestMinMax<unsigned long>("%zx-%zx", 0, ULONG_MAX); // NOLINT
}
-} // namespace __tsan
+} // namespace __sanitizer
diff --git a/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc b/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc
new file mode 100644
index 000000000000..00b260479da9
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_scanf_interceptor_test.cc
@@ -0,0 +1,85 @@
+//===-- sanitizer_scanf_interceptor_test.cc -------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for *scanf interceptors implementation in sanitizer_common.
+//
+//===----------------------------------------------------------------------===//
+#include <vector>
+
+#include "interception/interception.h"
+#include "sanitizer_test_utils.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "gtest/gtest.h"
+
+using namespace __sanitizer;
+
+#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
+ ((std::vector<unsigned> *)ctx)->push_back(size)
+
+#include "sanitizer_common/sanitizer_common_interceptors_scanf.inc"
+
+static void testScanf2(void *ctx, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ scanf_common(ctx, format, ap);
+ va_end(ap);
+}
+
+static void testScanf(const char *format, unsigned n, ...) {
+ std::vector<unsigned> scanf_sizes;
+ // 16 args should be enough.
+ testScanf2((void *)&scanf_sizes, format,
+ (void*)0, (void*)0, (void*)0, (void*)0,
+ (void*)0, (void*)0, (void*)0, (void*)0,
+ (void*)0, (void*)0, (void*)0, (void*)0,
+ (void*)0, (void*)0, (void*)0, (void*)0);
+ ASSERT_EQ(n, scanf_sizes.size()) <<
+ "Unexpected number of format arguments: '" << format << "'";
+ va_list ap;
+ va_start(ap, n);
+ for (unsigned i = 0; i < n; ++i)
+ EXPECT_EQ(va_arg(ap, unsigned), scanf_sizes[i]) <<
+ "Unexpect write size for argument " << i << ", format string '" <<
+ format << "'";
+ va_end(ap);
+}
+
+TEST(SanitizerCommonInterceptors, Scanf) {
+ const unsigned I = sizeof(int); // NOLINT
+ const unsigned L = sizeof(long); // NOLINT
+ const unsigned LL = sizeof(long long); // NOLINT
+ const unsigned S = sizeof(short); // NOLINT
+ const unsigned C = sizeof(char); // NOLINT
+ const unsigned D = sizeof(double); // NOLINT
+ const unsigned F = sizeof(float); // NOLINT
+
+ testScanf("%d", 1, I);
+ testScanf("%d%d%d", 3, I, I, I);
+ testScanf("ab%u%dc", 2, I, I);
+ testScanf("%ld", 1, L);
+ testScanf("%llu", 1, LL);
+ testScanf("a %hd%hhx", 2, S, C);
+
+ testScanf("%%", 0);
+ testScanf("a%%", 0);
+ testScanf("a%%b", 0);
+ testScanf("a%%%%b", 0);
+ testScanf("a%%b%%", 0);
+ testScanf("a%%%%%%b", 0);
+ testScanf("a%%%%%b", 0);
+ testScanf("a%%%%%f", 1, F);
+ testScanf("a%%%lxb", 1, L);
+ testScanf("a%lf%%%lxb", 2, D, L);
+ testScanf("%nf", 1, I);
+
+ testScanf("%10s", 1, 11);
+ testScanf("%%10s", 0);
+ testScanf("%*10s", 0);
+ testScanf("%*d", 0);
+}
diff --git a/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc b/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc
new file mode 100644
index 000000000000..5350c2ab8dbc
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_stackdepot_test.cc
@@ -0,0 +1,69 @@
+//===-- sanitizer_stackdepot_test.cc --------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "gtest/gtest.h"
+
+namespace __sanitizer {
+
+TEST(SanitizerCommon, StackDepotBasic) {
+ uptr s1[] = {1, 2, 3, 4, 5};
+ u32 i1 = StackDepotPut(s1, ARRAY_SIZE(s1));
+ uptr sz1 = 0;
+ const uptr *sp1 = StackDepotGet(i1, &sz1);
+ EXPECT_NE(sp1, (uptr*)0);
+ EXPECT_EQ(sz1, ARRAY_SIZE(s1));
+ EXPECT_EQ(internal_memcmp(sp1, s1, sizeof(s1)), 0);
+}
+
+TEST(SanitizerCommon, StackDepotAbsent) {
+ uptr sz1 = 0;
+ const uptr *sp1 = StackDepotGet((1 << 30) - 1, &sz1);
+ EXPECT_EQ(sp1, (uptr*)0);
+}
+
+TEST(SanitizerCommon, StackDepotEmptyStack) {
+ u32 i1 = StackDepotPut(0, 0);
+ uptr sz1 = 0;
+ const uptr *sp1 = StackDepotGet(i1, &sz1);
+ EXPECT_EQ(sp1, (uptr*)0);
+}
+
+TEST(SanitizerCommon, StackDepotZeroId) {
+ uptr sz1 = 0;
+ const uptr *sp1 = StackDepotGet(0, &sz1);
+ EXPECT_EQ(sp1, (uptr*)0);
+}
+
+TEST(SanitizerCommon, StackDepotSame) {
+ uptr s1[] = {1, 2, 3, 4, 6};
+ u32 i1 = StackDepotPut(s1, ARRAY_SIZE(s1));
+ u32 i2 = StackDepotPut(s1, ARRAY_SIZE(s1));
+ EXPECT_EQ(i1, i2);
+ uptr sz1 = 0;
+ const uptr *sp1 = StackDepotGet(i1, &sz1);
+ EXPECT_NE(sp1, (uptr*)0);
+ EXPECT_EQ(sz1, ARRAY_SIZE(s1));
+ EXPECT_EQ(internal_memcmp(sp1, s1, sizeof(s1)), 0);
+}
+
+TEST(SanitizerCommon, StackDepotSeveral) {
+ uptr s1[] = {1, 2, 3, 4, 7};
+ u32 i1 = StackDepotPut(s1, ARRAY_SIZE(s1));
+ uptr s2[] = {1, 2, 3, 4, 8, 9};
+ u32 i2 = StackDepotPut(s2, ARRAY_SIZE(s2));
+ EXPECT_NE(i1, i2);
+}
+
+} // namespace __sanitizer
diff --git a/lib/sanitizer_common/tests/sanitizer_test_main.cc b/lib/sanitizer_common/tests/sanitizer_test_main.cc
new file mode 100644
index 000000000000..12d1d15af917
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_test_main.cc
@@ -0,0 +1,19 @@
+//===-- sanitizer_test_main.cc --------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+#include "gtest/gtest.h"
+
+int main(int argc, char **argv) {
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/lib/sanitizer_common/tests/sanitizer_test_utils.h b/lib/sanitizer_common/tests/sanitizer_test_utils.h
new file mode 100644
index 000000000000..6129ea8a5370
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_test_utils.h
@@ -0,0 +1,80 @@
+//===-- sanitizer_test_utils.h ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of *Sanitizer runtime.
+// Common unit tests utilities.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_TEST_UTILS_H
+#define SANITIZER_TEST_UTILS_H
+
+#if defined(_WIN32)
+typedef unsigned __int8 uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+typedef __int8 int8_t;
+typedef __int16 int16_t;
+typedef __int32 int32_t;
+typedef __int64 int64_t;
+# define NOINLINE __declspec(noinline)
+# define USED
+#else // defined(_WIN32)
+# define NOINLINE __attribute__((noinline))
+# define USED __attribute__((used))
+#include <stdint.h>
+#endif // defined(_WIN32)
+
+#if !defined(__has_feature)
+#define __has_feature(x) 0
+#endif
+
+#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS \
+ __attribute__((no_address_safety_analysis))
+#else
+# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS
+#endif
+
+#if __LP64__ || defined(_WIN64)
+# define SANITIZER_WORDSIZE 64
+#else
+# define SANITIZER_WORDSIZE 32
+#endif
+
+// Make the compiler thinks that something is going on there.
+inline void break_optimization(void *arg) {
+ __asm__ __volatile__("" : : "r" (arg) : "memory");
+}
+
+// This function returns its parameter but in such a way that compiler
+// can not prove it.
+template<class T>
+NOINLINE
+static T Ident(T t) {
+ T ret = t;
+ break_optimization(&ret);
+ return ret;
+}
+
+// Simple stand-alone pseudorandom number generator.
+// Current algorithm is ANSI C linear congruential PRNG.
+static inline uint32_t my_rand_r(uint32_t* state) {
+ return (*state = *state * 1103515245 + 12345) >> 16;
+}
+
+static uint32_t global_seed = 0;
+
+static inline uint32_t my_rand() {
+ return my_rand_r(&global_seed);
+}
+
+
+#endif // SANITIZER_TEST_UTILS_H
diff --git a/lib/sanitizer_common/tests/standalone_malloc_test.cc b/lib/sanitizer_common/tests/standalone_malloc_test.cc
new file mode 100644
index 000000000000..9e6f7c93b04b
--- /dev/null
+++ b/lib/sanitizer_common/tests/standalone_malloc_test.cc
@@ -0,0 +1,87 @@
+#include <stdio.h>
+#include <vector>
+#include <pthread.h>
+#include <malloc.h>
+#include <algorithm>
+
+using namespace std;
+
+const size_t kNumThreds = 16;
+const size_t kNumIters = 1 << 23;
+
+inline void break_optimization(void *arg) {
+ __asm__ __volatile__("" : : "r" (arg) : "memory");
+}
+
+__attribute__((noinline))
+static void *MallocThread(void *t) {
+ size_t total_malloced = 0, total_freed = 0;
+ size_t max_in_use = 0;
+ size_t tid = reinterpret_cast<size_t>(t);
+ vector<pair<char *, size_t> > allocated;
+ allocated.reserve(kNumIters);
+ for (size_t i = 1; i < kNumIters; i++) {
+ if ((i % (kNumIters / 4)) == 0 && tid == 0)
+ fprintf(stderr, " T[%ld] iter %ld\n", tid, i);
+ bool allocate = (i % 5) <= 2; // 60% malloc, 40% free
+ if (i > kNumIters / 4)
+ allocate = i % 2; // then switch to 50% malloc, 50% free
+ if (allocate) {
+ size_t size = 1 + (i % 200);
+ if ((i % 10001) == 0)
+ size *= 4096;
+ total_malloced += size;
+ char *x = new char[size];
+ x[0] = x[size - 1] = x[size / 2] = 0;
+ allocated.push_back(make_pair(x, size));
+ max_in_use = max(max_in_use, total_malloced - total_freed);
+ } else {
+ if (allocated.empty()) continue;
+ size_t slot = i % allocated.size();
+ char *p = allocated[slot].first;
+ p[0] = 0; // emulate last user touch of the block
+ size_t size = allocated[slot].second;
+ total_freed += size;
+ swap(allocated[slot], allocated.back());
+ allocated.pop_back();
+ delete [] p;
+ }
+ }
+ if (tid == 0)
+ fprintf(stderr, " T[%ld] total_malloced: %ldM in use %ldM max %ldM\n",
+ tid, total_malloced >> 20, (total_malloced - total_freed) >> 20,
+ max_in_use >> 20);
+ for (size_t i = 0; i < allocated.size(); i++)
+ delete [] allocated[i].first;
+ return 0;
+}
+
+template <int depth>
+struct DeepStack {
+ __attribute__((noinline))
+ static void *run(void *t) {
+ break_optimization(0);
+ DeepStack<depth - 1>::run(t);
+ break_optimization(0);
+ return 0;
+ }
+};
+
+template<>
+struct DeepStack<0> {
+ static void *run(void *t) {
+ MallocThread(t);
+ return 0;
+ }
+};
+
+// Build with -Dstandalone_malloc_test=main to make it a separate program.
+int standalone_malloc_test() {
+ pthread_t t[kNumThreds];
+ for (size_t i = 0; i < kNumThreds; i++)
+ pthread_create(&t[i], 0, DeepStack<200>::run, reinterpret_cast<void *>(i));
+ for (size_t i = 0; i < kNumThreds; i++)
+ pthread_join(t[i], 0);
+ malloc_stats();
+ return 0;
+}
diff --git a/lib/tsan/CMakeLists.txt b/lib/tsan/CMakeLists.txt
index acfb854d2606..34e3a2ea524e 100644
--- a/lib/tsan/CMakeLists.txt
+++ b/lib/tsan/CMakeLists.txt
@@ -1,8 +1,22 @@
-# Build for the AddressSanitizer runtime support library.
+# Build for the ThreadSanitizer runtime support library.
-file(GLOB TSAN_SOURCES "*.cc")
+include_directories(..)
-if(CAN_TARGET_X86_64)
- add_library(clang_rt.tsan-x86_64 STATIC ${TSAN_SOURCES})
- set_target_properties(clang_rt.tsan-x86_64 PROPERTIES COMPILE_FLAGS "${TARGET_X86_64_CFLAGS}")
+set(TSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+# FIXME: Add support for compile flags:
+# -Wframe-larger-than=512,
+# -Wglobal-constructors,
+# --sysroot=.
+
+if("${CMAKE_BUILD_TYPE}" EQUAL "Release")
+ set(TSAN_COMMON_DEFINITIONS DEBUG=0)
+else()
+ set(TSAN_COMMON_DEFINITIONS DEBUG=1)
+endif()
+
+add_subdirectory(rtl)
+
+if(LLVM_INCLUDE_TESTS)
+ add_subdirectory(tests)
endif()
+add_subdirectory(lit_tests)
diff --git a/lib/tsan/Makefile.old b/lib/tsan/Makefile.old
index 2091f61335e9..593482fbb5da 100644
--- a/lib/tsan/Makefile.old
+++ b/lib/tsan/Makefile.old
@@ -1,6 +1,6 @@
DEBUG=0
LDFLAGS=-ldl -lpthread -pie
-CXXFLAGS = -fPIE -g -Wall -Werror -DTSAN_DEBUG=$(DEBUG)
+CXXFLAGS = -fPIE -g -Wall -Werror -DTSAN_DEBUG=$(DEBUG) -DSANITIZER_DEBUG=$(DEBUG)
# Silence warnings that Clang produces for gtest code.
# Use -Wno-attributes so that gcc doesn't complain about unknown warning types.
CXXFLAGS += -Wno-attributes
@@ -8,24 +8,25 @@ ifeq ($(DEBUG), 0)
CXXFLAGS += -O3
endif
ifeq ($(CXX), clang++)
- CXXFLAGS+= -Wno-unused-private-field -Wno-static-in-inline
+ CXXFLAGS+= -Wno-unused-private-field -Wno-static-in-inline -Wgnu
endif
LIBTSAN=rtl/libtsan.a
GTEST_ROOT=third_party/googletest
GTEST_INCLUDE=-I$(GTEST_ROOT)/include
GTEST_BUILD_DIR=$(GTEST_ROOT)/build
-GTEST_LIB=$(GTEST_BUILD_DIR)/gtest-all.o
+GTEST_LIB_NAME=gtest-all.o
+GTEST_LIB=$(GTEST_BUILD_DIR)/$(GTEST_LIB_NAME)
SANITIZER_COMMON_TESTS_SRC=$(wildcard ../sanitizer_common/tests/*_test.cc)
SANITIZER_COMMON_TESTS_OBJ=$(patsubst %.cc,%.o,$(SANITIZER_COMMON_TESTS_SRC))
-RTL_TEST_SRC=$(wildcard rtl_tests/*.cc)
+RTL_TEST_SRC=$(wildcard tests/rtl/*.cc)
RTL_TEST_OBJ=$(patsubst %.cc,%.o,$(RTL_TEST_SRC))
-UNIT_TEST_SRC=$(wildcard unit_tests/*_test.cc)
+UNIT_TEST_SRC=$(wildcard tests/unit/*_test.cc)
UNIT_TEST_OBJ=$(patsubst %.cc,%.o,$(UNIT_TEST_SRC))
UNIT_TEST_HDR=$(wildcard rtl/*.h) $(wildcard ../sanitizer_common/*.h)
-INCLUDES=-Irtl -I.. $(GTEST_INCLUDE)
+INCLUDES=-Irtl -I.. -I../../include $(GTEST_INCLUDE)
all: libtsan test
@@ -34,9 +35,8 @@ help:
@ echo "The most useful targets are:"
@ echo " make install_deps # Install third-party dependencies required for building"
@ echo " make presubmit # Run it every time before committing"
- @ echo " make lint # Run the style checker"
@ echo
- @ echo "For more info, see http://code.google.com/p/data-race-test/wiki/ThreadSanitizer2"
+ @ echo "For more info, see http://code.google.com/p/thread-sanitizer/wiki/Development"
$(LIBTSAN): libtsan
@@ -54,10 +54,10 @@ test: libtsan tsan_test
run: all
(ulimit -s 8192; ./tsan_test)
- ./output_tests/test_output.sh
+ ./lit_tests/test_output.sh
presubmit:
- $(MAKE) -f Makefile.old lint -j 4
+ ../sanitizer_common/scripts/check_lint.sh
# Debug build with clang.
$(MAKE) -f Makefile.old clean
$(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=clang CXX=clang++
@@ -71,34 +71,23 @@ presubmit:
$(MAKE) -f Makefile.old clean
$(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=gcc CXX=g++
./check_analyze.sh
+ # Sanity check for Go runtime
+ (cd go && ./buildgo.sh)
+ # Check cmake build
+ ./check_cmake.sh
@ echo PRESUBMIT PASSED
-RTL_LINT_FITLER=-legal/copyright,-build/include,-readability/casting,-build/header_guard,-build/namespaces
-
-lint: lint_tsan lint_tests
-lint_tsan:
- third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FITLER) rtl/*.{cc,h} \
- ../sanitizer_common/*.{cc,h}
-lint_tests:
- third_party/cpplint/cpplint.py --filter=$(RTL_LINT_FITLER) \
- rtl_tests/*.{cc,h} unit_tests/*.cc ../sanitizer_common/tests/*.cc
-
install_deps:
rm -rf third_party
mkdir third_party
(cd third_party && \
- svn co -r613 http://googletest.googlecode.com/svn/trunk googletest && \
- svn co -r82 http://google-styleguide.googlecode.com/svn/trunk/cpplint cpplint \
+ svn co -r613 http://googletest.googlecode.com/svn/trunk googletest \
)
-# Remove verbose printf from lint. Not strictly necessary.
-hack_cpplint:
- sed -i "s/ sys.stderr.write('Done processing.*//g" third_party/cpplint/cpplint.py
-
$(GTEST_LIB):
mkdir -p $(GTEST_BUILD_DIR) && \
cd $(GTEST_BUILD_DIR) && \
- $(MAKE) -f ../make/Makefile CXXFLAGS="$(CXXFLAGS)" CFLAGS="$(CFLAGS)" CC=$(CC) CXX=$(CXX)
+ $(MAKE) -f ../make/Makefile CXXFLAGS="$(CXXFLAGS)" CFLAGS="$(CFLAGS)" CC=$(CC) CXX=$(CXX) $(GTEST_LIB_NAME)
clean:
rm -f asm_*.s libtsan.nm libtsan.objdump */*.o tsan_test
diff --git a/lib/tsan/check_cmake.sh b/lib/tsan/check_cmake.sh
new file mode 100755
index 000000000000..5f11e727f091
--- /dev/null
+++ b/lib/tsan/check_cmake.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+set -u
+set -e
+
+ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+mkdir -p $ROOT/build
+cd $ROOT/build
+CC=clang CXX=clang++ cmake -DLLVM_ENABLE_WERROR=ON -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON $ROOT/../../../..
+make -j64
+make check-tsan check-sanitizer -j64
+
diff --git a/lib/tsan/go/buildgo.sh b/lib/tsan/go/buildgo.sh
index a0d2f6761b41..a153afd6ee8e 100755
--- a/lib/tsan/go/buildgo.sh
+++ b/lib/tsan/go/buildgo.sh
@@ -1,24 +1,12 @@
#!/bin/bash
set -e
-if [ "`uname -a | grep Linux`" != "" ]; then
- LINUX=1
- SUFFIX="linux_amd64"
-elif [ "`uname -a | grep Darwin`" != "" ]; then
- MAC=1
- SUFFIX="darwin_amd64"
-else
- echo Unknown platform
- exit 1
-fi
-
SRCS="
tsan_go.cc
../rtl/tsan_clock.cc
../rtl/tsan_flags.cc
../rtl/tsan_md5.cc
../rtl/tsan_mutex.cc
- ../rtl/tsan_printf.cc
../rtl/tsan_report.cc
../rtl/tsan_rtl.cc
../rtl/tsan_rtl_mutex.cc
@@ -31,48 +19,59 @@ SRCS="
../../sanitizer_common/sanitizer_common.cc
../../sanitizer_common/sanitizer_flags.cc
../../sanitizer_common/sanitizer_libc.cc
- ../../sanitizer_common/sanitizer_posix.cc
../../sanitizer_common/sanitizer_printf.cc
- ../../sanitizer_common/sanitizer_symbolizer.cc
"
-if [ "$LINUX" != "" ]; then
+if [ "`uname -a | grep Linux`" != "" ]; then
+ SUFFIX="linux_amd64"
+ OSCFLAGS="-fPIC -ffreestanding"
+ OSLDFLAGS="-lpthread -fPIC -fpie"
SRCS+="
../rtl/tsan_platform_linux.cc
+ ../../sanitizer_common/sanitizer_posix.cc
../../sanitizer_common/sanitizer_linux.cc
"
-elif [ "$MAC" != "" ]; then
- SRCS+="
- ../rtl/tsan_platform_mac.cc
- ../../sanitizer_common/sanitizer_mac.cc
- "
+elif [ "`uname -a | grep Darwin`" != "" ]; then
+ SUFFIX="darwin_amd64"
+ OSCFLAGS="-fPIC"
+ OSLDFLAGS="-lpthread -fPIC -fpie"
+ SRCS+="
+ ../rtl/tsan_platform_mac.cc
+ ../../sanitizer_common/sanitizer_posix.cc
+ ../../sanitizer_common/sanitizer_mac.cc
+ "
+elif [ "`uname -a | grep MINGW`" != "" ]; then
+ SUFFIX="windows_amd64"
+ OSCFLAGS="-Wno-error=attributes -Wno-attributes"
+ OSLDFLAGS=""
+ SRCS+="
+ ../rtl/tsan_platform_windows.cc
+ ../../sanitizer_common/sanitizer_win.cc
+ "
+else
+ echo Unknown platform
+ exit 1
fi
SRCS+=$ADD_SRCS
-#ASMS="../rtl/tsan_rtl_amd64.S"
rm -f gotsan.cc
for F in $SRCS; do
cat $F >> gotsan.cc
done
-FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -fPIC -g -Wall -Werror -fno-exceptions -DTSAN_GO -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4"
+FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -m64 -Wall -Werror -fno-exceptions -DTSAN_GO -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4 $OSCFLAGS"
if [ "$DEBUG" == "" ]; then
FLAGS+=" -DTSAN_DEBUG=0 -O3 -fomit-frame-pointer"
else
FLAGS+=" -DTSAN_DEBUG=1 -g"
fi
-if [ "$LINUX" != "" ]; then
- FLAGS+=" -ffreestanding"
-fi
-
echo gcc gotsan.cc -S -o tmp.s $FLAGS $CFLAGS
gcc gotsan.cc -S -o tmp.s $FLAGS $CFLAGS
cat tmp.s $ASMS > gotsan.s
echo as gotsan.s -o race_$SUFFIX.syso
as gotsan.s -o race_$SUFFIX.syso
-gcc test.c race_$SUFFIX.syso -lpthread -o test
-TSAN_OPTIONS="exitcode=0" ./test
-
+gcc test.c race_$SUFFIX.syso -m64 -o test $OSLDFLAGS
+GORACE="exitcode=0 atexit_sleep_ms=0" ./test
diff --git a/lib/tsan/go/test.c b/lib/tsan/go/test.c
index a9a5b3dbfcad..2414a1e9925f 100644
--- a/lib/tsan/go/test.c
+++ b/lib/tsan/go/test.c
@@ -15,6 +15,7 @@
void __tsan_init();
void __tsan_fini();
+void __tsan_map_shadow(void *addr, unsigned long size);
void __tsan_go_start(int pgoid, int chgoid, void *pc);
void __tsan_go_end(int goid);
void __tsan_read(int goid, void *addr, void *pc);
@@ -35,6 +36,7 @@ char buf[10];
int main(void) {
__tsan_init();
+ __tsan_map_shadow(buf, sizeof(buf) + 4096);
__tsan_func_enter(0, &main);
__tsan_malloc(0, buf, 10, 0);
__tsan_release(0, buf);
diff --git a/lib/tsan/go/tsan_go.cc b/lib/tsan/go/tsan_go.cc
index 4b3076c46ce7..360608a0cf1b 100644
--- a/lib/tsan/go/tsan_go.cc
+++ b/lib/tsan/go/tsan_go.cc
@@ -18,7 +18,9 @@
namespace __tsan {
-static ThreadState *goroutines[kMaxTid];
+const int kMaxGoroutinesEver = 128*1024;
+
+static ThreadState *goroutines[kMaxGoroutinesEver];
void InitializeInterceptors() {
}
@@ -33,7 +35,7 @@ bool IsExpectedReport(uptr addr, uptr size) {
void internal_start_thread(void(*func)(void*), void *arg) {
}
-ReportStack *SymbolizeData(uptr addr) {
+ReportLocation *SymbolizeData(uptr addr) {
return 0;
}
@@ -79,9 +81,14 @@ ReportStack *SymbolizeCode(uptr addr) {
extern "C" {
static void AllocGoroutine(int tid) {
- goroutines[tid] = (ThreadState*)internal_alloc(MBlockThreadContex,
+ if (tid >= kMaxGoroutinesEver) {
+ Printf("FATAL: Reached goroutine limit\n");
+ Die();
+ }
+ ThreadState *thr = (ThreadState*)internal_alloc(MBlockThreadContex,
sizeof(ThreadState));
- internal_memset(goroutines[tid], 0, sizeof(ThreadState));
+ internal_memset(thr, 0, sizeof(*thr));
+ goroutines[tid] = thr;
}
void __tsan_init() {
@@ -98,7 +105,11 @@ void __tsan_fini() {
thr->in_rtl++;
int res = Finalize(thr);
thr->in_rtl--;
- exit(res);
+ exit(res);
+}
+
+void __tsan_map_shadow(uptr addr, uptr size) {
+ MapShadow(addr, size);
}
void __tsan_read(int goid, void *addr, void *pc) {
@@ -111,6 +122,18 @@ void __tsan_write(int goid, void *addr, void *pc) {
MemoryAccess(thr, (uptr)pc, (uptr)addr, 0, true);
}
+void __tsan_read_range(int goid, void *addr, uptr size, uptr step, void *pc) {
+ ThreadState *thr = goroutines[goid];
+ for (uptr i = 0; i < size; i += step)
+ MemoryAccess(thr, (uptr)pc, (uptr)addr + i, 0, false);
+}
+
+void __tsan_write_range(int goid, void *addr, uptr size, uptr step, void *pc) {
+ ThreadState *thr = goroutines[goid];
+ for (uptr i = 0; i < size; i += step)
+ MemoryAccess(thr, (uptr)pc, (uptr)addr + i, 0, true);
+}
+
void __tsan_func_enter(int goid, void *pc) {
ThreadState *thr = goroutines[goid];
FuncEntry(thr, (uptr)pc);
@@ -123,9 +146,10 @@ void __tsan_func_exit(int goid) {
void __tsan_malloc(int goid, void *p, uptr sz, void *pc) {
ThreadState *thr = goroutines[goid];
+ if (thr == 0) // probably before __tsan_init()
+ return;
thr->in_rtl++;
- MemoryResetRange(thr, (uptr)pc, (uptr)p, sz);
- MemoryAccessRange(thr, (uptr)pc, (uptr)p, sz, true);
+ MemoryRangeImitateWrite(thr, (uptr)pc, (uptr)p, sz);
thr->in_rtl--;
}
@@ -142,7 +166,7 @@ void __tsan_go_start(int pgoid, int chgoid, void *pc) {
thr->in_rtl++;
parent->in_rtl++;
int goid2 = ThreadCreate(parent, (uptr)pc, 0, true);
- ThreadStart(thr, goid2);
+ ThreadStart(thr, goid2, 0);
parent->in_rtl--;
thr->in_rtl--;
}
@@ -152,6 +176,8 @@ void __tsan_go_end(int goid) {
thr->in_rtl++;
ThreadFinish(thr);
thr->in_rtl--;
+ internal_free(thr);
+ goroutines[goid] = 0;
}
void __tsan_acquire(int goid, void *addr) {
@@ -159,7 +185,6 @@ void __tsan_acquire(int goid, void *addr) {
thr->in_rtl++;
Acquire(thr, 0, (uptr)addr);
thr->in_rtl--;
- //internal_free(thr);
}
void __tsan_release(int goid, void *addr) {
@@ -178,8 +203,42 @@ void __tsan_release_merge(int goid, void *addr) {
void __tsan_finalizer_goroutine(int goid) {
ThreadState *thr = goroutines[goid];
- ThreadFinalizerGoroutine(thr);
-}
+ AcquireGlobal(thr, 0);
+}
+
+#ifdef _WIN32
+// MinGW gcc emits calls to the function.
+void ___chkstk_ms(void) {
+// The implementation must be along the lines of:
+// .code64
+// PUBLIC ___chkstk_ms
+// //cfi_startproc()
+// ___chkstk_ms:
+// push rcx
+// //cfi_push(%rcx)
+// push rax
+// //cfi_push(%rax)
+// cmp rax, PAGE_SIZE
+// lea rcx, [rsp + 24]
+// jb l_LessThanAPage
+// .l_MoreThanAPage:
+// sub rcx, PAGE_SIZE
+// or rcx, 0
+// sub rax, PAGE_SIZE
+// cmp rax, PAGE_SIZE
+// ja l_MoreThanAPage
+// .l_LessThanAPage:
+// sub rcx, rax
+// or [rcx], 0
+// pop rax
+// //cfi_pop(%rax)
+// pop rcx
+// //cfi_pop(%rcx)
+// ret
+// //cfi_endproc()
+// END
+}
+#endif
} // extern "C"
} // namespace __tsan
diff --git a/lib/tsan/lit_tests/CMakeLists.txt b/lib/tsan/lit_tests/CMakeLists.txt
new file mode 100644
index 000000000000..ff2508dd75af
--- /dev/null
+++ b/lib/tsan/lit_tests/CMakeLists.txt
@@ -0,0 +1,36 @@
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg
+ )
+
+if(COMPILER_RT_CAN_EXECUTE_TESTS)
+ # Run TSan output tests only if we're sure we can produce working binaries.
+ set(TSAN_TEST_DEPS
+ clang clang-headers FileCheck count not llvm-symbolizer
+ ${TSAN_RUNTIME_LIBRARIES}
+ )
+ set(TSAN_TEST_PARAMS
+ tsan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+ if(LLVM_INCLUDE_TESTS)
+ list(APPEND TSAN_TEST_DEPS TsanUnitTests)
+ endif()
+ add_lit_testsuite(check-tsan "Running ThreadSanitizer tests"
+ ${CMAKE_CURRENT_BINARY_DIR}
+ PARAMS ${TSAN_TEST_PARAMS}
+ DEPENDS ${TSAN_TEST_DEPS}
+ )
+ set_target_properties(check-tsan PROPERTIES FOLDER "TSan unittests")
+elseif(LLVM_INCLUDE_TESTS)
+ # Otherwise run only TSan unit tests (they are linked using the
+ # host compiler).
+ add_lit_testsuite(check-tsan "Running ThreadSanitizer tests"
+ ${CMAKE_CURRENT_BINARY_DIR}/Unit
+ DEPENDS TsanUnitTests llvm-symbolizer)
+ set_target_properties(check-tsan PROPERTIES FOLDER "TSan unittests")
+endif()
diff --git a/lib/tsan/lit_tests/Helpers/blacklist.txt b/lib/tsan/lit_tests/Helpers/blacklist.txt
new file mode 100644
index 000000000000..22225e542ff3
--- /dev/null
+++ b/lib/tsan/lit_tests/Helpers/blacklist.txt
@@ -0,0 +1 @@
+fun:*Blacklisted_Thread2*
diff --git a/lib/tsan/lit_tests/Helpers/lit.local.cfg b/lib/tsan/lit_tests/Helpers/lit.local.cfg
new file mode 100644
index 000000000000..9246b10352a7
--- /dev/null
+++ b/lib/tsan/lit_tests/Helpers/lit.local.cfg
@@ -0,0 +1,2 @@
+# Files in this directory are helper files for other output tests.
+config.suffixes = []
diff --git a/lib/tsan/lit_tests/Unit/lit.cfg b/lib/tsan/lit_tests/Unit/lit.cfg
new file mode 100644
index 000000000000..6688697c0c1b
--- /dev/null
+++ b/lib/tsan/lit_tests/Unit/lit.cfg
@@ -0,0 +1,37 @@
+# -*- Python -*-
+
+import os
+
+def get_required_attr(config, attr_name):
+ attr_value = getattr(config, attr_name, None)
+ if not attr_value:
+ lit.fatal("No attribute %r in test configuration! You may need to run "
+ "tests from your build directory or add this attribute "
+ "to lit.site.cfg " % attr_name)
+ return attr_value
+
+# Setup attributes common for all compiler-rt projects.
+llvm_src_root = get_required_attr(config, 'llvm_src_root')
+compiler_rt_lit_unit_cfg = os.path.join(llvm_src_root, "projects",
+ "compiler-rt", "lib",
+ "lit.common.unit.cfg")
+lit.load_config(config, compiler_rt_lit_unit_cfg)
+
+# Setup config name.
+config.name = 'ThreadSanitizer-Unit'
+
+# Setup test source and exec root. For unit tests, we define
+# it as build directory with TSan unit tests.
+llvm_obj_root = get_required_attr(config, "llvm_obj_root")
+config.test_exec_root = os.path.join(llvm_obj_root, "projects",
+ "compiler-rt", "lib",
+ "tsan", "tests")
+config.test_source_root = config.test_exec_root
+
+# Get path to external LLVM symbolizer to run ThreadSanitizer unit tests.
+llvm_tools_dir = getattr(config, 'llvm_tools_dir', None)
+if llvm_tools_dir:
+ llvm_symbolizer_path = os.path.join(llvm_tools_dir, "llvm-symbolizer")
+ config.environment['TSAN_OPTIONS'] = ("external_symbolizer_path=" +
+ llvm_symbolizer_path)
+
diff --git a/lib/tsan/lit_tests/Unit/lit.site.cfg.in b/lib/tsan/lit_tests/Unit/lit.site.cfg.in
new file mode 100644
index 000000000000..23654b9be2ee
--- /dev/null
+++ b/lib/tsan/lit_tests/Unit/lit.site.cfg.in
@@ -0,0 +1,18 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+config.build_type = "@CMAKE_BUILD_TYPE@"
+config.llvm_obj_root = "@LLVM_BINARY_DIR@"
+config.llvm_src_root = "@LLVM_SOURCE_DIR@"
+config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
+
+# LLVM tools dir can be passed in lit parameters, so try to
+# apply substitution.
+try:
+ config.llvm_tools_dir = config.llvm_tools_dir % lit.params
+except KeyError,e:
+ key, = e.args
+ lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key))
+
+# Let the main config do the real work.
+lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/Unit/lit.cfg")
diff --git a/lib/tsan/lit_tests/blacklist.cc b/lib/tsan/lit_tests/blacklist.cc
new file mode 100644
index 000000000000..5baf926e6272
--- /dev/null
+++ b/lib/tsan/lit_tests/blacklist.cc
@@ -0,0 +1,31 @@
+// Test blacklist functionality for TSan.
+
+// RUN: %clangxx_tsan -O1 %s \
+// RUN: -fsanitize-blacklist=%p/Helpers/blacklist.txt \
+// RUN: -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ Global++;
+ return NULL;
+}
+
+void *Blacklisted_Thread2(void *x) {
+ Global--;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Blacklisted_Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("PASS\n");
+ return 0;
+}
+
+// CHECK-NOT: ThreadSanitizer: data race
diff --git a/lib/tsan/lit_tests/fd_close_norace.cc b/lib/tsan/lit_tests/fd_close_norace.cc
new file mode 100644
index 000000000000..a8b1a6d7b9e2
--- /dev/null
+++ b/lib/tsan/lit_tests/fd_close_norace.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+void *Thread1(void *x) {
+ int f = open("/dev/random", O_RDONLY);
+ close(f);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ int f = open("/dev/random", O_RDONLY);
+ close(f);
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+
+
diff --git a/lib/tsan/lit_tests/fd_dup_norace.cc b/lib/tsan/lit_tests/fd_dup_norace.cc
new file mode 100644
index 000000000000..8826f90fc485
--- /dev/null
+++ b/lib/tsan/lit_tests/fd_dup_norace.cc
@@ -0,0 +1,34 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int fds[2];
+
+void *Thread1(void *x) {
+ char buf;
+ read(fds[0], &buf, 1);
+ close(fds[0]);
+ return 0;
+}
+
+void *Thread2(void *x) {
+ close(fds[1]);
+ return 0;
+}
+
+int main() {
+ fds[0] = open("/dev/random", O_RDONLY);
+ fds[1] = dup2(fds[0], 100);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/lit_tests/fd_location.cc b/lib/tsan/lit_tests/fd_location.cc
new file mode 100644
index 000000000000..35f9aabb0377
--- /dev/null
+++ b/lib/tsan/lit_tests/fd_location.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int fds[2];
+
+void *Thread1(void *x) {
+ write(fds[1], "a", 1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ close(fds[0]);
+ close(fds[1]);
+ return NULL;
+}
+
+int main() {
+ pipe(fds);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is file descriptor {{[0-9]+}} created by main thread at:
+// CHECK: #0 pipe
+// CHECK: #1 main
+
diff --git a/lib/tsan/lit_tests/fd_pipe_norace.cc b/lib/tsan/lit_tests/fd_pipe_norace.cc
new file mode 100644
index 000000000000..2da69ea21112
--- /dev/null
+++ b/lib/tsan/lit_tests/fd_pipe_norace.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int fds[2];
+int X;
+
+void *Thread1(void *x) {
+ X = 42;
+ write(fds[1], "a", 1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ char buf;
+ while (read(fds[0], &buf, 1) != 1) {
+ }
+ X = 43;
+ return NULL;
+}
+
+int main() {
+ pipe(fds);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/lit_tests/fd_pipe_race.cc b/lib/tsan/lit_tests/fd_pipe_race.cc
new file mode 100644
index 000000000000..dfdb7795aae6
--- /dev/null
+++ b/lib/tsan/lit_tests/fd_pipe_race.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int fds[2];
+
+void *Thread1(void *x) {
+ write(fds[1], "a", 1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ close(fds[0]);
+ close(fds[1]);
+ return NULL;
+}
+
+int main() {
+ pipe(fds);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 8
+// CHECK: #0 close
+// CHECK: #1 Thread2
+// CHECK: Previous read of size 8
+// CHECK: #0 write
+// CHECK: #1 Thread1
+
+
diff --git a/lib/tsan/lit_tests/fd_socket_connect_norace.cc b/lib/tsan/lit_tests/fd_socket_connect_norace.cc
new file mode 100644
index 000000000000..065299a9c6b6
--- /dev/null
+++ b/lib/tsan/lit_tests/fd_socket_connect_norace.cc
@@ -0,0 +1,45 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+struct sockaddr_in addr;
+int X;
+
+void *ClientThread(void *x) {
+ int c = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ X = 42;
+ if (connect(c, (struct sockaddr*)&addr, sizeof(addr))) {
+ perror("connect");
+ exit(1);
+ }
+ close(c);
+ return NULL;
+}
+
+int main() {
+ int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ addr.sin_family = AF_INET;
+ inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
+ addr.sin_port = INADDR_ANY;
+ socklen_t len = sizeof(addr);
+ bind(s, (sockaddr*)&addr, len);
+ getsockname(s, (sockaddr*)&addr, &len);
+ listen(s, 10);
+ pthread_t t;
+ pthread_create(&t, 0, ClientThread, 0);
+ int c = accept(s, 0, 0);
+ X = 42;
+ pthread_join(t, 0);
+ close(c);
+ close(s);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+
diff --git a/lib/tsan/lit_tests/fd_socket_norace.cc b/lib/tsan/lit_tests/fd_socket_norace.cc
new file mode 100644
index 000000000000..243fc9de2238
--- /dev/null
+++ b/lib/tsan/lit_tests/fd_socket_norace.cc
@@ -0,0 +1,52 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+struct sockaddr_in addr;
+int X;
+
+void *ClientThread(void *x) {
+ X = 42;
+ int c = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (connect(c, (struct sockaddr*)&addr, sizeof(addr))) {
+ perror("connect");
+ exit(1);
+ }
+ if (send(c, "a", 1, 0) != 1) {
+ perror("send");
+ exit(1);
+ }
+ close(c);
+ return NULL;
+}
+
+int main() {
+ int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ addr.sin_family = AF_INET;
+ inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
+ addr.sin_port = INADDR_ANY;
+ socklen_t len = sizeof(addr);
+ bind(s, (sockaddr*)&addr, len);
+ getsockname(s, (sockaddr*)&addr, &len);
+ listen(s, 10);
+ pthread_t t;
+ pthread_create(&t, 0, ClientThread, 0);
+ int c = accept(s, 0, 0);
+ char buf;
+ while (read(c, &buf, 1) != 1) {
+ }
+ X = 43;
+ close(c);
+ close(s);
+ pthread_join(t, 0);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+
diff --git a/lib/tsan/lit_tests/fd_socketpair_norace.cc b/lib/tsan/lit_tests/fd_socketpair_norace.cc
new file mode 100644
index 000000000000..f91e4eca0fe9
--- /dev/null
+++ b/lib/tsan/lit_tests/fd_socketpair_norace.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+int fds[2];
+int X;
+
+void *Thread1(void *x) {
+ X = 42;
+ write(fds[1], "a", 1);
+ close(fds[1]);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ char buf;
+ while (read(fds[0], &buf, 1) != 1) {
+ }
+ X = 43;
+ close(fds[0]);
+ return NULL;
+}
+
+int main() {
+ socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/lit_tests/fd_stdout_race.cc b/lib/tsan/lit_tests/fd_stdout_race.cc
new file mode 100644
index 000000000000..6581fc503a1b
--- /dev/null
+++ b/lib/tsan/lit_tests/fd_stdout_race.cc
@@ -0,0 +1,41 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int X;
+
+void *Thread1(void *x) {
+ sleep(1);
+ int f = open("/dev/random", O_RDONLY);
+ char buf;
+ read(f, &buf, 1);
+ close(f);
+ X = 42;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ X = 43;
+ write(STDOUT_FILENO, "a", 1);
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 4
+// CHECK: #0 Thread1
+// CHECK: Previous write of size 4
+// CHECK: #0 Thread2
+
+
diff --git a/lib/tsan/output_tests/free_race.c b/lib/tsan/lit_tests/free_race.c
index fb7fbac77b04..7a2ec0cdbed0 100644
--- a/lib/tsan/output_tests/free_race.c
+++ b/lib/tsan/lit_tests/free_race.c
@@ -1,3 +1,4 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
@@ -15,7 +16,7 @@ void *Thread1(void *x) {
}
void *Thread2(void *x) {
- usleep(1000000);
+ sleep(1);
pthread_mutex_lock(&mtx);
mem[0] = 42;
pthread_mutex_unlock(&mtx);
@@ -34,10 +35,9 @@ int main() {
}
// CHECK: WARNING: ThreadSanitizer: heap-use-after-free
-// CHECK: Write of size 4 at {{.*}} by main thread:
+// CHECK: Write of size 4 at {{.*}} by main thread{{.*}}:
// CHECK: #0 Thread2
// CHECK: #1 main
-// CHECK: Previous write of size 8 at {{.*}} by thread 1:
+// CHECK: Previous write of size 8 at {{.*}} by thread T1{{.*}}:
// CHECK: #0 free
// CHECK: #1 Thread1
-
diff --git a/lib/tsan/output_tests/free_race2.c b/lib/tsan/lit_tests/free_race2.c
index 7b2bdec039e1..095f82ea0818 100644
--- a/lib/tsan/output_tests/free_race2.c
+++ b/lib/tsan/lit_tests/free_race2.c
@@ -1,3 +1,4 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <stdlib.h>
void __attribute__((noinline)) foo(int *mem) {
@@ -23,4 +24,3 @@ int main() {
// CHECK: #0 free
// CHECK: #1 foo
// CHECK: #2 main
-
diff --git a/lib/tsan/lit_tests/global_race.cc b/lib/tsan/lit_tests/global_race.cc
new file mode 100644
index 000000000000..0892d07da2cb
--- /dev/null
+++ b/lib/tsan/lit_tests/global_race.cc
@@ -0,0 +1,25 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stddef.h>
+
+int GlobalData[10];
+
+void *Thread(void *a) {
+ GlobalData[2] = 42;
+ return 0;
+}
+
+int main() {
+ fprintf(stderr, "addr=%p\n", GlobalData);
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ GlobalData[2] = 43;
+ pthread_join(t, 0);
+}
+
+// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// Requires llvm-symbolizer, so disabled for now.
+// CHECK0: Location is global 'GlobalData' of size 40 at [[ADDR]]
+// CHECK0: (global_race.cc.exe+0x[0-9,a-f]+)
diff --git a/lib/tsan/output_tests/heap_race.cc b/lib/tsan/lit_tests/heap_race.cc
index e92bb379737e..297f8dbdec7d 100644
--- a/lib/tsan/output_tests/heap_race.cc
+++ b/lib/tsan/lit_tests/heap_race.cc
@@ -1,3 +1,4 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <stddef.h>
diff --git a/lib/tsan/lit_tests/ignore_race.cc b/lib/tsan/lit_tests/ignore_race.cc
new file mode 100644
index 000000000000..23d74d0ed840
--- /dev/null
+++ b/lib/tsan/lit_tests/ignore_race.cc
@@ -0,0 +1,31 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+extern "C" void AnnotateIgnoreWritesBegin(const char *f, int l);
+extern "C" void AnnotateIgnoreWritesEnd(const char *f, int l);
+extern "C" void AnnotateIgnoreReadsBegin(const char *f, int l);
+extern "C" void AnnotateIgnoreReadsEnd(const char *f, int l);
+
+void *Thread(void *x) {
+ AnnotateIgnoreWritesBegin(__FILE__, __LINE__);
+ AnnotateIgnoreReadsBegin(__FILE__, __LINE__);
+ Global = 42;
+ AnnotateIgnoreReadsEnd(__FILE__, __LINE__);
+ AnnotateIgnoreWritesEnd(__FILE__, __LINE__);
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ sleep(1);
+ Global = 43;
+ pthread_join(t, 0);
+ printf("OK\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/lit_tests/java.h b/lib/tsan/lit_tests/java.h
new file mode 100644
index 000000000000..7d61f5802864
--- /dev/null
+++ b/lib/tsan/lit_tests/java.h
@@ -0,0 +1,17 @@
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+extern "C" {
+typedef unsigned long jptr; // NOLINT
+void __tsan_java_init(jptr heap_begin, jptr heap_size);
+int __tsan_java_fini();
+void __tsan_java_alloc(jptr ptr, jptr size);
+void __tsan_java_free(jptr ptr, jptr size);
+void __tsan_java_move(jptr src, jptr dst, jptr size);
+void __tsan_java_mutex_lock(jptr addr);
+void __tsan_java_mutex_unlock(jptr addr);
+void __tsan_java_mutex_read_lock(jptr addr);
+void __tsan_java_mutex_read_unlock(jptr addr);
+}
diff --git a/lib/tsan/lit_tests/java_alloc.cc b/lib/tsan/lit_tests/java_alloc.cc
new file mode 100644
index 000000000000..4dbce70c31eb
--- /dev/null
+++ b/lib/tsan/lit_tests/java_alloc.cc
@@ -0,0 +1,32 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include "java.h"
+
+int const kHeapSize = 1024 * 1024;
+
+void stress(jptr addr) {
+ for (jptr sz = 8; sz <= 32; sz <<= 1) {
+ for (jptr i = 0; i < kHeapSize / 4 / sz; i++) {
+ __tsan_java_alloc(addr + i * sz, sz);
+ }
+ __tsan_java_move(addr, addr + kHeapSize / 2, kHeapSize / 4);
+ __tsan_java_free(addr + kHeapSize / 2, kHeapSize / 4);
+ }
+}
+
+void *Thread(void *p) {
+ stress((jptr)p);
+ return 0;
+}
+
+int main() {
+ jptr jheap = (jptr)malloc(kHeapSize);
+ __tsan_java_init(jheap, kHeapSize);
+ pthread_t th;
+ pthread_create(&th, 0, Thread, (void*)(jheap + kHeapSize / 4));
+ stress(jheap);
+ pthread_join(th, 0);
+ printf("OK\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/lit_tests/java_lock.cc b/lib/tsan/lit_tests/java_lock.cc
new file mode 100644
index 000000000000..f66f1e7097fa
--- /dev/null
+++ b/lib/tsan/lit_tests/java_lock.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include "java.h"
+
+jptr varaddr;
+jptr lockaddr;
+
+void *Thread(void *p) {
+ __tsan_java_mutex_lock(lockaddr);
+ *(int*)varaddr = 42;
+ __tsan_java_mutex_unlock(lockaddr);
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc((jptr)jheap, kBlockSize);
+ varaddr = (jptr)jheap;
+ lockaddr = (jptr)jheap + 8;
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+ __tsan_java_mutex_lock(lockaddr);
+ *(int*)varaddr = 43;
+ __tsan_java_mutex_unlock(lockaddr);
+ pthread_join(th, 0);
+ __tsan_java_free((jptr)jheap, kBlockSize);
+ printf("OK\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/lit_tests/java_lock_move.cc b/lib/tsan/lit_tests/java_lock_move.cc
new file mode 100644
index 000000000000..48b5a5a88d33
--- /dev/null
+++ b/lib/tsan/lit_tests/java_lock_move.cc
@@ -0,0 +1,40 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include "java.h"
+
+jptr varaddr;
+jptr lockaddr;
+jptr varaddr2;
+jptr lockaddr2;
+
+void *Thread(void *p) {
+ sleep(1);
+ __tsan_java_mutex_lock(lockaddr2);
+ *(int*)varaddr2 = 42;
+ __tsan_java_mutex_unlock(lockaddr2);
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 64;
+ int const kMove = 1024;
+ __tsan_java_alloc((jptr)jheap, kBlockSize);
+ varaddr = (jptr)jheap;
+ lockaddr = (jptr)jheap + 46;
+ varaddr2 = varaddr + kMove;
+ lockaddr2 = lockaddr + kMove;
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+ __tsan_java_mutex_lock(lockaddr);
+ *(int*)varaddr = 43;
+ __tsan_java_mutex_unlock(lockaddr);
+ __tsan_java_move(varaddr, varaddr2, kBlockSize);
+ pthread_join(th, 0);
+ __tsan_java_free(varaddr2, kBlockSize);
+ printf("OK\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/lit_tests/java_race.cc b/lib/tsan/lit_tests/java_race.cc
new file mode 100644
index 000000000000..722bb6e8d09c
--- /dev/null
+++ b/lib/tsan/lit_tests/java_race.cc
@@ -0,0 +1,23 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include "java.h"
+
+void *Thread(void *p) {
+ *(int*)p = 42;
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc((jptr)jheap, kBlockSize);
+ pthread_t th;
+ pthread_create(&th, 0, Thread, jheap);
+ *(int*)jheap = 43;
+ pthread_join(th, 0);
+ __tsan_java_free((jptr)jheap, kBlockSize);
+ return __tsan_java_fini();
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/lit_tests/java_race_move.cc b/lib/tsan/lit_tests/java_race_move.cc
new file mode 100644
index 000000000000..bb63ea985c58
--- /dev/null
+++ b/lib/tsan/lit_tests/java_race_move.cc
@@ -0,0 +1,31 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include "java.h"
+
+jptr varaddr;
+jptr varaddr2;
+
+void *Thread(void *p) {
+ sleep(1);
+ *(int*)varaddr2 = 42;
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 64;
+ int const kMove = 1024;
+ __tsan_java_alloc((jptr)jheap, kBlockSize);
+ varaddr = (jptr)jheap + 16;
+ varaddr2 = varaddr + kMove;
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+ *(int*)varaddr = 43;
+ __tsan_java_move(varaddr, varaddr2, kBlockSize);
+ pthread_join(th, 0);
+ __tsan_java_free(varaddr2, kBlockSize);
+ return __tsan_java_fini();
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/lit_tests/java_rwlock.cc b/lib/tsan/lit_tests/java_rwlock.cc
new file mode 100644
index 000000000000..1e8940afd7d0
--- /dev/null
+++ b/lib/tsan/lit_tests/java_rwlock.cc
@@ -0,0 +1,33 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include "java.h"
+
+jptr varaddr;
+jptr lockaddr;
+
+void *Thread(void *p) {
+ __tsan_java_mutex_read_lock(lockaddr);
+ *(int*)varaddr = 42;
+ __tsan_java_mutex_read_unlock(lockaddr);
+ return 0;
+}
+
+int main() {
+ int const kHeapSize = 1024 * 1024;
+ void *jheap = malloc(kHeapSize);
+ __tsan_java_init((jptr)jheap, kHeapSize);
+ const int kBlockSize = 16;
+ __tsan_java_alloc((jptr)jheap, kBlockSize);
+ varaddr = (jptr)jheap;
+ lockaddr = (jptr)jheap + 8;
+ pthread_t th;
+ pthread_create(&th, 0, Thread, 0);
+ __tsan_java_mutex_lock(lockaddr);
+ *(int*)varaddr = 43;
+ __tsan_java_mutex_unlock(lockaddr);
+ pthread_join(th, 0);
+ __tsan_java_free((jptr)jheap, kBlockSize);
+ printf("OK\n");
+ return __tsan_java_fini();
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/lit_tests/lit.cfg b/lib/tsan/lit_tests/lit.cfg
new file mode 100644
index 000000000000..7e2db7b8fd0b
--- /dev/null
+++ b/lib/tsan/lit_tests/lit.cfg
@@ -0,0 +1,93 @@
+# -*- Python -*-
+
+import os
+
+# Setup config name.
+config.name = 'ThreadSanitizer'
+
+# Setup source root.
+config.test_source_root = os.path.dirname(__file__)
+
+def DisplayNoConfigMessage():
+ lit.fatal("No site specific configuration available! " +
+ "Try running your test from the build tree or running " +
+ "make check-tsan")
+
+# Figure out LLVM source root.
+llvm_src_root = getattr(config, 'llvm_src_root', None)
+if llvm_src_root is None:
+ # We probably haven't loaded the site-specific configuration: the user
+ # is likely trying to run a test file directly, and the site configuration
+ # wasn't created by the build system.
+ tsan_site_cfg = lit.params.get('tsan_site_config', None)
+ if (tsan_site_cfg) and (os.path.exists(tsan_site_cfg)):
+ lit.load_config(config, tsan_site_cfg)
+ raise SystemExit
+
+ # Try to guess the location of site-specific configuration using llvm-config
+ # util that can point where the build tree is.
+ llvm_config = lit.util.which("llvm-config", config.environment["PATH"])
+ if not llvm_config:
+ DisplayNoConfigMessage()
+
+ # Validate that llvm-config points to the same source tree.
+ llvm_src_root = lit.util.capture(["llvm-config", "--src-root"]).strip()
+ tsan_test_src_root = os.path.join(llvm_src_root, "projects", "compiler-rt",
+ "lib", "tsan", "lit_tests")
+ if (os.path.realpath(tsan_test_src_root) !=
+ os.path.realpath(config.test_source_root)):
+ DisplayNoConfigMessage()
+
+ # Find out the presumed location of generated site config.
+ llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip()
+ tsan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt",
+ "lib", "tsan", "lit_tests", "lit.site.cfg")
+ if (not tsan_site_cfg) or (not os.path.exists(tsan_site_cfg)):
+ DisplayNoConfigMessage()
+
+ lit.load_config(config, tsan_site_cfg)
+ raise SystemExit
+
+# Setup attributes common for all compiler-rt projects.
+compiler_rt_lit_cfg = os.path.join(llvm_src_root, "projects", "compiler-rt",
+ "lib", "lit.common.cfg")
+if (not compiler_rt_lit_cfg) or (not os.path.exists(compiler_rt_lit_cfg)):
+ lit.fatal("Can't find common compiler-rt lit config at: %r"
+ % compiler_rt_lit_cfg)
+lit.load_config(config, compiler_rt_lit_cfg)
+
+# Setup environment variables for running ThreadSanitizer.
+tsan_options = "atexit_sleep_ms=0"
+# Get path to external LLVM symbolizer to run ThreadSanitizer output tests.
+llvm_tools_dir = getattr(config, 'llvm_tools_dir', None)
+if llvm_tools_dir:
+ llvm_symbolizer_path = os.path.join(llvm_tools_dir, "llvm-symbolizer")
+ tsan_options += " " + "external_symbolizer_path=" + llvm_symbolizer_path
+
+config.environment['TSAN_OPTIONS'] = tsan_options
+
+# Setup default compiler flags used with -fsanitize=thread option.
+# FIXME: Review the set of required flags and check if it can be reduced.
+clang_tsan_cflags = ("-fsanitize=thread "
+ + "-fPIE "
+ + "-fno-builtin "
+ + "-g "
+ + "-Wall "
+ + "-pie "
+ + "-lpthread "
+ + "-ldl ")
+clang_tsan_cxxflags = "-ccc-cxx " + clang_tsan_cflags
+config.substitutions.append( ("%clangxx_tsan ", (" " + config.clang + " " +
+ clang_tsan_cxxflags + " ")) )
+config.substitutions.append( ("%clang_tsan ", (" " + config.clang + " " +
+ clang_tsan_cflags + " ")) )
+
+# Define CHECK-%os to check for OS-dependent output.
+config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os)))
+
+# Default test suffixes.
+config.suffixes = ['.c', '.cc', '.cpp']
+
+# ThreadSanitizer tests are currently supported on Linux only.
+if config.host_os not in ['Linux']:
+ config.unsupported = True
diff --git a/lib/tsan/lit_tests/lit.site.cfg.in b/lib/tsan/lit_tests/lit.site.cfg.in
new file mode 100644
index 000000000000..b1c6ccf544ea
--- /dev/null
+++ b/lib/tsan/lit_tests/lit.site.cfg.in
@@ -0,0 +1,19 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+config.clang = "@LLVM_BINARY_DIR@/bin/clang"
+config.host_os = "@HOST_OS@"
+config.llvm_src_root = "@LLVM_SOURCE_DIR@"
+config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
+config.target_triple = "@TARGET_TRIPLE@"
+
+# LLVM tools dir can be passed in lit parameters, so try to
+# apply substitution.
+try:
+ config.llvm_tools_dir = config.llvm_tools_dir % lit.params
+except KeyError,e:
+ key, = e.args
+ lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key))
+
+# Let the main config do the real work.
+lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg")
diff --git a/lib/tsan/output_tests/memcpy_race.cc b/lib/tsan/lit_tests/memcpy_race.cc
index c6b79a709e48..806740dda241 100644
--- a/lib/tsan/output_tests/memcpy_race.cc
+++ b/lib/tsan/lit_tests/memcpy_race.cc
@@ -1,3 +1,4 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stddef.h>
#include <stdio.h>
@@ -14,7 +15,7 @@ void *Thread1(void *x) {
}
void *Thread2(void *x) {
- usleep(500*1000);
+ sleep(1);
memcpy(data+3, data2, 4);
return NULL;
}
@@ -31,10 +32,9 @@ int main() {
// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
// CHECK: WARNING: ThreadSanitizer: data race
-// CHECK: Write of size 1 at [[ADDR]] by thread 2:
+// CHECK: Write of size 1 at [[ADDR]] by thread T2:
// CHECK: #0 memcpy
// CHECK: #1 Thread2
-// CHECK: Previous write of size 1 at [[ADDR]] by thread 1:
+// CHECK: Previous write of size 1 at [[ADDR]] by thread T1:
// CHECK: #0 memcpy
// CHECK: #1 Thread1
-
diff --git a/lib/tsan/output_tests/mop_with_offset.cc b/lib/tsan/lit_tests/mop_with_offset.cc
index fc497bfb4a74..0c11ef6b9187 100644
--- a/lib/tsan/output_tests/mop_with_offset.cc
+++ b/lib/tsan/lit_tests/mop_with_offset.cc
@@ -1,3 +1,4 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stddef.h>
#include <stdio.h>
@@ -10,7 +11,7 @@ void *Thread1(void *x) {
}
void *Thread2(void *x) {
- usleep(500*1000);
+ sleep(1);
char *p = (char*)x;
p[2] = 1;
return NULL;
@@ -31,6 +32,5 @@ int main() {
// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]]
// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]]
// CHECK: WARNING: ThreadSanitizer: data race
-// CHECK: Write of size 1 at [[PTR2]] by thread 2:
-// CHECK: Previous write of size 4 at [[PTR1]] by thread 1:
-
+// CHECK: Write of size 1 at [[PTR2]] by thread T2:
+// CHECK: Previous write of size 4 at [[PTR1]] by thread T1:
diff --git a/lib/tsan/output_tests/mop_with_offset2.cc b/lib/tsan/lit_tests/mop_with_offset2.cc
index bbeda554929f..ee0d64a0afbf 100644
--- a/lib/tsan/output_tests/mop_with_offset2.cc
+++ b/lib/tsan/lit_tests/mop_with_offset2.cc
@@ -1,10 +1,11 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
void *Thread1(void *x) {
- usleep(500*1000);
+ sleep(1);
int *p = (int*)x;
p[0] = 1;
return NULL;
@@ -31,6 +32,5 @@ int main() {
// CHECK: ptr1=[[PTR1:0x[0-9,a-f]+]]
// CHECK: ptr2=[[PTR2:0x[0-9,a-f]+]]
// CHECK: WARNING: ThreadSanitizer: data race
-// CHECK: Write of size 4 at [[PTR1]] by thread 1:
-// CHECK: Previous write of size 1 at [[PTR2]] by thread 2:
-
+// CHECK: Write of size 4 at [[PTR1]] by thread T1:
+// CHECK: Previous write of size 1 at [[PTR2]] by thread T2:
diff --git a/lib/tsan/lit_tests/mutex_destroy_locked.cc b/lib/tsan/lit_tests/mutex_destroy_locked.cc
new file mode 100644
index 000000000000..991eaf5426e2
--- /dev/null
+++ b/lib/tsan/lit_tests/mutex_destroy_locked.cc
@@ -0,0 +1,21 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+int main() {
+ pthread_mutex_t m;
+ pthread_mutex_init(&m, 0);
+ pthread_mutex_lock(&m);
+ pthread_mutex_destroy(&m);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: destroy of a locked mutex
+// CHECK: #0 pthread_mutex_destroy
+// CHECK: #1 main
+// CHECK: and:
+// CHECK: #0 pthread_mutex_lock
+// CHECK: #1 main
+// CHECK: Mutex {{.*}} created at:
+// CHECK: #0 pthread_mutex_init
+// CHECK: #1 main
diff --git a/lib/tsan/lit_tests/mutexset1.cc b/lib/tsan/lit_tests/mutexset1.cc
new file mode 100644
index 000000000000..f32a770ab075
--- /dev/null
+++ b/lib/tsan/lit_tests/mutexset1.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+pthread_mutex_t mtx;
+
+void *Thread1(void *x) {
+ sleep(1);
+ pthread_mutex_lock(&mtx);
+ Global++;
+ pthread_mutex_unlock(&mtx);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ Global--;
+ return NULL;
+}
+
+int main() {
+ // CHECK: WARNING: ThreadSanitizer: data race
+ // CHECK: Write of size 4 at {{.*}} by thread T1
+ // CHECK: (mutexes: write [[M1:M[0-9]+]]):
+ // CHECK: Previous write of size 4 at {{.*}} by thread T2:
+ // CHECK: Mutex [[M1]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset1.cc:[[@LINE+1]]
+ pthread_mutex_init(&mtx, 0);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&mtx);
+}
diff --git a/lib/tsan/lit_tests/mutexset2.cc b/lib/tsan/lit_tests/mutexset2.cc
new file mode 100644
index 000000000000..15d230332512
--- /dev/null
+++ b/lib/tsan/lit_tests/mutexset2.cc
@@ -0,0 +1,37 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+pthread_mutex_t mtx;
+
+void *Thread1(void *x) {
+ pthread_mutex_lock(&mtx);
+ Global++;
+ pthread_mutex_unlock(&mtx);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ Global--;
+ return NULL;
+}
+
+int main() {
+ // CHECK: WARNING: ThreadSanitizer: data race
+ // CHECK: Write of size 4 at {{.*}} by thread T2:
+ // CHECK: Previous write of size 4 at {{.*}} by thread T1
+ // CHECK: (mutexes: write [[M1:M[0-9]+]]):
+ // CHECK: Mutex [[M1]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset2.cc:[[@LINE+1]]
+ pthread_mutex_init(&mtx, 0);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&mtx);
+}
diff --git a/lib/tsan/lit_tests/mutexset3.cc b/lib/tsan/lit_tests/mutexset3.cc
new file mode 100644
index 000000000000..6ac7ad15e4f9
--- /dev/null
+++ b/lib/tsan/lit_tests/mutexset3.cc
@@ -0,0 +1,45 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+pthread_mutex_t mtx1;
+pthread_mutex_t mtx2;
+
+void *Thread1(void *x) {
+ sleep(1);
+ pthread_mutex_lock(&mtx1);
+ pthread_mutex_lock(&mtx2);
+ Global++;
+ pthread_mutex_unlock(&mtx2);
+ pthread_mutex_unlock(&mtx1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ Global--;
+ return NULL;
+}
+
+int main() {
+ // CHECK: WARNING: ThreadSanitizer: data race
+ // CHECK: Write of size 4 at {{.*}} by thread T1
+ // CHECK: (mutexes: write [[M1:M[0-9]+]], write [[M2:M[0-9]+]]):
+ // CHECK: Previous write of size 4 at {{.*}} by thread T2:
+ // CHECK: Mutex [[M1]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset3.cc:[[@LINE+4]]
+ // CHECK: Mutex [[M2]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset3.cc:[[@LINE+2]]
+ pthread_mutex_init(&mtx1, 0);
+ pthread_mutex_init(&mtx2, 0);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&mtx1);
+ pthread_mutex_destroy(&mtx2);
+}
diff --git a/lib/tsan/lit_tests/mutexset4.cc b/lib/tsan/lit_tests/mutexset4.cc
new file mode 100644
index 000000000000..75684cf9ae5b
--- /dev/null
+++ b/lib/tsan/lit_tests/mutexset4.cc
@@ -0,0 +1,45 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+pthread_mutex_t mtx1;
+pthread_mutex_t mtx2;
+
+void *Thread1(void *x) {
+ pthread_mutex_lock(&mtx1);
+ pthread_mutex_lock(&mtx2);
+ Global++;
+ pthread_mutex_unlock(&mtx2);
+ pthread_mutex_unlock(&mtx1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ sleep(1);
+ Global--;
+ return NULL;
+}
+
+int main() {
+ // CHECK: WARNING: ThreadSanitizer: data race
+ // CHECK: Write of size 4 at {{.*}} by thread T2:
+ // CHECK: Previous write of size 4 at {{.*}} by thread T1
+ // CHECK: (mutexes: write [[M1:M[0-9]+]], write [[M2:M[0-9]+]]):
+ // CHECK: Mutex [[M1]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset4.cc:[[@LINE+4]]
+ // CHECK: Mutex [[M2]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset4.cc:[[@LINE+2]]
+ pthread_mutex_init(&mtx1, 0);
+ pthread_mutex_init(&mtx2, 0);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&mtx1);
+ pthread_mutex_destroy(&mtx2);
+}
diff --git a/lib/tsan/lit_tests/mutexset5.cc b/lib/tsan/lit_tests/mutexset5.cc
new file mode 100644
index 000000000000..6e75810aff22
--- /dev/null
+++ b/lib/tsan/lit_tests/mutexset5.cc
@@ -0,0 +1,46 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+pthread_mutex_t mtx1;
+pthread_mutex_t mtx2;
+
+void *Thread1(void *x) {
+ sleep(1);
+ pthread_mutex_lock(&mtx1);
+ Global++;
+ pthread_mutex_unlock(&mtx1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ pthread_mutex_lock(&mtx2);
+ Global--;
+ pthread_mutex_unlock(&mtx2);
+ return NULL;
+}
+
+int main() {
+ // CHECK: WARNING: ThreadSanitizer: data race
+ // CHECK: Write of size 4 at {{.*}} by thread T1
+ // CHECK: (mutexes: write [[M1:M[0-9]+]]):
+ // CHECK: Previous write of size 4 at {{.*}} by thread T2
+ // CHECK: (mutexes: write [[M2:M[0-9]+]]):
+ // CHECK: Mutex [[M1]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset5.cc:[[@LINE+4]]
+ // CHECK: Mutex [[M2]] created at:
+ // CHECK: #0 pthread_mutex_init
+ // CHECK: #1 main {{.*}}/mutexset5.cc:[[@LINE+5]]
+ pthread_mutex_init(&mtx1, 0);
+ pthread_mutex_init(&mtx2, 0);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&mtx1);
+ pthread_mutex_destroy(&mtx2);
+}
diff --git a/lib/tsan/lit_tests/mutexset6.cc b/lib/tsan/lit_tests/mutexset6.cc
new file mode 100644
index 000000000000..4b19a12e0434
--- /dev/null
+++ b/lib/tsan/lit_tests/mutexset6.cc
@@ -0,0 +1,53 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+pthread_mutex_t mtx1;
+pthread_spinlock_t mtx2;
+pthread_rwlock_t mtx3;
+
+void *Thread1(void *x) {
+ sleep(1);
+ pthread_mutex_lock(&mtx1);
+ Global++;
+ pthread_mutex_unlock(&mtx1);
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ pthread_mutex_lock(&mtx1);
+ pthread_mutex_unlock(&mtx1);
+ pthread_spin_lock(&mtx2);
+ pthread_rwlock_rdlock(&mtx3);
+ Global--;
+ pthread_spin_unlock(&mtx2);
+ pthread_rwlock_unlock(&mtx3);
+ return NULL;
+}
+
+int main() {
+ // CHECK: WARNING: ThreadSanitizer: data race
+ // CHECK: Write of size 4 at {{.*}} by thread T1
+ // CHECK: (mutexes: write [[M1:M[0-9]+]]):
+ // CHECK: Previous write of size 4 at {{.*}} by thread T2
+ // CHECK: (mutexes: write [[M2:M[0-9]+]], read [[M3:M[0-9]+]]):
+ // CHECK: Mutex [[M1]] created at:
+ // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+5]]
+ // CHECK: Mutex [[M2]] created at:
+ // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+4]]
+ // CHECK: Mutex [[M3]] created at:
+ // CHECK: #1 main {{.*}}/mutexset6.cc:[[@LINE+3]]
+ pthread_mutex_init(&mtx1, 0);
+ pthread_spin_init(&mtx2, 0);
+ pthread_rwlock_init(&mtx3, 0);
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ pthread_mutex_destroy(&mtx1);
+ pthread_spin_destroy(&mtx2);
+ pthread_rwlock_destroy(&mtx3);
+}
diff --git a/lib/tsan/lit_tests/mutexset7.cc b/lib/tsan/lit_tests/mutexset7.cc
new file mode 100644
index 000000000000..141bde2b5015
--- /dev/null
+++ b/lib/tsan/lit_tests/mutexset7.cc
@@ -0,0 +1,38 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+void *Thread1(void *x) {
+ sleep(1);
+ Global++;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ pthread_mutex_t mtx;
+ pthread_mutex_init(&mtx, 0);
+ pthread_mutex_lock(&mtx);
+ Global--;
+ pthread_mutex_unlock(&mtx);
+ pthread_mutex_destroy(&mtx);
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 4 at {{.*}} by thread T1:
+// CHECK: Previous write of size 4 at {{.*}} by thread T2
+// CHECK: (mutexes: write [[M1:M[0-9]+]]):
+// CHECK: Mutex [[M1]] is already destroyed
+// CHECK-NOT: Mutex {{.*}} created at
+
diff --git a/lib/tsan/output_tests/race_on_barrier.c b/lib/tsan/lit_tests/race_on_barrier.c
index 98d7a1d847f9..3e76f8bf5e20 100644
--- a/lib/tsan/output_tests/race_on_barrier.c
+++ b/lib/tsan/lit_tests/race_on_barrier.c
@@ -1,3 +1,4 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <stddef.h>
@@ -13,7 +14,7 @@ void *Thread1(void *x) {
}
void *Thread2(void *x) {
- usleep(1000000);
+ sleep(1);
pthread_barrier_wait(&B);
return NULL;
}
@@ -28,4 +29,3 @@ int main() {
}
// CHECK: WARNING: ThreadSanitizer: data race
-
diff --git a/lib/tsan/output_tests/race_on_barrier2.c b/lib/tsan/lit_tests/race_on_barrier2.c
index dbdb6b557004..46a4f50b133d 100644
--- a/lib/tsan/output_tests/race_on_barrier2.c
+++ b/lib/tsan/lit_tests/race_on_barrier2.c
@@ -1,3 +1,4 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <stddef.h>
diff --git a/lib/tsan/lit_tests/race_on_heap.cc b/lib/tsan/lit_tests/race_on_heap.cc
new file mode 100644
index 000000000000..dc679e8bf3f9
--- /dev/null
+++ b/lib/tsan/lit_tests/race_on_heap.cc
@@ -0,0 +1,47 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+void *Thread1(void *p) {
+ *(int*)p = 42;
+ return 0;
+}
+
+void *Thread2(void *p) {
+ *(int*)p = 44;
+ return 0;
+}
+
+void *alloc() {
+ return malloc(99);
+}
+
+void *AllocThread(void* arg) {
+ return alloc();
+}
+
+int main() {
+ void *p = 0;
+ pthread_t t[2];
+ pthread_create(&t[0], 0, AllocThread, 0);
+ pthread_join(t[0], &p);
+ fprintf(stderr, "addr=%p\n", p);
+ pthread_create(&t[0], 0, Thread1, (char*)p + 16);
+ pthread_create(&t[1], 0, Thread2, (char*)p + 16);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+ return 0;
+}
+
+// CHECK: addr=[[ADDR:0x[0-9,a-f]+]]
+// CHECK: WARNING: ThreadSanitizer: data race
+// ...
+// CHECK: Location is heap block of size 99 at [[ADDR]] allocated by thread T1:
+// CHCEKL #0 malloc
+// CHECK: #1 alloc
+// CHECK: #2 AllocThread
+// ...
+// CHECK: Thread T1 (tid={{.*}}, finished) created by main thread at:
+// CHECK: #0 pthread_create
+// CHECK: #1 main
diff --git a/lib/tsan/output_tests/race_on_mutex.c b/lib/tsan/lit_tests/race_on_mutex.c
index 45c75be782d1..de1c2d4160a6 100644
--- a/lib/tsan/output_tests/race_on_mutex.c
+++ b/lib/tsan/lit_tests/race_on_mutex.c
@@ -1,3 +1,4 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
#include <stddef.h>
@@ -15,7 +16,7 @@ void *Thread1(void *x) {
}
void *Thread2(void *x) {
- usleep(1000000);
+ sleep(1);
pthread_mutex_lock(&Mtx);
Global = 43;
pthread_mutex_unlock(&Mtx);
@@ -33,9 +34,9 @@ int main() {
}
// CHECK: WARNING: ThreadSanitizer: data race
-// CHECK-NEXT: Read of size 1 at {{.*}} by thread 2:
-// CHECK-NEXT: #0 pthread_mutex_lock {{.*}} ({{.*}})
-// CHECK-NEXT: #1 Thread2 {{.*}}race_on_mutex.c:19{{(:3)?}} ({{.*}})
-// CHECK-NEXT: Previous write of size 1 at {{.*}} by thread 1:
+// CHECK-NEXT: Read of size 1 at {{.*}} by thread T2:
+// CHECK-NEXT: #0 pthread_mutex_lock
+// CHECK-NEXT: #1 Thread2{{.*}} {{.*}}race_on_mutex.c:20{{(:3)?}} ({{.*}})
+// CHECK: Previous write of size 1 at {{.*}} by thread T1:
// CHECK-NEXT: #0 pthread_mutex_init {{.*}} ({{.*}})
-// CHECK-NEXT: #1 Thread1 {{.*}}race_on_mutex.c:10{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #1 Thread1{{.*}} {{.*}}race_on_mutex.c:11{{(:3)?}} ({{.*}})
diff --git a/lib/tsan/lit_tests/race_on_read.cc b/lib/tsan/lit_tests/race_on_read.cc
new file mode 100644
index 000000000000..7d226814816e
--- /dev/null
+++ b/lib/tsan/lit_tests/race_on_read.cc
@@ -0,0 +1,32 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+int fd;
+char buf;
+
+void *Thread(void *x) {
+ read(fd, &buf, 1);
+ return NULL;
+}
+
+int main() {
+ fd = open("/dev/random", O_RDONLY);
+ if (fd < 0) return 1;
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread, NULL);
+ pthread_create(&t[1], NULL, Thread, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ close(fd);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 1
+// CHECK: #0 read
+// CHECK: Previous write of size 1
+// CHECK: #0 read
diff --git a/lib/tsan/output_tests/race_with_finished_thread.cc b/lib/tsan/lit_tests/race_with_finished_thread.cc
index 1f60f4ba349b..a267290e661e 100644
--- a/lib/tsan/output_tests/race_with_finished_thread.cc
+++ b/lib/tsan/lit_tests/race_with_finished_thread.cc
@@ -1,3 +1,4 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stddef.h>
#include <stdio.h>
@@ -18,7 +19,7 @@ void *Thread1(void *x) {
}
void *Thread2(void *x) {
- usleep(1000*1000);
+ sleep(1);
g_data = 43;
return NULL;
}
@@ -33,11 +34,10 @@ int main() {
}
// CHECK: WARNING: ThreadSanitizer: data race
-// CHECK: Write of size 4 at {{.*}} by thread 2:
-// CHECK: Previous write of size 4 at {{.*}} by thread 1:
+// CHECK: Write of size 4 at {{.*}} by thread T2:
+// CHECK: Previous write of size 4 at {{.*}} by thread T1:
// CHECK: #0 foobar
// CHECK: #1 Thread1
-// CHECK: Thread 1 (finished) created at:
+// CHECK: Thread T1 (tid={{.*}}, finished) created by main thread at:
// CHECK: #0 pthread_create
// CHECK: #1 main
-
diff --git a/lib/tsan/lit_tests/signal_errno.cc b/lib/tsan/lit_tests/signal_errno.cc
new file mode 100644
index 000000000000..af9ccce9045a
--- /dev/null
+++ b/lib/tsan/lit_tests/signal_errno.cc
@@ -0,0 +1,42 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+
+pthread_t mainth;
+volatile int done;
+
+static void handler(int, siginfo_t *s, void *c) {
+ errno = 1;
+ done = 1;
+}
+
+static void* sendsignal(void *p) {
+ pthread_kill(mainth, SIGPROF);
+ return 0;
+}
+
+int main() {
+ mainth = pthread_self();
+ struct sigaction act = {};
+ act.sa_sigaction = &handler;
+ sigaction(SIGPROF, &act, 0);
+ pthread_t th;
+ pthread_create(&th, 0, sendsignal, 0);
+ while (done == 0) {
+ volatile char *p = (char*)malloc(1);
+ p[0] = 0;
+ free((void*)p);
+ pthread_yield();
+ }
+ pthread_join(th, 0);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: signal handler spoils errno
+// CHECK: #0 handler(int, siginfo*, void*) {{.*}}signal_errno.cc
+
diff --git a/lib/tsan/lit_tests/signal_malloc.cc b/lib/tsan/lit_tests/signal_malloc.cc
new file mode 100644
index 000000000000..cee997cdb763
--- /dev/null
+++ b/lib/tsan/lit_tests/signal_malloc.cc
@@ -0,0 +1,25 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static void handler(int, siginfo_t*, void*) {
+ // CHECK: WARNING: ThreadSanitizer: signal-unsafe call inside of a signal
+ // CHECK: #0 malloc
+ // CHECK: #1 handler(int, siginfo*, void*) {{.*}}signal_malloc.cc:[[@LINE+1]]
+ volatile char *p = (char*)malloc(1);
+ p[0] = 0;
+ free((void*)p);
+}
+
+int main() {
+ struct sigaction act = {};
+ act.sa_sigaction = &handler;
+ sigaction(SIGPROF, &act, 0);
+ kill(getpid(), SIGPROF);
+ sleep(1);
+ return 0;
+}
+
diff --git a/lib/tsan/output_tests/simple_race.c b/lib/tsan/lit_tests/simple_race.c
index ed831fd8c5a3..44aff897406a 100644
--- a/lib/tsan/output_tests/simple_race.c
+++ b/lib/tsan/lit_tests/simple_race.c
@@ -1,3 +1,4 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
diff --git a/lib/tsan/output_tests/simple_race.cc b/lib/tsan/lit_tests/simple_race.cc
index 8d2cabff772c..ec29c92ee1a8 100644
--- a/lib/tsan/output_tests/simple_race.cc
+++ b/lib/tsan/lit_tests/simple_race.cc
@@ -1,3 +1,4 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdio.h>
diff --git a/lib/tsan/lit_tests/simple_stack.c b/lib/tsan/lit_tests/simple_stack.c
new file mode 100644
index 000000000000..4539cb7c1f37
--- /dev/null
+++ b/lib/tsan/lit_tests/simple_stack.c
@@ -0,0 +1,66 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+void __attribute__((noinline)) foo1() {
+ Global = 42;
+}
+
+void __attribute__((noinline)) bar1() {
+ volatile int tmp = 42; (void)tmp;
+ foo1();
+}
+
+void __attribute__((noinline)) foo2() {
+ volatile int v = Global; (void)v;
+}
+
+void __attribute__((noinline)) bar2() {
+ volatile int tmp = 42; (void)tmp;
+ foo2();
+}
+
+void *Thread1(void *x) {
+ sleep(1);
+ bar1();
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ bar2();
+ return NULL;
+}
+
+void StartThread(pthread_t *t, void *(*f)(void*)) {
+ pthread_create(t, NULL, f, NULL);
+}
+
+int main() {
+ pthread_t t[2];
+ StartThread(&t[0], Thread1);
+ StartThread(&t[1], Thread2);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NEXT: Write of size 4 at {{.*}} by thread T1:
+// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack.c:9{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack.c:14{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack.c:28{{(:3)?}} ({{.*}})
+// CHECK: Previous read of size 4 at {{.*}} by thread T2:
+// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack.c:18{{(:26)?}} ({{.*}})
+// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack.c:23{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 Thread2{{.*}} {{.*}}simple_stack.c:33{{(:3)?}} ({{.*}})
+// CHECK: Thread T1 (tid={{.*}}, running) created by main thread at:
+// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}})
+// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:43{{(:3)?}} ({{.*}})
+// CHECK: Thread T2 ({{.*}}) created by main thread at:
+// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}})
+// CHECK-NEXT: #1 StartThread{{.*}} {{.*}}simple_stack.c:38{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack.c:44{{(:3)?}} ({{.*}})
diff --git a/lib/tsan/lit_tests/simple_stack2.cc b/lib/tsan/lit_tests/simple_stack2.cc
new file mode 100644
index 000000000000..bf27a15ffad5
--- /dev/null
+++ b/lib/tsan/lit_tests/simple_stack2.cc
@@ -0,0 +1,53 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int Global;
+
+void __attribute__((noinline)) foo1() {
+ Global = 42;
+}
+
+void __attribute__((noinline)) bar1() {
+ volatile int tmp = 42;
+ int tmp2 = tmp;
+ (void)tmp2;
+ foo1();
+}
+
+void __attribute__((noinline)) foo2() {
+ volatile int tmp = Global;
+ int tmp2 = tmp;
+ (void)tmp2;
+}
+
+void __attribute__((noinline)) bar2() {
+ volatile int tmp = 42;
+ int tmp2 = tmp;
+ (void)tmp2;
+ foo2();
+}
+
+void *Thread1(void *x) {
+ sleep(1);
+ bar1();
+ return NULL;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, NULL, Thread1, NULL);
+ bar2();
+ pthread_join(t, NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NEXT: Write of size 4 at {{.*}} by thread T1:
+// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack2.cc:9{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack2.cc:16{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack2.cc:34{{(:3)?}} ({{.*}})
+// CHECK: Previous read of size 4 at {{.*}} by main thread:
+// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack2.cc:20{{(:28)?}} ({{.*}})
+// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack2.cc:29{{(:3)?}} ({{.*}})
+// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack2.cc:41{{(:3)?}} ({{.*}})
diff --git a/lib/tsan/lit_tests/sleep_sync.cc b/lib/tsan/lit_tests/sleep_sync.cc
new file mode 100644
index 000000000000..c3d47d311749
--- /dev/null
+++ b/lib/tsan/lit_tests/sleep_sync.cc
@@ -0,0 +1,30 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+int X = 0;
+
+void MySleep() {
+ sleep(1);
+}
+
+void *Thread(void *p) {
+ MySleep(); // Assume the main thread has done the write.
+ X = 42;
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ X = 43;
+ pthread_join(t, 0);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// ...
+// CHECK: As if synchronized via sleep:
+// CHECK-NEXT: #0 sleep
+// CHECK-NEXT: #1 MySleep
+// CHECK-NEXT: #2 Thread
diff --git a/lib/tsan/lit_tests/sleep_sync2.cc b/lib/tsan/lit_tests/sleep_sync2.cc
new file mode 100644
index 000000000000..d9961bccc808
--- /dev/null
+++ b/lib/tsan/lit_tests/sleep_sync2.cc
@@ -0,0 +1,22 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+int X = 0;
+
+void *Thread(void *p) {
+ X = 42;
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ sleep(1);
+ pthread_create(&t, 0, Thread, 0);
+ X = 43;
+ pthread_join(t, 0);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: As if synchronized via sleep
diff --git a/lib/tsan/lit_tests/stack_race.cc b/lib/tsan/lit_tests/stack_race.cc
new file mode 100644
index 000000000000..beeb57353bf3
--- /dev/null
+++ b/lib/tsan/lit_tests/stack_race.cc
@@ -0,0 +1,20 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+
+void *Thread(void *a) {
+ *(int*)a = 43;
+ return 0;
+}
+
+int main() {
+ int Var = 42;
+ pthread_t t;
+ pthread_create(&t, 0, Thread, &Var);
+ Var = 43;
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is stack of main thread.
+
diff --git a/lib/tsan/lit_tests/stack_race2.cc b/lib/tsan/lit_tests/stack_race2.cc
new file mode 100644
index 000000000000..5bdf1bd664a1
--- /dev/null
+++ b/lib/tsan/lit_tests/stack_race2.cc
@@ -0,0 +1,28 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+#include <unistd.h>
+
+void *Thread2(void *a) {
+ *(int*)a = 43;
+ return 0;
+}
+
+void *Thread(void *a) {
+ int Var = 42;
+ pthread_t t;
+ pthread_create(&t, 0, Thread2, &Var);
+ Var = 42;
+ pthread_join(t, 0);
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is stack of thread T1.
+
diff --git a/lib/tsan/output_tests/static_init1.cc b/lib/tsan/lit_tests/static_init1.cc
index 75d281954e1a..4faf5bc54743 100644
--- a/lib/tsan/output_tests/static_init1.cc
+++ b/lib/tsan/lit_tests/static_init1.cc
@@ -1,3 +1,4 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
@@ -20,6 +21,7 @@ int main() {
pthread_create(&t[1], 0, Thread, 0);
pthread_join(t[0], 0);
pthread_join(t[1], 0);
+ printf("PASS\n");
}
// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/static_init2.cc b/lib/tsan/lit_tests/static_init2.cc
index f6e95965521a..96ef821a7525 100644
--- a/lib/tsan/output_tests/static_init2.cc
+++ b/lib/tsan/lit_tests/static_init2.cc
@@ -1,10 +1,11 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
struct Cache {
int x;
- Cache(int x)
+ explicit Cache(int x)
: x(x) {
}
};
@@ -26,6 +27,7 @@ int main() {
pthread_create(&t[1], 0, Thread, 0);
pthread_join(t[0], 0);
pthread_join(t[1], 0);
+ printf("PASS\n");
}
// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/static_init3.cc b/lib/tsan/lit_tests/static_init3.cc
index 718f811d0df4..40fd4b940f55 100644
--- a/lib/tsan/output_tests/static_init3.cc
+++ b/lib/tsan/lit_tests/static_init3.cc
@@ -1,3 +1,4 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
diff --git a/lib/tsan/output_tests/static_init4.cc b/lib/tsan/lit_tests/static_init4.cc
index cdacbce80026..5ecc39926a23 100644
--- a/lib/tsan/output_tests/static_init4.cc
+++ b/lib/tsan/lit_tests/static_init4.cc
@@ -1,3 +1,4 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
@@ -5,7 +6,7 @@
struct Cache {
int x;
- Cache(int x)
+ explicit Cache(int x)
: x(x) {
}
};
@@ -30,6 +31,7 @@ int main() {
pthread_create(&t[1], 0, Thread1, 0);
pthread_join(t[0], 0);
pthread_join(t[1], 0);
+ printf("PASS\n");
}
// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/static_init5.cc b/lib/tsan/lit_tests/static_init5.cc
index 4b050c9fa69b..1d0ed6d54ca2 100644
--- a/lib/tsan/output_tests/static_init5.cc
+++ b/lib/tsan/lit_tests/static_init5.cc
@@ -1,3 +1,4 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
@@ -5,7 +6,7 @@
struct Cache {
int x;
- Cache(int x)
+ explicit Cache(int x)
: x(x) {
}
};
@@ -16,7 +17,7 @@ void *AsyncInit(void *p) {
Cache *CreateCache() {
pthread_t t;
- pthread_create(&t, 0, AsyncInit, (void*)rand());
+ pthread_create(&t, 0, AsyncInit, (void*)(long)rand());
void *res;
pthread_join(t, &res);
return (Cache*)res;
@@ -35,6 +36,7 @@ int main() {
pthread_create(&t[1], 0, Thread1, 0);
pthread_join(t[0], 0);
pthread_join(t[1], 0);
+ printf("PASS\n");
}
// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/lit_tests/static_init6.cc b/lib/tsan/lit_tests/static_init6.cc
new file mode 100644
index 000000000000..c9099f9b6790
--- /dev/null
+++ b/lib/tsan/lit_tests/static_init6.cc
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan -static-libstdc++ -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sched.h>
+
+struct Cache {
+ int x;
+ explicit Cache(int x)
+ : x(x) {
+ }
+};
+
+void *AsyncInit(void *p) {
+ return new Cache((int)(long)p);
+}
+
+Cache *CreateCache() {
+ pthread_t t;
+ pthread_create(&t, 0, AsyncInit, (void*)(long)rand());
+ void *res;
+ pthread_join(t, &res);
+ return (Cache*)res;
+}
+
+void *Thread1(void *x) {
+ static Cache *c = CreateCache();
+ if (c->x >= RAND_MAX)
+ exit(1);
+ return 0;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], 0, Thread1, 0);
+ pthread_create(&t[1], 0, Thread1, 0);
+ pthread_join(t[0], 0);
+ pthread_join(t[1], 0);
+ printf("PASS\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
diff --git a/lib/tsan/output_tests/suppress_same_address.cc b/lib/tsan/lit_tests/suppress_same_address.cc
index 6e98970a16ef..174d1cc8fcb3 100644
--- a/lib/tsan/output_tests/suppress_same_address.cc
+++ b/lib/tsan/lit_tests/suppress_same_address.cc
@@ -1,3 +1,4 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
int X;
@@ -24,4 +25,3 @@ int main() {
}
// CHECK: ThreadSanitizer: reported 1 warnings
-
diff --git a/lib/tsan/output_tests/suppress_same_stacks.cc b/lib/tsan/lit_tests/suppress_same_stacks.cc
index 6046a4ea9f3a..32bff9d50071 100644
--- a/lib/tsan/output_tests/suppress_same_stacks.cc
+++ b/lib/tsan/lit_tests/suppress_same_stacks.cc
@@ -1,3 +1,4 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
volatile int N; // Prevent loop unrolling.
@@ -24,4 +25,3 @@ int main() {
}
// CHECK: ThreadSanitizer: reported 1 warnings
-
diff --git a/lib/tsan/output_tests/test_output.sh b/lib/tsan/lit_tests/test_output.sh
index bd9cd9158763..d21c9a797ad3 100755
--- a/lib/tsan/output_tests/test_output.sh
+++ b/lib/tsan/lit_tests/test_output.sh
@@ -4,13 +4,14 @@ ulimit -s 8192
set -e # fail on any error
ROOTDIR=$(dirname $0)/..
+BLACKLIST=$ROOTDIR/lit_tests/Helpers/blacklist.txt
# Assuming clang is in path.
CC=clang
CXX=clang++
# TODO: add testing for all of -O0...-O3
-CFLAGS="-fthread-sanitizer -fPIE -O1 -g -fno-builtin -Wall"
+CFLAGS="-fsanitize=thread -fsanitize-blacklist=$BLACKLIST -fPIE -O1 -g -fno-builtin -Wall"
LDFLAGS="-pie -lpthread -ldl $ROOTDIR/rtl/libtsan.a"
test_file() {
@@ -21,10 +22,7 @@ test_file() {
EXE=$SRC.exe
$COMPILER $SRC $CFLAGS -c -o $OBJ
$COMPILER $OBJ $LDFLAGS -o $EXE
- RES=$(TSAN_OPTIONS="atexit_sleep_ms=0" $EXE 2>&1 || true)
- if [ "$3" != "" ]; then
- printf "%s\n" "$RES"
- fi
+ RES=$($EXE 2>&1 || true)
printf "%s\n" "$RES" | FileCheck $SRC
if [ "$3" == "" ]; then
rm -f $EXE $OBJ
@@ -32,7 +30,7 @@ test_file() {
}
if [ "$1" == "" ]; then
- for c in $ROOTDIR/output_tests/*.{c,cc}; do
+ for c in $ROOTDIR/lit_tests/*.{c,cc}; do
if [[ $c == */failing_* ]]; then
echo SKIPPING FAILING TEST $c
continue
@@ -41,9 +39,11 @@ if [ "$1" == "" ]; then
case $c in
*.c) COMPILER=$CC
esac
- test_file $c $COMPILER
+ test_file $c $COMPILER &
+ done
+ for job in `jobs -p`; do
+ wait $job || exit 1
done
- wait
else
- test_file $ROOTDIR/output_tests/$1 $CXX "DUMP"
+ test_file $ROOTDIR/lit_tests/$1 $CXX "DUMP"
fi
diff --git a/lib/tsan/output_tests/thread_leak.c b/lib/tsan/lit_tests/thread_leak.c
index 88a11be4e9c0..c5e669e5d99f 100644
--- a/lib/tsan/output_tests/thread_leak.c
+++ b/lib/tsan/lit_tests/thread_leak.c
@@ -1,4 +1,6 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
+#include <stdio.h>
void *Thread(void *x) {
return 0;
@@ -8,8 +10,8 @@ int main() {
pthread_t t;
pthread_create(&t, 0, Thread, 0);
pthread_join(t, 0);
+ printf("PASS\n");
return 0;
}
// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
-
diff --git a/lib/tsan/output_tests/thread_leak2.c b/lib/tsan/lit_tests/thread_leak2.c
index 71e9c50b8a2f..39f6b5e02e39 100644
--- a/lib/tsan/output_tests/thread_leak2.c
+++ b/lib/tsan/lit_tests/thread_leak2.c
@@ -1,4 +1,6 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
+#include <stdio.h>
void *Thread(void *x) {
return 0;
@@ -8,8 +10,8 @@ int main() {
pthread_t t;
pthread_create(&t, 0, Thread, 0);
pthread_detach(t);
+ printf("PASS\n");
return 0;
}
// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
-
diff --git a/lib/tsan/output_tests/thread_leak3.c b/lib/tsan/lit_tests/thread_leak3.c
index 058b6e54d9da..c48219fe73fa 100644
--- a/lib/tsan/output_tests/thread_leak3.c
+++ b/lib/tsan/lit_tests/thread_leak3.c
@@ -1,3 +1,4 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
void *Thread(void *x) {
@@ -11,4 +12,3 @@ int main() {
}
// CHECK: WARNING: ThreadSanitizer: thread leak
-
diff --git a/lib/tsan/lit_tests/thread_name.cc b/lib/tsan/lit_tests/thread_name.cc
new file mode 100644
index 000000000000..0ca0b1769976
--- /dev/null
+++ b/lib/tsan/lit_tests/thread_name.cc
@@ -0,0 +1,34 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+extern "C" void AnnotateThreadName(const char *f, int l, const char *name);
+
+int Global;
+
+void *Thread1(void *x) {
+ sleep(1);
+ AnnotateThreadName(__FILE__, __LINE__, "Thread1");
+ Global++;
+ return NULL;
+}
+
+void *Thread2(void *x) {
+ pthread_setname_np(pthread_self(), "Thread2");
+ Global--;
+ return NULL;
+}
+
+int main() {
+ pthread_t t[2];
+ pthread_create(&t[0], NULL, Thread1, NULL);
+ pthread_create(&t[1], NULL, Thread2, NULL);
+ pthread_join(t[0], NULL);
+ pthread_join(t[1], NULL);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Thread T1 'Thread1'
+// CHECK: Thread T2 'Thread2'
+
diff --git a/lib/tsan/output_tests/tiny_race.c b/lib/tsan/lit_tests/tiny_race.c
index 3a8d192a671a..44cc1332f2ab 100644
--- a/lib/tsan/output_tests/tiny_race.c
+++ b/lib/tsan/lit_tests/tiny_race.c
@@ -1,3 +1,4 @@
+// RUN: %clang_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
int Global;
void *Thread1(void *x) {
diff --git a/lib/tsan/lit_tests/tls_race.cc b/lib/tsan/lit_tests/tls_race.cc
new file mode 100644
index 000000000000..bed6aafaacfc
--- /dev/null
+++ b/lib/tsan/lit_tests/tls_race.cc
@@ -0,0 +1,19 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+
+void *Thread(void *a) {
+ *(int*)a = 43;
+ return 0;
+}
+
+int main() {
+ static __thread int Var = 42;
+ pthread_t t;
+ pthread_create(&t, 0, Thread, &Var);
+ Var = 43;
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is TLS of main thread.
diff --git a/lib/tsan/lit_tests/tls_race2.cc b/lib/tsan/lit_tests/tls_race2.cc
new file mode 100644
index 000000000000..110abaa6a9df
--- /dev/null
+++ b/lib/tsan/lit_tests/tls_race2.cc
@@ -0,0 +1,28 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stddef.h>
+#include <unistd.h>
+
+void *Thread2(void *a) {
+ *(int*)a = 43;
+ return 0;
+}
+
+void *Thread(void *a) {
+ static __thread int Var = 42;
+ pthread_t t;
+ pthread_create(&t, 0, Thread2, &Var);
+ Var = 42;
+ pthread_join(t, 0);
+ return 0;
+}
+
+int main() {
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ pthread_join(t, 0);
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Location is TLS of thread T1.
+
diff --git a/lib/tsan/lit_tests/user_fopen.cc b/lib/tsan/lit_tests/user_fopen.cc
new file mode 100644
index 000000000000..794d598719b4
--- /dev/null
+++ b/lib/tsan/lit_tests/user_fopen.cc
@@ -0,0 +1,34 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdio.h>
+#include <stdlib.h>
+
+// defined by tsan.
+extern "C" FILE *__interceptor_fopen(const char *file, const char *mode);
+extern "C" int __interceptor_fileno(FILE *f);
+
+extern "C" FILE *fopen(const char *file, const char *mode) {
+ static int first = 0;
+ if (__sync_lock_test_and_set(&first, 1) == 0)
+ printf("user fopen\n");
+ return __interceptor_fopen(file, mode);
+}
+
+extern "C" int fileno(FILE *f) {
+ static int first = 0;
+ if (__sync_lock_test_and_set(&first, 1) == 0)
+ printf("user fileno\n");
+ return 1;
+}
+
+int main() {
+ FILE *f = fopen("/dev/zero", "r");
+ if (f) {
+ char buf;
+ fread(&buf, 1, 1, f);
+ fclose(f);
+ }
+}
+
+// CHECK: user fopen
+// CHECK-NOT: ThreadSanitizer
+
diff --git a/lib/tsan/lit_tests/user_malloc.cc b/lib/tsan/lit_tests/user_malloc.cc
new file mode 100644
index 000000000000..0be6d54fb13a
--- /dev/null
+++ b/lib/tsan/lit_tests/user_malloc.cc
@@ -0,0 +1,27 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <stdio.h>
+
+// defined by tsan.
+extern "C" void *__interceptor_malloc(unsigned long size);
+extern "C" void __interceptor_free(void *p);
+
+extern "C" void *malloc(unsigned long size) {
+ static int first = 0;
+ if (__sync_lock_test_and_set(&first, 1) == 0)
+ printf("user malloc\n");
+ return __interceptor_malloc(size);
+}
+
+extern "C" void free(void *p) {
+ __interceptor_free(p);
+}
+
+int main() {
+ volatile char *p = (char*)malloc(10);
+ p[0] = 0;
+ free((void*)p);
+}
+
+// CHECK: user malloc
+// CHECK-NOT: ThreadSanitizer
+
diff --git a/lib/tsan/output_tests/virtual_inheritance_compile_bug.cc b/lib/tsan/lit_tests/virtual_inheritance_compile_bug.cc
index fd2febe895b4..2275b8b8d211 100644
--- a/lib/tsan/output_tests/virtual_inheritance_compile_bug.cc
+++ b/lib/tsan/lit_tests/virtual_inheritance_compile_bug.cc
@@ -1,10 +1,12 @@
// Regression test for http://code.google.com/p/thread-sanitizer/issues/detail?id=3.
// The C++ variant is much more compact that the LLVM IR equivalent.
+
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <stdio.h>
-struct AAA { virtual long aaa () { return 0; } };
-struct BBB: virtual AAA { unsigned long bbb; };
+struct AAA { virtual long aaa () { return 0; } }; // NOLINT
+struct BBB: virtual AAA { unsigned long bbb; }; // NOLINT
struct CCC: virtual AAA { };
-struct DDD: CCC, BBB { DDD (); };
+struct DDD: CCC, BBB { DDD(); }; // NOLINT
DDD::DDD() { }
int main() {
DDD d;
diff --git a/lib/tsan/output_tests/vptr_benign_race.cc b/lib/tsan/lit_tests/vptr_benign_race.cc
index fec4ffbb6bc0..8c9fc596e17b 100644
--- a/lib/tsan/output_tests/vptr_benign_race.cc
+++ b/lib/tsan/lit_tests/vptr_benign_race.cc
@@ -1,3 +1,4 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
diff --git a/lib/tsan/output_tests/vptr_harmful_race.cc b/lib/tsan/lit_tests/vptr_harmful_race.cc
index a19e6abc7bc3..f51ba7ee57f0 100644
--- a/lib/tsan/output_tests/vptr_harmful_race.cc
+++ b/lib/tsan/lit_tests/vptr_harmful_race.cc
@@ -1,3 +1,4 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
diff --git a/lib/tsan/lit_tests/write_in_reader_lock.cc b/lib/tsan/lit_tests/write_in_reader_lock.cc
new file mode 100644
index 000000000000..db8bac32b6e4
--- /dev/null
+++ b/lib/tsan/lit_tests/write_in_reader_lock.cc
@@ -0,0 +1,35 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <unistd.h>
+
+pthread_rwlock_t rwlock;
+int GLOB;
+
+void *Thread1(void *p) {
+ (void)p;
+ pthread_rwlock_rdlock(&rwlock);
+ // Write under reader lock.
+ sleep(1);
+ GLOB++;
+ pthread_rwlock_unlock(&rwlock);
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ pthread_rwlock_init(&rwlock, NULL);
+ pthread_rwlock_rdlock(&rwlock);
+ pthread_t t;
+ pthread_create(&t, 0, Thread1, 0);
+ volatile int x = GLOB;
+ (void)x;
+ pthread_rwlock_unlock(&rwlock);
+ pthread_join(t, 0);
+ pthread_rwlock_destroy(&rwlock);
+ return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: Write of size 4 at {{.*}} by thread T1{{.*}}:
+// CHECK: #0 Thread1(void*) {{.*}}write_in_reader_lock.cc:13
+// CHECK: Previous read of size 4 at {{.*}} by main thread{{.*}}:
+// CHECK: #0 main {{.*}}write_in_reader_lock.cc:23
diff --git a/lib/tsan/output_tests/simple_stack.c b/lib/tsan/output_tests/simple_stack.c
deleted file mode 100644
index 2e94f23f3c46..000000000000
--- a/lib/tsan/output_tests/simple_stack.c
+++ /dev/null
@@ -1,65 +0,0 @@
-#include <pthread.h>
-#include <stdio.h>
-#include <unistd.h>
-
-int Global;
-
-void __attribute__((noinline)) foo1() {
- Global = 42;
-}
-
-void __attribute__((noinline)) bar1() {
- volatile int tmp = 42; (void)tmp;
- foo1();
-}
-
-void __attribute__((noinline)) foo2() {
- volatile int v = Global; (void)v;
-}
-
-void __attribute__((noinline)) bar2() {
- volatile int tmp = 42; (void)tmp;
- foo2();
-}
-
-void *Thread1(void *x) {
- usleep(1000000);
- bar1();
- return NULL;
-}
-
-void *Thread2(void *x) {
- bar2();
- return NULL;
-}
-
-void StartThread(pthread_t *t, void *(*f)(void*)) {
- pthread_create(t, NULL, f, NULL);
-}
-
-int main() {
- pthread_t t[2];
- StartThread(&t[0], Thread1);
- StartThread(&t[1], Thread2);
- pthread_join(t[0], NULL);
- pthread_join(t[1], NULL);
- return 0;
-}
-
-// CHECK: WARNING: ThreadSanitizer: data race
-// CHECK-NEXT: Write of size 4 at {{.*}} by thread 1:
-// CHECK-NEXT: #0 foo1 {{.*}}simple_stack.c:8{{(:3)?}} ({{.*}})
-// CHECK-NEXT: #1 bar1 {{.*}}simple_stack.c:13{{(:3)?}} ({{.*}})
-// CHECK-NEXT: #2 Thread1 {{.*}}simple_stack.c:27{{(:3)?}} ({{.*}})
-// CHECK-NEXT: Previous read of size 4 at {{.*}} by thread 2:
-// CHECK-NEXT: #0 foo2 {{.*}}simple_stack.c:17{{(:26)?}} ({{.*}})
-// CHECK-NEXT: #1 bar2 {{.*}}simple_stack.c:22{{(:3)?}} ({{.*}})
-// CHECK-NEXT: #2 Thread2 {{.*}}simple_stack.c:32{{(:3)?}} ({{.*}})
-// CHECK-NEXT: Thread 1 (running) created at:
-// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}})
-// CHECK-NEXT: #1 StartThread {{.*}}simple_stack.c:37{{(:3)?}} ({{.*}})
-// CHECK-NEXT: #2 main {{.*}}simple_stack.c:42{{(:3)?}} ({{.*}})
-// CHECK-NEXT: Thread 2 ({{.*}}) created at:
-// CHECK-NEXT: #0 pthread_create {{.*}} ({{.*}})
-// CHECK-NEXT: #1 StartThread {{.*}}simple_stack.c:37{{(:3)?}} ({{.*}})
-// CHECK-NEXT: #2 main {{.*}}simple_stack.c:43{{(:3)?}} ({{.*}})
diff --git a/lib/tsan/output_tests/simple_stack2.cc b/lib/tsan/output_tests/simple_stack2.cc
deleted file mode 100644
index 336cc9ff599d..000000000000
--- a/lib/tsan/output_tests/simple_stack2.cc
+++ /dev/null
@@ -1,46 +0,0 @@
-#include <pthread.h>
-#include <stdio.h>
-#include <unistd.h>
-
-int Global;
-
-void __attribute__((noinline)) foo1() {
- Global = 42;
-}
-
-void __attribute__((noinline)) bar1() {
- volatile int tmp = 42; int tmp2 = tmp; (void)tmp2;
- foo1();
-}
-
-void __attribute__((noinline)) foo2() {
- volatile int tmp = Global; int tmp2 = tmp; (void)tmp2;
-}
-
-void __attribute__((noinline)) bar2() {
- volatile int tmp = 42; int tmp2 = tmp; (void)tmp2;
- foo2();
-}
-
-void *Thread1(void *x) {
- usleep(1000000);
- bar1();
- return NULL;
-}
-
-int main() {
- pthread_t t;
- pthread_create(&t, NULL, Thread1, NULL);
- bar2();
- pthread_join(t, NULL);
-}
-
-// CHECK: WARNING: ThreadSanitizer: data race
-// CHECK-NEXT: Write of size 4 at {{.*}} by thread 1:
-// CHECK-NEXT: #0 foo1{{.*}} {{.*}}simple_stack2.cc:8{{(:3)?}} ({{.*}})
-// CHECK-NEXT: #1 bar1{{.*}} {{.*}}simple_stack2.cc:13{{(:3)?}} ({{.*}})
-// CHECK-NEXT: #2 Thread1{{.*}} {{.*}}simple_stack2.cc:27{{(:3)?}} ({{.*}})
-// CHECK-NEXT: Previous read of size 4 at {{.*}} by main thread:
-// CHECK-NEXT: #0 foo2{{.*}} {{.*}}simple_stack2.cc:17{{(:28)?}} ({{.*}})
-// CHECK-NEXT: #1 bar2{{.*}} {{.*}}simple_stack2.cc:22{{(:3)?}} ({{.*}})
-// CHECK-NEXT: #2 main{{.*}} {{.*}}simple_stack2.cc:34{{(:3)?}} ({{.*}})
diff --git a/lib/tsan/rtl/CMakeLists.txt b/lib/tsan/rtl/CMakeLists.txt
new file mode 100644
index 000000000000..d91e2e43ca4c
--- /dev/null
+++ b/lib/tsan/rtl/CMakeLists.txt
@@ -0,0 +1,58 @@
+set(TSAN_SOURCES
+ tsan_clock.cc
+ tsan_flags.cc
+ tsan_fd.cc
+ tsan_interceptors.cc
+ tsan_interface_ann.cc
+ tsan_interface_atomic.cc
+ tsan_interface.cc
+ tsan_interface_java.cc
+ tsan_md5.cc
+ tsan_mman.cc
+ tsan_mutex.cc
+ tsan_mutexset.cc
+ tsan_report.cc
+ tsan_rtl.cc
+ tsan_rtl_mutex.cc
+ tsan_rtl_report.cc
+ tsan_rtl_thread.cc
+ tsan_stat.cc
+ tsan_suppressions.cc
+ tsan_symbolize.cc
+ tsan_sync.cc
+ )
+
+if(APPLE)
+ list(APPEND TSAN_SOURCES tsan_platform_mac.cc)
+elseif(UNIX)
+ # Assume Linux
+ list(APPEND TSAN_SOURCES
+ tsan_platform_linux.cc
+ tsan_symbolize_addr2line_linux.cc)
+endif()
+
+set(TSAN_RUNTIME_LIBRARIES)
+# TSan is currently supported on 64-bit Linux only.
+if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE)
+ set(TSAN_ASM_SOURCES tsan_rtl_amd64.S)
+ # Pass ASM file directly to the C++ compiler.
+ set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES
+ LANGUAGE C
+ )
+ add_library(clang_rt.tsan-x86_64 STATIC
+ ${TSAN_SOURCES}
+ ${TSAN_ASM_SOURCES}
+ $<TARGET_OBJECTS:RTInterception.x86_64>
+ $<TARGET_OBJECTS:RTSanitizerCommon.x86_64>
+ )
+ set_target_compile_flags(clang_rt.tsan-x86_64
+ ${TSAN_CFLAGS} ${TARGET_x86_64_CFLAGS}
+ )
+ list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-x86_64)
+endif()
+
+if(TSAN_RUNTIME_LIBRARIES)
+ set_property(TARGET ${TSAN_RUNTIME_LIBRARIES} APPEND PROPERTY
+ COMPILE_DEFINITIONS ${TSAN_COMMON_DEFINITIONS})
+ add_clang_compiler_rt_libraries(${TSAN_RUNTIME_LIBRARIES})
+endif()
diff --git a/lib/tsan/rtl/Makefile.mk b/lib/tsan/rtl/Makefile.mk
index d5d6327b5435..a6a7fc8b86e8 100644
--- a/lib/tsan/rtl/Makefile.mk
+++ b/lib/tsan/rtl/Makefile.mk
@@ -18,6 +18,8 @@ Implementation := Generic
# FIXME: use automatic dependencies?
Dependencies := $(wildcard $(Dir)/*.h)
+Dependencies += $(wildcard $(Dir)/../../interception/*.h)
+Dependencies += $(wildcard $(Dir)/../../interception/mach_override/*.h)
# Define a convenience variable for all the tsan functions.
TsanFunctions += $(Sources:%.cc=%) $(AsmSources:%.S=%)
diff --git a/lib/tsan/rtl/Makefile.old b/lib/tsan/rtl/Makefile.old
index 9b79f576d4aa..f522ec6b47d7 100644
--- a/lib/tsan/rtl/Makefile.old
+++ b/lib/tsan/rtl/Makefile.old
@@ -1,12 +1,15 @@
-CXXFLAGS = -fPIE -g -Wall -Werror -fno-builtin -DTSAN_DEBUG=$(DEBUG)
+CXXFLAGS = -fPIE -g -Wall -Werror -fno-builtin -DTSAN_DEBUG=$(DEBUG) -DSANITIZER_DEBUG=$(DEBUG)
ifeq ($(DEBUG), 0)
- CXXFLAGS += -O3
+ CXXFLAGS += -O3
+endif
+ifeq ($(CXX), clang++)
+ CXXFLAGS+= -Wgnu
endif
# For interception. FIXME: move interception one level higher.
INTERCEPTION=../../interception
COMMON=../../sanitizer_common
-INCLUDES= -I../..
+INCLUDES= -I../.. -I../../../include
EXTRA_CXXFLAGS=-fno-exceptions
NO_SYSROOT=--sysroot=.
CXXFLAGS+=$(EXTRA_CXXFLAGS)
diff --git a/lib/tsan/rtl/tsan_clock.cc b/lib/tsan/rtl/tsan_clock.cc
index 32ed91dc2103..f8745ec3200c 100644
--- a/lib/tsan/rtl/tsan_clock.cc
+++ b/lib/tsan/rtl/tsan_clock.cc
@@ -105,13 +105,6 @@ void ThreadClock::acq_rel(SyncClock *dst) {
release(dst);
}
-void ThreadClock::Disable(unsigned tid) {
- u64 c0 = clk_[tid];
- for (uptr i = 0; i < kMaxTidInClock; i++)
- clk_[i] = (u64)-1;
- clk_[tid] = c0;
-}
-
SyncClock::SyncClock()
: clk_(MBlockClock) {
}
diff --git a/lib/tsan/rtl/tsan_clock.h b/lib/tsan/rtl/tsan_clock.h
index 02ddb9abdb30..0ee93749b881 100644
--- a/lib/tsan/rtl/tsan_clock.h
+++ b/lib/tsan/rtl/tsan_clock.h
@@ -61,8 +61,6 @@ struct ThreadClock {
nclk_ = tid + 1;
}
- void Disable(unsigned tid);
-
uptr size() const {
return nclk_;
}
diff --git a/lib/tsan/rtl/tsan_defs.h b/lib/tsan/rtl/tsan_defs.h
index ca8f0aecc83a..e0c04733f0a3 100644
--- a/lib/tsan/rtl/tsan_defs.h
+++ b/lib/tsan/rtl/tsan_defs.h
@@ -24,31 +24,45 @@
namespace __tsan {
+#ifdef TSAN_GO
+const bool kGoMode = true;
+const bool kCppMode = false;
+const char *const kTsanOptionsEnv = "GORACE";
+#else
+const bool kGoMode = false;
+const bool kCppMode = true;
+const char *const kTsanOptionsEnv = "TSAN_OPTIONS";
+#endif
+
const int kTidBits = 13;
const unsigned kMaxTid = 1 << kTidBits;
const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit.
const int kClkBits = 43;
#ifndef TSAN_GO
-const int kShadowStackSize = 1024;
+const int kShadowStackSize = 4 * 1024;
+const int kTraceStackSize = 256;
#endif
#ifdef TSAN_SHADOW_COUNT
# if TSAN_SHADOW_COUNT == 2 \
|| TSAN_SHADOW_COUNT == 4 || TSAN_SHADOW_COUNT == 8
-const unsigned kShadowCnt = TSAN_SHADOW_COUNT;
+const uptr kShadowCnt = TSAN_SHADOW_COUNT;
# else
# error "TSAN_SHADOW_COUNT must be one of 2,4,8"
# endif
#else
// Count of shadow values in a shadow cell.
-const unsigned kShadowCnt = 8;
+const uptr kShadowCnt = 4;
#endif
// That many user bytes are mapped onto a single shadow cell.
-const unsigned kShadowCell = 8;
+const uptr kShadowCell = 8;
// Size of a single shadow value (u64).
-const unsigned kShadowSize = 8;
+const uptr kShadowSize = 8;
+
+// Shadow memory is kShadowMultiplier times larger than user memory.
+const uptr kShadowMultiplier = kShadowSize * kShadowCnt / kShadowCell;
#if defined(TSAN_COLLECT_STATS) && TSAN_COLLECT_STATS
const bool kCollectStats = true;
@@ -114,11 +128,23 @@ T max(T a, T b) {
}
template<typename T>
-T RoundUp(T p, int align) {
+T RoundUp(T p, u64 align) {
DCHECK_EQ(align & (align - 1), 0);
return (T)(((u64)p + align - 1) & ~(align - 1));
}
+template<typename T>
+T RoundDown(T p, u64 align) {
+ DCHECK_EQ(align & (align - 1), 0);
+ return (T)((u64)p & ~(align - 1));
+}
+
+// Zeroizes high part, returns 'bits' lsb bits.
+template<typename T>
+T GetLsb(T v, int bits) {
+ return (T)((u64)v & ((1ull << bits) - 1));
+}
+
struct MD5Hash {
u64 hash[2];
bool operator==(const MD5Hash &other) const;
@@ -133,6 +159,7 @@ struct ReportStack;
class ReportDesc;
class RegionAlloc;
class StackTrace;
+struct MBlock;
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_fd.cc b/lib/tsan/rtl/tsan_fd.cc
new file mode 100644
index 000000000000..ef375a4d98f6
--- /dev/null
+++ b/lib/tsan/rtl/tsan_fd.cc
@@ -0,0 +1,265 @@
+//===-- tsan_fd.cc --------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tsan_fd.h"
+#include "tsan_rtl.h"
+#include <sanitizer_common/sanitizer_atomic.h>
+
+namespace __tsan {
+
+const int kTableSizeL1 = 1024;
+const int kTableSizeL2 = 1024;
+const int kTableSize = kTableSizeL1 * kTableSizeL2;
+
+struct FdSync {
+ atomic_uint64_t rc;
+};
+
+struct FdDesc {
+ FdSync *sync;
+ int creation_tid;
+ u32 creation_stack;
+};
+
+struct FdContext {
+ atomic_uintptr_t tab[kTableSizeL1];
+ // Addresses used for synchronization.
+ FdSync globsync;
+ FdSync filesync;
+ FdSync socksync;
+ u64 connectsync;
+};
+
+static FdContext fdctx;
+
+static FdSync *allocsync() {
+ FdSync *s = (FdSync*)internal_alloc(MBlockFD, sizeof(FdSync));
+ atomic_store(&s->rc, 1, memory_order_relaxed);
+ return s;
+}
+
+static FdSync *ref(FdSync *s) {
+ if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1)
+ atomic_fetch_add(&s->rc, 1, memory_order_relaxed);
+ return s;
+}
+
+static void unref(ThreadState *thr, uptr pc, FdSync *s) {
+ if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) {
+ if (atomic_fetch_sub(&s->rc, 1, memory_order_acq_rel) == 1) {
+ CHECK_NE(s, &fdctx.globsync);
+ CHECK_NE(s, &fdctx.filesync);
+ CHECK_NE(s, &fdctx.socksync);
+ SyncVar *v = CTX()->synctab.GetAndRemove(thr, pc, (uptr)s);
+ if (v)
+ DestroyAndFree(v);
+ internal_free(s);
+ }
+ }
+}
+
+static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) {
+ CHECK_LT(fd, kTableSize);
+ atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2];
+ uptr l1 = atomic_load(pl1, memory_order_consume);
+ if (l1 == 0) {
+ uptr size = kTableSizeL2 * sizeof(FdDesc);
+ void *p = internal_alloc(MBlockFD, size);
+ internal_memset(p, 0, size);
+ MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size);
+ if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel))
+ l1 = (uptr)p;
+ else
+ internal_free(p);
+ }
+ return &((FdDesc*)l1)[fd % kTableSizeL2]; // NOLINT
+}
+
+// pd must be already ref'ed.
+static void init(ThreadState *thr, uptr pc, int fd, FdSync *s) {
+ FdDesc *d = fddesc(thr, pc, fd);
+ // As a matter of fact, we don't intercept all close calls.
+ // See e.g. libc __res_iclose().
+ if (d->sync) {
+ unref(thr, pc, d->sync);
+ d->sync = 0;
+ }
+ if (flags()->io_sync == 0) {
+ unref(thr, pc, s);
+ } else if (flags()->io_sync == 1) {
+ d->sync = s;
+ } else if (flags()->io_sync == 2) {
+ unref(thr, pc, s);
+ d->sync = &fdctx.globsync;
+ }
+ d->creation_tid = thr->tid;
+ d->creation_stack = CurrentStackId(thr, pc);
+ // To catch races between fd usage and open.
+ MemoryRangeImitateWrite(thr, pc, (uptr)d, 8);
+}
+
+void FdInit() {
+ atomic_store(&fdctx.globsync.rc, (u64)-1, memory_order_relaxed);
+ atomic_store(&fdctx.filesync.rc, (u64)-1, memory_order_relaxed);
+ atomic_store(&fdctx.socksync.rc, (u64)-1, memory_order_relaxed);
+}
+
+void FdOnFork(ThreadState *thr, uptr pc) {
+ // On fork() we need to reset all fd's, because the child is going
+ // close all them, and that will cause races between previous read/write
+ // and the close.
+ for (int l1 = 0; l1 < kTableSizeL1; l1++) {
+ FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
+ if (tab == 0)
+ break;
+ for (int l2 = 0; l2 < kTableSizeL2; l2++) {
+ FdDesc *d = &tab[l2];
+ MemoryResetRange(thr, pc, (uptr)d, 8);
+ }
+ }
+}
+
+bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) {
+ for (int l1 = 0; l1 < kTableSizeL1; l1++) {
+ FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
+ if (tab == 0)
+ break;
+ if (addr >= (uptr)tab && addr < (uptr)(tab + kTableSizeL2)) {
+ int l2 = (addr - (uptr)tab) / sizeof(FdDesc);
+ FdDesc *d = &tab[l2];
+ *fd = l1 * kTableSizeL1 + l2;
+ *tid = d->creation_tid;
+ *stack = d->creation_stack;
+ return true;
+ }
+ }
+ return false;
+}
+
+void FdAcquire(ThreadState *thr, uptr pc, int fd) {
+ FdDesc *d = fddesc(thr, pc, fd);
+ FdSync *s = d->sync;
+ DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s);
+ MemoryRead8Byte(thr, pc, (uptr)d);
+ if (s)
+ Acquire(thr, pc, (uptr)s);
+}
+
+void FdRelease(ThreadState *thr, uptr pc, int fd) {
+ FdDesc *d = fddesc(thr, pc, fd);
+ FdSync *s = d->sync;
+ DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s);
+ if (s)
+ Release(thr, pc, (uptr)s);
+ MemoryRead8Byte(thr, pc, (uptr)d);
+}
+
+void FdAccess(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd);
+ FdDesc *d = fddesc(thr, pc, fd);
+ MemoryRead8Byte(thr, pc, (uptr)d);
+}
+
+void FdClose(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdClose(%d)\n", thr->tid, fd);
+ FdDesc *d = fddesc(thr, pc, fd);
+ // To catch races between fd usage and close.
+ MemoryWrite8Byte(thr, pc, (uptr)d);
+ // We need to clear it, because if we do not intercept any call out there
+ // that creates fd, we will hit false postives.
+ MemoryResetRange(thr, pc, (uptr)d, 8);
+ unref(thr, pc, d->sync);
+ d->sync = 0;
+ d->creation_tid = 0;
+ d->creation_stack = 0;
+}
+
+void FdFileCreate(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd);
+ init(thr, pc, fd, &fdctx.filesync);
+}
+
+void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) {
+ DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd);
+ // Ignore the case when user dups not yet connected socket.
+ FdDesc *od = fddesc(thr, pc, oldfd);
+ MemoryRead8Byte(thr, pc, (uptr)od);
+ FdClose(thr, pc, newfd);
+ init(thr, pc, newfd, ref(od->sync));
+}
+
+void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) {
+ DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd);
+ FdSync *s = allocsync();
+ init(thr, pc, rfd, ref(s));
+ init(thr, pc, wfd, ref(s));
+ unref(thr, pc, s);
+}
+
+void FdEventCreate(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd);
+ init(thr, pc, fd, allocsync());
+}
+
+void FdSignalCreate(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd);
+ init(thr, pc, fd, 0);
+}
+
+void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd);
+ init(thr, pc, fd, 0);
+}
+
+void FdPollCreate(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd);
+ init(thr, pc, fd, allocsync());
+}
+
+void FdSocketCreate(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd);
+ // It can be a UDP socket.
+ init(thr, pc, fd, &fdctx.socksync);
+}
+
+void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) {
+ DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd);
+ // Synchronize connect->accept.
+ Acquire(thr, pc, (uptr)&fdctx.connectsync);
+ init(thr, pc, newfd, &fdctx.socksync);
+}
+
+void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd);
+ // Synchronize connect->accept.
+ Release(thr, pc, (uptr)&fdctx.connectsync);
+}
+
+void FdSocketConnect(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd);
+ init(thr, pc, fd, &fdctx.socksync);
+}
+
+uptr File2addr(char *path) {
+ (void)path;
+ static u64 addr;
+ return (uptr)&addr;
+}
+
+uptr Dir2addr(char *path) {
+ (void)path;
+ static u64 addr;
+ return (uptr)&addr;
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_fd.h b/lib/tsan/rtl/tsan_fd.h
new file mode 100644
index 000000000000..979198e2e74d
--- /dev/null
+++ b/lib/tsan/rtl/tsan_fd.h
@@ -0,0 +1,65 @@
+//===-- tsan_fd.h -----------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// This file handles synchronization via IO.
+// People use IO for synchronization along the lines of:
+//
+// int X;
+// int client_socket; // initialized elsewhere
+// int server_socket; // initialized elsewhere
+//
+// Thread 1:
+// X = 42;
+// send(client_socket, ...);
+//
+// Thread 2:
+// if (recv(server_socket, ...) > 0)
+// assert(X == 42);
+//
+// This file determines the scope of the file descriptor (pipe, socket,
+// all local files, etc) and executes acquire and release operations on
+// the scope as necessary. Some scopes are very fine grained (e.g. pipe
+// operations synchronize only with operations on the same pipe), while
+// others are corse-grained (e.g. all operations on local files synchronize
+// with each other).
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_FD_H
+#define TSAN_FD_H
+
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+void FdInit();
+void FdAcquire(ThreadState *thr, uptr pc, int fd);
+void FdRelease(ThreadState *thr, uptr pc, int fd);
+void FdAccess(ThreadState *thr, uptr pc, int fd);
+void FdClose(ThreadState *thr, uptr pc, int fd);
+void FdFileCreate(ThreadState *thr, uptr pc, int fd);
+void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd);
+void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd);
+void FdEventCreate(ThreadState *thr, uptr pc, int fd);
+void FdSignalCreate(ThreadState *thr, uptr pc, int fd);
+void FdInotifyCreate(ThreadState *thr, uptr pc, int fd);
+void FdPollCreate(ThreadState *thr, uptr pc, int fd);
+void FdSocketCreate(ThreadState *thr, uptr pc, int fd);
+void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd);
+void FdSocketConnecting(ThreadState *thr, uptr pc, int fd);
+void FdSocketConnect(ThreadState *thr, uptr pc, int fd);
+bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack);
+void FdOnFork(ThreadState *thr, uptr pc);
+
+uptr File2addr(char *path);
+uptr Dir2addr(char *path);
+
+} // namespace __tsan
+
+#endif // TSAN_INTERFACE_H
diff --git a/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc
index 8f91939db1de..88c4bb6a2e44 100644
--- a/lib/tsan/rtl/tsan_flags.cc
+++ b/lib/tsan/rtl/tsan_flags.cc
@@ -27,6 +27,7 @@ Flags *flags() {
#ifdef TSAN_EXTERNAL_HOOKS
void OverrideFlags(Flags *f);
#else
+SANITIZER_INTERFACE_ATTRIBUTE
void WEAK OverrideFlags(Flags *f) {
(void)f;
}
@@ -39,20 +40,25 @@ void InitializeFlags(Flags *f, const char *env) {
f->enable_annotations = true;
f->suppress_equal_stacks = true;
f->suppress_equal_addresses = true;
+ f->suppress_java = false;
+ f->report_bugs = true;
f->report_thread_leaks = true;
+ f->report_destroy_locked = true;
f->report_signal_unsafe = true;
f->force_seq_cst_atomics = false;
f->strip_path_prefix = "";
f->suppressions = "";
f->exitcode = 66;
- f->log_fileno = 2;
+ f->log_path = "stderr";
f->atexit_sleep_ms = 1000;
f->verbosity = 0;
f->profile_memory = "";
f->flush_memory_ms = 0;
f->stop_on_start = false;
f->running_on_valgrind = false;
- f->use_internal_symbolizer = false;
+ f->external_symbolizer_path = "";
+ f->history_size = kGoMode ? 1 : 2; // There are a lot of goroutines in Go.
+ f->io_sync = 1;
// Let a frontend override.
OverrideFlags(f);
@@ -61,19 +67,42 @@ void InitializeFlags(Flags *f, const char *env) {
ParseFlag(env, &f->enable_annotations, "enable_annotations");
ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks");
ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses");
+ ParseFlag(env, &f->suppress_java, "suppress_java");
+ ParseFlag(env, &f->report_bugs, "report_bugs");
ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks");
+ ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked");
ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe");
ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics");
ParseFlag(env, &f->strip_path_prefix, "strip_path_prefix");
ParseFlag(env, &f->suppressions, "suppressions");
ParseFlag(env, &f->exitcode, "exitcode");
- ParseFlag(env, &f->log_fileno, "log_fileno");
+ ParseFlag(env, &f->log_path, "log_path");
ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms");
ParseFlag(env, &f->verbosity, "verbosity");
ParseFlag(env, &f->profile_memory, "profile_memory");
ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms");
ParseFlag(env, &f->stop_on_start, "stop_on_start");
- ParseFlag(env, &f->use_internal_symbolizer, "use_internal_symbolizer");
+ ParseFlag(env, &f->external_symbolizer_path, "external_symbolizer_path");
+ ParseFlag(env, &f->history_size, "history_size");
+ ParseFlag(env, &f->io_sync, "io_sync");
+
+ if (!f->report_bugs) {
+ f->report_thread_leaks = false;
+ f->report_destroy_locked = false;
+ f->report_signal_unsafe = false;
+ }
+
+ if (f->history_size < 0 || f->history_size > 7) {
+ Printf("ThreadSanitizer: incorrect value for history_size"
+ " (must be [0..7])\n");
+ Die();
+ }
+
+ if (f->io_sync < 0 || f->io_sync > 2) {
+ Printf("ThreadSanitizer: incorrect value for io_sync"
+ " (must be [0..2])\n");
+ Die();
+ }
}
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_flags.h b/lib/tsan/rtl/tsan_flags.h
index c22132f2d32f..6547911ec7a3 100644
--- a/lib/tsan/rtl/tsan_flags.h
+++ b/lib/tsan/rtl/tsan_flags.h
@@ -31,8 +31,15 @@ struct Flags {
// Supress a race report if we've already output another race report
// on the same address.
bool suppress_equal_addresses;
+ // Suppress weird race reports that can be seen if JVM is embed
+ // into the process.
+ bool suppress_java;
+ // Turns off bug reporting entirely (useful for benchmarking).
+ bool report_bugs;
// Report thread leaks at exit?
bool report_thread_leaks;
+ // Report destruction of a locked mutex?
+ bool report_destroy_locked;
// Report violations of async signal-safety
// (e.g. malloc() call from a signal handler).
bool report_signal_unsafe;
@@ -45,8 +52,10 @@ struct Flags {
const char *suppressions;
// Override exit status if something was reported.
int exitcode;
- // Log fileno (1 - stdout, 2 - stderr).
- int log_fileno;
+ // Write logs to "log_path.pid".
+ // The special values are "stdout" and "stderr".
+ // The default is "stderr".
+ const char *log_path;
// Sleep in main thread before exiting for that many ms
// (useful to catch "at exit" races).
int atexit_sleep_ms;
@@ -60,8 +69,19 @@ struct Flags {
bool stop_on_start;
// Controls whether RunningOnValgrind() returns true or false.
bool running_on_valgrind;
- // If set, uses in-process symbolizer from common sanitizer runtime.
- bool use_internal_symbolizer;
+ // Path to external symbolizer.
+ const char *external_symbolizer_path;
+ // Per-thread history size, controls how many previous memory accesses
+ // are remembered per thread. Possible values are [0..7].
+ // history_size=0 amounts to 32K memory accesses. Each next value doubles
+ // the amount of memory accesses, up to history_size=7 that amounts to
+ // 4M memory accesses. The default value is 2 (128K memory accesses).
+ int history_size;
+ // Controls level of synchronization implied by IO operations.
+ // 0 - no synchronization
+ // 1 - reasonable level of synchronization (write->read)
+ // 2 - global synchronization of all IO operations
+ int io_sync;
};
Flags *flags();
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
index a962250568b7..be58ca92cf91 100644
--- a/lib/tsan/rtl/tsan_interceptors.cc
+++ b/lib/tsan/rtl/tsan_interceptors.cc
@@ -1,4 +1,4 @@
-//===-- tsan_interceptors_linux.cc ----------------------------------------===//
+//===-- tsan_interceptors.cc ----------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -9,16 +9,20 @@
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
+// FIXME: move as many interceptors as possible into
+// sanitizer_common/sanitizer_common_interceptors.h
//===----------------------------------------------------------------------===//
-#include "interception/interception.h"
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_placement_new.h"
-#include "tsan_rtl.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "interception/interception.h"
#include "tsan_interface.h"
#include "tsan_platform.h"
+#include "tsan_rtl.h"
#include "tsan_mman.h"
+#include "tsan_fd.h"
using namespace __tsan; // NOLINT
@@ -51,7 +55,7 @@ extern "C" void *pthread_self();
extern "C" void _exit(int status);
extern "C" int __cxa_atexit(void (*func)(void *arg), void *arg, void *dso);
extern "C" int *__errno_location();
-extern "C" int usleep(unsigned usec);
+extern "C" int fileno_unlocked(void *stream);
const int PTHREAD_MUTEX_RECURSIVE = 1;
const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
const int kPthreadAttrSize = 56;
@@ -69,6 +73,12 @@ const int PTHREAD_BARRIER_SERIAL_THREAD = -1;
const int MAP_FIXED = 0x10;
typedef long long_t; // NOLINT
+// From /usr/include/unistd.h
+# define F_ULOCK 0 /* Unlock a previously locked region. */
+# define F_LOCK 1 /* Lock a region for exclusive use. */
+# define F_TLOCK 2 /* Test and lock a region for exclusive use. */
+# define F_TEST 3 /* Test a region for other processes locks. */
+
typedef void (*sighandler_t)(int sig);
#define errno (*__errno_location())
@@ -94,6 +104,10 @@ const sighandler_t SIG_ERR = (sighandler_t)-1;
const int SA_SIGINFO = 4;
const int SIG_SETMASK = 2;
+namespace std {
+struct nothrow_t {};
+} // namespace std
+
static sigaction_t sigactions[kSigCount];
namespace __tsan {
@@ -105,6 +119,7 @@ struct SignalDesc {
};
struct SignalContext {
+ int in_blocking_func;
int int_signal_send;
int pending_signal_count;
SignalDesc pending_signals[kSigCount];
@@ -115,10 +130,8 @@ static SignalContext *SigCtx(ThreadState *thr) {
SignalContext *ctx = (SignalContext*)thr->signal_ctx;
if (ctx == 0 && thr->is_alive) {
ScopedInRtl in_rtl;
- ctx = (SignalContext*)internal_alloc(
- MBlockSignal, sizeof(*ctx));
- MemoryResetRange(thr, 0, (uptr)ctx, sizeof(*ctx));
- internal_memset(ctx, 0, sizeof(*ctx));
+ ctx = (SignalContext*)MmapOrDie(sizeof(*ctx), "SignalContext");
+ MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx));
thr->signal_ctx = ctx;
}
return ctx;
@@ -126,60 +139,55 @@ static SignalContext *SigCtx(ThreadState *thr) {
static unsigned g_thread_finalize_key;
-static void process_pending_signals(ThreadState *thr);
-
class ScopedInterceptor {
public:
- ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc)
- : thr_(thr)
- , in_rtl_(thr->in_rtl) {
- if (thr_->in_rtl == 0) {
- Initialize(thr);
- FuncEntry(thr, pc);
- thr_->in_rtl++;
- DPrintf("#%d: intercept %s()\n", thr_->tid, fname);
- } else {
- thr_->in_rtl++;
- }
- }
-
- ~ScopedInterceptor() {
- thr_->in_rtl--;
- if (thr_->in_rtl == 0) {
- FuncExit(thr_);
- process_pending_signals(thr_);
- }
- CHECK_EQ(in_rtl_, thr_->in_rtl);
- }
-
+ ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc);
+ ~ScopedInterceptor();
private:
ThreadState *const thr_;
const int in_rtl_;
};
+ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname,
+ uptr pc)
+ : thr_(thr)
+ , in_rtl_(thr->in_rtl) {
+ if (thr_->in_rtl == 0) {
+ Initialize(thr);
+ FuncEntry(thr, pc);
+ thr_->in_rtl++;
+ DPrintf("#%d: intercept %s()\n", thr_->tid, fname);
+ } else {
+ thr_->in_rtl++;
+ }
+}
+
+ScopedInterceptor::~ScopedInterceptor() {
+ thr_->in_rtl--;
+ if (thr_->in_rtl == 0) {
+ FuncExit(thr_);
+ ProcessPendingSignals(thr_);
+ }
+ CHECK_EQ(in_rtl_, thr_->in_rtl);
+}
+
#define SCOPED_INTERCEPTOR_RAW(func, ...) \
ThreadState *thr = cur_thread(); \
StatInc(thr, StatInterceptor); \
StatInc(thr, StatInt_##func); \
- ScopedInterceptor si(thr, #func, \
- (__sanitizer::uptr)__builtin_return_address(0)); \
- const uptr pc = (uptr)&func; \
+ const uptr caller_pc = GET_CALLER_PC(); \
+ ScopedInterceptor si(thr, #func, caller_pc); \
+ const uptr pc = __sanitizer::StackTrace::GetPreviousInstructionPc( \
+ __sanitizer::StackTrace::GetCurrentPc()); \
(void)pc; \
/**/
#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
- if (thr->in_rtl > 1) \
- return REAL(func)(__VA_ARGS__); \
-/**/
-
-#define SCOPED_INTERCEPTOR_LIBC(func, ...) \
- ThreadState *thr = cur_thread(); \
- StatInc(thr, StatInterceptor); \
- StatInc(thr, StatInt_##func); \
- ScopedInterceptor si(thr, #func, callpc); \
- const uptr pc = (uptr)&func; \
- (void)pc; \
+ if (REAL(func) == 0) { \
+ Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \
+ Die(); \
+ } \
if (thr->in_rtl > 1) \
return REAL(func)(__VA_ARGS__); \
/**/
@@ -187,30 +195,40 @@ class ScopedInterceptor {
#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
-// May be overriden by front-end.
-extern "C" void WEAK __tsan_malloc_hook(void *ptr, uptr size) {
- (void)ptr;
- (void)size;
-}
+#define BLOCK_REAL(name) (BlockingCall(thr), REAL(name))
-extern "C" void WEAK __tsan_free_hook(void *ptr) {
- (void)ptr;
+struct BlockingCall {
+ explicit BlockingCall(ThreadState *thr)
+ : ctx(SigCtx(thr)) {
+ ctx->in_blocking_func++;
+ }
+
+ ~BlockingCall() {
+ ctx->in_blocking_func--;
+ }
+
+ SignalContext *ctx;
+};
+
+TSAN_INTERCEPTOR(unsigned, sleep, unsigned sec) {
+ SCOPED_TSAN_INTERCEPTOR(sleep, sec);
+ unsigned res = BLOCK_REAL(sleep)(sec);
+ AfterSleep(thr, pc);
+ return res;
}
-static void invoke_malloc_hook(void *ptr, uptr size) {
- Context *ctx = CTX();
- ThreadState *thr = cur_thread();
- if (ctx == 0 || !ctx->initialized || thr->in_rtl)
- return;
- __tsan_malloc_hook(ptr, size);
+TSAN_INTERCEPTOR(int, usleep, long_t usec) {
+ SCOPED_TSAN_INTERCEPTOR(usleep, usec);
+ int res = BLOCK_REAL(usleep)(usec);
+ AfterSleep(thr, pc);
+ return res;
}
-static void invoke_free_hook(void *ptr) {
- Context *ctx = CTX();
- ThreadState *thr = cur_thread();
- if (ctx == 0 || !ctx->initialized || thr->in_rtl)
- return;
- __tsan_free_hook(ptr);
+TSAN_INTERCEPTOR(int, nanosleep, void *req, void *rem) {
+ SCOPED_TSAN_INTERCEPTOR(nanosleep, req, rem);
+ int res = BLOCK_REAL(nanosleep)(req, rem);
+ AfterSleep(thr, pc);
+ return res;
}
class AtExitContext {
@@ -269,7 +287,6 @@ static void finalize(void *arg) {
{
ScopedInRtl in_rtl;
DestroyAndFree(atexit_ctx);
- usleep(flags()->atexit_sleep_ms * 1000);
}
int status = Finalize(cur_thread());
if (status)
@@ -279,45 +296,20 @@ static void finalize(void *arg) {
TSAN_INTERCEPTOR(int, atexit, void (*f)()) {
SCOPED_TSAN_INTERCEPTOR(atexit, f);
return atexit_ctx->atexit(thr, pc, f);
- return 0;
}
TSAN_INTERCEPTOR(void, longjmp, void *env, int val) {
SCOPED_TSAN_INTERCEPTOR(longjmp, env, val);
- TsanPrintf("ThreadSanitizer: longjmp() is not supported\n");
+ Printf("ThreadSanitizer: longjmp() is not supported\n");
Die();
}
TSAN_INTERCEPTOR(void, siglongjmp, void *env, int val) {
SCOPED_TSAN_INTERCEPTOR(siglongjmp, env, val);
- TsanPrintf("ThreadSanitizer: siglongjmp() is not supported\n");
+ Printf("ThreadSanitizer: siglongjmp() is not supported\n");
Die();
}
-static uptr fd2addr(int fd) {
- (void)fd;
- static u64 addr;
- return (uptr)&addr;
-}
-
-static uptr epollfd2addr(int fd) {
- (void)fd;
- static u64 addr;
- return (uptr)&addr;
-}
-
-static uptr file2addr(char *path) {
- (void)path;
- static u64 addr;
- return (uptr)&addr;
-}
-
-static uptr dir2addr(char *path) {
- (void)path;
- static u64 addr;
- return (uptr)&addr;
-}
-
TSAN_INTERCEPTOR(void*, malloc, uptr size) {
void *p = 0;
{
@@ -328,12 +320,17 @@ TSAN_INTERCEPTOR(void*, malloc, uptr size) {
return p;
}
+TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) {
+ SCOPED_TSAN_INTERCEPTOR(__libc_memalign, align, sz);
+ return user_alloc(thr, pc, sz, align);
+}
+
TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
void *p = 0;
{
SCOPED_INTERCEPTOR_RAW(calloc, size, n);
p = user_alloc(thr, pc, n * size);
- internal_memset(p, 0, n * size);
+ if (p) internal_memset(p, 0, n * size);
}
invoke_malloc_hook(p, n * size);
return p;
@@ -366,6 +363,47 @@ TSAN_INTERCEPTOR(void, cfree, void *p) {
user_free(thr, pc, p);
}
+#define OPERATOR_NEW_BODY(mangled_name) \
+ void *p = 0; \
+ { \
+ SCOPED_INTERCEPTOR_RAW(mangled_name, size); \
+ p = user_alloc(thr, pc, size); \
+ } \
+ invoke_malloc_hook(p, size); \
+ return p;
+
+void *operator new(__sanitizer::uptr size) {
+ OPERATOR_NEW_BODY(_Znwm);
+}
+void *operator new[](__sanitizer::uptr size) {
+ OPERATOR_NEW_BODY(_Znam);
+}
+void *operator new(__sanitizer::uptr size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t);
+}
+void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t);
+}
+
+#define OPERATOR_DELETE_BODY(mangled_name) \
+ if (ptr == 0) return; \
+ invoke_free_hook(ptr); \
+ SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \
+ user_free(thr, pc, ptr);
+
+void operator delete(void *ptr) {
+ OPERATOR_DELETE_BODY(_ZdlPv);
+}
+void operator delete[](void *ptr) {
+ OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t);
+}
+void operator delete(void *ptr, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(_ZdaPv);
+}
+void operator delete[](void *ptr, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t);
+}
+
TSAN_INTERCEPTOR(uptr, strlen, const char *s) {
SCOPED_TSAN_INTERCEPTOR(strlen, s);
uptr len = internal_strlen(s);
@@ -536,137 +574,61 @@ TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
return res;
}
-#ifdef __LP64__
-
-// void *operator new(size_t)
-TSAN_INTERCEPTOR(void*, _Znwm, uptr sz) {
- void *p = 0;
- {
- SCOPED_TSAN_INTERCEPTOR(_Znwm, sz);
- p = user_alloc(thr, pc, sz);
- }
- invoke_malloc_hook(p, sz);
- return p;
-}
-
-// void *operator new(size_t, nothrow_t)
-TSAN_INTERCEPTOR(void*, _ZnwmRKSt9nothrow_t, uptr sz) {
- void *p = 0;
- {
- SCOPED_TSAN_INTERCEPTOR(_ZnwmRKSt9nothrow_t, sz);
- p = user_alloc(thr, pc, sz);
- }
- invoke_malloc_hook(p, sz);
- return p;
-}
-
-// void *operator new[](size_t)
-TSAN_INTERCEPTOR(void*, _Znam, uptr sz) {
- void *p = 0;
- {
- SCOPED_TSAN_INTERCEPTOR(_Znam, sz);
- p = user_alloc(thr, pc, sz);
- }
- invoke_malloc_hook(p, sz);
- return p;
-}
-
-// void *operator new[](size_t, nothrow_t)
-TSAN_INTERCEPTOR(void*, _ZnamRKSt9nothrow_t, uptr sz) {
- void *p = 0;
- {
- SCOPED_TSAN_INTERCEPTOR(_ZnamRKSt9nothrow_t, sz);
- p = user_alloc(thr, pc, sz);
- }
- invoke_malloc_hook(p, sz);
- return p;
-}
-
-#else
-#error "Not implemented"
-#endif
-
-// void operator delete(void*)
-TSAN_INTERCEPTOR(void, _ZdlPv, void *p) {
- if (p == 0)
- return;
- invoke_free_hook(p);
- SCOPED_TSAN_INTERCEPTOR(_ZdlPv, p);
- user_free(thr, pc, p);
-}
-
-// void operator delete(void*, nothrow_t)
-TSAN_INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *p) {
- if (p == 0)
- return;
- invoke_free_hook(p);
- SCOPED_TSAN_INTERCEPTOR(_ZdlPvRKSt9nothrow_t, p);
- user_free(thr, pc, p);
-}
-
-// void operator delete[](void*)
-TSAN_INTERCEPTOR(void, _ZdaPv, void *p) {
- if (p == 0)
- return;
- invoke_free_hook(p);
- SCOPED_TSAN_INTERCEPTOR(_ZdaPv, p);
- user_free(thr, pc, p);
-}
-
-// void operator delete[](void*, nothrow_t)
-TSAN_INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *p) {
- if (p == 0)
- return;
- invoke_free_hook(p);
- SCOPED_TSAN_INTERCEPTOR(_ZdaPvRKSt9nothrow_t, p);
- user_free(thr, pc, p);
-}
-
TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
SCOPED_TSAN_INTERCEPTOR(memalign, align, sz);
- return user_alloc_aligned(thr, pc, sz, align);
+ return user_alloc(thr, pc, sz, align);
}
TSAN_INTERCEPTOR(void*, valloc, uptr sz) {
SCOPED_TSAN_INTERCEPTOR(valloc, sz);
- return user_alloc_aligned(thr, pc, sz, kPageSize);
+ return user_alloc(thr, pc, sz, GetPageSizeCached());
}
TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
SCOPED_TSAN_INTERCEPTOR(pvalloc, sz);
- sz = RoundUp(sz, kPageSize);
- return user_alloc_aligned(thr, pc, sz, kPageSize);
+ sz = RoundUp(sz, GetPageSizeCached());
+ return user_alloc(thr, pc, sz, GetPageSizeCached());
}
TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
SCOPED_TSAN_INTERCEPTOR(posix_memalign, memptr, align, sz);
- *memptr = user_alloc_aligned(thr, pc, sz, align);
+ *memptr = user_alloc(thr, pc, sz, align);
return 0;
}
// Used in thread-safe function static initialization.
-TSAN_INTERCEPTOR(int, __cxa_guard_acquire, char *m) {
- SCOPED_TSAN_INTERCEPTOR(__cxa_guard_acquire, m);
- int res = REAL(__cxa_guard_acquire)(m);
- if (res) {
- // This thread does the init.
- } else {
- Acquire(thr, pc, (uptr)m);
+extern "C" int INTERFACE_ATTRIBUTE __cxa_guard_acquire(atomic_uint32_t *g) {
+ SCOPED_INTERCEPTOR_RAW(__cxa_guard_acquire, g);
+ for (;;) {
+ u32 cmp = atomic_load(g, memory_order_acquire);
+ if (cmp == 0) {
+ if (atomic_compare_exchange_strong(g, &cmp, 1<<16, memory_order_relaxed))
+ return 1;
+ } else if (cmp == 1) {
+ Acquire(thr, pc, (uptr)g);
+ return 0;
+ } else {
+ internal_sched_yield();
+ }
}
- return res;
}
-TSAN_INTERCEPTOR(void, __cxa_guard_release, char *m) {
- SCOPED_TSAN_INTERCEPTOR(__cxa_guard_release, m);
- Release(thr, pc, (uptr)m);
- REAL(__cxa_guard_release)(m);
+extern "C" void INTERFACE_ATTRIBUTE __cxa_guard_release(atomic_uint32_t *g) {
+ SCOPED_INTERCEPTOR_RAW(__cxa_guard_release, g);
+ Release(thr, pc, (uptr)g);
+ atomic_store(g, 1, memory_order_release);
+}
+
+extern "C" void INTERFACE_ATTRIBUTE __cxa_guard_abort(atomic_uint32_t *g) {
+ SCOPED_INTERCEPTOR_RAW(__cxa_guard_abort, g);
+ atomic_store(g, 0, memory_order_relaxed);
}
static void thread_finalize(void *v) {
uptr iter = (uptr)v;
if (iter > 1) {
if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) {
- TsanPrintf("ThreadSanitizer: failed to set thread key\n");
+ Printf("ThreadSanitizer: failed to set thread key\n");
Die();
}
return;
@@ -678,7 +640,7 @@ static void thread_finalize(void *v) {
SignalContext *sctx = thr->signal_ctx;
if (sctx) {
thr->signal_ctx = 0;
- internal_free(sctx);
+ UnmapOrDie(sctx, sizeof(*sctx));
}
}
}
@@ -699,13 +661,13 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
ThreadState *thr = cur_thread();
ScopedInRtl in_rtl;
if (pthread_setspecific(g_thread_finalize_key, (void*)4)) {
- TsanPrintf("ThreadSanitizer: failed to set thread key\n");
+ Printf("ThreadSanitizer: failed to set thread key\n");
Die();
}
while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
pthread_yield();
atomic_store(&p->tid, 0, memory_order_release);
- ThreadStart(thr, tid);
+ ThreadStart(thr, tid, GetTid());
CHECK_EQ(thr->in_rtl, 1);
}
void *res = callback(param);
@@ -740,7 +702,7 @@ TSAN_INTERCEPTOR(int, pthread_create,
atomic_store(&p.tid, 0, memory_order_relaxed);
int res = REAL(pthread_create)(th, attr, __tsan_thread_start_func, &p);
if (res == 0) {
- int tid = ThreadCreate(cur_thread(), pc, *(uptr*)th, detached);
+ int tid = ThreadCreate(thr, pc, *(uptr*)th, detached);
CHECK_NE(tid, 0);
atomic_store(&p.tid, tid, memory_order_release);
while (atomic_load(&p.tid, memory_order_acquire) != 0)
@@ -754,9 +716,9 @@ TSAN_INTERCEPTOR(int, pthread_create,
TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
SCOPED_TSAN_INTERCEPTOR(pthread_join, th, ret);
int tid = ThreadTid(thr, pc, (uptr)th);
- int res = REAL(pthread_join)(th, ret);
+ int res = BLOCK_REAL(pthread_join)(th, ret);
if (res == 0) {
- ThreadJoin(cur_thread(), pc, tid);
+ ThreadJoin(thr, pc, tid);
}
return res;
}
@@ -766,7 +728,7 @@ TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
int tid = ThreadTid(thr, pc, (uptr)th);
int res = REAL(pthread_detach)(th);
if (res == 0) {
- ThreadDetach(cur_thread(), pc, tid);
+ ThreadDetach(thr, pc, tid);
}
return res;
}
@@ -782,7 +744,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_init, void *m, void *a) {
recursive = (type == PTHREAD_MUTEX_RECURSIVE
|| type == PTHREAD_MUTEX_RECURSIVE_NP);
}
- MutexCreate(cur_thread(), pc, (uptr)m, false, recursive);
+ MutexCreate(thr, pc, (uptr)m, false, recursive, false);
}
return res;
}
@@ -791,7 +753,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_destroy, m);
int res = REAL(pthread_mutex_destroy)(m);
if (res == 0 || res == EBUSY) {
- MutexDestroy(cur_thread(), pc, (uptr)m);
+ MutexDestroy(thr, pc, (uptr)m);
}
return res;
}
@@ -800,7 +762,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_lock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_lock, m);
int res = REAL(pthread_mutex_lock)(m);
if (res == 0) {
- MutexLock(cur_thread(), pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
}
return res;
}
@@ -809,7 +771,7 @@ TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_trylock, m);
int res = REAL(pthread_mutex_trylock)(m);
if (res == 0) {
- MutexLock(cur_thread(), pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
}
return res;
}
@@ -818,14 +780,14 @@ TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime);
int res = REAL(pthread_mutex_timedlock)(m, abstime);
if (res == 0) {
- MutexLock(cur_thread(), pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
}
return res;
}
TSAN_INTERCEPTOR(int, pthread_mutex_unlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_mutex_unlock, m);
- MutexUnlock(cur_thread(), pc, (uptr)m);
+ MutexUnlock(thr, pc, (uptr)m);
int res = REAL(pthread_mutex_unlock)(m);
return res;
}
@@ -834,7 +796,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) {
SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared);
int res = REAL(pthread_spin_init)(m, pshared);
if (res == 0) {
- MutexCreate(cur_thread(), pc, (uptr)m, false, false);
+ MutexCreate(thr, pc, (uptr)m, false, false, false);
}
return res;
}
@@ -843,7 +805,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_destroy, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_spin_destroy, m);
int res = REAL(pthread_spin_destroy)(m);
if (res == 0) {
- MutexDestroy(cur_thread(), pc, (uptr)m);
+ MutexDestroy(thr, pc, (uptr)m);
}
return res;
}
@@ -852,7 +814,7 @@ TSAN_INTERCEPTOR(int, pthread_spin_lock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_spin_lock, m);
int res = REAL(pthread_spin_lock)(m);
if (res == 0) {
- MutexLock(cur_thread(), pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
}
return res;
}
@@ -861,14 +823,14 @@ TSAN_INTERCEPTOR(int, pthread_spin_trylock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_spin_trylock, m);
int res = REAL(pthread_spin_trylock)(m);
if (res == 0) {
- MutexLock(cur_thread(), pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
}
return res;
}
TSAN_INTERCEPTOR(int, pthread_spin_unlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_spin_unlock, m);
- MutexUnlock(cur_thread(), pc, (uptr)m);
+ MutexUnlock(thr, pc, (uptr)m);
int res = REAL(pthread_spin_unlock)(m);
return res;
}
@@ -877,7 +839,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_init, void *m, void *a) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_init, m, a);
int res = REAL(pthread_rwlock_init)(m, a);
if (res == 0) {
- MutexCreate(cur_thread(), pc, (uptr)m, true, false);
+ MutexCreate(thr, pc, (uptr)m, true, false, false);
}
return res;
}
@@ -886,7 +848,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_destroy, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_destroy, m);
int res = REAL(pthread_rwlock_destroy)(m);
if (res == 0) {
- MutexDestroy(cur_thread(), pc, (uptr)m);
+ MutexDestroy(thr, pc, (uptr)m);
}
return res;
}
@@ -895,7 +857,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_rdlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_rdlock, m);
int res = REAL(pthread_rwlock_rdlock)(m);
if (res == 0) {
- MutexReadLock(cur_thread(), pc, (uptr)m);
+ MutexReadLock(thr, pc, (uptr)m);
}
return res;
}
@@ -904,7 +866,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_tryrdlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_tryrdlock, m);
int res = REAL(pthread_rwlock_tryrdlock)(m);
if (res == 0) {
- MutexReadLock(cur_thread(), pc, (uptr)m);
+ MutexReadLock(thr, pc, (uptr)m);
}
return res;
}
@@ -913,7 +875,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime);
int res = REAL(pthread_rwlock_timedrdlock)(m, abstime);
if (res == 0) {
- MutexReadLock(cur_thread(), pc, (uptr)m);
+ MutexReadLock(thr, pc, (uptr)m);
}
return res;
}
@@ -922,7 +884,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m);
int res = REAL(pthread_rwlock_wrlock)(m);
if (res == 0) {
- MutexLock(cur_thread(), pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
}
return res;
}
@@ -931,7 +893,7 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_trywrlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_trywrlock, m);
int res = REAL(pthread_rwlock_trywrlock)(m);
if (res == 0) {
- MutexLock(cur_thread(), pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
}
return res;
}
@@ -940,23 +902,27 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime);
int res = REAL(pthread_rwlock_timedwrlock)(m, abstime);
if (res == 0) {
- MutexLock(cur_thread(), pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
}
return res;
}
TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_unlock, m);
- MutexReadOrWriteUnlock(cur_thread(), pc, (uptr)m);
+ MutexReadOrWriteUnlock(thr, pc, (uptr)m);
int res = REAL(pthread_rwlock_unlock)(m);
return res;
}
+// libpthread.so contains several versions of pthread_cond_init symbol.
+// When we just dlsym() it, we get the wrong (old) version.
+/*
TSAN_INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, c, a);
int res = REAL(pthread_cond_init)(c, a);
return res;
}
+*/
TSAN_INTERCEPTOR(int, pthread_cond_destroy, void *c) {
SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, c);
@@ -978,17 +944,17 @@ TSAN_INTERCEPTOR(int, pthread_cond_broadcast, void *c) {
TSAN_INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) {
SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, c, m);
- MutexUnlock(cur_thread(), pc, (uptr)m);
+ MutexUnlock(thr, pc, (uptr)m);
int res = REAL(pthread_cond_wait)(c, m);
- MutexLock(cur_thread(), pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
return res;
}
TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, c, m, abstime);
- MutexUnlock(cur_thread(), pc, (uptr)m);
+ MutexUnlock(thr, pc, (uptr)m);
int res = REAL(pthread_cond_timedwait)(c, m, abstime);
- MutexLock(cur_thread(), pc, (uptr)m);
+ MutexLock(thr, pc, (uptr)m);
return res;
}
@@ -1008,12 +974,12 @@ TSAN_INTERCEPTOR(int, pthread_barrier_destroy, void *b) {
TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) {
SCOPED_TSAN_INTERCEPTOR(pthread_barrier_wait, b);
- Release(cur_thread(), pc, (uptr)b);
+ Release(thr, pc, (uptr)b);
MemoryRead1Byte(thr, pc, (uptr)b);
int res = REAL(pthread_barrier_wait)(b);
MemoryRead1Byte(thr, pc, (uptr)b);
if (res == 0 || res == PTHREAD_BARRIER_SERIAL_THREAD) {
- Acquire(cur_thread(), pc, (uptr)b);
+ Acquire(thr, pc, (uptr)b);
}
return res;
}
@@ -1031,14 +997,14 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
(*f)();
CHECK_EQ(thr->in_rtl, 0);
thr->in_rtl = old_in_rtl;
- Release(cur_thread(), pc, (uptr)o);
+ Release(thr, pc, (uptr)o);
atomic_store(a, 2, memory_order_release);
} else {
while (v != 2) {
pthread_yield();
v = atomic_load(a, memory_order_acquire);
}
- Acquire(cur_thread(), pc, (uptr)o);
+ Acquire(thr, pc, (uptr)o);
}
return 0;
}
@@ -1057,34 +1023,34 @@ TSAN_INTERCEPTOR(int, sem_destroy, void *s) {
TSAN_INTERCEPTOR(int, sem_wait, void *s) {
SCOPED_TSAN_INTERCEPTOR(sem_wait, s);
- int res = REAL(sem_wait)(s);
+ int res = BLOCK_REAL(sem_wait)(s);
if (res == 0) {
- Acquire(cur_thread(), pc, (uptr)s);
+ Acquire(thr, pc, (uptr)s);
}
return res;
}
TSAN_INTERCEPTOR(int, sem_trywait, void *s) {
SCOPED_TSAN_INTERCEPTOR(sem_trywait, s);
- int res = REAL(sem_trywait)(s);
+ int res = BLOCK_REAL(sem_trywait)(s);
if (res == 0) {
- Acquire(cur_thread(), pc, (uptr)s);
+ Acquire(thr, pc, (uptr)s);
}
return res;
}
TSAN_INTERCEPTOR(int, sem_timedwait, void *s, void *abstime) {
SCOPED_TSAN_INTERCEPTOR(sem_timedwait, s, abstime);
- int res = REAL(sem_timedwait)(s, abstime);
+ int res = BLOCK_REAL(sem_timedwait)(s, abstime);
if (res == 0) {
- Acquire(cur_thread(), pc, (uptr)s);
+ Acquire(thr, pc, (uptr)s);
}
return res;
}
TSAN_INTERCEPTOR(int, sem_post, void *s) {
SCOPED_TSAN_INTERCEPTOR(sem_post, s);
- Release(cur_thread(), pc, (uptr)s);
+ Release(thr, pc, (uptr)s);
int res = REAL(sem_post)(s);
return res;
}
@@ -1093,43 +1059,193 @@ TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) {
SCOPED_TSAN_INTERCEPTOR(sem_getvalue, s, sval);
int res = REAL(sem_getvalue)(s, sval);
if (res == 0) {
- Acquire(cur_thread(), pc, (uptr)s);
+ Acquire(thr, pc, (uptr)s);
}
return res;
}
-TSAN_INTERCEPTOR(long_t, read, int fd, void *buf, long_t sz) {
- SCOPED_TSAN_INTERCEPTOR(read, fd, buf, sz);
- int res = REAL(read)(fd, buf, sz);
- if (res >= 0) {
- Acquire(cur_thread(), pc, fd2addr(fd));
- }
+TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
+ SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode);
+ int fd = REAL(open)(name, flags, mode);
+ if (fd >= 0)
+ FdFileCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
+ SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode);
+ int fd = REAL(open64)(name, flags, mode);
+ if (fd >= 0)
+ FdFileCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
+ SCOPED_TSAN_INTERCEPTOR(creat, name, mode);
+ int fd = REAL(creat)(name, mode);
+ if (fd >= 0)
+ FdFileCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
+ SCOPED_TSAN_INTERCEPTOR(creat64, name, mode);
+ int fd = REAL(creat64)(name, mode);
+ if (fd >= 0)
+ FdFileCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, dup, int oldfd) {
+ SCOPED_TSAN_INTERCEPTOR(dup, oldfd);
+ int newfd = REAL(dup)(oldfd);
+ if (oldfd >= 0 && newfd >= 0 && newfd != oldfd)
+ FdDup(thr, pc, oldfd, newfd);
+ return newfd;
+}
+
+TSAN_INTERCEPTOR(int, dup2, int oldfd, int newfd) {
+ SCOPED_TSAN_INTERCEPTOR(dup2, oldfd, newfd);
+ int newfd2 = REAL(dup2)(oldfd, newfd);
+ if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
+ FdDup(thr, pc, oldfd, newfd2);
+ return newfd2;
+}
+
+TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags);
+ int newfd2 = REAL(dup3)(oldfd, newfd, flags);
+ if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
+ FdDup(thr, pc, oldfd, newfd2);
+ return newfd2;
+}
+
+TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(eventfd, initval, flags);
+ int fd = REAL(eventfd)(initval, flags);
+ if (fd >= 0)
+ FdEventCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(signalfd, fd, mask, flags);
+ if (fd >= 0)
+ FdClose(thr, pc, fd);
+ fd = REAL(signalfd)(fd, mask, flags);
+ if (fd >= 0)
+ FdSignalCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, inotify_init, int fake) {
+ SCOPED_TSAN_INTERCEPTOR(inotify_init, fake);
+ int fd = REAL(inotify_init)(fake);
+ if (fd >= 0)
+ FdInotifyCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, inotify_init1, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(inotify_init1, flags);
+ int fd = REAL(inotify_init1)(flags);
+ if (fd >= 0)
+ FdInotifyCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, socket, int domain, int type, int protocol) {
+ SCOPED_TSAN_INTERCEPTOR(socket, domain, type, protocol);
+ int fd = REAL(socket)(domain, type, protocol);
+ if (fd >= 0)
+ FdSocketCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, socketpair, int domain, int type, int protocol, int *fd) {
+ SCOPED_TSAN_INTERCEPTOR(socketpair, domain, type, protocol, fd);
+ int res = REAL(socketpair)(domain, type, protocol, fd);
+ if (res == 0 && fd[0] >= 0 && fd[1] >= 0)
+ FdPipeCreate(thr, pc, fd[0], fd[1]);
return res;
}
-TSAN_INTERCEPTOR(long_t, pread, int fd, void *buf, long_t sz, unsigned off) {
- SCOPED_TSAN_INTERCEPTOR(pread, fd, buf, sz, off);
- int res = REAL(pread)(fd, buf, sz, off);
- if (res >= 0) {
- Acquire(cur_thread(), pc, fd2addr(fd));
- }
+TSAN_INTERCEPTOR(int, connect, int fd, void *addr, unsigned addrlen) {
+ SCOPED_TSAN_INTERCEPTOR(connect, fd, addr, addrlen);
+ FdSocketConnecting(thr, pc, fd);
+ int res = REAL(connect)(fd, addr, addrlen);
+ if (res == 0 && fd >= 0)
+ FdSocketConnect(thr, pc, fd);
return res;
}
-TSAN_INTERCEPTOR(long_t, pread64, int fd, void *buf, long_t sz, u64 off) {
- SCOPED_TSAN_INTERCEPTOR(pread64, fd, buf, sz, off);
- int res = REAL(pread64)(fd, buf, sz, off);
- if (res >= 0) {
- Acquire(cur_thread(), pc, fd2addr(fd));
- }
+TSAN_INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) {
+ SCOPED_TSAN_INTERCEPTOR(accept, fd, addr, addrlen);
+ int fd2 = REAL(accept)(fd, addr, addrlen);
+ if (fd >= 0 && fd2 >= 0)
+ FdSocketAccept(thr, pc, fd, fd2);
+ return fd2;
+}
+
+TSAN_INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) {
+ SCOPED_TSAN_INTERCEPTOR(accept4, fd, addr, addrlen, f);
+ int fd2 = REAL(accept4)(fd, addr, addrlen, f);
+ if (fd >= 0 && fd2 >= 0)
+ FdSocketAccept(thr, pc, fd, fd2);
+ return fd2;
+}
+
+TSAN_INTERCEPTOR(int, epoll_create, int size) {
+ SCOPED_TSAN_INTERCEPTOR(epoll_create, size);
+ int fd = REAL(epoll_create)(size);
+ if (fd >= 0)
+ FdPollCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, epoll_create1, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(epoll_create1, flags);
+ int fd = REAL(epoll_create1)(flags);
+ if (fd >= 0)
+ FdPollCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, close, int fd) {
+ SCOPED_TSAN_INTERCEPTOR(close, fd);
+ if (fd >= 0)
+ FdClose(thr, pc, fd);
+ return REAL(close)(fd);
+}
+
+TSAN_INTERCEPTOR(int, __close, int fd) {
+ SCOPED_TSAN_INTERCEPTOR(__close, fd);
+ if (fd >= 0)
+ FdClose(thr, pc, fd);
+ return REAL(__close)(fd);
+}
+
+TSAN_INTERCEPTOR(int, pipe, int *pipefd) {
+ SCOPED_TSAN_INTERCEPTOR(pipe, pipefd);
+ int res = REAL(pipe)(pipefd);
+ if (res == 0 && pipefd[0] >= 0 && pipefd[1] >= 0)
+ FdPipeCreate(thr, pc, pipefd[0], pipefd[1]);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(pipe2, pipefd, flags);
+ int res = REAL(pipe2)(pipefd, flags);
+ if (res == 0 && pipefd[0] >= 0 && pipefd[1] >= 0)
+ FdPipeCreate(thr, pc, pipefd[0], pipefd[1]);
return res;
}
TSAN_INTERCEPTOR(long_t, readv, int fd, void *vec, int cnt) {
SCOPED_TSAN_INTERCEPTOR(readv, fd, vec, cnt);
int res = REAL(readv)(fd, vec, cnt);
- if (res >= 0) {
- Acquire(cur_thread(), pc, fd2addr(fd));
+ if (res >= 0 && fd >= 0) {
+ FdAcquire(thr, pc, fd);
}
return res;
}
@@ -1137,57 +1253,40 @@ TSAN_INTERCEPTOR(long_t, readv, int fd, void *vec, int cnt) {
TSAN_INTERCEPTOR(long_t, preadv64, int fd, void *vec, int cnt, u64 off) {
SCOPED_TSAN_INTERCEPTOR(preadv64, fd, vec, cnt, off);
int res = REAL(preadv64)(fd, vec, cnt, off);
- if (res >= 0) {
- Acquire(cur_thread(), pc, fd2addr(fd));
+ if (res >= 0 && fd >= 0) {
+ FdAcquire(thr, pc, fd);
}
return res;
}
-TSAN_INTERCEPTOR(long_t, write, int fd, void *buf, long_t sz) {
- SCOPED_TSAN_INTERCEPTOR(write, fd, buf, sz);
- Release(cur_thread(), pc, fd2addr(fd));
- int res = REAL(write)(fd, buf, sz);
- return res;
-}
-
-TSAN_INTERCEPTOR(long_t, pwrite, int fd, void *buf, long_t sz, unsigned off) {
- SCOPED_TSAN_INTERCEPTOR(pwrite, fd, buf, sz, off);
- Release(cur_thread(), pc, fd2addr(fd));
- int res = REAL(pwrite)(fd, buf, sz, off);
- return res;
-}
-
-TSAN_INTERCEPTOR(long_t, pwrite64, int fd, void *buf, long_t sz, unsigned off) {
- SCOPED_TSAN_INTERCEPTOR(pwrite64, fd, buf, sz, off);
- Release(cur_thread(), pc, fd2addr(fd));
- int res = REAL(pwrite64)(fd, buf, sz, off);
- return res;
-}
-
TSAN_INTERCEPTOR(long_t, writev, int fd, void *vec, int cnt) {
SCOPED_TSAN_INTERCEPTOR(writev, fd, vec, cnt);
- Release(cur_thread(), pc, fd2addr(fd));
+ if (fd >= 0)
+ FdRelease(thr, pc, fd);
int res = REAL(writev)(fd, vec, cnt);
return res;
}
TSAN_INTERCEPTOR(long_t, pwritev64, int fd, void *vec, int cnt, u64 off) {
SCOPED_TSAN_INTERCEPTOR(pwritev64, fd, vec, cnt, off);
- Release(cur_thread(), pc, fd2addr(fd));
+ if (fd >= 0)
+ FdRelease(thr, pc, fd);
int res = REAL(pwritev64)(fd, vec, cnt, off);
return res;
}
TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) {
SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags);
- Release(cur_thread(), pc, fd2addr(fd));
+ if (fd >= 0)
+ FdRelease(thr, pc, fd);
int res = REAL(send)(fd, buf, len, flags);
return res;
}
TSAN_INTERCEPTOR(long_t, sendmsg, int fd, void *msg, int flags) {
SCOPED_TSAN_INTERCEPTOR(sendmsg, fd, msg, flags);
- Release(cur_thread(), pc, fd2addr(fd));
+ if (fd >= 0)
+ FdRelease(thr, pc, fd);
int res = REAL(sendmsg)(fd, msg, flags);
return res;
}
@@ -1195,8 +1294,8 @@ TSAN_INTERCEPTOR(long_t, sendmsg, int fd, void *msg, int flags) {
TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) {
SCOPED_TSAN_INTERCEPTOR(recv, fd, buf, len, flags);
int res = REAL(recv)(fd, buf, len, flags);
- if (res >= 0) {
- Acquire(cur_thread(), pc, fd2addr(fd));
+ if (res >= 0 && fd >= 0) {
+ FdAcquire(thr, pc, fd);
}
return res;
}
@@ -1204,15 +1303,15 @@ TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) {
TSAN_INTERCEPTOR(long_t, recvmsg, int fd, void *msg, int flags) {
SCOPED_TSAN_INTERCEPTOR(recvmsg, fd, msg, flags);
int res = REAL(recvmsg)(fd, msg, flags);
- if (res >= 0) {
- Acquire(cur_thread(), pc, fd2addr(fd));
+ if (res >= 0 && fd >= 0) {
+ FdAcquire(thr, pc, fd);
}
return res;
}
TSAN_INTERCEPTOR(int, unlink, char *path) {
SCOPED_TSAN_INTERCEPTOR(unlink, path);
- Release(cur_thread(), pc, file2addr(path));
+ Release(thr, pc, File2addr(path));
int res = REAL(unlink)(path);
return res;
}
@@ -1220,19 +1319,57 @@ TSAN_INTERCEPTOR(int, unlink, char *path) {
TSAN_INTERCEPTOR(void*, fopen, char *path, char *mode) {
SCOPED_TSAN_INTERCEPTOR(fopen, path, mode);
void *res = REAL(fopen)(path, mode);
- Acquire(cur_thread(), pc, file2addr(path));
+ Acquire(thr, pc, File2addr(path));
+ if (res) {
+ int fd = fileno_unlocked(res);
+ if (fd >= 0)
+ FdFileCreate(thr, pc, fd);
+ }
return res;
}
+TSAN_INTERCEPTOR(void*, freopen, char *path, char *mode, void *stream) {
+ SCOPED_TSAN_INTERCEPTOR(freopen, path, mode, stream);
+ if (stream) {
+ int fd = fileno_unlocked(stream);
+ if (fd >= 0)
+ FdClose(thr, pc, fd);
+ }
+ void *res = REAL(freopen)(path, mode, stream);
+ Acquire(thr, pc, File2addr(path));
+ if (res) {
+ int fd = fileno_unlocked(res);
+ if (fd >= 0)
+ FdFileCreate(thr, pc, fd);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, fclose, void *stream) {
+ {
+ SCOPED_TSAN_INTERCEPTOR(fclose, stream);
+ if (stream) {
+ int fd = fileno_unlocked(stream);
+ if (fd >= 0)
+ FdClose(thr, pc, fd);
+ }
+ }
+ return REAL(fclose)(stream);
+}
+
TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) {
- SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f);
- MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true);
+ {
+ SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f);
+ MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true);
+ }
return REAL(fread)(ptr, size, nmemb, f);
}
TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) {
- SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f);
- MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false);
+ {
+ SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f);
+ MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false);
+ }
return REAL(fwrite)(p, size, nmemb, f);
}
@@ -1244,7 +1381,7 @@ TSAN_INTERCEPTOR(int, puts, const char *s) {
TSAN_INTERCEPTOR(int, rmdir, char *path) {
SCOPED_TSAN_INTERCEPTOR(rmdir, path);
- Release(cur_thread(), pc, dir2addr(path));
+ Release(thr, pc, Dir2addr(path));
int res = REAL(rmdir)(path);
return res;
}
@@ -1252,28 +1389,37 @@ TSAN_INTERCEPTOR(int, rmdir, char *path) {
TSAN_INTERCEPTOR(void*, opendir, char *path) {
SCOPED_TSAN_INTERCEPTOR(opendir, path);
void *res = REAL(opendir)(path);
- Acquire(cur_thread(), pc, dir2addr(path));
+ if (res != 0)
+ Acquire(thr, pc, Dir2addr(path));
return res;
}
TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev);
- if (op == EPOLL_CTL_ADD) {
- Release(cur_thread(), pc, epollfd2addr(epfd));
+ if (op == EPOLL_CTL_ADD && epfd >= 0) {
+ FdRelease(thr, pc, epfd);
}
int res = REAL(epoll_ctl)(epfd, op, fd, ev);
+ if (fd >= 0)
+ FdAccess(thr, pc, fd);
return res;
}
TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) {
SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout);
- int res = REAL(epoll_wait)(epfd, ev, cnt, timeout);
- if (res > 0) {
- Acquire(cur_thread(), pc, epollfd2addr(epfd));
+ int res = BLOCK_REAL(epoll_wait)(epfd, ev, cnt, timeout);
+ if (res > 0 && epfd >= 0) {
+ FdAcquire(thr, pc, epfd);
}
return res;
}
+TSAN_INTERCEPTOR(int, poll, void *fds, long_t nfds, int timeout) {
+ SCOPED_TSAN_INTERCEPTOR(poll, fds, nfds, timeout);
+ int res = BLOCK_REAL(poll)(fds, nfds, timeout);
+ return res;
+}
+
static void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
my_siginfo_t *info, void *ctx) {
ThreadState *thr = cur_thread();
@@ -1281,7 +1427,12 @@ static void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
// Don't mess with synchronous signals.
if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE ||
- (sctx && sig == sctx->int_signal_send)) {
+ // If we are sending signal to ourselves, we must process it now.
+ (sctx && sig == sctx->int_signal_send) ||
+ // If we are in blocking function, we can safely process it now
+ // (but check if we are in a recursive interceptor,
+ // i.e. pthread_join()->munmap()).
+ (sctx && sctx->in_blocking_func == 1 && thr->in_rtl == 1)) {
CHECK(thr->in_rtl == 0 || thr->in_rtl == 1);
int in_rtl = thr->in_rtl;
thr->in_rtl = 0;
@@ -1340,11 +1491,11 @@ TSAN_INTERCEPTOR(int, sigaction, int sig, sigaction_t *act, sigaction_t *old) {
}
TSAN_INTERCEPTOR(sighandler_t, signal, int sig, sighandler_t h) {
- sigaction_t act = {};
+ sigaction_t act;
act.sa_handler = h;
REAL(memset)(&act.sa_mask, -1, sizeof(act.sa_mask));
act.sa_flags = 0;
- sigaction_t old = {};
+ sigaction_t old;
int res = sigaction(sig, &act, &old);
if (res)
return SIG_ERR;
@@ -1395,11 +1546,89 @@ TSAN_INTERCEPTOR(int, pthread_kill, void *tid, int sig) {
return res;
}
-static void process_pending_signals(ThreadState *thr) {
+TSAN_INTERCEPTOR(int, gettimeofday, void *tv, void *tz) {
+ SCOPED_TSAN_INTERCEPTOR(gettimeofday, tv, tz);
+ // It's intercepted merely to process pending signals.
+ return REAL(gettimeofday)(tv, tz);
+}
+
+// Linux kernel has a bug that leads to kernel deadlock if a process
+// maps TBs of memory and then calls mlock().
+static void MlockIsUnsupported() {
+ static atomic_uint8_t printed;
+ if (atomic_exchange(&printed, 1, memory_order_relaxed))
+ return;
+ Printf("INFO: ThreadSanitizer ignores mlock/mlockall/munlock/munlockall\n");
+}
+
+TSAN_INTERCEPTOR(int, mlock, const void *addr, uptr len) {
+ MlockIsUnsupported();
+ return 0;
+}
+
+TSAN_INTERCEPTOR(int, munlock, const void *addr, uptr len) {
+ MlockIsUnsupported();
+ return 0;
+}
+
+TSAN_INTERCEPTOR(int, mlockall, int flags) {
+ MlockIsUnsupported();
+ return 0;
+}
+
+TSAN_INTERCEPTOR(int, munlockall, void) {
+ MlockIsUnsupported();
+ return 0;
+}
+
+TSAN_INTERCEPTOR(int, fork, int fake) {
+ SCOPED_TSAN_INTERCEPTOR(fork, fake);
+ // It's intercepted merely to process pending signals.
+ int pid = REAL(fork)(fake);
+ if (pid == 0) {
+ // child
+ FdOnFork(thr, pc);
+ } else if (pid > 0) {
+ // parent
+ }
+ return pid;
+}
+
+struct TsanInterceptorContext {
+ ThreadState *thr;
+ const uptr caller_pc;
+ const uptr pc;
+};
+
+#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
+ MemoryAccessRange(((TsanInterceptorContext*)ctx)->thr, \
+ ((TsanInterceptorContext*)ctx)->pc, \
+ (uptr)ptr, size, true)
+#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
+ MemoryAccessRange(((TsanInterceptorContext*)ctx)->thr, \
+ ((TsanInterceptorContext*)ctx)->pc, \
+ (uptr)ptr, size, false)
+#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
+ SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__) \
+ TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \
+ ctx = (void*)&_ctx; \
+ (void)ctx;
+#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
+ FdAcquire(((TsanInterceptorContext*)ctx)->thr, pc, fd)
+#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
+ FdRelease(((TsanInterceptorContext*)ctx)->thr, pc, fd)
+#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
+ ThreadSetName(((TsanInterceptorContext*)ctx)->thr, name)
+#include "sanitizer_common/sanitizer_common_interceptors.inc"
+
+namespace __tsan {
+
+void ProcessPendingSignals(ThreadState *thr) {
CHECK_EQ(thr->in_rtl, 0);
SignalContext *sctx = SigCtx(thr);
if (sctx == 0 || sctx->pending_signal_count == 0 || thr->in_signal_handler)
return;
+ Context *ctx = CTX();
thr->in_signal_handler = true;
sctx->pending_signal_count = 0;
// These are too big for stack.
@@ -1419,16 +1648,19 @@ static void process_pending_signals(ThreadState *thr) {
sigactions[sig].sa_sigaction(sig, &signal->siginfo, &signal->ctx);
else
sigactions[sig].sa_handler(sig);
- if (errno != 0) {
+ if (flags()->report_bugs && errno != 0) {
ScopedInRtl in_rtl;
- StackTrace stack;
+ __tsan::StackTrace stack;
uptr pc = signal->sigaction ?
(uptr)sigactions[sig].sa_sigaction :
(uptr)sigactions[sig].sa_handler;
stack.Init(&pc, 1);
+ Lock l(&ctx->thread_mtx);
ScopedReport rep(ReportTypeErrnoInSignal);
- rep.AddStack(&stack);
- OutputReport(rep, rep.GetReport()->stacks[0]);
+ if (!IsFiredSuppression(ctx, rep, stack)) {
+ rep.AddStack(&stack);
+ OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
+ }
}
errno = saved_errno;
}
@@ -1439,7 +1671,10 @@ static void process_pending_signals(ThreadState *thr) {
thr->in_signal_handler = false;
}
-namespace __tsan {
+static void unreachable() {
+ Printf("FATAL: ThreadSanitizer: unreachable called\n");
+ Die();
+}
void InitializeInterceptors() {
CHECK_GT(cur_thread()->in_rtl, 0);
@@ -1449,10 +1684,13 @@ void InitializeInterceptors() {
REAL(memcpy) = internal_memcpy;
REAL(memcmp) = internal_memcmp;
+ SANITIZER_COMMON_INTERCEPTORS_INIT;
+
TSAN_INTERCEPT(longjmp);
TSAN_INTERCEPT(siglongjmp);
TSAN_INTERCEPT(malloc);
+ TSAN_INTERCEPT(__libc_memalign);
TSAN_INTERCEPT(calloc);
TSAN_INTERCEPT(realloc);
TSAN_INTERCEPT(free);
@@ -1465,15 +1703,6 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(pvalloc);
TSAN_INTERCEPT(posix_memalign);
- TSAN_INTERCEPT(_Znwm);
- TSAN_INTERCEPT(_ZnwmRKSt9nothrow_t);
- TSAN_INTERCEPT(_Znam);
- TSAN_INTERCEPT(_ZnamRKSt9nothrow_t);
- TSAN_INTERCEPT(_ZdlPv);
- TSAN_INTERCEPT(_ZdlPvRKSt9nothrow_t);
- TSAN_INTERCEPT(_ZdaPv);
- TSAN_INTERCEPT(_ZdaPvRKSt9nothrow_t);
-
TSAN_INTERCEPT(strlen);
TSAN_INTERCEPT(memset);
TSAN_INTERCEPT(memcpy);
@@ -1490,9 +1719,6 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(strncpy);
TSAN_INTERCEPT(strstr);
- TSAN_INTERCEPT(__cxa_guard_acquire);
- TSAN_INTERCEPT(__cxa_guard_release);
-
TSAN_INTERCEPT(pthread_create);
TSAN_INTERCEPT(pthread_join);
TSAN_INTERCEPT(pthread_detach);
@@ -1520,7 +1746,7 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(pthread_rwlock_timedwrlock);
TSAN_INTERCEPT(pthread_rwlock_unlock);
- TSAN_INTERCEPT(pthread_cond_init);
+ // TSAN_INTERCEPT(pthread_cond_init);
TSAN_INTERCEPT(pthread_cond_destroy);
TSAN_INTERCEPT(pthread_cond_signal);
TSAN_INTERCEPT(pthread_cond_broadcast);
@@ -1541,14 +1767,30 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(sem_post);
TSAN_INTERCEPT(sem_getvalue);
- TSAN_INTERCEPT(read);
- TSAN_INTERCEPT(pread);
- TSAN_INTERCEPT(pread64);
+ TSAN_INTERCEPT(open);
+ TSAN_INTERCEPT(open64);
+ TSAN_INTERCEPT(creat);
+ TSAN_INTERCEPT(creat64);
+ TSAN_INTERCEPT(dup);
+ TSAN_INTERCEPT(dup2);
+ TSAN_INTERCEPT(dup3);
+ TSAN_INTERCEPT(eventfd);
+ TSAN_INTERCEPT(signalfd);
+ TSAN_INTERCEPT(inotify_init);
+ TSAN_INTERCEPT(inotify_init1);
+ TSAN_INTERCEPT(socket);
+ TSAN_INTERCEPT(socketpair);
+ TSAN_INTERCEPT(connect);
+ TSAN_INTERCEPT(accept);
+ TSAN_INTERCEPT(accept4);
+ TSAN_INTERCEPT(epoll_create);
+ TSAN_INTERCEPT(epoll_create1);
+ TSAN_INTERCEPT(close);
+ TSAN_INTERCEPT(pipe);
+ TSAN_INTERCEPT(pipe2);
+
TSAN_INTERCEPT(readv);
TSAN_INTERCEPT(preadv64);
- TSAN_INTERCEPT(write);
- TSAN_INTERCEPT(pwrite);
- TSAN_INTERCEPT(pwrite64);
TSAN_INTERCEPT(writev);
TSAN_INTERCEPT(pwritev64);
TSAN_INTERCEPT(send);
@@ -1558,6 +1800,8 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(unlink);
TSAN_INTERCEPT(fopen);
+ TSAN_INTERCEPT(freopen);
+ TSAN_INTERCEPT(fclose);
TSAN_INTERCEPT(fread);
TSAN_INTERCEPT(fwrite);
TSAN_INTERCEPT(puts);
@@ -1566,25 +1810,42 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(epoll_ctl);
TSAN_INTERCEPT(epoll_wait);
+ TSAN_INTERCEPT(poll);
TSAN_INTERCEPT(sigaction);
TSAN_INTERCEPT(signal);
TSAN_INTERCEPT(raise);
TSAN_INTERCEPT(kill);
TSAN_INTERCEPT(pthread_kill);
+ TSAN_INTERCEPT(sleep);
+ TSAN_INTERCEPT(usleep);
+ TSAN_INTERCEPT(nanosleep);
+ TSAN_INTERCEPT(gettimeofday);
+ TSAN_INTERCEPT(mlock);
+ TSAN_INTERCEPT(munlock);
+ TSAN_INTERCEPT(mlockall);
+ TSAN_INTERCEPT(munlockall);
+
+ TSAN_INTERCEPT(fork);
+
+ // Need to setup it, because interceptors check that the function is resolved.
+ // But atexit is emitted directly into the module, so can't be resolved.
+ REAL(atexit) = (int(*)(void(*)()))unreachable;
atexit_ctx = new(internal_alloc(MBlockAtExit, sizeof(AtExitContext)))
AtExitContext();
if (__cxa_atexit(&finalize, 0, 0)) {
- TsanPrintf("ThreadSanitizer: failed to setup atexit callback\n");
+ Printf("ThreadSanitizer: failed to setup atexit callback\n");
Die();
}
if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) {
- TsanPrintf("ThreadSanitizer: failed to create thread key\n");
+ Printf("ThreadSanitizer: failed to create thread key\n");
Die();
}
+
+ FdInit();
}
void internal_start_thread(void(*func)(void *arg), void *arg) {
diff --git a/lib/tsan/rtl/tsan_interface.h b/lib/tsan/rtl/tsan_interface.h
index ed21ec60544e..7480fc893f2d 100644
--- a/lib/tsan/rtl/tsan_interface.h
+++ b/lib/tsan/rtl/tsan_interface.h
@@ -16,6 +16,8 @@
#ifndef TSAN_INTERFACE_H
#define TSAN_INTERFACE_H
+#include <sanitizer/common_interface_defs.h>
+
// This header should NOT include any other headers.
// All functions in this header are extern "C" and start with __tsan_.
@@ -25,24 +27,30 @@ extern "C" {
// This function should be called at the very beginning of the process,
// before any instrumented code is executed and before any call to malloc.
-void __tsan_init();
-
-void __tsan_read1(void *addr);
-void __tsan_read2(void *addr);
-void __tsan_read4(void *addr);
-void __tsan_read8(void *addr);
-void __tsan_read16(void *addr);
-
-void __tsan_write1(void *addr);
-void __tsan_write2(void *addr);
-void __tsan_write4(void *addr);
-void __tsan_write8(void *addr);
-void __tsan_write16(void *addr);
-
-void __tsan_vptr_update(void **vptr_p, void *new_val);
-
-void __tsan_func_entry(void *call_pc);
-void __tsan_func_exit();
+void __tsan_init() SANITIZER_INTERFACE_ATTRIBUTE;
+
+void __tsan_read1(void *addr) SANITIZER_INTERFACE_ATTRIBUTE;
+void __tsan_read2(void *addr) SANITIZER_INTERFACE_ATTRIBUTE;
+void __tsan_read4(void *addr) SANITIZER_INTERFACE_ATTRIBUTE;
+void __tsan_read8(void *addr) SANITIZER_INTERFACE_ATTRIBUTE;
+void __tsan_read16(void *addr) SANITIZER_INTERFACE_ATTRIBUTE;
+
+void __tsan_write1(void *addr) SANITIZER_INTERFACE_ATTRIBUTE;
+void __tsan_write2(void *addr) SANITIZER_INTERFACE_ATTRIBUTE;
+void __tsan_write4(void *addr) SANITIZER_INTERFACE_ATTRIBUTE;
+void __tsan_write8(void *addr) SANITIZER_INTERFACE_ATTRIBUTE;
+void __tsan_write16(void *addr) SANITIZER_INTERFACE_ATTRIBUTE;
+
+void __tsan_vptr_update(void **vptr_p, void *new_val)
+ SANITIZER_INTERFACE_ATTRIBUTE;
+
+void __tsan_func_entry(void *call_pc) SANITIZER_INTERFACE_ATTRIBUTE;
+void __tsan_func_exit() SANITIZER_INTERFACE_ATTRIBUTE;
+
+void __tsan_read_range(void *addr, unsigned long size) // NOLINT
+ SANITIZER_INTERFACE_ATTRIBUTE;
+void __tsan_write_range(void *addr, unsigned long size) // NOLINT
+ SANITIZER_INTERFACE_ATTRIBUTE;
#ifdef __cplusplus
} // extern "C"
diff --git a/lib/tsan/rtl/tsan_interface_ann.cc b/lib/tsan/rtl/tsan_interface_ann.cc
index a605b6c9d3f0..51ebbf2266dd 100644
--- a/lib/tsan/rtl/tsan_interface_ann.cc
+++ b/lib/tsan/rtl/tsan_interface_ann.cc
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "tsan_interface_ann.h"
#include "tsan_mutex.h"
@@ -52,11 +53,11 @@ class ScopedAnnotation {
if (!flags()->enable_annotations) \
return; \
ThreadState *thr = cur_thread(); \
+ const uptr pc = (uptr)__builtin_return_address(0); \
StatInc(thr, StatAnnotation); \
StatInc(thr, Stat##typ); \
ScopedAnnotation sa(thr, __FUNCTION__, f, l, \
(uptr)__builtin_return_address(0)); \
- const uptr pc = (uptr)&__FUNCTION__; \
(void)pc; \
/**/
@@ -159,73 +160,92 @@ bool IsExpectedReport(uptr addr, uptr size) {
using namespace __tsan; // NOLINT
extern "C" {
-void AnnotateHappensBefore(char *f, int l, uptr addr) {
+void INTERFACE_ATTRIBUTE AnnotateHappensBefore(char *f, int l, uptr addr) {
SCOPED_ANNOTATION(AnnotateHappensBefore);
Release(cur_thread(), CALLERPC, addr);
}
-void AnnotateHappensAfter(char *f, int l, uptr addr) {
+void INTERFACE_ATTRIBUTE AnnotateHappensAfter(char *f, int l, uptr addr) {
SCOPED_ANNOTATION(AnnotateHappensAfter);
Acquire(cur_thread(), CALLERPC, addr);
}
-void AnnotateCondVarSignal(char *f, int l, uptr cv) {
+void INTERFACE_ATTRIBUTE AnnotateCondVarSignal(char *f, int l, uptr cv) {
SCOPED_ANNOTATION(AnnotateCondVarSignal);
}
-void AnnotateCondVarSignalAll(char *f, int l, uptr cv) {
+void INTERFACE_ATTRIBUTE AnnotateCondVarSignalAll(char *f, int l, uptr cv) {
SCOPED_ANNOTATION(AnnotateCondVarSignalAll);
}
-void AnnotateMutexIsNotPHB(char *f, int l, uptr mu) {
+void INTERFACE_ATTRIBUTE AnnotateMutexIsNotPHB(char *f, int l, uptr mu) {
SCOPED_ANNOTATION(AnnotateMutexIsNotPHB);
}
-void AnnotateCondVarWait(char *f, int l, uptr cv, uptr lock) {
+void INTERFACE_ATTRIBUTE AnnotateCondVarWait(char *f, int l, uptr cv,
+ uptr lock) {
SCOPED_ANNOTATION(AnnotateCondVarWait);
}
-void AnnotateRWLockCreate(char *f, int l, uptr lock) {
+void INTERFACE_ATTRIBUTE AnnotateRWLockCreate(char *f, int l, uptr m) {
SCOPED_ANNOTATION(AnnotateRWLockCreate);
+ MutexCreate(thr, pc, m, true, true, false);
}
-void AnnotateRWLockDestroy(char *f, int l, uptr lock) {
+void INTERFACE_ATTRIBUTE AnnotateRWLockCreateStatic(char *f, int l, uptr m) {
+ SCOPED_ANNOTATION(AnnotateRWLockCreateStatic);
+ MutexCreate(thr, pc, m, true, true, true);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateRWLockDestroy(char *f, int l, uptr m) {
SCOPED_ANNOTATION(AnnotateRWLockDestroy);
+ MutexDestroy(thr, pc, m);
}
-void AnnotateRWLockAcquired(char *f, int l, uptr lock, uptr is_w) {
+void INTERFACE_ATTRIBUTE AnnotateRWLockAcquired(char *f, int l, uptr m,
+ uptr is_w) {
SCOPED_ANNOTATION(AnnotateRWLockAcquired);
+ if (is_w)
+ MutexLock(thr, pc, m);
+ else
+ MutexReadLock(thr, pc, m);
}
-void AnnotateRWLockReleased(char *f, int l, uptr lock, uptr is_w) {
+void INTERFACE_ATTRIBUTE AnnotateRWLockReleased(char *f, int l, uptr m,
+ uptr is_w) {
SCOPED_ANNOTATION(AnnotateRWLockReleased);
+ if (is_w)
+ MutexUnlock(thr, pc, m);
+ else
+ MutexReadUnlock(thr, pc, m);
}
-void AnnotateTraceMemory(char *f, int l, uptr mem) {
+void INTERFACE_ATTRIBUTE AnnotateTraceMemory(char *f, int l, uptr mem) {
SCOPED_ANNOTATION(AnnotateTraceMemory);
}
-void AnnotateFlushState(char *f, int l) {
+void INTERFACE_ATTRIBUTE AnnotateFlushState(char *f, int l) {
SCOPED_ANNOTATION(AnnotateFlushState);
}
-void AnnotateNewMemory(char *f, int l, uptr mem, uptr size) {
+void INTERFACE_ATTRIBUTE AnnotateNewMemory(char *f, int l, uptr mem,
+ uptr size) {
SCOPED_ANNOTATION(AnnotateNewMemory);
}
-void AnnotateNoOp(char *f, int l, uptr mem) {
+void INTERFACE_ATTRIBUTE AnnotateNoOp(char *f, int l, uptr mem) {
SCOPED_ANNOTATION(AnnotateNoOp);
}
static void ReportMissedExpectedRace(ExpectRace *race) {
- TsanPrintf("==================\n");
- TsanPrintf("WARNING: ThreadSanitizer: missed expected data race\n");
- TsanPrintf(" %s addr=%zx %s:%d\n",
+ Printf("==================\n");
+ Printf("WARNING: ThreadSanitizer: missed expected data race\n");
+ Printf(" %s addr=%zx %s:%d\n",
race->desc, race->addr, race->file, race->line);
- TsanPrintf("==================\n");
+ Printf("==================\n");
}
-void AnnotateFlushExpectedRaces(char *f, int l) {
+void INTERFACE_ATTRIBUTE AnnotateFlushExpectedRaces(char *f, int l) {
SCOPED_ANNOTATION(AnnotateFlushExpectedRaces);
Lock lock(&dyn_ann_ctx->mtx);
while (dyn_ann_ctx->expect.next != &dyn_ann_ctx->expect) {
@@ -240,32 +260,39 @@ void AnnotateFlushExpectedRaces(char *f, int l) {
}
}
-void AnnotateEnableRaceDetection(char *f, int l, int enable) {
+void INTERFACE_ATTRIBUTE AnnotateEnableRaceDetection(
+ char *f, int l, int enable) {
SCOPED_ANNOTATION(AnnotateEnableRaceDetection);
// FIXME: Reconsider this functionality later. It may be irrelevant.
}
-void AnnotateMutexIsUsedAsCondVar(char *f, int l, uptr mu) {
+void INTERFACE_ATTRIBUTE AnnotateMutexIsUsedAsCondVar(
+ char *f, int l, uptr mu) {
SCOPED_ANNOTATION(AnnotateMutexIsUsedAsCondVar);
}
-void AnnotatePCQGet(char *f, int l, uptr pcq) {
+void INTERFACE_ATTRIBUTE AnnotatePCQGet(
+ char *f, int l, uptr pcq) {
SCOPED_ANNOTATION(AnnotatePCQGet);
}
-void AnnotatePCQPut(char *f, int l, uptr pcq) {
+void INTERFACE_ATTRIBUTE AnnotatePCQPut(
+ char *f, int l, uptr pcq) {
SCOPED_ANNOTATION(AnnotatePCQPut);
}
-void AnnotatePCQDestroy(char *f, int l, uptr pcq) {
+void INTERFACE_ATTRIBUTE AnnotatePCQDestroy(
+ char *f, int l, uptr pcq) {
SCOPED_ANNOTATION(AnnotatePCQDestroy);
}
-void AnnotatePCQCreate(char *f, int l, uptr pcq) {
+void INTERFACE_ATTRIBUTE AnnotatePCQCreate(
+ char *f, int l, uptr pcq) {
SCOPED_ANNOTATION(AnnotatePCQCreate);
}
-void AnnotateExpectRace(char *f, int l, uptr mem, char *desc) {
+void INTERFACE_ATTRIBUTE AnnotateExpectRace(
+ char *f, int l, uptr mem, char *desc) {
SCOPED_ANNOTATION(AnnotateExpectRace);
Lock lock(&dyn_ann_ctx->mtx);
AddExpectRace(&dyn_ann_ctx->expect,
@@ -273,7 +300,8 @@ void AnnotateExpectRace(char *f, int l, uptr mem, char *desc) {
DPrintf("Add expected race: %s addr=%zx %s:%d\n", desc, mem, f, l);
}
-static void BenignRaceImpl(char *f, int l, uptr mem, uptr size, char *desc) {
+static void BenignRaceImpl(
+ char *f, int l, uptr mem, uptr size, char *desc) {
Lock lock(&dyn_ann_ctx->mtx);
AddExpectRace(&dyn_ann_ctx->benign,
f, l, mem, size, desc);
@@ -281,69 +309,76 @@ static void BenignRaceImpl(char *f, int l, uptr mem, uptr size, char *desc) {
}
// FIXME: Turn it off later. WTF is benign race?1?? Go talk to Hans Boehm.
-void AnnotateBenignRaceSized(char *f, int l, uptr mem, uptr size, char *desc) {
+void INTERFACE_ATTRIBUTE AnnotateBenignRaceSized(
+ char *f, int l, uptr mem, uptr size, char *desc) {
SCOPED_ANNOTATION(AnnotateBenignRaceSized);
BenignRaceImpl(f, l, mem, size, desc);
}
-void AnnotateBenignRace(char *f, int l, uptr mem, char *desc) {
+void INTERFACE_ATTRIBUTE AnnotateBenignRace(
+ char *f, int l, uptr mem, char *desc) {
SCOPED_ANNOTATION(AnnotateBenignRace);
BenignRaceImpl(f, l, mem, 1, desc);
}
-void AnnotateIgnoreReadsBegin(char *f, int l) {
+void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsBegin(char *f, int l) {
SCOPED_ANNOTATION(AnnotateIgnoreReadsBegin);
IgnoreCtl(cur_thread(), false, true);
}
-void AnnotateIgnoreReadsEnd(char *f, int l) {
+void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsEnd(char *f, int l) {
SCOPED_ANNOTATION(AnnotateIgnoreReadsEnd);
IgnoreCtl(cur_thread(), false, false);
}
-void AnnotateIgnoreWritesBegin(char *f, int l) {
+void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesBegin(char *f, int l) {
SCOPED_ANNOTATION(AnnotateIgnoreWritesBegin);
IgnoreCtl(cur_thread(), true, true);
}
-void AnnotateIgnoreWritesEnd(char *f, int l) {
+void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesEnd(char *f, int l) {
SCOPED_ANNOTATION(AnnotateIgnoreWritesEnd);
- IgnoreCtl(cur_thread(), true, false);
+ IgnoreCtl(thr, true, false);
}
-void AnnotatePublishMemoryRange(char *f, int l, uptr addr, uptr size) {
+void INTERFACE_ATTRIBUTE AnnotatePublishMemoryRange(
+ char *f, int l, uptr addr, uptr size) {
SCOPED_ANNOTATION(AnnotatePublishMemoryRange);
}
-void AnnotateUnpublishMemoryRange(char *f, int l, uptr addr, uptr size) {
+void INTERFACE_ATTRIBUTE AnnotateUnpublishMemoryRange(
+ char *f, int l, uptr addr, uptr size) {
SCOPED_ANNOTATION(AnnotateUnpublishMemoryRange);
}
-void AnnotateThreadName(char *f, int l, char *name) {
+void INTERFACE_ATTRIBUTE AnnotateThreadName(
+ char *f, int l, char *name) {
SCOPED_ANNOTATION(AnnotateThreadName);
+ ThreadSetName(thr, name);
}
-void WTFAnnotateHappensBefore(char *f, int l, uptr addr) {
+void INTERFACE_ATTRIBUTE WTFAnnotateHappensBefore(char *f, int l, uptr addr) {
SCOPED_ANNOTATION(AnnotateHappensBefore);
}
-void WTFAnnotateHappensAfter(char *f, int l, uptr addr) {
+void INTERFACE_ATTRIBUTE WTFAnnotateHappensAfter(char *f, int l, uptr addr) {
SCOPED_ANNOTATION(AnnotateHappensAfter);
}
-void WTFAnnotateBenignRaceSized(char *f, int l, uptr mem, uptr sz, char *desc) {
+void INTERFACE_ATTRIBUTE WTFAnnotateBenignRaceSized(
+ char *f, int l, uptr mem, uptr sz, char *desc) {
SCOPED_ANNOTATION(AnnotateBenignRaceSized);
}
-int RunningOnValgrind() {
+int INTERFACE_ATTRIBUTE RunningOnValgrind() {
return flags()->running_on_valgrind;
}
-double __attribute__((weak)) ValgrindSlowdown(void) {
+double __attribute__((weak)) INTERFACE_ATTRIBUTE ValgrindSlowdown(void) {
return 10.0;
}
-const char *ThreadSanitizerQuery(const char *query) {
+const char INTERFACE_ATTRIBUTE* ThreadSanitizerQuery(const char *query) {
if (internal_strcmp(query, "pure_happens_before") == 0)
return "1";
else
diff --git a/lib/tsan/rtl/tsan_interface_ann.h b/lib/tsan/rtl/tsan_interface_ann.h
index 09e807a0087d..ed809073327e 100644
--- a/lib/tsan/rtl/tsan_interface_ann.h
+++ b/lib/tsan/rtl/tsan_interface_ann.h
@@ -14,6 +14,8 @@
#ifndef TSAN_INTERFACE_ANN_H
#define TSAN_INTERFACE_ANN_H
+#include <sanitizer/common_interface_defs.h>
+
// This header should NOT include any other headers.
// All functions in this header are extern "C" and start with __tsan_.
@@ -21,8 +23,8 @@
extern "C" {
#endif
-void __tsan_acquire(void *addr);
-void __tsan_release(void *addr);
+void __tsan_acquire(void *addr) SANITIZER_INTERFACE_ATTRIBUTE;
+void __tsan_release(void *addr) SANITIZER_INTERFACE_ATTRIBUTE;
#ifdef __cplusplus
} // extern "C"
diff --git a/lib/tsan/rtl/tsan_interface_atomic.cc b/lib/tsan/rtl/tsan_interface_atomic.cc
index a3982a161b9b..a9d75e5bf76c 100644
--- a/lib/tsan/rtl/tsan_interface_atomic.cc
+++ b/lib/tsan/rtl/tsan_interface_atomic.cc
@@ -11,6 +11,14 @@
//
//===----------------------------------------------------------------------===//
+// ThreadSanitizer atomic operations are based on C++11/C1x standards.
+// For background see C++11 standard. A slightly older, publically
+// available draft of the standard (not entirely up-to-date, but close enough
+// for casual browsing) is available here:
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
+// The following page contains more background information:
+// http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/
+
#include "sanitizer_common/sanitizer_placement_new.h"
#include "tsan_interface_atomic.h"
#include "tsan_flags.h"
@@ -39,12 +47,13 @@ typedef __tsan_atomic8 a8;
typedef __tsan_atomic16 a16;
typedef __tsan_atomic32 a32;
typedef __tsan_atomic64 a64;
-const int mo_relaxed = __tsan_memory_order_relaxed;
-const int mo_consume = __tsan_memory_order_consume;
-const int mo_acquire = __tsan_memory_order_acquire;
-const int mo_release = __tsan_memory_order_release;
-const int mo_acq_rel = __tsan_memory_order_acq_rel;
-const int mo_seq_cst = __tsan_memory_order_seq_cst;
+typedef __tsan_atomic128 a128;
+const morder mo_relaxed = __tsan_memory_order_relaxed;
+const morder mo_consume = __tsan_memory_order_consume;
+const morder mo_acquire = __tsan_memory_order_acquire;
+const morder mo_release = __tsan_memory_order_release;
+const morder mo_acq_rel = __tsan_memory_order_acq_rel;
+const morder mo_seq_cst = __tsan_memory_order_seq_cst;
static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) {
StatInc(thr, StatAtomic);
@@ -52,7 +61,8 @@ static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) {
StatInc(thr, size == 1 ? StatAtomic1
: size == 2 ? StatAtomic2
: size == 4 ? StatAtomic4
- : StatAtomic8);
+ : size == 8 ? StatAtomic8
+ : StatAtomic16);
StatInc(thr, mo == mo_relaxed ? StatAtomicRelaxed
: mo == mo_consume ? StatAtomicConsume
: mo == mo_acquire ? StatAtomicAcquire
@@ -61,9 +71,152 @@ static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) {
: StatAtomicSeq_Cst);
}
+static bool IsLoadOrder(morder mo) {
+ return mo == mo_relaxed || mo == mo_consume
+ || mo == mo_acquire || mo == mo_seq_cst;
+}
+
+static bool IsStoreOrder(morder mo) {
+ return mo == mo_relaxed || mo == mo_release || mo == mo_seq_cst;
+}
+
+static bool IsReleaseOrder(morder mo) {
+ return mo == mo_release || mo == mo_acq_rel || mo == mo_seq_cst;
+}
+
+static bool IsAcquireOrder(morder mo) {
+ return mo == mo_consume || mo == mo_acquire
+ || mo == mo_acq_rel || mo == mo_seq_cst;
+}
+
+static bool IsAcqRelOrder(morder mo) {
+ return mo == mo_acq_rel || mo == mo_seq_cst;
+}
+
+static morder ConvertOrder(morder mo) {
+ if (mo > (morder)100500) {
+ mo = morder(mo - 100500);
+ if (mo == morder(1 << 0))
+ mo = mo_relaxed;
+ else if (mo == morder(1 << 1))
+ mo = mo_consume;
+ else if (mo == morder(1 << 2))
+ mo = mo_acquire;
+ else if (mo == morder(1 << 3))
+ mo = mo_release;
+ else if (mo == morder(1 << 4))
+ mo = mo_acq_rel;
+ else if (mo == morder(1 << 5))
+ mo = mo_seq_cst;
+ }
+ CHECK_GE(mo, mo_relaxed);
+ CHECK_LE(mo, mo_seq_cst);
+ return mo;
+}
+
+template<typename T> T func_xchg(volatile T *v, T op) {
+ T res = __sync_lock_test_and_set(v, op);
+ // __sync_lock_test_and_set does not contain full barrier.
+ __sync_synchronize();
+ return res;
+}
+
+template<typename T> T func_add(volatile T *v, T op) {
+ return __sync_fetch_and_add(v, op);
+}
+
+template<typename T> T func_sub(volatile T *v, T op) {
+ return __sync_fetch_and_sub(v, op);
+}
+
+template<typename T> T func_and(volatile T *v, T op) {
+ return __sync_fetch_and_and(v, op);
+}
+
+template<typename T> T func_or(volatile T *v, T op) {
+ return __sync_fetch_and_or(v, op);
+}
+
+template<typename T> T func_xor(volatile T *v, T op) {
+ return __sync_fetch_and_xor(v, op);
+}
+
+template<typename T> T func_nand(volatile T *v, T op) {
+ // clang does not support __sync_fetch_and_nand.
+ T cmp = *v;
+ for (;;) {
+ T newv = ~(cmp & op);
+ T cur = __sync_val_compare_and_swap(v, cmp, newv);
+ if (cmp == cur)
+ return cmp;
+ cmp = cur;
+ }
+}
+
+template<typename T> T func_cas(volatile T *v, T cmp, T xch) {
+ return __sync_val_compare_and_swap(v, cmp, xch);
+}
+
+// clang does not support 128-bit atomic ops.
+// Atomic ops are executed under tsan internal mutex,
+// here we assume that the atomic variables are not accessed
+// from non-instrumented code.
+#ifndef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16
+a128 func_xchg(volatile a128 *v, a128 op) {
+ a128 cmp = *v;
+ *v = op;
+ return cmp;
+}
+
+a128 func_add(volatile a128 *v, a128 op) {
+ a128 cmp = *v;
+ *v = cmp + op;
+ return cmp;
+}
+
+a128 func_sub(volatile a128 *v, a128 op) {
+ a128 cmp = *v;
+ *v = cmp - op;
+ return cmp;
+}
+
+a128 func_and(volatile a128 *v, a128 op) {
+ a128 cmp = *v;
+ *v = cmp & op;
+ return cmp;
+}
+
+a128 func_or(volatile a128 *v, a128 op) {
+ a128 cmp = *v;
+ *v = cmp | op;
+ return cmp;
+}
+
+a128 func_xor(volatile a128 *v, a128 op) {
+ a128 cmp = *v;
+ *v = cmp ^ op;
+ return cmp;
+}
+
+a128 func_nand(volatile a128 *v, a128 op) {
+ a128 cmp = *v;
+ *v = ~(cmp & op);
+ return cmp;
+}
+
+a128 func_cas(volatile a128 *v, a128 cmp, a128 xch) {
+ a128 cur = *v;
+ if (cur == cmp)
+ *v = xch;
+ return cur;
+}
+#endif
+
#define SCOPED_ATOMIC(func, ...) \
+ mo = ConvertOrder(mo); \
mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \
ThreadState *const thr = cur_thread(); \
+ ProcessPendingSignals(thr); \
const uptr pc = (uptr)__builtin_return_address(0); \
AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \
ScopedAtomic sa(thr, pc, __FUNCTION__); \
@@ -73,93 +226,130 @@ static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) {
template<typename T>
static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a,
morder mo) {
- CHECK(mo & (mo_relaxed | mo_consume | mo_acquire | mo_seq_cst));
+ CHECK(IsLoadOrder(mo));
+ // This fast-path is critical for performance.
+ // Assume the access is atomic.
+ if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a))
+ return *a;
+ SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, false);
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->clock.acquire(&s->clock);
T v = *a;
- if (mo & (mo_consume | mo_acquire | mo_seq_cst))
- Acquire(thr, pc, (uptr)a);
+ s->mtx.ReadUnlock();
+ __sync_synchronize();
return v;
}
template<typename T>
static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
morder mo) {
- CHECK(mo & (mo_relaxed | mo_release | mo_seq_cst));
- if (mo & (mo_release | mo_seq_cst))
- Release(thr, pc, (uptr)a);
+ CHECK(IsStoreOrder(mo));
+ // This fast-path is critical for performance.
+ // Assume the access is atomic.
+ // Strictly saying even relaxed store cuts off release sequence,
+ // so must reset the clock.
+ if (!IsReleaseOrder(mo) && sizeof(T) <= sizeof(a)) {
+ *a = v;
+ return;
+ }
+ __sync_synchronize();
+ SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ thr->clock.ReleaseStore(&s->clock);
*a = v;
+ s->mtx.Unlock();
+ // Trainling memory barrier to provide sequential consistency
+ // for Dekker-like store-load synchronization.
+ __sync_synchronize();
+}
+
+template<typename T, T (*F)(volatile T *v, T op)>
+static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) {
+ SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ if (IsAcqRelOrder(mo))
+ thr->clock.acq_rel(&s->clock);
+ else if (IsReleaseOrder(mo))
+ thr->clock.release(&s->clock);
+ else if (IsAcquireOrder(mo))
+ thr->clock.acquire(&s->clock);
+ v = F(a, v);
+ s->mtx.Unlock();
+ return v;
}
template<typename T>
static T AtomicExchange(ThreadState *thr, uptr pc, volatile T *a, T v,
morder mo) {
- if (mo & (mo_release | mo_acq_rel | mo_seq_cst))
- Release(thr, pc, (uptr)a);
- v = __sync_lock_test_and_set(a, v);
- if (mo & (mo_consume | mo_acquire | mo_acq_rel | mo_seq_cst))
- Acquire(thr, pc, (uptr)a);
- return v;
+ return AtomicRMW<T, func_xchg>(thr, pc, a, v, mo);
}
template<typename T>
static T AtomicFetchAdd(ThreadState *thr, uptr pc, volatile T *a, T v,
morder mo) {
- if (mo & (mo_release | mo_acq_rel | mo_seq_cst))
- Release(thr, pc, (uptr)a);
- v = __sync_fetch_and_add(a, v);
- if (mo & (mo_consume | mo_acquire | mo_acq_rel | mo_seq_cst))
- Acquire(thr, pc, (uptr)a);
- return v;
+ return AtomicRMW<T, func_add>(thr, pc, a, v, mo);
+}
+
+template<typename T>
+static T AtomicFetchSub(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ return AtomicRMW<T, func_sub>(thr, pc, a, v, mo);
}
template<typename T>
static T AtomicFetchAnd(ThreadState *thr, uptr pc, volatile T *a, T v,
morder mo) {
- if (mo & (mo_release | mo_acq_rel | mo_seq_cst))
- Release(thr, pc, (uptr)a);
- v = __sync_fetch_and_and(a, v);
- if (mo & (mo_consume | mo_acquire | mo_acq_rel | mo_seq_cst))
- Acquire(thr, pc, (uptr)a);
- return v;
+ return AtomicRMW<T, func_and>(thr, pc, a, v, mo);
}
template<typename T>
static T AtomicFetchOr(ThreadState *thr, uptr pc, volatile T *a, T v,
morder mo) {
- if (mo & (mo_release | mo_acq_rel | mo_seq_cst))
- Release(thr, pc, (uptr)a);
- v = __sync_fetch_and_or(a, v);
- if (mo & (mo_consume | mo_acquire | mo_acq_rel | mo_seq_cst))
- Acquire(thr, pc, (uptr)a);
- return v;
+ return AtomicRMW<T, func_or>(thr, pc, a, v, mo);
}
template<typename T>
static T AtomicFetchXor(ThreadState *thr, uptr pc, volatile T *a, T v,
morder mo) {
- if (mo & (mo_release | mo_acq_rel | mo_seq_cst))
- Release(thr, pc, (uptr)a);
- v = __sync_fetch_and_xor(a, v);
- if (mo & (mo_consume | mo_acquire | mo_acq_rel | mo_seq_cst))
- Acquire(thr, pc, (uptr)a);
- return v;
+ return AtomicRMW<T, func_xor>(thr, pc, a, v, mo);
+}
+
+template<typename T>
+static T AtomicFetchNand(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ return AtomicRMW<T, func_nand>(thr, pc, a, v, mo);
}
template<typename T>
static bool AtomicCAS(ThreadState *thr, uptr pc,
- volatile T *a, T *c, T v, morder mo) {
- if (mo & (mo_release | mo_acq_rel | mo_seq_cst))
- Release(thr, pc, (uptr)a);
+ volatile T *a, T *c, T v, morder mo, morder fmo) {
+ (void)fmo; // Unused because llvm does not pass it yet.
+ SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true);
+ thr->clock.set(thr->tid, thr->fast_state.epoch());
+ if (IsAcqRelOrder(mo))
+ thr->clock.acq_rel(&s->clock);
+ else if (IsReleaseOrder(mo))
+ thr->clock.release(&s->clock);
+ else if (IsAcquireOrder(mo))
+ thr->clock.acquire(&s->clock);
T cc = *c;
- T pr = __sync_val_compare_and_swap(a, cc, v);
- if (mo & (mo_consume | mo_acquire | mo_acq_rel | mo_seq_cst))
- Acquire(thr, pc, (uptr)a);
+ T pr = func_cas(a, cc, v);
+ s->mtx.Unlock();
if (pr == cc)
return true;
*c = pr;
return false;
}
+template<typename T>
+static T AtomicCAS(ThreadState *thr, uptr pc,
+ volatile T *a, T c, T v, morder mo, morder fmo) {
+ AtomicCAS(thr, pc, a, &c, v, mo, fmo);
+ return c;
+}
+
static void AtomicFence(ThreadState *thr, uptr pc, morder mo) {
+ // FIXME(dvyukov): not implemented.
__sync_synchronize();
}
@@ -179,6 +369,12 @@ a64 __tsan_atomic64_load(const volatile a64 *a, morder mo) {
SCOPED_ATOMIC(Load, a, mo);
}
+#if __TSAN_HAS_INT128
+a128 __tsan_atomic128_load(const volatile a128 *a, morder mo) {
+ SCOPED_ATOMIC(Load, a, mo);
+}
+#endif
+
void __tsan_atomic8_store(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(Store, a, v, mo);
}
@@ -195,6 +391,12 @@ void __tsan_atomic64_store(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(Store, a, v, mo);
}
+#if __TSAN_HAS_INT128
+void __tsan_atomic128_store(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(Store, a, v, mo);
+}
+#endif
+
a8 __tsan_atomic8_exchange(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(Exchange, a, v, mo);
}
@@ -211,6 +413,12 @@ a64 __tsan_atomic64_exchange(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(Exchange, a, v, mo);
}
+#if __TSAN_HAS_INT128
+a128 __tsan_atomic128_exchange(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+#endif
+
a8 __tsan_atomic8_fetch_add(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(FetchAdd, a, v, mo);
}
@@ -227,6 +435,34 @@ a64 __tsan_atomic64_fetch_add(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(FetchAdd, a, v, mo);
}
+#if __TSAN_HAS_INT128
+a128 __tsan_atomic128_fetch_add(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+#endif
+
+a8 __tsan_atomic8_fetch_sub(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+
+a16 __tsan_atomic16_fetch_sub(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+
+a32 __tsan_atomic32_fetch_sub(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+
+a64 __tsan_atomic64_fetch_sub(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+
+#if __TSAN_HAS_INT128
+a128 __tsan_atomic128_fetch_sub(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+#endif
+
a8 __tsan_atomic8_fetch_and(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(FetchAnd, a, v, mo);
}
@@ -243,6 +479,12 @@ a64 __tsan_atomic64_fetch_and(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(FetchAnd, a, v, mo);
}
+#if __TSAN_HAS_INT128
+a128 __tsan_atomic128_fetch_and(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+#endif
+
a8 __tsan_atomic8_fetch_or(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(FetchOr, a, v, mo);
}
@@ -259,6 +501,12 @@ a64 __tsan_atomic64_fetch_or(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(FetchOr, a, v, mo);
}
+#if __TSAN_HAS_INT128
+a128 __tsan_atomic128_fetch_or(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+#endif
+
a8 __tsan_atomic8_fetch_xor(volatile a8 *a, a8 v, morder mo) {
SCOPED_ATOMIC(FetchXor, a, v, mo);
}
@@ -275,47 +523,118 @@ a64 __tsan_atomic64_fetch_xor(volatile a64 *a, a64 v, morder mo) {
SCOPED_ATOMIC(FetchXor, a, v, mo);
}
+#if __TSAN_HAS_INT128
+a128 __tsan_atomic128_fetch_xor(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+#endif
+
+a8 __tsan_atomic8_fetch_nand(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(FetchNand, a, v, mo);
+}
+
+a16 __tsan_atomic16_fetch_nand(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(FetchNand, a, v, mo);
+}
+
+a32 __tsan_atomic32_fetch_nand(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(FetchNand, a, v, mo);
+}
+
+a64 __tsan_atomic64_fetch_nand(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(FetchNand, a, v, mo);
+}
+
+#if __TSAN_HAS_INT128
+a128 __tsan_atomic128_fetch_nand(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(FetchNand, a, v, mo);
+}
+#endif
+
int __tsan_atomic8_compare_exchange_strong(volatile a8 *a, a8 *c, a8 v,
- morder mo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo);
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
int __tsan_atomic16_compare_exchange_strong(volatile a16 *a, a16 *c, a16 v,
- morder mo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo);
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
int __tsan_atomic32_compare_exchange_strong(volatile a32 *a, a32 *c, a32 v,
- morder mo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo);
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
int __tsan_atomic64_compare_exchange_strong(volatile a64 *a, a64 *c, a64 v,
- morder mo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo);
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+#if __TSAN_HAS_INT128
+int __tsan_atomic128_compare_exchange_strong(volatile a128 *a, a128 *c, a128 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
+#endif
int __tsan_atomic8_compare_exchange_weak(volatile a8 *a, a8 *c, a8 v,
- morder mo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo);
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
int __tsan_atomic16_compare_exchange_weak(volatile a16 *a, a16 *c, a16 v,
- morder mo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo);
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
int __tsan_atomic32_compare_exchange_weak(volatile a32 *a, a32 *c, a32 v,
- morder mo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo);
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
int __tsan_atomic64_compare_exchange_weak(volatile a64 *a, a64 *c, a64 v,
- morder mo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo);
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+#if __TSAN_HAS_INT128
+int __tsan_atomic128_compare_exchange_weak(volatile a128 *a, a128 *c, a128 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+#endif
+
+a8 __tsan_atomic8_compare_exchange_val(volatile a8 *a, a8 c, a8 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+a16 __tsan_atomic16_compare_exchange_val(volatile a16 *a, a16 c, a16 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
}
+a32 __tsan_atomic32_compare_exchange_val(volatile a32 *a, a32 c, a32 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+a64 __tsan_atomic64_compare_exchange_val(volatile a64 *a, a64 c, a64 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+#if __TSAN_HAS_INT128
+a128 __tsan_atomic64_compare_exchange_val(volatile a128 *a, a128 c, a128 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+#endif
+
void __tsan_atomic_thread_fence(morder mo) {
char* a;
SCOPED_ATOMIC(Fence, mo);
}
+
+void __tsan_atomic_signal_fence(morder mo) {
+}
diff --git a/lib/tsan/rtl/tsan_interface_atomic.h b/lib/tsan/rtl/tsan_interface_atomic.h
index dff32b1473f4..5352d56679f7 100644
--- a/lib/tsan/rtl/tsan_interface_atomic.h
+++ b/lib/tsan/rtl/tsan_interface_atomic.h
@@ -13,109 +13,193 @@
#ifndef TSAN_INTERFACE_ATOMIC_H
#define TSAN_INTERFACE_ATOMIC_H
+#ifndef INTERFACE_ATTRIBUTE
+# define INTERFACE_ATTRIBUTE __attribute__((visibility("default")))
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
-typedef char __tsan_atomic8;
-typedef short __tsan_atomic16; // NOLINT
-typedef int __tsan_atomic32;
-typedef long __tsan_atomic64; // NOLINT
+typedef char __tsan_atomic8;
+typedef short __tsan_atomic16; // NOLINT
+typedef int __tsan_atomic32;
+typedef long __tsan_atomic64; // NOLINT
+
+#if defined(__SIZEOF_INT128__) \
+ || (__clang_major__ * 100 + __clang_minor__ >= 302)
+__extension__ typedef __int128 __tsan_atomic128;
+#define __TSAN_HAS_INT128 1
+#else
+typedef char __tsan_atomic128;
+#define __TSAN_HAS_INT128 0
+#endif
+// Part of ABI, do not change.
+// http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/atomic?view=markup
typedef enum {
- __tsan_memory_order_relaxed = 1 << 0,
- __tsan_memory_order_consume = 1 << 1,
- __tsan_memory_order_acquire = 1 << 2,
- __tsan_memory_order_release = 1 << 3,
- __tsan_memory_order_acq_rel = 1 << 4,
- __tsan_memory_order_seq_cst = 1 << 5,
+ __tsan_memory_order_relaxed,
+ __tsan_memory_order_consume,
+ __tsan_memory_order_acquire,
+ __tsan_memory_order_release,
+ __tsan_memory_order_acq_rel,
+ __tsan_memory_order_seq_cst
} __tsan_memory_order;
__tsan_atomic8 __tsan_atomic8_load(const volatile __tsan_atomic8 *a,
- __tsan_memory_order mo);
+ __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic16 __tsan_atomic16_load(const volatile __tsan_atomic16 *a,
- __tsan_memory_order mo);
+ __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic32 __tsan_atomic32_load(const volatile __tsan_atomic32 *a,
- __tsan_memory_order mo);
+ __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic64 __tsan_atomic64_load(const volatile __tsan_atomic64 *a,
- __tsan_memory_order mo);
+ __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic128 __tsan_atomic128_load(const volatile __tsan_atomic128 *a,
+ __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
void __tsan_atomic8_store(volatile __tsan_atomic8 *a, __tsan_atomic8 v,
- __tsan_memory_order mo);
+ __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
void __tsan_atomic16_store(volatile __tsan_atomic16 *a, __tsan_atomic16 v,
- __tsan_memory_order mo);
+ __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
void __tsan_atomic32_store(volatile __tsan_atomic32 *a, __tsan_atomic32 v,
- __tsan_memory_order mo);
+ __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
void __tsan_atomic64_store(volatile __tsan_atomic64 *a, __tsan_atomic64 v,
- __tsan_memory_order mo);
+ __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+void __tsan_atomic128_store(volatile __tsan_atomic128 *a, __tsan_atomic128 v,
+ __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic8 __tsan_atomic8_exchange(volatile __tsan_atomic8 *a,
- __tsan_atomic8 v, __tsan_memory_order mo);
+ __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic16 __tsan_atomic16_exchange(volatile __tsan_atomic16 *a,
- __tsan_atomic16 v, __tsan_memory_order mo);
+ __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic32 __tsan_atomic32_exchange(volatile __tsan_atomic32 *a,
- __tsan_atomic32 v, __tsan_memory_order mo);
+ __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic64 __tsan_atomic64_exchange(volatile __tsan_atomic64 *a,
- __tsan_atomic64 v, __tsan_memory_order mo);
+ __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic128 __tsan_atomic128_exchange(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic8 __tsan_atomic8_fetch_add(volatile __tsan_atomic8 *a,
- __tsan_atomic8 v, __tsan_memory_order mo);
+ __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic16 __tsan_atomic16_fetch_add(volatile __tsan_atomic16 *a,
- __tsan_atomic16 v, __tsan_memory_order mo);
+ __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic32 __tsan_atomic32_fetch_add(volatile __tsan_atomic32 *a,
- __tsan_atomic32 v, __tsan_memory_order mo);
+ __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic64 __tsan_atomic64_fetch_add(volatile __tsan_atomic64 *a,
- __tsan_atomic64 v, __tsan_memory_order mo);
+ __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic128 __tsan_atomic128_fetch_add(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+
+__tsan_atomic8 __tsan_atomic8_fetch_sub(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic16 __tsan_atomic16_fetch_sub(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic32 __tsan_atomic32_fetch_sub(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic64 __tsan_atomic64_fetch_sub(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic128 __tsan_atomic128_fetch_sub(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic8 __tsan_atomic8_fetch_and(volatile __tsan_atomic8 *a,
- __tsan_atomic8 v, __tsan_memory_order mo);
+ __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic16 __tsan_atomic16_fetch_and(volatile __tsan_atomic16 *a,
- __tsan_atomic16 v, __tsan_memory_order mo);
+ __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic32 __tsan_atomic32_fetch_and(volatile __tsan_atomic32 *a,
- __tsan_atomic32 v, __tsan_memory_order mo);
+ __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic64 __tsan_atomic64_fetch_and(volatile __tsan_atomic64 *a,
- __tsan_atomic64 v, __tsan_memory_order mo);
+ __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic128 __tsan_atomic128_fetch_and(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic8 __tsan_atomic8_fetch_or(volatile __tsan_atomic8 *a,
- __tsan_atomic8 v, __tsan_memory_order mo);
+ __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic16 __tsan_atomic16_fetch_or(volatile __tsan_atomic16 *a,
- __tsan_atomic16 v, __tsan_memory_order mo);
+ __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic32 __tsan_atomic32_fetch_or(volatile __tsan_atomic32 *a,
- __tsan_atomic32 v, __tsan_memory_order mo);
+ __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic64 __tsan_atomic64_fetch_or(volatile __tsan_atomic64 *a,
- __tsan_atomic64 v, __tsan_memory_order mo);
+ __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic128 __tsan_atomic128_fetch_or(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic8 __tsan_atomic8_fetch_xor(volatile __tsan_atomic8 *a,
- __tsan_atomic8 v, __tsan_memory_order mo);
+ __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic16 __tsan_atomic16_fetch_xor(volatile __tsan_atomic16 *a,
- __tsan_atomic16 v, __tsan_memory_order mo);
+ __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic32 __tsan_atomic32_fetch_xor(volatile __tsan_atomic32 *a,
- __tsan_atomic32 v, __tsan_memory_order mo);
+ __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
__tsan_atomic64 __tsan_atomic64_fetch_xor(volatile __tsan_atomic64 *a,
- __tsan_atomic64 v, __tsan_memory_order mo);
+ __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic128 __tsan_atomic128_fetch_xor(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+
+__tsan_atomic8 __tsan_atomic8_fetch_nand(volatile __tsan_atomic8 *a,
+ __tsan_atomic8 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic16 __tsan_atomic16_fetch_nand(volatile __tsan_atomic16 *a,
+ __tsan_atomic16 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic32 __tsan_atomic32_fetch_nand(volatile __tsan_atomic32 *a,
+ __tsan_atomic32 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic64 __tsan_atomic64_fetch_nand(volatile __tsan_atomic64 *a,
+ __tsan_atomic64 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic128 __tsan_atomic128_fetch_nand(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 v, __tsan_memory_order mo) INTERFACE_ATTRIBUTE;
int __tsan_atomic8_compare_exchange_weak(volatile __tsan_atomic8 *a,
- __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo);
+ __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
int __tsan_atomic16_compare_exchange_weak(volatile __tsan_atomic16 *a,
- __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo);
+ __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
int __tsan_atomic32_compare_exchange_weak(volatile __tsan_atomic32 *a,
- __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo);
+ __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
int __tsan_atomic64_compare_exchange_weak(volatile __tsan_atomic64 *a,
- __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo);
+ __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+int __tsan_atomic128_compare_exchange_weak(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 *c, __tsan_atomic128 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
int __tsan_atomic8_compare_exchange_strong(volatile __tsan_atomic8 *a,
- __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo);
+ __tsan_atomic8 *c, __tsan_atomic8 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
int __tsan_atomic16_compare_exchange_strong(volatile __tsan_atomic16 *a,
- __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo);
+ __tsan_atomic16 *c, __tsan_atomic16 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
int __tsan_atomic32_compare_exchange_strong(volatile __tsan_atomic32 *a,
- __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo);
+ __tsan_atomic32 *c, __tsan_atomic32 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
int __tsan_atomic64_compare_exchange_strong(volatile __tsan_atomic64 *a,
- __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo);
+ __tsan_atomic64 *c, __tsan_atomic64 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+int __tsan_atomic128_compare_exchange_strong(volatile __tsan_atomic128 *a,
+ __tsan_atomic128 *c, __tsan_atomic128 v, __tsan_memory_order mo,
+ __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
-void __tsan_atomic_thread_fence(__tsan_memory_order mo);
+__tsan_atomic8 __tsan_atomic8_compare_exchange_val(
+ volatile __tsan_atomic8 *a, __tsan_atomic8 c, __tsan_atomic8 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic16 __tsan_atomic16_compare_exchange_val(
+ volatile __tsan_atomic16 *a, __tsan_atomic16 c, __tsan_atomic16 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic32 __tsan_atomic32_compare_exchange_val(
+ volatile __tsan_atomic32 *a, __tsan_atomic32 c, __tsan_atomic32 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic64 __tsan_atomic64_compare_exchange_val(
+ volatile __tsan_atomic64 *a, __tsan_atomic64 c, __tsan_atomic64 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+__tsan_atomic128 __tsan_atomic128_compare_exchange_val(
+ volatile __tsan_atomic128 *a, __tsan_atomic128 c, __tsan_atomic128 v,
+ __tsan_memory_order mo, __tsan_memory_order fail_mo) INTERFACE_ATTRIBUTE;
+
+void __tsan_atomic_thread_fence(__tsan_memory_order mo) INTERFACE_ATTRIBUTE;
+void __tsan_atomic_signal_fence(__tsan_memory_order mo) INTERFACE_ATTRIBUTE;
#ifdef __cplusplus
} // extern "C"
#endif
+#undef INTERFACE_ATTRIBUTE
+
#endif // #ifndef TSAN_INTERFACE_ATOMIC_H
diff --git a/lib/tsan/rtl/tsan_interface_inl.h b/lib/tsan/rtl/tsan_interface_inl.h
index 233f9028a63b..8a92155d57ef 100644
--- a/lib/tsan/rtl/tsan_interface_inl.h
+++ b/lib/tsan/rtl/tsan_interface_inl.h
@@ -63,3 +63,11 @@ void __tsan_func_entry(void *pc) {
void __tsan_func_exit() {
FuncExit(cur_thread());
}
+
+void __tsan_read_range(void *addr, uptr size) {
+ MemoryAccessRange(cur_thread(), CALLERPC, (uptr)addr, size, false);
+}
+
+void __tsan_write_range(void *addr, uptr size) {
+ MemoryAccessRange(cur_thread(), CALLERPC, (uptr)addr, size, true);
+}
diff --git a/lib/tsan/rtl/tsan_interface_java.cc b/lib/tsan/rtl/tsan_interface_java.cc
new file mode 100644
index 000000000000..e425c75800be
--- /dev/null
+++ b/lib/tsan/rtl/tsan_interface_java.cc
@@ -0,0 +1,305 @@
+//===-- tsan_interface_java.cc --------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tsan_interface_java.h"
+#include "tsan_rtl.h"
+#include "tsan_mutex.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+
+using namespace __tsan; // NOLINT
+
+namespace __tsan {
+
+const uptr kHeapShadow = 0x300000000000ull;
+const uptr kHeapAlignment = 8;
+
+struct BlockDesc {
+ bool begin;
+ Mutex mtx;
+ SyncVar *head;
+
+ BlockDesc()
+ : mtx(MutexTypeJavaMBlock, StatMtxJavaMBlock)
+ , head() {
+ CHECK_EQ(begin, false);
+ begin = true;
+ }
+
+ ~BlockDesc() {
+ CHECK_EQ(begin, true);
+ begin = false;
+ ThreadState *thr = cur_thread();
+ SyncVar *s = head;
+ while (s) {
+ SyncVar *s1 = s->next;
+ StatInc(thr, StatSyncDestroyed);
+ s->mtx.Lock();
+ s->mtx.Unlock();
+ thr->mset.Remove(s->GetId());
+ DestroyAndFree(s);
+ s = s1;
+ }
+ }
+};
+
+struct JavaContext {
+ const uptr heap_begin;
+ const uptr heap_size;
+ BlockDesc *heap_shadow;
+
+ JavaContext(jptr heap_begin, jptr heap_size)
+ : heap_begin(heap_begin)
+ , heap_size(heap_size) {
+ uptr size = heap_size / kHeapAlignment * sizeof(BlockDesc);
+ heap_shadow = (BlockDesc*)MmapFixedNoReserve(kHeapShadow, size);
+ if ((uptr)heap_shadow != kHeapShadow) {
+ Printf("ThreadSanitizer: failed to mmap Java heap shadow\n");
+ Die();
+ }
+ }
+};
+
+class ScopedJavaFunc {
+ public:
+ ScopedJavaFunc(ThreadState *thr, uptr pc)
+ : thr_(thr) {
+ Initialize(thr_);
+ FuncEntry(thr, pc);
+ CHECK_EQ(thr_->in_rtl, 0);
+ thr_->in_rtl++;
+ }
+
+ ~ScopedJavaFunc() {
+ thr_->in_rtl--;
+ CHECK_EQ(thr_->in_rtl, 0);
+ FuncExit(thr_);
+ // FIXME(dvyukov): process pending signals.
+ }
+
+ private:
+ ThreadState *thr_;
+};
+
+static u64 jctx_buf[sizeof(JavaContext) / sizeof(u64) + 1];
+static JavaContext *jctx;
+
+static BlockDesc *getblock(uptr addr) {
+ uptr i = (addr - jctx->heap_begin) / kHeapAlignment;
+ return &jctx->heap_shadow[i];
+}
+
+static uptr USED getmem(BlockDesc *b) {
+ uptr i = b - jctx->heap_shadow;
+ uptr p = jctx->heap_begin + i * kHeapAlignment;
+ CHECK_GE(p, jctx->heap_begin);
+ CHECK_LT(p, jctx->heap_begin + jctx->heap_size);
+ return p;
+}
+
+static BlockDesc *getblockbegin(uptr addr) {
+ for (BlockDesc *b = getblock(addr);; b--) {
+ CHECK_GE(b, jctx->heap_shadow);
+ if (b->begin)
+ return b;
+ }
+ return 0;
+}
+
+SyncVar* GetJavaSync(ThreadState *thr, uptr pc, uptr addr,
+ bool write_lock, bool create) {
+ if (jctx == 0 || addr < jctx->heap_begin
+ || addr >= jctx->heap_begin + jctx->heap_size)
+ return 0;
+ BlockDesc *b = getblockbegin(addr);
+ DPrintf("#%d: GetJavaSync %p->%p\n", thr->tid, addr, b);
+ Lock l(&b->mtx);
+ SyncVar *s = b->head;
+ for (; s; s = s->next) {
+ if (s->addr == addr) {
+ DPrintf("#%d: found existing sync for %p\n", thr->tid, addr);
+ break;
+ }
+ }
+ if (s == 0 && create) {
+ DPrintf("#%d: creating new sync for %p\n", thr->tid, addr);
+ s = CTX()->synctab.Create(thr, pc, addr);
+ s->next = b->head;
+ b->head = s;
+ }
+ if (s) {
+ if (write_lock)
+ s->mtx.Lock();
+ else
+ s->mtx.ReadLock();
+ }
+ return s;
+}
+
+SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) {
+ // We do not destroy Java mutexes other than in __tsan_java_free().
+ return 0;
+}
+
+} // namespace __tsan {
+
+#define SCOPED_JAVA_FUNC(func) \
+ ThreadState *thr = cur_thread(); \
+ const uptr caller_pc = GET_CALLER_PC(); \
+ const uptr pc = (uptr)&func; \
+ (void)pc; \
+ ScopedJavaFunc scoped(thr, caller_pc); \
+/**/
+
+void __tsan_java_init(jptr heap_begin, jptr heap_size) {
+ SCOPED_JAVA_FUNC(__tsan_java_init);
+ DPrintf("#%d: java_init(%p, %p)\n", thr->tid, heap_begin, heap_size);
+ CHECK_EQ(jctx, 0);
+ CHECK_GT(heap_begin, 0);
+ CHECK_GT(heap_size, 0);
+ CHECK_EQ(heap_begin % kHeapAlignment, 0);
+ CHECK_EQ(heap_size % kHeapAlignment, 0);
+ CHECK_LT(heap_begin, heap_begin + heap_size);
+ jctx = new(jctx_buf) JavaContext(heap_begin, heap_size);
+}
+
+int __tsan_java_fini() {
+ SCOPED_JAVA_FUNC(__tsan_java_fini);
+ DPrintf("#%d: java_fini()\n", thr->tid);
+ CHECK_NE(jctx, 0);
+ // FIXME(dvyukov): this does not call atexit() callbacks.
+ int status = Finalize(thr);
+ DPrintf("#%d: java_fini() = %d\n", thr->tid, status);
+ return status;
+}
+
+void __tsan_java_alloc(jptr ptr, jptr size) {
+ SCOPED_JAVA_FUNC(__tsan_java_alloc);
+ DPrintf("#%d: java_alloc(%p, %p)\n", thr->tid, ptr, size);
+ CHECK_NE(jctx, 0);
+ CHECK_NE(size, 0);
+ CHECK_EQ(ptr % kHeapAlignment, 0);
+ CHECK_EQ(size % kHeapAlignment, 0);
+ CHECK_GE(ptr, jctx->heap_begin);
+ CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size);
+
+ BlockDesc *b = getblock(ptr);
+ new(b) BlockDesc();
+}
+
+void __tsan_java_free(jptr ptr, jptr size) {
+ SCOPED_JAVA_FUNC(__tsan_java_free);
+ DPrintf("#%d: java_free(%p, %p)\n", thr->tid, ptr, size);
+ CHECK_NE(jctx, 0);
+ CHECK_NE(size, 0);
+ CHECK_EQ(ptr % kHeapAlignment, 0);
+ CHECK_EQ(size % kHeapAlignment, 0);
+ CHECK_GE(ptr, jctx->heap_begin);
+ CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size);
+
+ BlockDesc *beg = getblock(ptr);
+ BlockDesc *end = getblock(ptr + size);
+ for (BlockDesc *b = beg; b != end; b++) {
+ if (b->begin)
+ b->~BlockDesc();
+ }
+}
+
+void __tsan_java_move(jptr src, jptr dst, jptr size) {
+ SCOPED_JAVA_FUNC(__tsan_java_move);
+ DPrintf("#%d: java_move(%p, %p, %p)\n", thr->tid, src, dst, size);
+ CHECK_NE(jctx, 0);
+ CHECK_NE(size, 0);
+ CHECK_EQ(src % kHeapAlignment, 0);
+ CHECK_EQ(dst % kHeapAlignment, 0);
+ CHECK_EQ(size % kHeapAlignment, 0);
+ CHECK_GE(src, jctx->heap_begin);
+ CHECK_LE(src + size, jctx->heap_begin + jctx->heap_size);
+ CHECK_GE(dst, jctx->heap_begin);
+ CHECK_LE(dst + size, jctx->heap_begin + jctx->heap_size);
+ CHECK(dst >= src + size || src >= dst + size);
+
+ // Assuming it's not running concurrently with threads that do
+ // memory accesses and mutex operations (stop-the-world phase).
+ { // NOLINT
+ BlockDesc *s = getblock(src);
+ BlockDesc *d = getblock(dst);
+ BlockDesc *send = getblock(src + size);
+ for (; s != send; s++, d++) {
+ CHECK_EQ(d->begin, false);
+ if (s->begin) {
+ DPrintf("#%d: moving block %p->%p\n", thr->tid, getmem(s), getmem(d));
+ new(d) BlockDesc;
+ d->head = s->head;
+ for (SyncVar *sync = d->head; sync; sync = sync->next) {
+ uptr newaddr = sync->addr - src + dst;
+ DPrintf("#%d: moving sync %p->%p\n", thr->tid, sync->addr, newaddr);
+ sync->addr = newaddr;
+ }
+ s->head = 0;
+ s->~BlockDesc();
+ }
+ }
+ }
+
+ { // NOLINT
+ u64 *s = (u64*)MemToShadow(src);
+ u64 *d = (u64*)MemToShadow(dst);
+ u64 *send = (u64*)MemToShadow(src + size);
+ for (; s != send; s++, d++) {
+ *d = *s;
+ *s = 0;
+ }
+ }
+}
+
+void __tsan_java_mutex_lock(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_mutex_lock);
+ DPrintf("#%d: java_mutex_lock(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ MutexLock(thr, pc, addr);
+}
+
+void __tsan_java_mutex_unlock(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_mutex_unlock);
+ DPrintf("#%d: java_mutex_unlock(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ MutexUnlock(thr, pc, addr);
+}
+
+void __tsan_java_mutex_read_lock(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_mutex_read_lock);
+ DPrintf("#%d: java_mutex_read_lock(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ MutexReadLock(thr, pc, addr);
+}
+
+void __tsan_java_mutex_read_unlock(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_mutex_read_unlock);
+ DPrintf("#%d: java_mutex_read_unlock(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ MutexReadUnlock(thr, pc, addr);
+}
diff --git a/lib/tsan/rtl/tsan_interface_java.h b/lib/tsan/rtl/tsan_interface_java.h
new file mode 100644
index 000000000000..241483aaa015
--- /dev/null
+++ b/lib/tsan/rtl/tsan_interface_java.h
@@ -0,0 +1,74 @@
+//===-- tsan_interface_java.h -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Interface for verification of Java or mixed Java/C++ programs.
+// The interface is intended to be used from within a JVM and notify TSan
+// about such events like Java locks and GC memory compaction.
+//
+// For plain memory accesses and function entry/exit a JVM is intended to use
+// C++ interfaces: __tsan_readN/writeN and __tsan_func_enter/exit.
+//
+// For volatile memory accesses and atomic operations JVM is intended to use
+// standard atomics API: __tsan_atomicN_load/store/etc.
+//
+// For usage examples see lit_tests/java_*.cc
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_INTERFACE_JAVA_H
+#define TSAN_INTERFACE_JAVA_H
+
+#ifndef INTERFACE_ATTRIBUTE
+# define INTERFACE_ATTRIBUTE __attribute__((visibility("default")))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned long jptr; // NOLINT
+
+// Must be called before any other callback from Java.
+void __tsan_java_init(jptr heap_begin, jptr heap_size) INTERFACE_ATTRIBUTE;
+// Must be called when the application exits.
+// Not necessary the last callback (concurrently running threads are OK).
+// Returns exit status or 0 if tsan does not want to override it.
+int __tsan_java_fini() INTERFACE_ATTRIBUTE;
+
+// Callback for memory allocations.
+// May be omitted for allocations that are not subject to data races
+// nor contain synchronization objects (e.g. String).
+void __tsan_java_alloc(jptr ptr, jptr size) INTERFACE_ATTRIBUTE;
+// Callback for memory free.
+// Can be aggregated for several objects (preferably).
+void __tsan_java_free(jptr ptr, jptr size) INTERFACE_ATTRIBUTE;
+// Callback for memory move by GC.
+// Can be aggregated for several objects (preferably).
+// The ranges must not overlap.
+void __tsan_java_move(jptr src, jptr dst, jptr size) INTERFACE_ATTRIBUTE;
+
+// Mutex lock.
+// Addr is any unique address associated with the mutex.
+// Must not be called on recursive reentry.
+// Object.wait() is handled as a pair of unlock/lock.
+void __tsan_java_mutex_lock(jptr addr) INTERFACE_ATTRIBUTE;
+// Mutex unlock.
+void __tsan_java_mutex_unlock(jptr addr) INTERFACE_ATTRIBUTE;
+// Mutex read lock.
+void __tsan_java_mutex_read_lock(jptr addr) INTERFACE_ATTRIBUTE;
+// Mutex read unlock.
+void __tsan_java_mutex_read_unlock(jptr addr) INTERFACE_ATTRIBUTE;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#undef INTERFACE_ATTRIBUTE
+
+#endif // #ifndef TSAN_INTERFACE_JAVA_H
diff --git a/lib/tsan/rtl/tsan_mman.cc b/lib/tsan/rtl/tsan_mman.cc
index 7f956dfe26bf..82f7105d60db 100644
--- a/lib/tsan/rtl/tsan_mman.cc
+++ b/lib/tsan/rtl/tsan_mman.cc
@@ -11,34 +11,63 @@
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
#include "tsan_mman.h"
#include "tsan_rtl.h"
#include "tsan_report.h"
#include "tsan_flags.h"
+// May be overriden by front-end.
+extern "C" void WEAK __tsan_malloc_hook(void *ptr, uptr size) {
+ (void)ptr;
+ (void)size;
+}
+
+extern "C" void WEAK __tsan_free_hook(void *ptr) {
+ (void)ptr;
+}
+
namespace __tsan {
+static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64);
+Allocator *allocator() {
+ return reinterpret_cast<Allocator*>(&allocator_placeholder);
+}
+
+void InitializeAllocator() {
+ allocator()->Init();
+}
+
+void AlloctorThreadFinish(ThreadState *thr) {
+ allocator()->SwallowCache(&thr->alloc_cache);
+}
+
static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
if (!thr->in_signal_handler || !flags()->report_signal_unsafe)
return;
+ Context *ctx = CTX();
StackTrace stack;
stack.ObtainCurrent(thr, pc);
+ Lock l(&ctx->thread_mtx);
ScopedReport rep(ReportTypeSignalUnsafe);
- rep.AddStack(&stack);
- OutputReport(rep, rep.GetReport()->stacks[0]);
+ if (!IsFiredSuppression(ctx, rep, stack)) {
+ rep.AddStack(&stack);
+ OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
+ }
}
-void *user_alloc(ThreadState *thr, uptr pc, uptr sz) {
+void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
CHECK_GT(thr->in_rtl, 0);
- if (sz + sizeof(MBlock) < sz)
- return 0;
- MBlock *b = (MBlock*)InternalAlloc(sz + sizeof(MBlock));
- if (b == 0)
+ void *p = allocator()->Allocate(&thr->alloc_cache, sz, align);
+ if (p == 0)
return 0;
+ MBlock *b = new(allocator()->GetMetaData(p)) MBlock;
b->size = sz;
- void *p = b + 1;
+ b->head = 0;
+ b->alloc_tid = thr->unique_id;
+ b->alloc_stack_id = CurrentStackId(thr, pc);
if (CTX() && CTX()->initialized) {
- MemoryResetRange(thr, pc, (uptr)p, sz);
+ MemoryRangeImitateWrite(thr, pc, (uptr)p, sz);
}
DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p);
SignalUnsafeCall(thr, pc);
@@ -49,12 +78,24 @@ void user_free(ThreadState *thr, uptr pc, void *p) {
CHECK_GT(thr->in_rtl, 0);
CHECK_NE(p, (void*)0);
DPrintf("#%d: free(%p)\n", thr->tid, p);
- MBlock *b = user_mblock(thr, p);
- p = b + 1;
+ MBlock *b = (MBlock*)allocator()->GetMetaData(p);
+ if (b->head) {
+ Lock l(&b->mtx);
+ for (SyncVar *s = b->head; s;) {
+ SyncVar *res = s;
+ s = s->next;
+ StatInc(thr, StatSyncDestroyed);
+ res->mtx.Lock();
+ res->mtx.Unlock();
+ DestroyAndFree(res);
+ }
+ b->head = 0;
+ }
if (CTX() && CTX()->initialized && thr->in_rtl == 1) {
MemoryRangeFreed(thr, pc, (uptr)p, b->size);
}
- InternalFree(b);
+ b->~MBlock();
+ allocator()->Deallocate(&thr->alloc_cache, p);
SignalUnsafeCall(thr, pc);
}
@@ -78,26 +119,28 @@ void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) {
return p2;
}
-void *user_alloc_aligned(ThreadState *thr, uptr pc, uptr sz, uptr align) {
- CHECK_GT(thr->in_rtl, 0);
- void *p = user_alloc(thr, pc, sz + align);
- void *pa = RoundUp(p, align);
- DCHECK_LE((uptr)pa + sz, (uptr)p + sz + align);
- return pa;
-}
-
MBlock *user_mblock(ThreadState *thr, void *p) {
- CHECK_GT(thr->in_rtl, 0);
CHECK_NE(p, (void*)0);
- MBlock *b = (MBlock*)InternalAllocBlock(p);
- // FIXME: Output a warning, it's a user error.
- if (p < (char*)(b + 1) || p > (char*)(b + 1) + b->size) {
- TsanPrintf("user_mblock p=%p b=%p size=%zu beg=%p end=%p\n",
- p, b, b->size, (char*)(b + 1), (char*)(b + 1) + b->size);
- CHECK_GE(p, (char*)(b + 1));
- CHECK_LE(p, (char*)(b + 1) + b->size);
- }
- return b;
+ Allocator *a = allocator();
+ void *b = a->GetBlockBegin(p);
+ CHECK_NE(b, 0);
+ return (MBlock*)a->GetMetaData(b);
+}
+
+void invoke_malloc_hook(void *ptr, uptr size) {
+ Context *ctx = CTX();
+ ThreadState *thr = cur_thread();
+ if (ctx == 0 || !ctx->initialized || thr->in_rtl)
+ return;
+ __tsan_malloc_hook(ptr, size);
+}
+
+void invoke_free_hook(void *ptr) {
+ Context *ctx = CTX();
+ ThreadState *thr = cur_thread();
+ if (ctx == 0 || !ctx->initialized || thr->in_rtl)
+ return;
+ __tsan_free_hook(ptr);
}
void *internal_alloc(MBlockType typ, uptr sz) {
diff --git a/lib/tsan/rtl/tsan_mman.h b/lib/tsan/rtl/tsan_mman.h
index 53f147e40cea..5cf00eac8d03 100644
--- a/lib/tsan/rtl/tsan_mman.h
+++ b/lib/tsan/rtl/tsan_mman.h
@@ -17,13 +17,14 @@
namespace __tsan {
-// Descriptor of user's memory block.
-struct MBlock {
- uptr size;
-};
+const uptr kDefaultAlignment = 16;
+
+void InitializeAllocator();
+void AlloctorThreadFinish(ThreadState *thr);
// For user allocations.
-void *user_alloc(ThreadState *thr, uptr pc, uptr sz);
+void *user_alloc(ThreadState *thr, uptr pc, uptr sz,
+ uptr align = kDefaultAlignment);
// Does not accept NULL.
void user_free(ThreadState *thr, uptr pc, void *p);
void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz);
@@ -32,6 +33,10 @@ void *user_alloc_aligned(ThreadState *thr, uptr pc, uptr sz, uptr align);
// returns the descriptor of the block.
MBlock *user_mblock(ThreadState *thr, void *p);
+// Invoking malloc/free hooks that may be installed by the user.
+void invoke_malloc_hook(void *ptr, uptr size);
+void invoke_free_hook(void *ptr);
+
enum MBlockType {
MBlockScopedBuf,
MBlockString,
@@ -54,9 +59,10 @@ enum MBlockType {
MBlockSuppression,
MBlockExpectRace,
MBlockSignal,
+ MBlockFD,
// This must be the last.
- MBlockTypeCount,
+ MBlockTypeCount
};
// For internal data structures.
@@ -70,45 +76,5 @@ void DestroyAndFree(T *&p) {
p = 0;
}
-template<typename T>
-class InternalScopedBuf {
- public:
- explicit InternalScopedBuf(uptr cnt) {
- cnt_ = cnt;
- ptr_ = (T*)internal_alloc(MBlockScopedBuf, cnt * sizeof(T));
- }
-
- ~InternalScopedBuf() {
- internal_free(ptr_);
- }
-
- operator T *() {
- return ptr_;
- }
-
- T &operator[](uptr i) {
- return ptr_[i];
- }
-
- T *Ptr() {
- return ptr_;
- }
-
- uptr Count() {
- return cnt_;
- }
-
- uptr Size() {
- return cnt_ * sizeof(T);
- }
-
- private:
- T *ptr_;
- uptr cnt_;
-
- InternalScopedBuf(const InternalScopedBuf&);
- void operator = (const InternalScopedBuf&);
-};
-
} // namespace __tsan
#endif // TSAN_MMAN_H
diff --git a/lib/tsan/rtl/tsan_mutex.cc b/lib/tsan/rtl/tsan_mutex.cc
index 1a70f8fe4430..335ca2211d13 100644
--- a/lib/tsan/rtl/tsan_mutex.cc
+++ b/lib/tsan/rtl/tsan_mutex.cc
@@ -25,22 +25,28 @@ namespace __tsan {
// then Report mutex can be locked while under Threads mutex.
// The leaf mutexes can be locked under any other mutexes.
// Recursive locking is not supported.
+#if TSAN_DEBUG && !TSAN_GO
const MutexType MutexTypeLeaf = (MutexType)-1;
static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
- /*0 MutexTypeInvalid*/ {},
- /*1 MutexTypeTrace*/ {MutexTypeLeaf},
- /*2 MutexTypeThreads*/ {MutexTypeReport},
- /*3 MutexTypeReport*/ {},
- /*4 MutexTypeSyncVar*/ {},
- /*5 MutexTypeSyncTab*/ {MutexTypeSyncVar},
- /*6 MutexTypeSlab*/ {MutexTypeLeaf},
- /*7 MutexTypeAnnotations*/ {},
- /*8 MutexTypeAtExit*/ {MutexTypeSyncTab},
+ /*0 MutexTypeInvalid*/ {},
+ /*1 MutexTypeTrace*/ {MutexTypeLeaf},
+ /*2 MutexTypeThreads*/ {MutexTypeReport},
+ /*3 MutexTypeReport*/ {MutexTypeSyncTab, MutexTypeMBlock,
+ MutexTypeJavaMBlock},
+ /*4 MutexTypeSyncVar*/ {},
+ /*5 MutexTypeSyncTab*/ {MutexTypeSyncVar},
+ /*6 MutexTypeSlab*/ {MutexTypeLeaf},
+ /*7 MutexTypeAnnotations*/ {},
+ /*8 MutexTypeAtExit*/ {MutexTypeSyncTab},
+ /*9 MutexTypeMBlock*/ {MutexTypeSyncVar},
+ /*10 MutexTypeJavaMBlock*/ {MutexTypeSyncVar},
};
static bool CanLockAdj[MutexTypeCount][MutexTypeCount];
+#endif
void InitializeMutex() {
+#if TSAN_DEBUG && !TSAN_GO
// Build the "can lock" adjacency matrix.
// If [i][j]==true, then one can lock mutex j while under mutex i.
const int N = MutexTypeCount;
@@ -48,7 +54,7 @@ void InitializeMutex() {
bool leaf[N] = {};
for (int i = 1; i < N; i++) {
for (int j = 0; j < N; j++) {
- int z = CanLockTab[i][j];
+ MutexType z = CanLockTab[i][j];
if (z == MutexTypeInvalid)
continue;
if (z == MutexTypeLeaf) {
@@ -56,8 +62,8 @@ void InitializeMutex() {
leaf[i] = true;
continue;
}
- CHECK(!CanLockAdj[i][z]);
- CanLockAdj[i][z] = true;
+ CHECK(!CanLockAdj[i][(int)z]);
+ CanLockAdj[i][(int)z] = true;
cnt[i]++;
}
}
@@ -92,36 +98,40 @@ void InitializeMutex() {
}
}
#if 0
- TsanPrintf("Can lock graph:\n");
+ Printf("Can lock graph:\n");
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
- TsanPrintf("%d ", CanLockAdj[i][j]);
+ Printf("%d ", CanLockAdj[i][j]);
}
- TsanPrintf("\n");
+ Printf("\n");
}
- TsanPrintf("Can lock graph closure:\n");
+ Printf("Can lock graph closure:\n");
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
- TsanPrintf("%d ", CanLockAdj2[i][j]);
+ Printf("%d ", CanLockAdj2[i][j]);
}
- TsanPrintf("\n");
+ Printf("\n");
}
#endif
// Verify that the graph is acyclic.
for (int i = 0; i < N; i++) {
if (CanLockAdj2[i][i]) {
- TsanPrintf("Mutex %d participates in a cycle\n", i);
+ Printf("Mutex %d participates in a cycle\n", i);
Die();
}
}
+#endif
}
DeadlockDetector::DeadlockDetector() {
// Rely on zero initialization because some mutexes can be locked before ctor.
}
+#if TSAN_DEBUG && !TSAN_GO
void DeadlockDetector::Lock(MutexType t) {
- // TsanPrintf("LOCK %d @%zu\n", t, seq_ + 1);
+ // Printf("LOCK %d @%zu\n", t, seq_ + 1);
+ CHECK_GT(t, MutexTypeInvalid);
+ CHECK_LT(t, MutexTypeCount);
u64 max_seq = 0;
u64 max_idx = MutexTypeInvalid;
for (int i = 0; i != MutexTypeCount; i++) {
@@ -136,20 +146,21 @@ void DeadlockDetector::Lock(MutexType t) {
locked_[t] = ++seq_;
if (max_idx == MutexTypeInvalid)
return;
- // TsanPrintf(" last %d @%zu\n", max_idx, max_seq);
+ // Printf(" last %d @%zu\n", max_idx, max_seq);
if (!CanLockAdj[max_idx][t]) {
- TsanPrintf("ThreadSanitizer: internal deadlock detected\n");
- TsanPrintf("ThreadSanitizer: can't lock %d while under %zu\n",
+ Printf("ThreadSanitizer: internal deadlock detected\n");
+ Printf("ThreadSanitizer: can't lock %d while under %zu\n",
t, (uptr)max_idx);
- Die();
+ CHECK(0);
}
}
void DeadlockDetector::Unlock(MutexType t) {
- // TsanPrintf("UNLO %d @%zu #%zu\n", t, seq_, locked_[t]);
+ // Printf("UNLO %d @%zu #%zu\n", t, seq_, locked_[t]);
CHECK(locked_[t]);
locked_[t] = 0;
}
+#endif
const uptr kUnlocked = 0;
const uptr kWriteLock = 1;
@@ -256,4 +267,8 @@ void Mutex::ReadUnlock() {
#endif
}
+void Mutex::CheckLocked() {
+ CHECK_NE(atomic_load(&state_, memory_order_relaxed), 0);
+}
+
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_mutex.h b/lib/tsan/rtl/tsan_mutex.h
index 5b22a4145185..a2b489107a98 100644
--- a/lib/tsan/rtl/tsan_mutex.h
+++ b/lib/tsan/rtl/tsan_mutex.h
@@ -29,9 +29,11 @@ enum MutexType {
MutexTypeSlab,
MutexTypeAnnotations,
MutexTypeAtExit,
+ MutexTypeMBlock,
+ MutexTypeJavaMBlock,
// This must be the last.
- MutexTypeCount,
+ MutexTypeCount
};
class Mutex {
@@ -45,6 +47,8 @@ class Mutex {
void ReadLock();
void ReadUnlock();
+ void CheckLocked();
+
private:
atomic_uintptr_t state_;
#if TSAN_DEBUG
diff --git a/lib/tsan/rtl/tsan_mutexset.cc b/lib/tsan/rtl/tsan_mutexset.cc
new file mode 100644
index 000000000000..21587770f687
--- /dev/null
+++ b/lib/tsan/rtl/tsan_mutexset.cc
@@ -0,0 +1,89 @@
+//===-- tsan_mutexset.cc --------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_mutexset.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+const uptr MutexSet::kMaxSize;
+
+MutexSet::MutexSet() {
+ size_ = 0;
+ internal_memset(&descs_, 0, sizeof(descs_));
+}
+
+void MutexSet::Add(u64 id, bool write, u64 epoch) {
+ // Look up existing mutex with the same id.
+ for (uptr i = 0; i < size_; i++) {
+ if (descs_[i].id == id) {
+ descs_[i].count++;
+ descs_[i].epoch = epoch;
+ return;
+ }
+ }
+ // On overflow, find the oldest mutex and drop it.
+ if (size_ == kMaxSize) {
+ u64 minepoch = (u64)-1;
+ u64 mini = (u64)-1;
+ for (uptr i = 0; i < size_; i++) {
+ if (descs_[i].epoch < minepoch) {
+ minepoch = descs_[i].epoch;
+ mini = i;
+ }
+ }
+ RemovePos(mini);
+ CHECK_EQ(size_, kMaxSize - 1);
+ }
+ // Add new mutex descriptor.
+ descs_[size_].id = id;
+ descs_[size_].write = write;
+ descs_[size_].epoch = epoch;
+ descs_[size_].count = 1;
+ size_++;
+}
+
+void MutexSet::Del(u64 id, bool write) {
+ for (uptr i = 0; i < size_; i++) {
+ if (descs_[i].id == id) {
+ if (--descs_[i].count == 0)
+ RemovePos(i);
+ return;
+ }
+ }
+}
+
+void MutexSet::Remove(u64 id) {
+ for (uptr i = 0; i < size_; i++) {
+ if (descs_[i].id == id) {
+ RemovePos(i);
+ return;
+ }
+ }
+}
+
+void MutexSet::RemovePos(uptr i) {
+ CHECK_LT(i, size_);
+ descs_[i] = descs_[size_ - 1];
+ size_--;
+}
+
+uptr MutexSet::Size() const {
+ return size_;
+}
+
+MutexSet::Desc MutexSet::Get(uptr i) const {
+ CHECK_LT(i, size_);
+ return descs_[i];
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_mutexset.h b/lib/tsan/rtl/tsan_mutexset.h
new file mode 100644
index 000000000000..09223ff6cc48
--- /dev/null
+++ b/lib/tsan/rtl/tsan_mutexset.h
@@ -0,0 +1,65 @@
+//===-- tsan_mutexset.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// MutexSet holds the set of mutexes currently held by a thread.
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_MUTEXSET_H
+#define TSAN_MUTEXSET_H
+
+#include "tsan_defs.h"
+
+namespace __tsan {
+
+class MutexSet {
+ public:
+ // Holds limited number of mutexes.
+ // The oldest mutexes are discarded on overflow.
+ static const uptr kMaxSize = 64;
+ struct Desc {
+ u64 id;
+ u64 epoch;
+ int count;
+ bool write;
+ };
+
+ MutexSet();
+ // The 'id' is obtained from SyncVar::GetId().
+ void Add(u64 id, bool write, u64 epoch);
+ void Del(u64 id, bool write);
+ void Remove(u64 id); // Removes the mutex completely (if it's destroyed).
+ uptr Size() const;
+ Desc Get(uptr i) const;
+
+ private:
+#ifndef TSAN_GO
+ uptr size_;
+ Desc descs_[kMaxSize];
+#endif
+
+ void RemovePos(uptr i);
+};
+
+// Go does not have mutexes, so do not spend memory and time.
+// (Go sync.Mutex is actually a semaphore -- can be unlocked
+// in different goroutine).
+#ifdef TSAN_GO
+MutexSet::MutexSet() {}
+void MutexSet::Add(u64 id, bool write, u64 epoch) {}
+void MutexSet::Del(u64 id, bool write) {}
+void MutexSet::Remove(u64 id) {}
+void MutexSet::RemovePos(uptr i) {}
+uptr MutexSet::Size() const { return 0; }
+MutexSet::Desc MutexSet::Get(uptr i) const { return Desc(); }
+#endif
+
+} // namespace __tsan
+
+#endif // TSAN_REPORT_H
diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h
index b557fa1caec5..c859c3e85b19 100644
--- a/lib/tsan/rtl/tsan_platform.h
+++ b/lib/tsan/rtl/tsan_platform.h
@@ -12,31 +12,85 @@
// Platform-specific code.
//===----------------------------------------------------------------------===//
+/*
+C++ linux memory layout:
+0000 0000 0000 - 03c0 0000 0000: protected
+03c0 0000 0000 - 1000 0000 0000: shadow
+1000 0000 0000 - 6000 0000 0000: protected
+6000 0000 0000 - 6200 0000 0000: traces
+6200 0000 0000 - 7d00 0000 0000: -
+7d00 0000 0000 - 7e00 0000 0000: heap
+7e00 0000 0000 - 7fff ffff ffff: modules and main thread stack
+
+C++ COMPAT linux memory layout:
+0000 0000 0000 - 0400 0000 0000: protected
+0400 0000 0000 - 1000 0000 0000: shadow
+1000 0000 0000 - 2900 0000 0000: protected
+2900 0000 0000 - 2c00 0000 0000: modules
+2c00 0000 0000 - 6000 0000 0000: -
+6000 0000 0000 - 6200 0000 0000: traces
+6200 0000 0000 - 7d00 0000 0000: -
+7d00 0000 0000 - 7e00 0000 0000: heap
+7e00 0000 0000 - 7f00 0000 0000: -
+7f00 0000 0000 - 7fff ffff ffff: main thread stack
+
+Go linux and darwin memory layout:
+0000 0000 0000 - 0000 1000 0000: executable
+0000 1000 0000 - 00f8 0000 0000: -
+00f8 0000 0000 - 0118 0000 0000: heap
+0118 0000 0000 - 1000 0000 0000: -
+1000 0000 0000 - 1460 0000 0000: shadow
+1460 0000 0000 - 6000 0000 0000: -
+6000 0000 0000 - 6200 0000 0000: traces
+6200 0000 0000 - 7fff ffff ffff: -
+
+Go windows memory layout:
+0000 0000 0000 - 0000 1000 0000: executable
+0000 1000 0000 - 00f8 0000 0000: -
+00f8 0000 0000 - 0118 0000 0000: heap
+0118 0000 0000 - 0100 0000 0000: -
+0100 0000 0000 - 0560 0000 0000: shadow
+0560 0000 0000 - 0760 0000 0000: traces
+0760 0000 0000 - 07ff ffff ffff: -
+*/
+
#ifndef TSAN_PLATFORM_H
#define TSAN_PLATFORM_H
-#include "tsan_rtl.h"
+#include "tsan_defs.h"
+#include "tsan_trace.h"
-#if __LP64__
+#if defined(__LP64__) || defined(_WIN64)
namespace __tsan {
#if defined(TSAN_GO)
static const uptr kLinuxAppMemBeg = 0x000000000000ULL;
static const uptr kLinuxAppMemEnd = 0x00fcffffffffULL;
+# if defined(_WIN32)
+static const uptr kLinuxShadowMsk = 0x010000000000ULL;
+# else
static const uptr kLinuxShadowMsk = 0x100000000000ULL;
+# endif
// TSAN_COMPAT_SHADOW is intended for COMPAT virtual memory layout,
// when memory addresses are of the 0x2axxxxxxxxxx form.
// The option is enabled with 'setarch x86_64 -L'.
#elif defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
-static const uptr kLinuxAppMemBeg = 0x2a0000000000ULL;
+static const uptr kLinuxAppMemBeg = 0x290000000000ULL;
static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL;
#else
-static const uptr kLinuxAppMemBeg = 0x7ef000000000ULL;
+static const uptr kLinuxAppMemBeg = 0x7cf000000000ULL;
static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL;
#endif
static const uptr kLinuxAppMemMsk = 0x7c0000000000ULL;
+#if defined(_WIN32)
+const uptr kTraceMemBegin = 0x056000000000ULL;
+#else
+const uptr kTraceMemBegin = 0x600000000000ULL;
+#endif
+const uptr kTraceMemSize = 0x020000000000ULL;
+
// This has to be a macro to allow constant initialization of constants below.
#ifndef TSAN_GO
#define MemToShadow(addr) \
@@ -48,7 +102,7 @@ static const uptr kLinuxAppMemMsk = 0x7c0000000000ULL;
static const uptr kLinuxShadowBeg = MemToShadow(kLinuxAppMemBeg);
static const uptr kLinuxShadowEnd =
- MemToShadow(kLinuxAppMemEnd) | (kPageSize - 1);
+ MemToShadow(kLinuxAppMemEnd) | 0xff;
static inline bool IsAppMem(uptr mem) {
return mem >= kLinuxAppMemBeg && mem <= kLinuxAppMemEnd;
@@ -62,9 +116,6 @@ static inline uptr ShadowToMem(uptr shadow) {
CHECK(IsShadowMem(shadow));
#ifdef TSAN_GO
return (shadow & ~kLinuxShadowMsk) / kShadowCnt;
-#elif defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
- // COMPAT mapping is not quite one-to-one.
- return (shadow / kShadowCnt) | 0x280000000000ULL;
#else
return (shadow / kShadowCnt) | kLinuxAppMemMsk;
#endif
@@ -72,9 +123,10 @@ static inline uptr ShadowToMem(uptr shadow) {
// For COMPAT mapping returns an alternative address
// that mapped to the same shadow address.
+// COMPAT mapping is not quite one-to-one.
static inline uptr AlternativeAddress(uptr addr) {
#if defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
- return addr | kLinuxAppMemMsk;
+ return (addr & ~kLinuxAppMemMsk) | 0x280000000000ULL;
#else
return 0;
#endif
@@ -85,16 +137,24 @@ void FlushShadowMemory();
const char *InitializePlatform();
void FinalizePlatform();
+uptr ALWAYS_INLINE INLINE GetThreadTrace(int tid) {
+ uptr p = kTraceMemBegin + (uptr)tid * kTraceSize * sizeof(Event);
+ DCHECK_LT(p, kTraceMemBegin + kTraceMemSize);
+ return p;
+}
void internal_start_thread(void(*func)(void*), void *arg);
+// Says whether the addr relates to a global var.
+// Guesses with high probability, may yield both false positives and negatives.
+bool IsGlobalVar(uptr addr);
uptr GetTlsSize();
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
uptr *tls_addr, uptr *tls_size);
} // namespace __tsan
-#else // __LP64__
+#else // defined(__LP64__) || defined(_WIN64)
# error "Only 64-bit is supported"
#endif
diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc
index c791c96c14ac..6cc424975125 100644
--- a/lib/tsan/rtl/tsan_platform_linux.cc
+++ b/lib/tsan/rtl/tsan_platform_linux.cc
@@ -43,14 +43,6 @@
extern "C" int arch_prctl(int code, __sanitizer::uptr *addr);
-namespace __sanitizer {
-
-void Die() {
- _exit(1);
-}
-
-} // namespace __sanitizer
-
namespace __tsan {
#ifndef TSAN_GO
@@ -79,9 +71,7 @@ uptr GetShadowMemoryConsumption() {
}
void FlushShadowMemory() {
- madvise((void*)kLinuxShadowBeg,
- kLinuxShadowEnd - kLinuxShadowBeg,
- MADV_DONTNEED);
+ FlushUnneededShadowMemory(kLinuxShadowBeg, kLinuxShadowEnd - kLinuxShadowBeg);
}
#ifndef TSAN_GO
@@ -91,65 +81,88 @@ static void ProtectRange(uptr beg, uptr end) {
if (beg == end)
return;
if (beg != (uptr)Mprotect(beg, end - beg)) {
- TsanPrintf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
- TsanPrintf("FATAL: Make sure you are not using unlimited stack\n");
+ Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
+ Printf("FATAL: Make sure you are not using unlimited stack\n");
Die();
}
}
#endif
+#ifndef TSAN_GO
void InitializeShadowMemory() {
uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg,
kLinuxShadowEnd - kLinuxShadowBeg);
if (shadow != kLinuxShadowBeg) {
- TsanPrintf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
- TsanPrintf("FATAL: Make sure to compile with -fPIE and "
- "to link with -pie.\n");
+ Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
+ Printf("FATAL: Make sure to compile with -fPIE and "
+ "to link with -pie (%p, %p).\n", shadow, kLinuxShadowBeg);
Die();
}
-#ifndef TSAN_GO
const uptr kClosedLowBeg = 0x200000;
const uptr kClosedLowEnd = kLinuxShadowBeg - 1;
const uptr kClosedMidBeg = kLinuxShadowEnd + 1;
- const uptr kClosedMidEnd = kLinuxAppMemBeg - 1;
+ const uptr kClosedMidEnd = min(kLinuxAppMemBeg, kTraceMemBegin);
ProtectRange(kClosedLowBeg, kClosedLowEnd);
ProtectRange(kClosedMidBeg, kClosedMidEnd);
-#endif
-#ifndef TSAN_GO
DPrintf("kClosedLow %zx-%zx (%zuGB)\n",
kClosedLowBeg, kClosedLowEnd, (kClosedLowEnd - kClosedLowBeg) >> 30);
-#endif
DPrintf("kLinuxShadow %zx-%zx (%zuGB)\n",
kLinuxShadowBeg, kLinuxShadowEnd,
(kLinuxShadowEnd - kLinuxShadowBeg) >> 30);
-#ifndef TSAN_GO
DPrintf("kClosedMid %zx-%zx (%zuGB)\n",
kClosedMidBeg, kClosedMidEnd, (kClosedMidEnd - kClosedMidBeg) >> 30);
-#endif
DPrintf("kLinuxAppMem %zx-%zx (%zuGB)\n",
kLinuxAppMemBeg, kLinuxAppMemEnd,
(kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30);
DPrintf("stack %zx\n", (uptr)&shadow);
}
+#endif
+
+static uptr g_data_start;
+static uptr g_data_end;
#ifndef TSAN_GO
static void CheckPIE() {
// Ensure that the binary is indeed compiled with -pie.
- ProcessMaps proc_maps;
+ MemoryMappingLayout proc_maps;
uptr start, end;
if (proc_maps.Next(&start, &end,
/*offset*/0, /*filename*/0, /*filename_size*/0)) {
if ((u64)start < kLinuxAppMemBeg) {
- TsanPrintf("FATAL: ThreadSanitizer can not mmap the shadow memory ("
+ Printf("FATAL: ThreadSanitizer can not mmap the shadow memory ("
"something is mapped at 0x%zx < 0x%zx)\n",
start, kLinuxAppMemBeg);
- TsanPrintf("FATAL: Make sure to compile with -fPIE"
+ Printf("FATAL: Make sure to compile with -fPIE"
" and to link with -pie.\n");
Die();
}
}
}
+static void InitDataSeg() {
+ MemoryMappingLayout proc_maps;
+ uptr start, end, offset;
+ char name[128];
+ bool prev_is_data = false;
+ while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name))) {
+ DPrintf("%p-%p %p %s\n", start, end, offset, name);
+ bool is_data = offset != 0 && name[0] != 0;
+ // BSS may get merged with [heap] in /proc/self/maps. This is not very
+ // reliable.
+ bool is_bss = offset == 0 &&
+ (name[0] == 0 || internal_strcmp(name, "[heap]") == 0) && prev_is_data;
+ if (g_data_start == 0 && is_data)
+ g_data_start = start;
+ if (is_bss)
+ g_data_end = end;
+ prev_is_data = is_data;
+ }
+ DPrintf("guessed data_start=%p data_end=%p\n", g_data_start, g_data_end);
+ CHECK_LT(g_data_start, g_data_end);
+ CHECK_GE((uptr)&g_data_start, g_data_start);
+ CHECK_LT((uptr)&g_data_start, g_data_end);
+}
+
static uptr g_tls_size;
#ifdef __i386__
@@ -157,14 +170,14 @@ static uptr g_tls_size;
#else
# define INTERNAL_FUNCTION
#endif
-extern "C" void _dl_get_tls_static_info(size_t*, size_t*)
- __attribute__((weak)) INTERNAL_FUNCTION;
static int InitTlsSize() {
typedef void (*get_tls_func)(size_t*, size_t*) INTERNAL_FUNCTION;
- get_tls_func get_tls = &_dl_get_tls_static_info;
- if (get_tls == 0)
- get_tls = (get_tls_func)dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
+ get_tls_func get_tls;
+ void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
+ CHECK_EQ(sizeof(get_tls), sizeof(get_tls_static_info_ptr));
+ internal_memcpy(&get_tls, &get_tls_static_info_ptr,
+ sizeof(get_tls_static_info_ptr));
CHECK_NE(get_tls, 0);
size_t tls_size = 0;
size_t tls_align = 0;
@@ -173,22 +186,62 @@ static int InitTlsSize() {
}
#endif // #ifndef TSAN_GO
+static rlim_t getlim(int res) {
+ rlimit rlim;
+ CHECK_EQ(0, getrlimit(res, &rlim));
+ return rlim.rlim_cur;
+}
+
+static void setlim(int res, rlim_t lim) {
+ // The following magic is to prevent clang from replacing it with memset.
+ volatile rlimit rlim;
+ rlim.rlim_cur = lim;
+ rlim.rlim_max = lim;
+ setrlimit(res, (rlimit*)&rlim);
+}
+
const char *InitializePlatform() {
void *p = 0;
if (sizeof(p) == 8) {
// Disable core dumps, dumping of 16TB usually takes a bit long.
- // The following magic is to prevent clang from replacing it with memset.
- volatile rlimit lim;
- lim.rlim_cur = 0;
- lim.rlim_max = 0;
- setrlimit(RLIMIT_CORE, (rlimit*)&lim);
+ setlim(RLIMIT_CORE, 0);
+ }
+
+ // Go maps shadow memory lazily and works fine with limited address space.
+ // Unlimited stack is not a problem as well, because the executable
+ // is not compiled with -pie.
+ if (kCppMode) {
+ bool reexec = false;
+ // TSan doesn't play well with unlimited stack size (as stack
+ // overlaps with shadow memory). If we detect unlimited stack size,
+ // we re-exec the program with limited stack size as a best effort.
+ if (getlim(RLIMIT_STACK) == (rlim_t)-1) {
+ const uptr kMaxStackSize = 32 * 1024 * 1024;
+ Report("WARNING: Program is run with unlimited stack size, which "
+ "wouldn't work with ThreadSanitizer.\n");
+ Report("Re-execing with stack size limited to %zd bytes.\n",
+ kMaxStackSize);
+ SetStackSizeLimitInBytes(kMaxStackSize);
+ reexec = true;
+ }
+
+ if (getlim(RLIMIT_AS) != (rlim_t)-1) {
+ Report("WARNING: Program is run with limited virtual address space,"
+ " which wouldn't work with ThreadSanitizer.\n");
+ Report("Re-execing with unlimited virtual address space.\n");
+ setlim(RLIMIT_AS, -1);
+ reexec = true;
+ }
+ if (reexec)
+ ReExec();
}
#ifndef TSAN_GO
CheckPIE();
g_tls_size = (uptr)InitTlsSize();
+ InitDataSeg();
#endif
- return getenv("TSAN_OPTIONS");
+ return getenv(kTsanOptionsEnv);
}
void FinalizePlatform() {
@@ -232,6 +285,9 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
#endif
}
+bool IsGlobalVar(uptr addr) {
+ return g_data_start && addr >= g_data_start && addr < g_data_end;
+}
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_platform_mac.cc b/lib/tsan/rtl/tsan_platform_mac.cc
index 7451492744f2..183061d14638 100644
--- a/lib/tsan/rtl/tsan_platform_mac.cc
+++ b/lib/tsan/rtl/tsan_platform_mac.cc
@@ -37,14 +37,6 @@
#include <errno.h>
#include <sched.h>
-namespace __sanitizer {
-
-void Die() {
- _exit(1);
-}
-
-} // namespace __sanitizer
-
namespace __tsan {
ScopedInRtl::ScopedInRtl() {
@@ -60,13 +52,14 @@ uptr GetShadowMemoryConsumption() {
void FlushShadowMemory() {
}
+#ifndef TSAN_GO
void InitializeShadowMemory() {
uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg,
kLinuxShadowEnd - kLinuxShadowBeg);
if (shadow != kLinuxShadowBeg) {
- TsanPrintf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
- TsanPrintf("FATAL: Make sure to compile with -fPIE and "
- "to link with -pie.\n");
+ Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
+ Printf("FATAL: Make sure to compile with -fPIE and "
+ "to link with -pie.\n");
Die();
}
DPrintf("kLinuxShadow %zx-%zx (%zuGB)\n",
@@ -76,6 +69,7 @@ void InitializeShadowMemory() {
kLinuxAppMemBeg, kLinuxAppMemEnd,
(kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30);
}
+#endif
const char *InitializePlatform() {
void *p = 0;
@@ -88,7 +82,7 @@ const char *InitializePlatform() {
setrlimit(RLIMIT_CORE, (rlimit*)&lim);
}
- return getenv("TSAN_OPTIONS");
+ return getenv(kTsanOptionsEnv);
}
void FinalizePlatform() {
diff --git a/lib/tsan/rtl/tsan_platform_windows.cc b/lib/tsan/rtl/tsan_platform_windows.cc
new file mode 100644
index 000000000000..f23e84e7875d
--- /dev/null
+++ b/lib/tsan/rtl/tsan_platform_windows.cc
@@ -0,0 +1,58 @@
+//===-- tsan_platform_windows.cc ------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Windows-specific code.
+//===----------------------------------------------------------------------===//
+
+#ifdef _WIN32
+
+#include "tsan_platform.h"
+
+#include <stdlib.h>
+
+namespace __tsan {
+
+ScopedInRtl::ScopedInRtl() {
+}
+
+ScopedInRtl::~ScopedInRtl() {
+}
+
+uptr GetShadowMemoryConsumption() {
+ return 0;
+}
+
+void FlushShadowMemory() {
+}
+
+const char *InitializePlatform() {
+ return getenv(kTsanOptionsEnv);
+}
+
+void FinalizePlatform() {
+ fflush(0);
+}
+
+uptr GetTlsSize() {
+ return 0;
+}
+
+void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
+ uptr *tls_addr, uptr *tls_size) {
+ *stk_addr = 0;
+ *stk_size = 0;
+ *tls_addr = 0;
+ *tls_size = 0;
+}
+
+} // namespace __tsan
+
+#endif // #ifdef _WIN32
diff --git a/lib/tsan/rtl/tsan_printf.cc b/lib/tsan/rtl/tsan_printf.cc
deleted file mode 100644
index 6f41440fb929..000000000000
--- a/lib/tsan/rtl/tsan_printf.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-//===-- tsan_printf.cc ----------------------------------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_libc.h"
-#include "tsan_defs.h"
-#include "tsan_mman.h"
-#include "tsan_platform.h"
-
-#include <stdarg.h> // va_list
-
-namespace __sanitizer {
-int VSNPrintf(char *buff, int buff_length, const char *format, va_list args);
-} // namespace __sanitizer
-
-namespace __tsan {
-
-void TsanPrintf(const char *format, ...) {
- ScopedInRtl in_rtl;
- const uptr kMaxLen = 16 * 1024;
- InternalScopedBuf<char> buffer(kMaxLen);
- va_list args;
- va_start(args, format);
- uptr len = VSNPrintf(buffer, buffer.Size(), format, args);
- va_end(args);
- internal_write(CTX() ? flags()->log_fileno : 2,
- buffer, len < buffer.Size() ? len : buffer.Size() - 1);
-}
-
-} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_report.cc b/lib/tsan/rtl/tsan_report.cc
index c841a9827f4c..056dc97387b9 100644
--- a/lib/tsan/rtl/tsan_report.cc
+++ b/lib/tsan/rtl/tsan_report.cc
@@ -21,100 +21,153 @@ ReportDesc::ReportDesc()
, mops(MBlockReportMop)
, locs(MBlockReportLoc)
, mutexes(MBlockReportMutex)
- , threads(MBlockReportThread) {
+ , threads(MBlockReportThread)
+ , sleep() {
+}
+
+ReportMop::ReportMop()
+ : mset(MBlockReportMutex) {
}
ReportDesc::~ReportDesc() {
+ // FIXME(dvyukov): it must be leaking a lot of memory.
}
#ifndef TSAN_GO
+const int kThreadBufSize = 32;
+const char *thread_name(char *buf, int tid) {
+ if (tid == 0)
+ return "main thread";
+ internal_snprintf(buf, kThreadBufSize, "thread T%d", tid);
+ return buf;
+}
+
static void PrintHeader(ReportType typ) {
- TsanPrintf("WARNING: ThreadSanitizer: ");
+ Printf("WARNING: ThreadSanitizer: ");
if (typ == ReportTypeRace)
- TsanPrintf("data race");
+ Printf("data race");
else if (typ == ReportTypeUseAfterFree)
- TsanPrintf("heap-use-after-free");
+ Printf("heap-use-after-free");
else if (typ == ReportTypeThreadLeak)
- TsanPrintf("thread leak");
+ Printf("thread leak");
else if (typ == ReportTypeMutexDestroyLocked)
- TsanPrintf("destroy of a locked mutex");
+ Printf("destroy of a locked mutex");
else if (typ == ReportTypeSignalUnsafe)
- TsanPrintf("signal-unsafe call inside of a signal");
+ Printf("signal-unsafe call inside of a signal");
else if (typ == ReportTypeErrnoInSignal)
- TsanPrintf("signal handler spoils errno");
+ Printf("signal handler spoils errno");
- TsanPrintf(" (pid=%d)\n", GetPid());
+ Printf(" (pid=%d)\n", GetPid());
}
-static void PrintStack(const ReportStack *ent) {
+void PrintStack(const ReportStack *ent) {
+ if (ent == 0) {
+ Printf(" [failed to restore the stack]\n\n");
+ return;
+ }
for (int i = 0; ent; ent = ent->next, i++) {
- TsanPrintf(" #%d %s %s:%d", i, ent->func, ent->file, ent->line);
+ Printf(" #%d %s %s:%d", i, ent->func, ent->file, ent->line);
if (ent->col)
- TsanPrintf(":%d", ent->col);
+ Printf(":%d", ent->col);
if (ent->module && ent->offset)
- TsanPrintf(" (%s+%p)\n", ent->module, (void*)ent->offset);
+ Printf(" (%s+%p)\n", ent->module, (void*)ent->offset);
else
- TsanPrintf(" (%p)\n", (void*)ent->pc);
+ Printf(" (%p)\n", (void*)ent->pc);
+ }
+ Printf("\n");
+}
+
+static void PrintMutexSet(Vector<ReportMopMutex> const& mset) {
+ for (uptr i = 0; i < mset.Size(); i++) {
+ if (i == 0)
+ Printf(" (mutexes:");
+ const ReportMopMutex m = mset[i];
+ Printf(" %s M%llu", m.write ? "write" : "read", m.id);
+ Printf(i == mset.Size() - 1 ? ")" : ",");
}
}
static void PrintMop(const ReportMop *mop, bool first) {
- TsanPrintf(" %s of size %d at %p",
+ char thrbuf[kThreadBufSize];
+ Printf(" %s of size %d at %p by %s",
(first ? (mop->write ? "Write" : "Read")
: (mop->write ? "Previous write" : "Previous read")),
- mop->size, (void*)mop->addr);
- if (mop->tid == 0)
- TsanPrintf(" by main thread:\n");
- else
- TsanPrintf(" by thread %d:\n", mop->tid);
+ mop->size, (void*)mop->addr,
+ thread_name(thrbuf, mop->tid));
+ PrintMutexSet(mop->mset);
+ Printf(":\n");
PrintStack(mop->stack);
}
static void PrintLocation(const ReportLocation *loc) {
+ char thrbuf[kThreadBufSize];
if (loc->type == ReportLocationGlobal) {
- TsanPrintf(" Location is global '%s' of size %zu at %zx %s:%d\n",
- loc->name, loc->size, loc->addr, loc->file, loc->line);
+ Printf(" Location is global '%s' of size %zu at %zx (%s+%p)\n\n",
+ loc->name, loc->size, loc->addr, loc->module, loc->offset);
} else if (loc->type == ReportLocationHeap) {
- TsanPrintf(" Location is heap of size %zu at %zx allocated "
- "by thread %d:\n", loc->size, loc->addr, loc->tid);
+ char thrbuf[kThreadBufSize];
+ Printf(" Location is heap block of size %zu at %p allocated by %s:\n",
+ loc->size, loc->addr, thread_name(thrbuf, loc->tid));
PrintStack(loc->stack);
} else if (loc->type == ReportLocationStack) {
- TsanPrintf(" Location is stack of thread %d:\n", loc->tid);
+ Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
+ } else if (loc->type == ReportLocationTLS) {
+ Printf(" Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid));
+ } else if (loc->type == ReportLocationFD) {
+ Printf(" Location is file descriptor %d created by %s at:\n",
+ loc->fd, thread_name(thrbuf, loc->tid));
+ PrintStack(loc->stack);
}
}
static void PrintMutex(const ReportMutex *rm) {
- if (rm->stack == 0)
- return;
- TsanPrintf(" Mutex %d created at:\n", rm->id);
- PrintStack(rm->stack);
+ if (rm->destroyed) {
+ Printf(" Mutex M%llu is already destroyed.\n\n", rm->id);
+ } else {
+ Printf(" Mutex M%llu created at:\n", rm->id);
+ PrintStack(rm->stack);
+ }
}
static void PrintThread(const ReportThread *rt) {
if (rt->id == 0) // Little sense in describing the main thread.
return;
- TsanPrintf(" Thread %d", rt->id);
+ Printf(" Thread T%d", rt->id);
if (rt->name)
- TsanPrintf(" '%s'", rt->name);
- TsanPrintf(" (%s)", rt->running ? "running" : "finished");
+ Printf(" '%s'", rt->name);
+ char thrbuf[kThreadBufSize];
+ Printf(" (tid=%zu, %s) created by %s",
+ rt->pid, rt->running ? "running" : "finished",
+ thread_name(thrbuf, rt->parent_tid));
if (rt->stack)
- TsanPrintf(" created at:");
- TsanPrintf("\n");
+ Printf(" at:");
+ Printf("\n");
PrintStack(rt->stack);
}
+static void PrintSleep(const ReportStack *s) {
+ Printf(" As if synchronized via sleep:\n");
+ PrintStack(s);
+}
+
void PrintReport(const ReportDesc *rep) {
- TsanPrintf("==================\n");
+ Printf("==================\n");
PrintHeader(rep->typ);
- for (uptr i = 0; i < rep->stacks.Size(); i++)
+ for (uptr i = 0; i < rep->stacks.Size(); i++) {
+ if (i)
+ Printf(" and:\n");
PrintStack(rep->stacks[i]);
+ }
for (uptr i = 0; i < rep->mops.Size(); i++)
PrintMop(rep->mops[i], i == 0);
+ if (rep->sleep)
+ PrintSleep(rep->sleep);
+
for (uptr i = 0; i < rep->locs.Size(); i++)
PrintLocation(rep->locs[i]);
@@ -124,20 +177,25 @@ void PrintReport(const ReportDesc *rep) {
for (uptr i = 0; i < rep->threads.Size(); i++)
PrintThread(rep->threads[i]);
- TsanPrintf("==================\n");
+ Printf("==================\n");
}
#else
-static void PrintStack(const ReportStack *ent) {
+void PrintStack(const ReportStack *ent) {
+ if (ent == 0) {
+ Printf(" [failed to restore the stack]\n\n");
+ return;
+ }
for (int i = 0; ent; ent = ent->next, i++) {
- TsanPrintf(" %s()\n %s:%d +0x%zx\n",
+ Printf(" %s()\n %s:%d +0x%zx\n",
ent->func, ent->file, ent->line, (void*)ent->offset);
}
+ Printf("\n");
}
static void PrintMop(const ReportMop *mop, bool first) {
- TsanPrintf("%s by goroutine %d:\n",
+ Printf("%s by goroutine %d:\n",
(first ? (mop->write ? "Write" : "Read")
: (mop->write ? "Previous write" : "Previous read")),
mop->tid);
@@ -147,19 +205,19 @@ static void PrintMop(const ReportMop *mop, bool first) {
static void PrintThread(const ReportThread *rt) {
if (rt->id == 0) // Little sense in describing the main thread.
return;
- TsanPrintf("Goroutine %d (%s) created at:\n",
+ Printf("Goroutine %d (%s) created at:\n",
rt->id, rt->running ? "running" : "finished");
PrintStack(rt->stack);
}
void PrintReport(const ReportDesc *rep) {
- TsanPrintf("==================\n");
- TsanPrintf("WARNING: DATA RACE at %p\n", (void*)rep->mops[0]->addr);
+ Printf("==================\n");
+ Printf("WARNING: DATA RACE\n");
for (uptr i = 0; i < rep->mops.Size(); i++)
PrintMop(rep->mops[i], i == 0);
for (uptr i = 0; i < rep->threads.Size(); i++)
PrintThread(rep->threads[i]);
- TsanPrintf("==================\n");
+ Printf("==================\n");
}
#endif
diff --git a/lib/tsan/rtl/tsan_report.h b/lib/tsan/rtl/tsan_report.h
index d139296d54c0..f6715d1aae9b 100644
--- a/lib/tsan/rtl/tsan_report.h
+++ b/lib/tsan/rtl/tsan_report.h
@@ -24,7 +24,7 @@ enum ReportType {
ReportTypeThreadLeak,
ReportTypeMutexDestroyLocked,
ReportTypeSignalUnsafe,
- ReportTypeErrnoInSignal,
+ ReportTypeErrnoInSignal
};
struct ReportStack {
@@ -38,27 +38,38 @@ struct ReportStack {
int col;
};
+struct ReportMopMutex {
+ u64 id;
+ bool write;
+};
+
struct ReportMop {
int tid;
uptr addr;
int size;
bool write;
- int nmutex;
- int *mutex;
+ Vector<ReportMopMutex> mset;
ReportStack *stack;
+
+ ReportMop();
};
enum ReportLocationType {
ReportLocationGlobal,
ReportLocationHeap,
ReportLocationStack,
+ ReportLocationTLS,
+ ReportLocationFD
};
struct ReportLocation {
ReportLocationType type;
uptr addr;
uptr size;
+ char *module;
+ uptr offset;
int tid;
+ int fd;
char *name;
char *file;
int line;
@@ -67,13 +78,16 @@ struct ReportLocation {
struct ReportThread {
int id;
+ uptr pid;
bool running;
char *name;
+ int parent_tid;
ReportStack *stack;
};
struct ReportMutex {
- int id;
+ u64 id;
+ bool destroyed;
ReportStack *stack;
};
@@ -85,6 +99,7 @@ class ReportDesc {
Vector<ReportLocation*> locs;
Vector<ReportMutex*> mutexes;
Vector<ReportThread*> threads;
+ ReportStack *sleep;
ReportDesc();
~ReportDesc();
@@ -96,6 +111,7 @@ class ReportDesc {
// Format and output the report to the console/log. No additional logic.
void PrintReport(const ReportDesc *rep);
+void PrintStack(const ReportStack *stack);
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc
index 0ceb26c90e67..493ed2055dfc 100644
--- a/lib/tsan/rtl/tsan_rtl.cc
+++ b/lib/tsan/rtl/tsan_rtl.cc
@@ -15,7 +15,9 @@
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
#include "tsan_defs.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
@@ -47,11 +49,12 @@ Context::Context()
, nmissed_expected()
, thread_mtx(MutexTypeThreads, StatMtxThreads)
, racy_stacks(MBlockRacyStacks)
- , racy_addresses(MBlockRacyAddresses) {
+ , racy_addresses(MBlockRacyAddresses)
+ , fired_suppressions(MBlockRacyAddresses) {
}
// The objects are allocated in TLS, so one may rely on zero-initialization.
-ThreadState::ThreadState(Context *ctx, int tid, u64 epoch,
+ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch,
uptr stk_addr, uptr stk_size,
uptr tls_addr, uptr tls_size)
: fast_state(tid, epoch)
@@ -62,6 +65,7 @@ ThreadState::ThreadState(Context *ctx, int tid, u64 epoch,
// , in_rtl()
, shadow_stack_pos(&shadow_stack[0])
, tid(tid)
+ , unique_id(unique_id)
, stk_addr(stk_addr)
, stk_size(stk_size)
, tls_addr(tls_addr)
@@ -71,6 +75,7 @@ ThreadState::ThreadState(Context *ctx, int tid, u64 epoch,
ThreadContext::ThreadContext(int tid)
: tid(tid)
, unique_id()
+ , os_id()
, user_id()
, thr()
, status(ThreadStatusInvalid)
@@ -79,7 +84,8 @@ ThreadContext::ThreadContext(int tid)
, epoch0()
, epoch1()
, dead_info()
- , dead_next() {
+ , dead_next()
+ , name() {
}
static void WriteMemoryProfile(char *buf, uptr buf_size, int num) {
@@ -119,9 +125,9 @@ static void MemoryProfileThread(void *arg) {
ScopedInRtl in_rtl;
fd_t fd = (fd_t)(uptr)arg;
for (int i = 0; ; i++) {
- InternalScopedBuf<char> buf(4096);
- WriteMemoryProfile(buf.Ptr(), buf.Size(), i);
- internal_write(fd, buf.Ptr(), internal_strlen(buf.Ptr()));
+ InternalScopedBuffer<char> buf(4096);
+ WriteMemoryProfile(buf.data(), buf.size(), i);
+ internal_write(fd, buf.data(), internal_strlen(buf.data()));
SleepForSeconds(1);
}
}
@@ -129,12 +135,12 @@ static void MemoryProfileThread(void *arg) {
static void InitializeMemoryProfile() {
if (flags()->profile_memory == 0 || flags()->profile_memory[0] == 0)
return;
- InternalScopedBuf<char> filename(4096);
- internal_snprintf(filename.Ptr(), filename.Size(), "%s.%d",
+ InternalScopedBuffer<char> filename(4096);
+ internal_snprintf(filename.data(), filename.size(), "%s.%d",
flags()->profile_memory, GetPid());
- fd_t fd = internal_open(filename.Ptr(), true);
+ fd_t fd = internal_open(filename.data(), true);
if (fd == kInvalidFd) {
- TsanPrintf("Failed to open memory profile file '%s'\n", &filename[0]);
+ Printf("Failed to open memory profile file '%s'\n", &filename[0]);
Die();
}
internal_start_thread(&MemoryProfileThread, (void*)(uptr)fd);
@@ -156,41 +162,81 @@ static void InitializeMemoryFlush() {
internal_start_thread(&MemoryFlushThread, 0);
}
+void MapShadow(uptr addr, uptr size) {
+ MmapFixedNoReserve(MemToShadow(addr), size * kShadowMultiplier);
+}
+
+void MapThreadTrace(uptr addr, uptr size) {
+ DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size);
+ CHECK_GE(addr, kTraceMemBegin);
+ CHECK_LE(addr + size, kTraceMemBegin + kTraceMemSize);
+ if (addr != (uptr)MmapFixedNoReserve(addr, size)) {
+ Printf("FATAL: ThreadSanitizer can not mmap thread trace\n");
+ Die();
+ }
+}
+
void Initialize(ThreadState *thr) {
// Thread safe because done before all threads exist.
static bool is_initialized = false;
if (is_initialized)
return;
is_initialized = true;
+ // Install tool-specific callbacks in sanitizer_common.
+ SetCheckFailedCallback(TsanCheckFailed);
+
ScopedInRtl in_rtl;
+#ifndef TSAN_GO
+ InitializeAllocator();
+#endif
InitializeInterceptors();
const char *env = InitializePlatform();
InitializeMutex();
InitializeDynamicAnnotations();
ctx = new(ctx_placeholder) Context;
+#ifndef TSAN_GO
InitializeShadowMemory();
+#endif
ctx->dead_list_size = 0;
ctx->dead_list_head = 0;
ctx->dead_list_tail = 0;
InitializeFlags(&ctx->flags, env);
+ // Setup correct file descriptor for error reports.
+ if (internal_strcmp(flags()->log_path, "stdout") == 0)
+ __sanitizer_set_report_fd(kStdoutFd);
+ else if (internal_strcmp(flags()->log_path, "stderr") == 0)
+ __sanitizer_set_report_fd(kStderrFd);
+ else
+ __sanitizer_set_report_path(flags()->log_path);
InitializeSuppressions();
+#ifndef TSAN_GO
+ // Initialize external symbolizer before internal threads are started.
+ const char *external_symbolizer = flags()->external_symbolizer_path;
+ if (external_symbolizer != 0 && external_symbolizer[0] != '\0') {
+ if (!InitializeExternalSymbolizer(external_symbolizer)) {
+ Printf("Failed to start external symbolizer: '%s'\n",
+ external_symbolizer);
+ Die();
+ }
+ }
+#endif
InitializeMemoryProfile();
InitializeMemoryFlush();
if (ctx->flags.verbosity)
- TsanPrintf("***** Running under ThreadSanitizer v2 (pid %d) *****\n",
+ Printf("***** Running under ThreadSanitizer v2 (pid %d) *****\n",
GetPid());
// Initialize thread 0.
ctx->thread_seq = 0;
int tid = ThreadCreate(thr, 0, 0, true);
CHECK_EQ(tid, 0);
- ThreadStart(thr, tid);
+ ThreadStart(thr, tid, GetPid());
CHECK_EQ(thr->in_rtl, 1);
ctx->initialized = true;
if (flags()->stop_on_start) {
- TsanPrintf("ThreadSanitizer is suspended at startup (pid %d)."
+ Printf("ThreadSanitizer is suspended at startup (pid %d)."
" Call __tsan_resume().\n",
GetPid());
while (__tsan_resumed == 0);
@@ -202,34 +248,77 @@ int Finalize(ThreadState *thr) {
Context *ctx = __tsan::ctx;
bool failed = false;
+ if (flags()->atexit_sleep_ms > 0 && ThreadCount(thr) > 1)
+ SleepForMillis(flags()->atexit_sleep_ms);
+
+ // Wait for pending reports.
+ ctx->report_mtx.Lock();
+ ctx->report_mtx.Unlock();
+
ThreadFinalize(thr);
if (ctx->nreported) {
failed = true;
- TsanPrintf("ThreadSanitizer: reported %d warnings\n", ctx->nreported);
+#ifndef TSAN_GO
+ Printf("ThreadSanitizer: reported %d warnings\n", ctx->nreported);
+#else
+ Printf("Found %d data race(s)\n", ctx->nreported);
+#endif
}
if (ctx->nmissed_expected) {
failed = true;
- TsanPrintf("ThreadSanitizer: missed %d expected races\n",
+ Printf("ThreadSanitizer: missed %d expected races\n",
ctx->nmissed_expected);
}
+ StatAggregate(ctx->stat, thr->stat);
StatOutput(ctx->stat);
return failed ? flags()->exitcode : 0;
}
+#ifndef TSAN_GO
+u32 CurrentStackId(ThreadState *thr, uptr pc) {
+ if (thr->shadow_stack_pos == 0) // May happen during bootstrap.
+ return 0;
+ if (pc) {
+ thr->shadow_stack_pos[0] = pc;
+ thr->shadow_stack_pos++;
+ }
+ u32 id = StackDepotPut(thr->shadow_stack,
+ thr->shadow_stack_pos - thr->shadow_stack);
+ if (pc)
+ thr->shadow_stack_pos--;
+ return id;
+}
+#endif
+
void TraceSwitch(ThreadState *thr) {
thr->nomalloc++;
ScopedInRtl in_rtl;
Lock l(&thr->trace.mtx);
- unsigned trace = (thr->fast_state.epoch() / kTracePartSize) % kTraceParts;
+ unsigned trace = (thr->fast_state.epoch() / kTracePartSize) % TraceParts();
TraceHeader *hdr = &thr->trace.headers[trace];
hdr->epoch0 = thr->fast_state.epoch();
hdr->stack0.ObtainCurrent(thr, 0);
+ hdr->mset0 = thr->mset;
thr->nomalloc--;
}
+uptr TraceTopPC(ThreadState *thr) {
+ Event *events = (Event*)GetThreadTrace(thr->tid);
+ uptr pc = events[thr->fast_state.GetTracePos()];
+ return pc;
+}
+
+uptr TraceSize() {
+ return (uptr)(1ull << (kTracePartSizeBits + flags()->history_size + 1));
+}
+
+uptr TraceParts() {
+ return TraceSize() / kTracePartSize;
+}
+
#ifndef TSAN_GO
extern "C" void __tsan_trace_switch() {
TraceSwitch(cur_thread());
@@ -273,11 +362,11 @@ static inline bool BothReads(Shadow s, int kAccessIsWrite) {
return !kAccessIsWrite && !s.is_write();
}
-static inline bool OldIsRWStronger(Shadow old, int kAccessIsWrite) {
+static inline bool OldIsRWNotWeaker(Shadow old, int kAccessIsWrite) {
return old.is_write() || !kAccessIsWrite;
}
-static inline bool OldIsRWWeaker(Shadow old, int kAccessIsWrite) {
+static inline bool OldIsRWWeakerOrEqual(Shadow old, int kAccessIsWrite) {
return !old.is_write() || kAccessIsWrite;
}
@@ -286,12 +375,12 @@ static inline bool OldIsInSameSynchEpoch(Shadow old, ThreadState *thr) {
}
static inline bool HappensBefore(Shadow old, ThreadState *thr) {
- return thr->clock.get(old.tid()) >= old.epoch();
+ return thr->clock.get(old.TidWithIgnore()) >= old.epoch();
}
ALWAYS_INLINE
void MemoryAccessImpl(ThreadState *thr, uptr addr,
- int kAccessSizeLog, bool kAccessIsWrite, FastState fast_state,
+ int kAccessSizeLog, bool kAccessIsWrite,
u64 *shadow_mem, Shadow cur) {
StatInc(thr, StatMop);
StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
@@ -367,7 +456,7 @@ ALWAYS_INLINE
void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
int kAccessSizeLog, bool kAccessIsWrite) {
u64 *shadow_mem = (u64*)MemToShadow(addr);
- DPrintf2("#%d: tsan::OnMemoryAccess: @%p %p size=%d"
+ DPrintf2("#%d: MemoryAccess: @%p %p size=%d"
" is_write=%d shadow_mem=%p {%zx, %zx, %zx, %zx}\n",
(int)thr->fast_state.tid(), (void*)pc, (void*)addr,
(int)(1 << kAccessSizeLog), kAccessIsWrite, shadow_mem,
@@ -375,11 +464,11 @@ void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
(uptr)shadow_mem[2], (uptr)shadow_mem[3]);
#if TSAN_DEBUG
if (!IsAppMem(addr)) {
- TsanPrintf("Access to non app mem %zx\n", addr);
+ Printf("Access to non app mem %zx\n", addr);
DCHECK(IsAppMem(addr));
}
if (!IsShadowMem((uptr)shadow_mem)) {
- TsanPrintf("Bad shadow addr %p (%zx)\n", shadow_mem, addr);
+ Printf("Bad shadow addr %p (%zx)\n", shadow_mem, addr);
DCHECK(IsShadowMem((uptr)shadow_mem));
}
#endif
@@ -395,9 +484,9 @@ void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
// We must not store to the trace if we do not store to the shadow.
// That is, this call must be moved somewhere below.
- TraceAddEvent(thr, fast_state.epoch(), EventTypeMop, pc);
+ TraceAddEvent(thr, fast_state, EventTypeMop, pc);
- MemoryAccessImpl(thr, addr, kAccessSizeLog, kAccessIsWrite, fast_state,
+ MemoryAccessImpl(thr, addr, kAccessSizeLog, kAccessIsWrite,
shadow_mem, cur);
}
@@ -414,24 +503,29 @@ static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size,
addr += offset;
size -= offset;
}
- CHECK_EQ(addr % 8, 0);
- CHECK(IsAppMem(addr));
- CHECK(IsAppMem(addr + size - 1));
+ DCHECK_EQ(addr % 8, 0);
+ // If a user passes some insane arguments (memset(0)),
+ // let it just crash as usual.
+ if (!IsAppMem(addr) || !IsAppMem(addr + size - 1))
+ return;
(void)thr;
(void)pc;
// Some programs mmap like hundreds of GBs but actually used a small part.
// So, it's better to report a false positive on the memory
// then to hang here senselessly.
- const uptr kMaxResetSize = 1024*1024*1024;
+ const uptr kMaxResetSize = 4ull*1024*1024*1024;
if (size > kMaxResetSize)
size = kMaxResetSize;
- size = (size + 7) & ~7;
+ size = (size + (kShadowCell - 1)) & ~(kShadowCell - 1);
u64 *p = (u64*)MemToShadow(addr);
CHECK(IsShadowMem((uptr)p));
CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1)));
// FIXME: may overwrite a part outside the region
- for (uptr i = 0; i < size * kShadowCnt / kShadowCell; i++)
- p[i] = val;
+ for (uptr i = 0; i < size * kShadowCnt / kShadowCell;) {
+ p[i++] = val;
+ for (uptr j = 1; j < kShadowCnt; j++)
+ p[i++] = 0;
+ }
}
void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size) {
@@ -441,18 +535,28 @@ void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size) {
void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size) {
MemoryAccessRange(thr, pc, addr, size, true);
Shadow s(thr->fast_state);
+ s.ClearIgnoreBit();
s.MarkAsFreed();
s.SetWrite(true);
s.SetAddr0AndSizeLog(0, 3);
MemoryRangeSet(thr, pc, addr, size, s.raw());
}
+void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size) {
+ Shadow s(thr->fast_state);
+ s.ClearIgnoreBit();
+ s.SetWrite(true);
+ s.SetAddr0AndSizeLog(0, 3);
+ MemoryRangeSet(thr, pc, addr, size, s.raw());
+}
+
+ALWAYS_INLINE
void FuncEntry(ThreadState *thr, uptr pc) {
DCHECK_EQ(thr->in_rtl, 0);
StatInc(thr, StatFuncEnter);
DPrintf2("#%d: FuncEntry %p\n", (int)thr->fast_state.tid(), (void*)pc);
thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeFuncEnter, pc);
+ TraceAddEvent(thr, thr->fast_state, EventTypeFuncEnter, pc);
// Shadow stack maintenance can be replaced with
// stack unwinding during trace switch (which presumably must be faster).
@@ -476,12 +580,13 @@ void FuncEntry(ThreadState *thr, uptr pc) {
thr->shadow_stack_pos++;
}
+ALWAYS_INLINE
void FuncExit(ThreadState *thr) {
DCHECK_EQ(thr->in_rtl, 0);
StatInc(thr, StatFuncExit);
DPrintf2("#%d: FuncExit\n", (int)thr->fast_state.tid());
thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeFuncExit, 0);
+ TraceAddEvent(thr, thr->fast_state, EventTypeFuncExit, 0);
DCHECK_GT(thr->shadow_stack_pos, &thr->shadow_stack[0]);
#ifndef TSAN_GO
diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h
index c559cb2f080c..6b0ab0d385ef 100644
--- a/lib/tsan/rtl/tsan_rtl.h
+++ b/lib/tsan/rtl/tsan_rtl.h
@@ -27,6 +27,7 @@
#define TSAN_RTL_H
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_allocator.h"
#include "tsan_clock.h"
#include "tsan_defs.h"
#include "tsan_flags.h"
@@ -34,31 +35,90 @@
#include "tsan_trace.h"
#include "tsan_vector.h"
#include "tsan_report.h"
+#include "tsan_platform.h"
+#include "tsan_mutexset.h"
+
+#if SANITIZER_WORDSIZE != 64
+# error "ThreadSanitizer is supported only on 64-bit platforms"
+#endif
namespace __tsan {
-void TsanPrintf(const char *format, ...);
+// Descriptor of user's memory block.
+struct MBlock {
+ Mutex mtx;
+ uptr size;
+ u32 alloc_tid;
+ u32 alloc_stack_id;
+ SyncVar *head;
+
+ MBlock()
+ : mtx(MutexTypeMBlock, StatMtxMBlock) {
+ }
+};
+
+#ifndef TSAN_GO
+#if defined(TSAN_COMPAT_SHADOW) && TSAN_COMPAT_SHADOW
+const uptr kAllocatorSpace = 0x7d0000000000ULL;
+#else
+const uptr kAllocatorSpace = 0x7d0000000000ULL;
+#endif
+const uptr kAllocatorSize = 0x10000000000ULL; // 1T.
+
+struct TsanMapUnmapCallback {
+ void OnMap(uptr p, uptr size) const { }
+ void OnUnmap(uptr p, uptr size) const {
+ // We are about to unmap a chunk of user memory.
+ // Mark the corresponding shadow memory as not needed.
+ uptr shadow_beg = MemToShadow(p);
+ uptr shadow_end = MemToShadow(p + size);
+ CHECK(IsAligned(shadow_end|shadow_beg, GetPageSizeCached()));
+ FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg);
+ }
+};
+
+typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, sizeof(MBlock),
+ DefaultSizeClassMap> PrimaryAllocator;
+typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
+typedef LargeMmapAllocator<TsanMapUnmapCallback> SecondaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
+ SecondaryAllocator> Allocator;
+Allocator *allocator();
+#endif
+
+void TsanCheckFailed(const char *file, int line, const char *cond,
+ u64 v1, u64 v2);
// FastState (from most significant bit):
-// unused : 1
+// ignore : 1
// tid : kTidBits
// epoch : kClkBits
// unused : -
-// ignore_bit : 1
+// history_size : 3
class FastState {
public:
FastState(u64 tid, u64 epoch) {
x_ = tid << kTidShift;
x_ |= epoch << kClkShift;
- DCHECK(tid == this->tid());
- DCHECK(epoch == this->epoch());
+ DCHECK_EQ(tid, this->tid());
+ DCHECK_EQ(epoch, this->epoch());
+ DCHECK_EQ(GetIgnoreBit(), false);
}
explicit FastState(u64 x)
: x_(x) {
}
+ u64 raw() const {
+ return x_;
+ }
+
u64 tid() const {
+ u64 res = (x_ & ~kIgnoreBit) >> kTidShift;
+ return res;
+ }
+
+ u64 TidWithIgnore() const {
u64 res = x_ >> kTidShift;
return res;
}
@@ -77,13 +137,34 @@ class FastState {
void SetIgnoreBit() { x_ |= kIgnoreBit; }
void ClearIgnoreBit() { x_ &= ~kIgnoreBit; }
- bool GetIgnoreBit() const { return x_ & kIgnoreBit; }
+ bool GetIgnoreBit() const { return (s64)x_ < 0; }
+
+ void SetHistorySize(int hs) {
+ CHECK_GE(hs, 0);
+ CHECK_LE(hs, 7);
+ x_ = (x_ & ~7) | hs;
+ }
+
+ int GetHistorySize() const {
+ return (int)(x_ & 7);
+ }
+
+ void ClearHistorySize() {
+ x_ &= ~7;
+ }
+
+ u64 GetTracePos() const {
+ const int hs = GetHistorySize();
+ // When hs == 0, the trace consists of 2 parts.
+ const u64 mask = (1ull << (kTracePartSizeBits + hs + 1)) - 1;
+ return epoch() & mask;
+ }
private:
friend class Shadow;
static const int kTidShift = 64 - kTidBits - 1;
static const int kClkShift = kTidShift - kClkBits;
- static const u64 kIgnoreBit = 1ull;
+ static const u64 kIgnoreBit = 1ull << 63;
static const u64 kFreedBit = 1ull << 63;
u64 x_;
};
@@ -97,9 +178,14 @@ class FastState {
// addr0 : 3
class Shadow : public FastState {
public:
- explicit Shadow(u64 x) : FastState(x) { }
+ explicit Shadow(u64 x)
+ : FastState(x) {
+ }
- explicit Shadow(const FastState &s) : FastState(s.x_) { }
+ explicit Shadow(const FastState &s)
+ : FastState(s.x_) {
+ ClearHistorySize();
+ }
void SetAddr0AndSizeLog(u64 addr0, unsigned kAccessSizeLog) {
DCHECK_EQ(x_ & 31, 0);
@@ -118,11 +204,10 @@ class Shadow : public FastState {
}
bool IsZero() const { return x_ == 0; }
- u64 raw() const { return x_; }
static inline bool TidsAreEqual(const Shadow s1, const Shadow s2) {
u64 shifted_xor = (s1.x_ ^ s2.x_) >> kTidShift;
- DCHECK_EQ(shifted_xor == 0, s1.tid() == s2.tid());
+ DCHECK_EQ(shifted_xor == 0, s1.TidWithIgnore() == s2.TidWithIgnore());
return shifted_xor == 0;
}
@@ -199,10 +284,6 @@ class Shadow : public FastState {
}
};
-// Freed memory.
-// As if 8-byte write by thread 0xff..f at epoch 0xff..f, races with everything.
-const u64 kShadowFreed = 0xfffffffffffffff8ull;
-
struct SignalContext;
// This struct is stored in TLS.
@@ -236,9 +317,14 @@ struct ThreadState {
uptr *shadow_stack;
uptr *shadow_stack_end;
#endif
+ MutexSet mset;
ThreadClock clock;
+#ifndef TSAN_GO
+ AllocatorCache alloc_cache;
+#endif
u64 stat[StatCnt];
const int tid;
+ const int unique_id;
int in_rtl;
bool is_alive;
const uptr stk_addr;
@@ -251,11 +337,16 @@ struct ThreadState {
bool in_signal_handler;
SignalContext *signal_ctx;
+#ifndef TSAN_GO
+ u32 last_sleep_stack_id;
+ ThreadClock last_sleep_clock;
+#endif
+
// Set in regions of runtime that must be signal-safe and fork-safe.
// If set, malloc must not be called.
int nomalloc;
- explicit ThreadState(Context *ctx, int tid, u64 epoch,
+ explicit ThreadState(Context *ctx, int tid, int unique_id, u64 epoch,
uptr stk_addr, uptr stk_size,
uptr tls_addr, uptr tls_size);
};
@@ -274,7 +365,7 @@ enum ThreadStatus {
ThreadStatusCreated, // Created but not yet running.
ThreadStatusRunning, // The thread is currently running.
ThreadStatusFinished, // Joinable thread is finished but not yet joined.
- ThreadStatusDead, // Joined, but some info (trace) is still alive.
+ ThreadStatusDead // Joined, but some info (trace) is still alive.
};
// An info about a thread that is hold for some time after its termination.
@@ -285,6 +376,7 @@ struct ThreadDeadInfo {
struct ThreadContext {
const int tid;
int unique_id; // Non-rolling thread id.
+ uptr os_id; // pid
uptr user_id; // Some opaque user thread id (e.g. pthread_t).
ThreadState *thr;
ThreadStatus status;
@@ -297,8 +389,10 @@ struct ThreadContext {
u64 epoch0;
u64 epoch1;
StackTrace creation_stack;
+ int creation_tid;
ThreadDeadInfo *dead_info;
ThreadContext *dead_next; // In dead thread list.
+ char *name; // As annotated by user.
explicit ThreadContext(int tid);
};
@@ -319,6 +413,11 @@ struct RacyAddress {
uptr addr_max;
};
+struct FiredSuppression {
+ ReportType type;
+ uptr pc;
+};
+
struct Context {
Context();
@@ -342,6 +441,7 @@ struct Context {
Vector<RacyStacks> racy_stacks;
Vector<RacyAddress> racy_addresses;
+ Vector<FiredSuppression> fired_suppressions;
Flags flags;
@@ -366,10 +466,12 @@ class ScopedReport {
~ScopedReport();
void AddStack(const StackTrace *stack);
- void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack);
+ void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack,
+ const MutexSet *mset);
void AddThread(const ThreadContext *tctx);
void AddMutex(const SyncVar *s);
void AddLocation(uptr addr, uptr size);
+ void AddSleep(u32 stack_id);
const ReportDesc *GetReport() const;
@@ -377,10 +479,14 @@ class ScopedReport {
Context *ctx_;
ReportDesc *rep_;
+ void AddMutex(u64 id);
+
ScopedReport(const ScopedReport&);
void operator = (const ScopedReport&);
};
+void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset);
+
void StatAggregate(u64 *dst, u64 *src);
void StatOutput(u64 *stat);
void ALWAYS_INLINE INLINE StatInc(ThreadState *thr, StatType typ, u64 n = 1) {
@@ -388,34 +494,47 @@ void ALWAYS_INLINE INLINE StatInc(ThreadState *thr, StatType typ, u64 n = 1) {
thr->stat[typ] += n;
}
+void MapShadow(uptr addr, uptr size);
+void MapThreadTrace(uptr addr, uptr size);
void InitializeShadowMemory();
void InitializeInterceptors();
void InitializeDynamicAnnotations();
void ReportRace(ThreadState *thr);
-bool OutputReport(const ScopedReport &srep,
+bool OutputReport(Context *ctx,
+ const ScopedReport &srep,
const ReportStack *suppress_stack = 0);
+bool IsFiredSuppression(Context *ctx,
+ const ScopedReport &srep,
+ const StackTrace &trace);
bool IsExpectedReport(uptr addr, uptr size);
#if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1
-# define DPrintf TsanPrintf
+# define DPrintf Printf
#else
# define DPrintf(...)
#endif
#if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 2
-# define DPrintf2 TsanPrintf
+# define DPrintf2 Printf
#else
# define DPrintf2(...)
#endif
+u32 CurrentStackId(ThreadState *thr, uptr pc);
+void PrintCurrentStack(ThreadState *thr, uptr pc);
+
void Initialize(ThreadState *thr);
int Finalize(ThreadState *thr);
+SyncVar* GetJavaSync(ThreadState *thr, uptr pc, uptr addr,
+ bool write_lock, bool create);
+SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr);
+
void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
int kAccessSizeLog, bool kAccessIsWrite);
void MemoryAccessImpl(ThreadState *thr, uptr addr,
- int kAccessSizeLog, bool kAccessIsWrite, FastState fast_state,
+ int kAccessSizeLog, bool kAccessIsWrite,
u64 *shadow_mem, Shadow cur);
void MemoryRead1Byte(ThreadState *thr, uptr pc, uptr addr);
void MemoryWrite1Byte(ThreadState *thr, uptr pc, uptr addr);
@@ -425,21 +544,25 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
uptr size, bool is_write);
void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size);
void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size);
+void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size);
void IgnoreCtl(ThreadState *thr, bool write, bool begin);
void FuncEntry(ThreadState *thr, uptr pc);
void FuncExit(ThreadState *thr);
int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached);
-void ThreadStart(ThreadState *thr, int tid);
+void ThreadStart(ThreadState *thr, int tid, uptr os_id);
void ThreadFinish(ThreadState *thr);
int ThreadTid(ThreadState *thr, uptr pc, uptr uid);
void ThreadJoin(ThreadState *thr, uptr pc, int tid);
void ThreadDetach(ThreadState *thr, uptr pc, int tid);
void ThreadFinalize(ThreadState *thr);
-void ThreadFinalizerGoroutine(ThreadState *thr);
+void ThreadSetName(ThreadState *thr, const char *name);
+int ThreadCount(ThreadState *thr);
+void ProcessPendingSignals(ThreadState *thr);
-void MutexCreate(ThreadState *thr, uptr pc, uptr addr, bool rw, bool recursive);
+void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
+ bool rw, bool recursive, bool linker_init);
void MutexDestroy(ThreadState *thr, uptr pc, uptr addr);
void MutexLock(ThreadState *thr, uptr pc, uptr addr);
void MutexUnlock(ThreadState *thr, uptr pc, uptr addr);
@@ -448,8 +571,10 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr);
void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr);
void Acquire(ThreadState *thr, uptr pc, uptr addr);
+void AcquireGlobal(ThreadState *thr, uptr pc);
void Release(ThreadState *thr, uptr pc, uptr addr);
void ReleaseStore(ThreadState *thr, uptr pc, uptr addr);
+void AfterSleep(ThreadState *thr, uptr pc);
// The hacky call uses custom calling convention and an assembly thunk.
// It is considerably faster that a normal call for the caller
@@ -461,27 +586,39 @@ void ReleaseStore(ThreadState *thr, uptr pc, uptr addr);
// The caller may not create the stack frame for itself at all,
// so we create a reserve stack frame for it (1024b must be enough).
#define HACKY_CALL(f) \
- __asm__ __volatile__("sub $0x400, %%rsp;" \
+ __asm__ __volatile__("sub $1024, %%rsp;" \
+ "/*.cfi_adjust_cfa_offset 1024;*/" \
+ ".hidden " #f "_thunk;" \
"call " #f "_thunk;" \
- "add $0x400, %%rsp;" ::: "memory");
+ "add $1024, %%rsp;" \
+ "/*.cfi_adjust_cfa_offset -1024;*/" \
+ ::: "memory", "cc");
#else
#define HACKY_CALL(f) f()
#endif
void TraceSwitch(ThreadState *thr);
+uptr TraceTopPC(ThreadState *thr);
+uptr TraceSize();
+uptr TraceParts();
extern "C" void __tsan_trace_switch();
-void ALWAYS_INLINE INLINE TraceAddEvent(ThreadState *thr, u64 epoch,
- EventType typ, uptr addr) {
+void ALWAYS_INLINE INLINE TraceAddEvent(ThreadState *thr, FastState fs,
+ EventType typ, u64 addr) {
+ DCHECK_GE((int)typ, 0);
+ DCHECK_LE((int)typ, 7);
+ DCHECK_EQ(GetLsb(addr, 61), addr);
StatInc(thr, StatEvents);
- if (UNLIKELY((epoch % kTracePartSize) == 0)) {
+ u64 pos = fs.GetTracePos();
+ if (UNLIKELY((pos % kTracePartSize) == 0)) {
#ifndef TSAN_GO
HACKY_CALL(__tsan_trace_switch);
#else
TraceSwitch(thr);
#endif
}
- Event *evp = &thr->trace.events[epoch % kTraceSize];
+ Event *trace = (Event*)GetThreadTrace(fs.tid());
+ Event *evp = &trace[pos];
Event ev = (u64)addr | ((u64)typ << 61);
*evp = ev;
}
diff --git a/lib/tsan/rtl/tsan_rtl_amd64.S b/lib/tsan/rtl/tsan_rtl_amd64.S
index 2028ec508611..af878563573e 100644
--- a/lib/tsan/rtl/tsan_rtl_amd64.S
+++ b/lib/tsan/rtl/tsan_rtl_amd64.S
@@ -1,20 +1,43 @@
.section .text
+.hidden __tsan_trace_switch
.globl __tsan_trace_switch_thunk
__tsan_trace_switch_thunk:
+ .cfi_startproc
# Save scratch registers.
push %rax
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rax, 0
push %rcx
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rcx, 0
push %rdx
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rdx, 0
push %rsi
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rsi, 0
push %rdi
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rdi, 0
push %r8
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r8, 0
push %r9
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r9, 0
push %r10
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r10, 0
push %r11
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r11, 0
# Align stack frame.
push %rbx # non-scratch
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rbx, 0
mov %rsp, %rbx # save current rsp
+ .cfi_def_cfa_register %rbx
shr $4, %rsp # clear 4 lsb, align to 16
shl $4, %rsp
@@ -22,34 +45,79 @@ __tsan_trace_switch_thunk:
# Unalign stack frame back.
mov %rbx, %rsp # restore the original rsp
+ .cfi_def_cfa_register %rsp
pop %rbx
+ .cfi_adjust_cfa_offset -8
# Restore scratch registers.
pop %r11
+ .cfi_adjust_cfa_offset -8
pop %r10
+ .cfi_adjust_cfa_offset -8
pop %r9
+ .cfi_adjust_cfa_offset -8
pop %r8
+ .cfi_adjust_cfa_offset -8
pop %rdi
+ .cfi_adjust_cfa_offset -8
pop %rsi
+ .cfi_adjust_cfa_offset -8
pop %rdx
+ .cfi_adjust_cfa_offset -8
pop %rcx
+ .cfi_adjust_cfa_offset -8
pop %rax
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %rax
+ .cfi_restore %rbx
+ .cfi_restore %rcx
+ .cfi_restore %rdx
+ .cfi_restore %rsi
+ .cfi_restore %rdi
+ .cfi_restore %r8
+ .cfi_restore %r9
+ .cfi_restore %r10
+ .cfi_restore %r11
ret
+ .cfi_endproc
+.hidden __tsan_report_race
.globl __tsan_report_race_thunk
__tsan_report_race_thunk:
+ .cfi_startproc
# Save scratch registers.
push %rax
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rax, 0
push %rcx
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rcx, 0
push %rdx
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rdx, 0
push %rsi
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rsi, 0
push %rdi
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rdi, 0
push %r8
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r8, 0
push %r9
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r9, 0
push %r10
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r10, 0
push %r11
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %r11, 0
# Align stack frame.
push %rbx # non-scratch
+ .cfi_adjust_cfa_offset 8
+ .cfi_rel_offset %rbx, 0
mov %rsp, %rbx # save current rsp
+ .cfi_def_cfa_register %rbx
shr $4, %rsp # clear 4 lsb, align to 16
shl $4, %rsp
@@ -57,15 +125,42 @@ __tsan_report_race_thunk:
# Unalign stack frame back.
mov %rbx, %rsp # restore the original rsp
+ .cfi_def_cfa_register %rsp
pop %rbx
+ .cfi_adjust_cfa_offset -8
# Restore scratch registers.
pop %r11
+ .cfi_adjust_cfa_offset -8
pop %r10
+ .cfi_adjust_cfa_offset -8
pop %r9
+ .cfi_adjust_cfa_offset -8
pop %r8
+ .cfi_adjust_cfa_offset -8
pop %rdi
+ .cfi_adjust_cfa_offset -8
pop %rsi
+ .cfi_adjust_cfa_offset -8
pop %rdx
+ .cfi_adjust_cfa_offset -8
pop %rcx
+ .cfi_adjust_cfa_offset -8
pop %rax
+ .cfi_adjust_cfa_offset -8
+ .cfi_restore %rax
+ .cfi_restore %rbx
+ .cfi_restore %rcx
+ .cfi_restore %rdx
+ .cfi_restore %rsi
+ .cfi_restore %rdi
+ .cfi_restore %r8
+ .cfi_restore %r9
+ .cfi_restore %r10
+ .cfi_restore %r11
ret
+ .cfi_endproc
+
+#ifdef __linux__
+/* We do not need executable stack. */
+.section .note.GNU-stack,"",@progbits
+#endif
diff --git a/lib/tsan/rtl/tsan_rtl_mutex.cc b/lib/tsan/rtl/tsan_rtl_mutex.cc
index 882def835658..d812f12be560 100644
--- a/lib/tsan/rtl/tsan_rtl_mutex.cc
+++ b/lib/tsan/rtl/tsan_rtl_mutex.cc
@@ -12,22 +12,26 @@
//===----------------------------------------------------------------------===//
#include "tsan_rtl.h"
+#include "tsan_flags.h"
#include "tsan_sync.h"
#include "tsan_report.h"
#include "tsan_symbolize.h"
+#include "tsan_platform.h"
namespace __tsan {
void MutexCreate(ThreadState *thr, uptr pc, uptr addr,
- bool rw, bool recursive) {
+ bool rw, bool recursive, bool linker_init) {
Context *ctx = CTX();
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexCreate %zx\n", thr->tid, addr);
StatInc(thr, StatMutexCreate);
- MemoryWrite1Byte(thr, pc, addr);
- SyncVar *s = ctx->synctab.GetAndLock(thr, pc, addr, true);
+ if (!linker_init && IsAppMem(addr))
+ MemoryWrite1Byte(thr, pc, addr);
+ SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true);
s->is_rw = rw;
s->is_recursive = recursive;
+ s->is_linker_init = linker_init;
s->mtx.Unlock();
}
@@ -36,34 +40,54 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr);
StatInc(thr, StatMutexDestroy);
- MemoryWrite1Byte(thr, pc, addr);
+#ifndef TSAN_GO
+ // Global mutexes not marked as LINKER_INITIALIZED
+ // cause tons of not interesting reports, so just ignore it.
+ if (IsGlobalVar(addr))
+ return;
+#endif
SyncVar *s = ctx->synctab.GetAndRemove(thr, pc, addr);
if (s == 0)
return;
- if (s->owner_tid != SyncVar::kInvalidTid && !s->is_broken) {
+ if (IsAppMem(addr))
+ MemoryWrite1Byte(thr, pc, addr);
+ if (flags()->report_destroy_locked
+ && s->owner_tid != SyncVar::kInvalidTid
+ && !s->is_broken) {
s->is_broken = true;
+ Lock l(&ctx->thread_mtx);
ScopedReport rep(ReportTypeMutexDestroyLocked);
rep.AddMutex(s);
+ StackTrace trace;
+ trace.ObtainCurrent(thr, pc);
+ rep.AddStack(&trace);
+ FastState last(s->last_lock);
+ RestoreStack(last.tid(), last.epoch(), &trace, 0);
+ rep.AddStack(&trace);
rep.AddLocation(s->addr, 1);
- OutputReport(rep);
+ OutputReport(ctx, rep);
}
+ thr->mset.Remove(s->GetId());
DestroyAndFree(s);
}
void MutexLock(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexLock %zx\n", thr->tid, addr);
- MemoryRead1Byte(thr, pc, addr);
+ if (IsAppMem(addr))
+ MemoryRead1Byte(thr, pc, addr);
+ SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeLock, addr);
- SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+ TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId());
if (s->owner_tid == SyncVar::kInvalidTid) {
CHECK_EQ(s->recursion, 0);
s->owner_tid = thr->tid;
+ s->last_lock = thr->fast_state.raw();
} else if (s->owner_tid == thr->tid) {
CHECK_GT(s->recursion, 0);
} else {
- TsanPrintf("ThreadSanitizer WARNING: double lock\n");
+ Printf("ThreadSanitizer WARNING: double lock\n");
+ PrintCurrentStack(thr, pc);
}
if (s->recursion == 0) {
StatInc(thr, StatMutexLock);
@@ -76,25 +100,29 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr) {
StatInc(thr, StatMutexRecLock);
}
s->recursion++;
+ thr->mset.Add(s->GetId(), true, thr->fast_state.epoch());
s->mtx.Unlock();
}
void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexUnlock %zx\n", thr->tid, addr);
- MemoryRead1Byte(thr, pc, addr);
+ if (IsAppMem(addr))
+ MemoryRead1Byte(thr, pc, addr);
+ SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeUnlock, addr);
- SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+ TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
if (s->recursion == 0) {
if (!s->is_broken) {
s->is_broken = true;
- TsanPrintf("ThreadSanitizer WARNING: unlock of unlocked mutex\n");
+ Printf("ThreadSanitizer WARNING: unlock of unlocked mutex\n");
+ PrintCurrentStack(thr, pc);
}
} else if (s->owner_tid != thr->tid) {
if (!s->is_broken) {
s->is_broken = true;
- TsanPrintf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
+ Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
+ PrintCurrentStack(thr, pc);
}
} else {
s->recursion--;
@@ -103,12 +131,13 @@ void MutexUnlock(ThreadState *thr, uptr pc, uptr addr) {
s->owner_tid = SyncVar::kInvalidTid;
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
- thr->clock.release(&s->clock);
+ thr->clock.ReleaseStore(&s->clock);
StatInc(thr, StatSyncRelease);
} else {
StatInc(thr, StatMutexRecUnlock);
}
}
+ thr->mset.Del(s->GetId(), true);
s->mtx.Unlock();
}
@@ -116,15 +145,20 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexReadLock %zx\n", thr->tid, addr);
StatInc(thr, StatMutexReadLock);
- MemoryRead1Byte(thr, pc, addr);
+ if (IsAppMem(addr))
+ MemoryRead1Byte(thr, pc, addr);
+ SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeRLock, addr);
- SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, false);
- if (s->owner_tid != SyncVar::kInvalidTid)
- TsanPrintf("ThreadSanitizer WARNING: read lock of a write locked mutex\n");
+ TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId());
+ if (s->owner_tid != SyncVar::kInvalidTid) {
+ Printf("ThreadSanitizer WARNING: read lock of a write locked mutex\n");
+ PrintCurrentStack(thr, pc);
+ }
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.acquire(&s->clock);
+ s->last_lock = thr->fast_state.raw();
StatInc(thr, StatSyncAcquire);
+ thr->mset.Add(s->GetId(), false, thr->fast_state.epoch());
s->mtx.ReadUnlock();
}
@@ -132,36 +166,45 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr);
StatInc(thr, StatMutexReadUnlock);
- MemoryRead1Byte(thr, pc, addr);
+ if (IsAppMem(addr))
+ MemoryRead1Byte(thr, pc, addr);
+ SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeRUnlock, addr);
- SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
- if (s->owner_tid != SyncVar::kInvalidTid)
- TsanPrintf("ThreadSanitizer WARNING: read unlock of a write "
+ TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
+ if (s->owner_tid != SyncVar::kInvalidTid) {
+ Printf("ThreadSanitizer WARNING: read unlock of a write "
"locked mutex\n");
+ PrintCurrentStack(thr, pc);
+ }
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
thr->clock.release(&s->read_clock);
StatInc(thr, StatSyncRelease);
s->mtx.Unlock();
+ thr->mset.Del(s->GetId(), false);
}
void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr);
- MemoryRead1Byte(thr, pc, addr);
- SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+ if (IsAppMem(addr))
+ MemoryRead1Byte(thr, pc, addr);
+ SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
+ bool write = true;
if (s->owner_tid == SyncVar::kInvalidTid) {
// Seems to be read unlock.
+ write = false;
StatInc(thr, StatMutexReadUnlock);
thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeRUnlock, addr);
+ TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
thr->clock.release(&s->read_clock);
StatInc(thr, StatSyncRelease);
} else if (s->owner_tid == thr->tid) {
// Seems to be write unlock.
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
CHECK_GT(s->recursion, 0);
s->recursion--;
if (s->recursion == 0) {
@@ -171,36 +214,50 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
// The sequence of events is quite tricky and doubled in several places.
// First, it's a bug to increment the epoch w/o writing to the trace.
// Then, the acquire/release logic can be factored out as well.
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeUnlock, addr);
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
- thr->clock.release(&s->clock);
+ thr->clock.ReleaseStore(&s->clock);
StatInc(thr, StatSyncRelease);
} else {
StatInc(thr, StatMutexRecUnlock);
}
} else if (!s->is_broken) {
s->is_broken = true;
- TsanPrintf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
+ Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
+ PrintCurrentStack(thr, pc);
}
+ thr->mset.Del(s->GetId(), write);
s->mtx.Unlock();
}
void Acquire(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
- SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, false);
+ SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false);
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.acquire(&s->clock);
StatInc(thr, StatSyncAcquire);
s->mtx.ReadUnlock();
}
+void AcquireGlobal(ThreadState *thr, uptr pc) {
+ Context *ctx = CTX();
+ Lock l(&ctx->thread_mtx);
+ for (unsigned i = 0; i < kMaxTid; i++) {
+ ThreadContext *tctx = ctx->threads[i];
+ if (tctx == 0)
+ continue;
+ if (tctx->status == ThreadStatusRunning)
+ thr->clock.set(i, tctx->thr->fast_state.epoch());
+ else
+ thr->clock.set(i, tctx->epoch1);
+ }
+}
+
void Release(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: Release %zx\n", thr->tid, addr);
- SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+ SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.release(&s->clock);
StatInc(thr, StatSyncRelease);
@@ -210,11 +267,28 @@ void Release(ThreadState *thr, uptr pc, uptr addr) {
void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) {
CHECK_GT(thr->in_rtl, 0);
DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr);
- SyncVar *s = CTX()->synctab.GetAndLock(thr, pc, addr, true);
+ SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true);
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->clock.ReleaseStore(&s->clock);
StatInc(thr, StatSyncRelease);
s->mtx.Unlock();
}
+#ifndef TSAN_GO
+void AfterSleep(ThreadState *thr, uptr pc) {
+ Context *ctx = CTX();
+ thr->last_sleep_stack_id = CurrentStackId(thr, pc);
+ Lock l(&ctx->thread_mtx);
+ for (unsigned i = 0; i < kMaxTid; i++) {
+ ThreadContext *tctx = ctx->threads[i];
+ if (tctx == 0)
+ continue;
+ if (tctx->status == ThreadStatusRunning)
+ thr->last_sleep_clock.set(i, tctx->thr->fast_state.epoch());
+ else
+ thr->last_sleep_clock.set(i, tctx->epoch1);
+ }
+}
+#endif
+
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_rtl_report.cc b/lib/tsan/rtl/tsan_rtl_report.cc
index f66e17e4815c..1a780e4b8070 100644
--- a/lib/tsan/rtl/tsan_rtl_report.cc
+++ b/lib/tsan/rtl/tsan_rtl_report.cc
@@ -13,6 +13,8 @@
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_common.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
#include "tsan_suppressions.h"
@@ -21,26 +23,26 @@
#include "tsan_sync.h"
#include "tsan_mman.h"
#include "tsan_flags.h"
+#include "tsan_fd.h"
-namespace __sanitizer {
-using namespace __tsan;
+namespace __tsan {
+
+using namespace __sanitizer; // NOLINT
-void CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) {
+void TsanCheckFailed(const char *file, int line, const char *cond,
+ u64 v1, u64 v2) {
ScopedInRtl in_rtl;
- TsanPrintf("FATAL: ThreadSanitizer CHECK failed: "
- "%s:%d \"%s\" (0x%zx, 0x%zx)\n",
- file, line, cond, (uptr)v1, (uptr)v2);
+ Printf("FATAL: ThreadSanitizer CHECK failed: "
+ "%s:%d \"%s\" (0x%zx, 0x%zx)\n",
+ file, line, cond, (uptr)v1, (uptr)v2);
Die();
}
-} // namespace __sanitizer
-
-namespace __tsan {
-
// Can be overriden by an application/test to intercept reports.
#ifdef TSAN_EXTERNAL_HOOKS
bool OnReport(const ReportDesc *rep, bool suppressed);
#else
+SANITIZER_INTERFACE_ATTRIBUTE
bool WEAK OnReport(const ReportDesc *rep, bool suppressed) {
(void)rep;
return suppressed;
@@ -84,9 +86,9 @@ static void StackStripMain(ReportStack *stack) {
} else if (last || last2) {
// Ensure that we recovered stack completely. Trimmed stack
// can actually happen if we do not instrument some code,
- // so it's only a DCHECK. However we must try hard to not miss it
+ // so it's only a debug print. However we must try hard to not miss it
// due to our fault.
- TsanPrintf("Bottom stack frame of stack %zx is missed\n", stack->pc);
+ DPrintf("Bottom stack frame of stack %zx is missed\n", stack->pc);
}
#else
if (last && 0 == internal_strcmp(last, "schedunlock"))
@@ -119,6 +121,7 @@ static ReportStack *SymbolizeStack(const StackTrace& trace) {
ScopedReport::ScopedReport(ReportType typ) {
ctx_ = CTX();
+ ctx_->thread_mtx.CheckLocked();
void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc));
rep_ = new(mem) ReportDesc;
rep_->typ = typ;
@@ -127,8 +130,7 @@ ScopedReport::ScopedReport(ReportType typ) {
ScopedReport::~ScopedReport() {
ctx_->report_mtx.Unlock();
- rep_->~ReportDesc();
- internal_free(rep_);
+ DestroyAndFree(rep_);
}
void ScopedReport::AddStack(const StackTrace *stack) {
@@ -137,7 +139,7 @@ void ScopedReport::AddStack(const StackTrace *stack) {
}
void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
- const StackTrace *stack) {
+ const StackTrace *stack, const MutexSet *mset) {
void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
ReportMop *mop = new(mem) ReportMop;
rep_->mops.PushBack(mop);
@@ -145,50 +147,195 @@ void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
mop->addr = addr + s.addr0();
mop->size = s.size();
mop->write = s.is_write();
- mop->nmutex = 0;
mop->stack = SymbolizeStack(*stack);
+ for (uptr i = 0; i < mset->Size(); i++) {
+ MutexSet::Desc d = mset->Get(i);
+ u64 uid = 0;
+ uptr addr = SyncVar::SplitId(d.id, &uid);
+ SyncVar *s = ctx_->synctab.GetIfExistsAndLock(addr, false);
+ // Check that the mutex is still alive.
+ // Another mutex can be created at the same address,
+ // so check uid as well.
+ if (s && s->CheckId(uid)) {
+ ReportMopMutex mtx = {s->uid, d.write};
+ mop->mset.PushBack(mtx);
+ AddMutex(s);
+ } else {
+ ReportMopMutex mtx = {d.id, d.write};
+ mop->mset.PushBack(mtx);
+ AddMutex(d.id);
+ }
+ if (s)
+ s->mtx.ReadUnlock();
+ }
}
void ScopedReport::AddThread(const ThreadContext *tctx) {
+ for (uptr i = 0; i < rep_->threads.Size(); i++) {
+ if (rep_->threads[i]->id == tctx->tid)
+ return;
+ }
void *mem = internal_alloc(MBlockReportThread, sizeof(ReportThread));
ReportThread *rt = new(mem) ReportThread();
rep_->threads.PushBack(rt);
rt->id = tctx->tid;
+ rt->pid = tctx->os_id;
rt->running = (tctx->status == ThreadStatusRunning);
+ rt->name = tctx->name ? internal_strdup(tctx->name) : 0;
+ rt->parent_tid = tctx->creation_tid;
rt->stack = SymbolizeStack(tctx->creation_stack);
}
+#ifndef TSAN_GO
+static ThreadContext *FindThread(int unique_id) {
+ Context *ctx = CTX();
+ ctx->thread_mtx.CheckLocked();
+ for (unsigned i = 0; i < kMaxTid; i++) {
+ ThreadContext *tctx = ctx->threads[i];
+ if (tctx && tctx->unique_id == unique_id) {
+ return tctx;
+ }
+ }
+ return 0;
+}
+
+ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) {
+ Context *ctx = CTX();
+ ctx->thread_mtx.CheckLocked();
+ for (unsigned i = 0; i < kMaxTid; i++) {
+ ThreadContext *tctx = ctx->threads[i];
+ if (tctx == 0 || tctx->status != ThreadStatusRunning)
+ continue;
+ ThreadState *thr = tctx->thr;
+ CHECK(thr);
+ if (addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size) {
+ *is_stack = true;
+ return tctx;
+ }
+ if (addr >= thr->tls_addr && addr < thr->tls_addr + thr->tls_size) {
+ *is_stack = false;
+ return tctx;
+ }
+ }
+ return 0;
+}
+#endif
+
void ScopedReport::AddMutex(const SyncVar *s) {
+ for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
+ if (rep_->mutexes[i]->id == s->uid)
+ return;
+ }
void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
ReportMutex *rm = new(mem) ReportMutex();
rep_->mutexes.PushBack(rm);
- rm->id = 42;
+ rm->id = s->uid;
+ rm->destroyed = false;
rm->stack = SymbolizeStack(s->creation_stack);
}
+void ScopedReport::AddMutex(u64 id) {
+ for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
+ if (rep_->mutexes[i]->id == id)
+ return;
+ }
+ void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
+ ReportMutex *rm = new(mem) ReportMutex();
+ rep_->mutexes.PushBack(rm);
+ rm->id = id;
+ rm->destroyed = true;
+ rm->stack = 0;
+}
+
void ScopedReport::AddLocation(uptr addr, uptr size) {
- ReportStack *symb = SymbolizeData(addr);
- if (symb) {
+ if (addr == 0)
+ return;
+#ifndef TSAN_GO
+ int fd = -1;
+ int creat_tid = -1;
+ u32 creat_stack = 0;
+ if (FdLocation(addr, &fd, &creat_tid, &creat_stack)
+ || FdLocation(AlternativeAddress(addr), &fd, &creat_tid, &creat_stack)) {
+ void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
+ ReportLocation *loc = new(mem) ReportLocation();
+ rep_->locs.PushBack(loc);
+ loc->type = ReportLocationFD;
+ loc->fd = fd;
+ loc->tid = creat_tid;
+ uptr ssz = 0;
+ const uptr *stack = StackDepotGet(creat_stack, &ssz);
+ if (stack) {
+ StackTrace trace;
+ trace.Init(stack, ssz);
+ loc->stack = SymbolizeStack(trace);
+ }
+ ThreadContext *tctx = FindThread(creat_tid);
+ if (tctx)
+ AddThread(tctx);
+ return;
+ }
+ if (allocator()->PointerIsMine((void*)addr)) {
+ MBlock *b = user_mblock(0, (void*)addr);
+ ThreadContext *tctx = FindThread(b->alloc_tid);
void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
ReportLocation *loc = new(mem) ReportLocation();
rep_->locs.PushBack(loc);
- loc->type = ReportLocationGlobal;
- loc->addr = addr;
- loc->size = size;
- loc->tid = 0;
- loc->name = symb->func;
- loc->file = symb->file;
- loc->line = symb->line;
+ loc->type = ReportLocationHeap;
+ loc->addr = (uptr)allocator()->GetBlockBegin((void*)addr);
+ loc->size = b->size;
+ loc->tid = tctx ? tctx->tid : b->alloc_tid;
+ loc->name = 0;
+ loc->file = 0;
+ loc->line = 0;
loc->stack = 0;
- internal_free(symb);
+ uptr ssz = 0;
+ const uptr *stack = StackDepotGet(b->alloc_stack_id, &ssz);
+ if (stack) {
+ StackTrace trace;
+ trace.Init(stack, ssz);
+ loc->stack = SymbolizeStack(trace);
+ }
+ if (tctx)
+ AddThread(tctx);
+ return;
}
+ bool is_stack = false;
+ if (ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack)) {
+ void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
+ ReportLocation *loc = new(mem) ReportLocation();
+ rep_->locs.PushBack(loc);
+ loc->type = is_stack ? ReportLocationStack : ReportLocationTLS;
+ loc->tid = tctx->tid;
+ AddThread(tctx);
+ }
+ ReportLocation *loc = SymbolizeData(addr);
+ if (loc) {
+ rep_->locs.PushBack(loc);
+ return;
+ }
+#endif
}
+#ifndef TSAN_GO
+void ScopedReport::AddSleep(u32 stack_id) {
+ uptr ssz = 0;
+ const uptr *stack = StackDepotGet(stack_id, &ssz);
+ if (stack) {
+ StackTrace trace;
+ trace.Init(stack, ssz);
+ rep_->sleep = SymbolizeStack(trace);
+ }
+}
+#endif
+
const ReportDesc *ScopedReport::GetReport() const {
return rep_;
}
-static void RestoreStack(int tid, const u64 epoch, StackTrace *stk) {
+void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) {
+ // This function restores stack trace and mutex set for the thread/epoch.
+ // It does so by getting stack trace and mutex set at the beginning of
+ // trace part, and then replaying the trace till the given epoch.
ThreadContext *tctx = CTX()->threads[tid];
if (tctx == 0)
return;
@@ -205,49 +352,62 @@ static void RestoreStack(int tid, const u64 epoch, StackTrace *stk) {
return;
}
Lock l(&trace->mtx);
- const int partidx = (epoch / (kTraceSize / kTraceParts)) % kTraceParts;
+ const int partidx = (epoch / kTracePartSize) % TraceParts();
TraceHeader* hdr = &trace->headers[partidx];
if (epoch < hdr->epoch0)
return;
- const u64 eend = epoch % kTraceSize;
- const u64 ebegin = eend / kTracePartSize * kTracePartSize;
+ const u64 epoch0 = RoundDown(epoch, TraceSize());
+ const u64 eend = epoch % TraceSize();
+ const u64 ebegin = RoundDown(eend, kTracePartSize);
DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n",
tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx);
- InternalScopedBuf<uptr> stack(1024); // FIXME: de-hardcode 1024
+ InternalScopedBuffer<uptr> stack(1024); // FIXME: de-hardcode 1024
for (uptr i = 0; i < hdr->stack0.Size(); i++) {
stack[i] = hdr->stack0.Get(i);
DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]);
}
+ if (mset)
+ *mset = hdr->mset0;
uptr pos = hdr->stack0.Size();
+ Event *events = (Event*)GetThreadTrace(tid);
for (uptr i = ebegin; i <= eend; i++) {
- Event ev = trace->events[i];
+ Event ev = events[i];
EventType typ = (EventType)(ev >> 61);
- uptr pc = (uptr)(ev & 0xffffffffffffull);
+ uptr pc = (uptr)(ev & ((1ull << 61) - 1));
DPrintf2(" %zu typ=%d pc=%zx\n", i, typ, pc);
if (typ == EventTypeMop) {
stack[pos] = pc;
} else if (typ == EventTypeFuncEnter) {
stack[pos++] = pc;
} else if (typ == EventTypeFuncExit) {
- // Since we have full stacks, this should never happen.
- DCHECK_GT(pos, 0);
if (pos > 0)
pos--;
}
+ if (mset) {
+ if (typ == EventTypeLock) {
+ mset->Add(pc, true, epoch0 + i);
+ } else if (typ == EventTypeUnlock) {
+ mset->Del(pc, true);
+ } else if (typ == EventTypeRLock) {
+ mset->Add(pc, false, epoch0 + i);
+ } else if (typ == EventTypeRUnlock) {
+ mset->Del(pc, false);
+ }
+ }
for (uptr j = 0; j <= pos; j++)
DPrintf2(" #%zu: %zx\n", j, stack[j]);
}
if (pos == 0 && stack[0] == 0)
return;
pos++;
- stk->Init(stack, pos);
+ stk->Init(stack.data(), pos);
}
static bool HandleRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
uptr addr_min, uptr addr_max) {
Context *ctx = CTX();
bool equal_stack = false;
- RacyStacks hash = {};
+ RacyStacks hash;
if (flags()->suppress_equal_stacks) {
hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr));
hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr));
@@ -298,20 +458,81 @@ static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
}
}
-bool OutputReport(const ScopedReport &srep, const ReportStack *suppress_stack) {
+bool OutputReport(Context *ctx,
+ const ScopedReport &srep,
+ const ReportStack *suppress_stack) {
const ReportDesc *rep = srep.GetReport();
- bool suppressed = IsSuppressed(rep->typ, suppress_stack);
- suppressed = OnReport(rep, suppressed);
- if (suppressed)
+ const uptr suppress_pc = IsSuppressed(rep->typ, suppress_stack);
+ if (suppress_pc != 0) {
+ FiredSuppression supp = {srep.GetReport()->typ, suppress_pc};
+ ctx->fired_suppressions.PushBack(supp);
+ }
+ if (OnReport(rep, suppress_pc != 0))
return false;
PrintReport(rep);
CTX()->nreported++;
return true;
}
+bool IsFiredSuppression(Context *ctx,
+ const ScopedReport &srep,
+ const StackTrace &trace) {
+ for (uptr k = 0; k < ctx->fired_suppressions.Size(); k++) {
+ if (ctx->fired_suppressions[k].type != srep.GetReport()->typ)
+ continue;
+ for (uptr j = 0; j < trace.Size(); j++) {
+ if (trace.Get(j) == ctx->fired_suppressions[k].pc)
+ return true;
+ }
+ }
+ return false;
+}
+
+// On programs that use Java we see weird reports like:
+// WARNING: ThreadSanitizer: data race (pid=22512)
+// Read of size 8 at 0x7d2b00084318 by thread 100:
+// #0 memcpy tsan_interceptors.cc:406 (foo+0x00000d8dfae3)
+// #1 <null> <null>:0 (0x7f7ad9b40193)
+// Previous write of size 8 at 0x7d2b00084318 by thread 105:
+// #0 strncpy tsan_interceptors.cc:501 (foo+0x00000d8e0919)
+// #1 <null> <null>:0 (0x7f7ad9b42707)
+static bool IsJavaNonsense(const ReportDesc *rep) {
+ for (uptr i = 0; i < rep->mops.Size(); i++) {
+ ReportMop *mop = rep->mops[i];
+ ReportStack *frame = mop->stack;
+ if (frame != 0 && frame->func != 0
+ && (internal_strcmp(frame->func, "memset") == 0
+ || internal_strcmp(frame->func, "memcpy") == 0
+ || internal_strcmp(frame->func, "memmove") == 0
+ || internal_strcmp(frame->func, "strcmp") == 0
+ || internal_strcmp(frame->func, "strncpy") == 0
+ || internal_strcmp(frame->func, "strlen") == 0
+ || internal_strcmp(frame->func, "free") == 0
+ || internal_strcmp(frame->func, "pthread_mutex_lock") == 0)) {
+ frame = frame->next;
+ if (frame == 0
+ || (frame->func == 0 && frame->file == 0 && frame->line == 0
+ && frame->module == 0)) {
+ if (frame) {
+ FiredSuppression supp = {rep->typ, frame->pc};
+ CTX()->fired_suppressions.PushBack(supp);
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
void ReportRace(ThreadState *thr) {
+ if (!flags()->report_bugs)
+ return;
ScopedInRtl in_rtl;
+ if (thr->in_signal_handler)
+ Printf("ThreadSanitizer: printing report from signal handler."
+ " Can crash or hang.\n");
+
bool freed = false;
{
Shadow s(thr->racy_state[1]);
@@ -339,21 +560,26 @@ void ReportRace(ThreadState *thr) {
ScopedReport rep(freed ? ReportTypeUseAfterFree : ReportTypeRace);
const uptr kMop = 2;
StackTrace traces[kMop];
- for (uptr i = 0; i < kMop; i++) {
- Shadow s(thr->racy_state[i]);
- RestoreStack(s.tid(), s.epoch(), &traces[i]);
- }
+ const uptr toppc = TraceTopPC(thr);
+ traces[0].ObtainCurrent(thr, toppc);
+ if (IsFiredSuppression(ctx, rep, traces[0]))
+ return;
+ InternalScopedBuffer<MutexSet> mset2(1);
+ new(mset2.data()) MutexSet();
+ Shadow s2(thr->racy_state[1]);
+ RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2.data());
if (HandleRacyStacks(thr, traces, addr_min, addr_max))
return;
for (uptr i = 0; i < kMop; i++) {
Shadow s(thr->racy_state[i]);
- rep.AddMemoryAccess(addr, s, &traces[i]);
+ rep.AddMemoryAccess(addr, s, &traces[i],
+ i == 0 ? &thr->mset : mset2.data());
}
- // Ensure that we have at least something for the current thread.
- CHECK_EQ(traces[0].IsEmpty(), false);
+ if (flags()->suppress_java && IsJavaNonsense(rep.GetReport()))
+ return;
for (uptr i = 0; i < kMop; i++) {
FastState s(thr->racy_state[i]);
@@ -363,10 +589,26 @@ void ReportRace(ThreadState *thr) {
rep.AddThread(tctx);
}
- if (!OutputReport(rep, rep.GetReport()->mops[0]->stack))
+ rep.AddLocation(addr_min, addr_max - addr_min);
+
+#ifndef TSAN_GO
+ { // NOLINT
+ Shadow s(thr->racy_state[1]);
+ if (s.epoch() <= thr->last_sleep_clock.get(s.tid()))
+ rep.AddSleep(thr->last_sleep_stack_id);
+ }
+#endif
+
+ if (!OutputReport(ctx, rep, rep.GetReport()->mops[0]->stack))
return;
AddRacyStacks(thr, traces, addr_min, addr_max);
}
+void PrintCurrentStack(ThreadState *thr, uptr pc) {
+ StackTrace trace;
+ trace.ObtainCurrent(thr, pc);
+ PrintStack(SymbolizeStack(trace));
+}
+
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc
index f7d5f13dca78..359775927834 100644
--- a/lib/tsan/rtl/tsan_rtl_thread.cc
+++ b/lib/tsan/rtl/tsan_rtl_thread.cc
@@ -35,7 +35,7 @@ static void MaybeReportThreadLeak(ThreadContext *tctx) {
return;
ScopedReport rep(ReportTypeThreadLeak);
rep.AddThread(tctx);
- OutputReport(rep);
+ OutputReport(CTX(), rep);
}
void ThreadFinalize(ThreadState *thr) {
@@ -52,6 +52,23 @@ void ThreadFinalize(ThreadState *thr) {
}
}
+int ThreadCount(ThreadState *thr) {
+ CHECK_GT(thr->in_rtl, 0);
+ Context *ctx = CTX();
+ Lock l(&ctx->thread_mtx);
+ int cnt = 0;
+ for (unsigned i = 0; i < kMaxTid; i++) {
+ ThreadContext *tctx = ctx->threads[i];
+ if (tctx == 0)
+ continue;
+ if (tctx->status != ThreadStatusCreated
+ && tctx->status != ThreadStatusRunning)
+ continue;
+ cnt++;
+ }
+ return cnt;
+}
+
static void ThreadDead(ThreadState *thr, ThreadContext *tctx) {
Context *ctx = CTX();
CHECK_GT(thr->in_rtl, 0);
@@ -81,8 +98,9 @@ int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
ThreadContext *tctx = 0;
if (ctx->dead_list_size > kThreadQuarantineSize
|| ctx->thread_seq >= kMaxTid) {
+ // Reusing old thread descriptor and tid.
if (ctx->dead_list_size == 0) {
- TsanPrintf("ThreadSanitizer: %d thread limit exceeded. Dying.\n",
+ Printf("ThreadSanitizer: %d thread limit exceeded. Dying.\n",
kMaxTid);
Die();
}
@@ -100,12 +118,18 @@ int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
tctx->sync.Reset();
tid = tctx->tid;
DestroyAndFree(tctx->dead_info);
+ if (tctx->name) {
+ internal_free(tctx->name);
+ tctx->name = 0;
+ }
} else {
+ // Allocating new thread descriptor and tid.
StatInc(thr, StatThreadMaxTid);
tid = ctx->thread_seq++;
void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext));
tctx = new(mem) ThreadContext(tid);
ctx->threads[tid] = tctx;
+ MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event));
}
CHECK_NE(tctx, 0);
CHECK_GE(tid, 0);
@@ -126,18 +150,18 @@ int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
if (tid) {
thr->fast_state.IncrementEpoch();
// Can't increment epoch w/o writing to the trace as well.
- TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeMop, 0);
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
thr->clock.release(&tctx->sync);
StatInc(thr, StatSyncRelease);
-
tctx->creation_stack.ObtainCurrent(thr, pc);
+ tctx->creation_tid = thr->tid;
}
return tid;
}
-void ThreadStart(ThreadState *thr, int tid) {
+void ThreadStart(ThreadState *thr, int tid, uptr os_id) {
CHECK_GT(thr->in_rtl, 0);
uptr stk_addr = 0;
uptr stk_size = 0;
@@ -169,10 +193,14 @@ void ThreadStart(ThreadState *thr, int tid) {
CHECK_NE(tctx, 0);
CHECK_EQ(tctx->status, ThreadStatusCreated);
tctx->status = ThreadStatusRunning;
- tctx->epoch0 = tctx->epoch1 + 1;
+ tctx->os_id = os_id;
+ // RoundUp so that one trace part does not contain events
+ // from different threads.
+ tctx->epoch0 = RoundUp(tctx->epoch1 + 1, kTracePartSize);
tctx->epoch1 = (u64)-1;
- new(thr) ThreadState(CTX(), tid, tctx->epoch0, stk_addr, stk_size,
- tls_addr, tls_size);
+ new(thr) ThreadState(CTX(), tid, tctx->unique_id,
+ tctx->epoch0, stk_addr, stk_size,
+ tls_addr, tls_size);
#ifdef TSAN_GO
// Setup dynamic shadow stack.
const int kInitStackSize = 8;
@@ -185,6 +213,9 @@ void ThreadStart(ThreadState *thr, int tid) {
thr->fast_synch_epoch = tctx->epoch0;
thr->clock.set(tid, tctx->epoch0);
thr->clock.acquire(&tctx->sync);
+ thr->fast_state.SetHistorySize(flags()->history_size);
+ const uptr trace = (tctx->epoch0 / kTracePartSize) % TraceParts();
+ thr->trace.headers[trace].epoch0 = tctx->epoch0;
StatInc(thr, StatSyncAcquire);
DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx "
"tls_addr=%zx tls_size=%zx\n",
@@ -219,7 +250,7 @@ void ThreadFinish(ThreadState *thr) {
} else {
thr->fast_state.IncrementEpoch();
// Can't increment epoch w/o writing to the trace as well.
- TraceAddEvent(thr, thr->fast_state.epoch(), EventTypeMop, 0);
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
thr->clock.set(thr->tid, thr->fast_state.epoch());
thr->fast_synch_epoch = thr->fast_state.epoch();
thr->clock.release(&tctx->sync);
@@ -230,14 +261,16 @@ void ThreadFinish(ThreadState *thr) {
// Save from info about the thread.
tctx->dead_info = new(internal_alloc(MBlockDeadInfo, sizeof(ThreadDeadInfo)))
ThreadDeadInfo();
- internal_memcpy(&tctx->dead_info->trace.events[0],
- &thr->trace.events[0], sizeof(thr->trace.events));
- for (int i = 0; i < kTraceParts; i++) {
+ for (uptr i = 0; i < TraceParts(); i++) {
+ tctx->dead_info->trace.headers[i].epoch0 = thr->trace.headers[i].epoch0;
tctx->dead_info->trace.headers[i].stack0.CopyFrom(
thr->trace.headers[i].stack0);
}
tctx->epoch1 = thr->fast_state.epoch();
+#ifndef TSAN_GO
+ AlloctorThreadFinish(thr);
+#endif
thr->~ThreadState();
StatAggregate(ctx->stat, thr->stat);
tctx->thr = 0;
@@ -270,9 +303,10 @@ void ThreadJoin(ThreadState *thr, uptr pc, int tid) {
Lock l(&ctx->thread_mtx);
ThreadContext *tctx = ctx->threads[tid];
if (tctx->status == ThreadStatusInvalid) {
- TsanPrintf("ThreadSanitizer: join of non-existent thread\n");
+ Printf("ThreadSanitizer: join of non-existent thread\n");
return;
}
+ // FIXME(dvyukov): print message and continue (it's user error).
CHECK_EQ(tctx->detached, false);
CHECK_EQ(tctx->status, ThreadStatusFinished);
thr->clock.acquire(&tctx->sync);
@@ -288,7 +322,7 @@ void ThreadDetach(ThreadState *thr, uptr pc, int tid) {
Lock l(&ctx->thread_mtx);
ThreadContext *tctx = ctx->threads[tid];
if (tctx->status == ThreadStatusInvalid) {
- TsanPrintf("ThreadSanitizer: detach of non-existent thread\n");
+ Printf("ThreadSanitizer: detach of non-existent thread\n");
return;
}
if (tctx->status == ThreadStatusFinished) {
@@ -298,8 +332,18 @@ void ThreadDetach(ThreadState *thr, uptr pc, int tid) {
}
}
-void ThreadFinalizerGoroutine(ThreadState *thr) {
- thr->clock.Disable(thr->tid);
+void ThreadSetName(ThreadState *thr, const char *name) {
+ Context *ctx = CTX();
+ Lock l(&ctx->thread_mtx);
+ ThreadContext *tctx = ctx->threads[thr->tid];
+ CHECK_NE(tctx, 0);
+ CHECK_EQ(tctx->status, ThreadStatusRunning);
+ if (tctx->name) {
+ internal_free(tctx->name);
+ tctx->name = 0;
+ }
+ if (name)
+ tctx->name = internal_strdup(name);
}
void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
@@ -314,19 +358,19 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
#if TSAN_DEBUG
if (!IsAppMem(addr)) {
- TsanPrintf("Access to non app mem %zx\n", addr);
+ Printf("Access to non app mem %zx\n", addr);
DCHECK(IsAppMem(addr));
}
if (!IsAppMem(addr + size - 1)) {
- TsanPrintf("Access to non app mem %zx\n", addr + size - 1);
+ Printf("Access to non app mem %zx\n", addr + size - 1);
DCHECK(IsAppMem(addr + size - 1));
}
if (!IsShadowMem((uptr)shadow_mem)) {
- TsanPrintf("Bad shadow addr %p (%zx)\n", shadow_mem, addr);
+ Printf("Bad shadow addr %p (%zx)\n", shadow_mem, addr);
DCHECK(IsShadowMem((uptr)shadow_mem));
}
if (!IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1))) {
- TsanPrintf("Bad shadow addr %p (%zx)\n",
+ Printf("Bad shadow addr %p (%zx)\n",
shadow_mem + size * kShadowCnt / 8 - 1, addr + size - 1);
DCHECK(IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1)));
}
@@ -340,7 +384,7 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
fast_state.IncrementEpoch();
thr->fast_state = fast_state;
- TraceAddEvent(thr, fast_state.epoch(), EventTypeMop, pc);
+ TraceAddEvent(thr, fast_state, EventTypeMop, pc);
bool unaligned = (addr % kShadowCell) != 0;
@@ -350,7 +394,7 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
Shadow cur(fast_state);
cur.SetWrite(is_write);
cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog);
- MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, fast_state,
+ MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write,
shadow_mem, cur);
}
if (unaligned)
@@ -361,7 +405,7 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
Shadow cur(fast_state);
cur.SetWrite(is_write);
cur.SetAddr0AndSizeLog(0, kAccessSizeLog);
- MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, fast_state,
+ MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write,
shadow_mem, cur);
shadow_mem += kShadowCnt;
}
@@ -371,7 +415,7 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
Shadow cur(fast_state);
cur.SetWrite(is_write);
cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog);
- MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, fast_state,
+ MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write,
shadow_mem, cur);
}
}
diff --git a/lib/tsan/rtl/tsan_stat.cc b/lib/tsan/rtl/tsan_stat.cc
index a7c33a5de760..82f1d6b5620f 100644
--- a/lib/tsan/rtl/tsan_stat.cc
+++ b/lib/tsan/rtl/tsan_stat.cc
@@ -77,6 +77,11 @@ void StatOutput(u64 *stat) {
name[StatAtomicStore] = " store ";
name[StatAtomicExchange] = " exchange ";
name[StatAtomicFetchAdd] = " fetch_add ";
+ name[StatAtomicFetchSub] = " fetch_sub ";
+ name[StatAtomicFetchAnd] = " fetch_and ";
+ name[StatAtomicFetchOr] = " fetch_or ";
+ name[StatAtomicFetchXor] = " fetch_xor ";
+ name[StatAtomicFetchNand] = " fetch_nand ";
name[StatAtomicCAS] = " compare_exchange ";
name[StatAtomicFence] = " fence ";
name[StatAtomicRelaxed] = " Including relaxed ";
@@ -89,11 +94,13 @@ void StatOutput(u64 *stat) {
name[StatAtomic2] = " size 2 ";
name[StatAtomic4] = " size 4 ";
name[StatAtomic8] = " size 8 ";
+ name[StatAtomic16] = " size 16 ";
name[StatInterceptor] = "Interceptors ";
name[StatInt_longjmp] = " longjmp ";
name[StatInt_siglongjmp] = " siglongjmp ";
name[StatInt_malloc] = " malloc ";
+ name[StatInt___libc_memalign] = " __libc_memalign ";
name[StatInt_calloc] = " calloc ";
name[StatInt_realloc] = " realloc ";
name[StatInt_free] = " free ";
@@ -131,6 +138,7 @@ void StatOutput(u64 *stat) {
name[StatInt_atexit] = " atexit ";
name[StatInt___cxa_guard_acquire] = " __cxa_guard_acquire ";
name[StatInt___cxa_guard_release] = " __cxa_guard_release ";
+ name[StatInt___cxa_guard_abort] = " __cxa_guard_abort ";
name[StatInt_pthread_create] = " pthread_create ";
name[StatInt_pthread_join] = " pthread_join ";
name[StatInt_pthread_detach] = " pthread_detach ";
@@ -173,7 +181,30 @@ void StatOutput(u64 *stat) {
name[StatInt_sem_timedwait] = " sem_timedwait ";
name[StatInt_sem_post] = " sem_post ";
name[StatInt_sem_getvalue] = " sem_getvalue ";
+ name[StatInt_open] = " open ";
+ name[StatInt_open64] = " open64 ";
+ name[StatInt_creat] = " creat ";
+ name[StatInt_creat64] = " creat64 ";
+ name[StatInt_dup] = " dup ";
+ name[StatInt_dup2] = " dup2 ";
+ name[StatInt_dup3] = " dup3 ";
+ name[StatInt_eventfd] = " eventfd ";
+ name[StatInt_signalfd] = " signalfd ";
+ name[StatInt_inotify_init] = " inotify_init ";
+ name[StatInt_inotify_init1] = " inotify_init1 ";
+ name[StatInt_socket] = " socket ";
+ name[StatInt_socketpair] = " socketpair ";
+ name[StatInt_connect] = " connect ";
+ name[StatInt_accept] = " accept ";
+ name[StatInt_accept4] = " accept4 ";
+ name[StatInt_epoll_create] = " epoll_create ";
+ name[StatInt_epoll_create1] = " epoll_create1 ";
+ name[StatInt_close] = " close ";
+ name[StatInt___close] = " __close ";
+ name[StatInt_pipe] = " pipe ";
+ name[StatInt_pipe2] = " pipe2 ";
name[StatInt_read] = " read ";
+ name[StatInt_prctl] = " prctl ";
name[StatInt_pread] = " pread ";
name[StatInt_pread64] = " pread64 ";
name[StatInt_readv] = " readv ";
@@ -189,6 +220,8 @@ void StatOutput(u64 *stat) {
name[StatInt_recvmsg] = " recvmsg ";
name[StatInt_unlink] = " unlink ";
name[StatInt_fopen] = " fopen ";
+ name[StatInt_freopen] = " freopen ";
+ name[StatInt_fclose] = " fclose ";
name[StatInt_fread] = " fread ";
name[StatInt_fwrite] = " fwrite ";
name[StatInt_puts] = " puts ";
@@ -196,7 +229,19 @@ void StatOutput(u64 *stat) {
name[StatInt_opendir] = " opendir ";
name[StatInt_epoll_ctl] = " epoll_ctl ";
name[StatInt_epoll_wait] = " epoll_wait ";
+ name[StatInt_poll] = " poll ";
name[StatInt_sigaction] = " sigaction ";
+ name[StatInt_sleep] = " sleep ";
+ name[StatInt_usleep] = " usleep ";
+ name[StatInt_nanosleep] = " nanosleep ";
+ name[StatInt_gettimeofday] = " gettimeofday ";
+ name[StatInt_fork] = " fork ";
+ name[StatInt_vscanf] = " vscanf ";
+ name[StatInt_vsscanf] = " vsscanf ";
+ name[StatInt_vfscanf] = " vfscanf ";
+ name[StatInt_scanf] = " scanf ";
+ name[StatInt_sscanf] = " sscanf ";
+ name[StatInt_fscanf] = " fscanf ";
name[StatAnnotation] = "Dynamic annotations ";
name[StatAnnotateHappensBefore] = " HappensBefore ";
@@ -240,10 +285,12 @@ void StatOutput(u64 *stat) {
name[StatMtxSlab] = " Slab ";
name[StatMtxAtExit] = " Atexit ";
name[StatMtxAnnotations] = " Annotations ";
+ name[StatMtxMBlock] = " MBlock ";
+ name[StatMtxJavaMBlock] = " JavaMBlock ";
- TsanPrintf("Statistics:\n");
+ Printf("Statistics:\n");
for (int i = 0; i < StatCnt; i++)
- TsanPrintf("%s: %zu\n", name[i], (uptr)stat[i]);
+ Printf("%s: %zu\n", name[i], (uptr)stat[i]);
}
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_stat.h b/lib/tsan/rtl/tsan_stat.h
index 71b1b13c0876..58c5f23af40b 100644
--- a/lib/tsan/rtl/tsan_stat.h
+++ b/lib/tsan/rtl/tsan_stat.h
@@ -73,9 +73,11 @@ enum StatType {
StatAtomicStore,
StatAtomicExchange,
StatAtomicFetchAdd,
+ StatAtomicFetchSub,
StatAtomicFetchAnd,
StatAtomicFetchOr,
StatAtomicFetchXor,
+ StatAtomicFetchNand,
StatAtomicCAS,
StatAtomicFence,
StatAtomicRelaxed,
@@ -88,12 +90,14 @@ enum StatType {
StatAtomic2,
StatAtomic4,
StatAtomic8,
+ StatAtomic16,
// Interceptors.
StatInterceptor,
StatInt_longjmp,
StatInt_siglongjmp,
StatInt_malloc,
+ StatInt___libc_memalign,
StatInt_calloc,
StatInt_realloc,
StatInt_free,
@@ -131,6 +135,7 @@ enum StatType {
StatInt_atexit,
StatInt___cxa_guard_acquire,
StatInt___cxa_guard_release,
+ StatInt___cxa_guard_abort,
StatInt_pthread_create,
StatInt_pthread_join,
StatInt_pthread_detach,
@@ -171,7 +176,30 @@ enum StatType {
StatInt_sem_timedwait,
StatInt_sem_post,
StatInt_sem_getvalue,
+ StatInt_open,
+ StatInt_open64,
+ StatInt_creat,
+ StatInt_creat64,
+ StatInt_dup,
+ StatInt_dup2,
+ StatInt_dup3,
+ StatInt_eventfd,
+ StatInt_signalfd,
+ StatInt_inotify_init,
+ StatInt_inotify_init1,
+ StatInt_socket,
+ StatInt_socketpair,
+ StatInt_connect,
+ StatInt_accept,
+ StatInt_accept4,
+ StatInt_epoll_create,
+ StatInt_epoll_create1,
+ StatInt_close,
+ StatInt___close,
+ StatInt_pipe,
+ StatInt_pipe2,
StatInt_read,
+ StatInt_prctl,
StatInt_pread,
StatInt_pread64,
StatInt_readv,
@@ -187,6 +215,8 @@ enum StatType {
StatInt_recvmsg,
StatInt_unlink,
StatInt_fopen,
+ StatInt_freopen,
+ StatInt_fclose,
StatInt_fread,
StatInt_fwrite,
StatInt_puts,
@@ -194,11 +224,23 @@ enum StatType {
StatInt_opendir,
StatInt_epoll_ctl,
StatInt_epoll_wait,
+ StatInt_poll,
StatInt_sigaction,
StatInt_signal,
StatInt_raise,
StatInt_kill,
StatInt_pthread_kill,
+ StatInt_sleep,
+ StatInt_usleep,
+ StatInt_nanosleep,
+ StatInt_gettimeofday,
+ StatInt_fork,
+ StatInt_vscanf,
+ StatInt_vsscanf,
+ StatInt_vfscanf,
+ StatInt_scanf,
+ StatInt_sscanf,
+ StatInt_fscanf,
// Dynamic annotations.
StatAnnotation,
@@ -209,6 +251,7 @@ enum StatType {
StatAnnotateMutexIsNotPHB,
StatAnnotateCondVarWait,
StatAnnotateRWLockCreate,
+ StatAnnotateRWLockCreateStatic,
StatAnnotateRWLockDestroy,
StatAnnotateRWLockAcquired,
StatAnnotateRWLockReleased,
@@ -244,9 +287,11 @@ enum StatType {
StatMtxSlab,
StatMtxAnnotations,
StatMtxAtExit,
+ StatMtxMBlock,
+ StatMtxJavaMBlock,
// This must be the last.
- StatCnt,
+ StatCnt
};
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc
index 7549a4f8ba83..5316f6db6a0a 100644
--- a/lib/tsan/rtl/tsan_suppressions.cc
+++ b/lib/tsan/rtl/tsan_suppressions.cc
@@ -26,27 +26,27 @@ static Suppression *g_suppressions;
static char *ReadFile(const char *filename) {
if (filename == 0 || filename[0] == 0)
return 0;
- InternalScopedBuf<char> tmp(4*1024);
- if (filename[0] == '/')
- internal_snprintf(tmp, tmp.Size(), "%s", filename);
+ InternalScopedBuffer<char> tmp(4*1024);
+ if (filename[0] == '/' || GetPwd() == 0)
+ internal_snprintf(tmp.data(), tmp.size(), "%s", filename);
else
- internal_snprintf(tmp, tmp.Size(), "%s/%s", GetPwd(), filename);
- fd_t fd = internal_open(tmp, false);
+ internal_snprintf(tmp.data(), tmp.size(), "%s/%s", GetPwd(), filename);
+ fd_t fd = internal_open(tmp.data(), false);
if (fd == kInvalidFd) {
- TsanPrintf("ThreadSanitizer: failed to open suppressions file '%s'\n",
- tmp.Ptr());
+ Printf("ThreadSanitizer: failed to open suppressions file '%s'\n",
+ tmp.data());
Die();
}
const uptr fsize = internal_filesize(fd);
if (fsize == (uptr)-1) {
- TsanPrintf("ThreadSanitizer: failed to stat suppressions file '%s'\n",
- tmp.Ptr());
+ Printf("ThreadSanitizer: failed to stat suppressions file '%s'\n",
+ tmp.data());
Die();
}
char *buf = (char*)internal_alloc(MBlockSuppression, fsize + 1);
if (fsize != internal_read(fd, buf, fsize)) {
- TsanPrintf("ThreadSanitizer: failed to read suppressions file '%s'\n",
- tmp.Ptr());
+ Printf("ThreadSanitizer: failed to read suppressions file '%s'\n",
+ tmp.data());
Die();
}
internal_close(fd);
@@ -110,7 +110,7 @@ Suppression *SuppressionParse(const char* supp) {
stype = SuppressionSignal;
line += sizeof("signal:") - 1;
} else {
- TsanPrintf("ThreadSanitizer: failed to parse suppressions file\n");
+ Printf("ThreadSanitizer: failed to parse suppressions file\n");
Die();
}
Suppression *s = (Suppression*)internal_alloc(MBlockSuppression,
@@ -134,9 +134,9 @@ void InitializeSuppressions() {
g_suppressions = SuppressionParse(supp);
}
-bool IsSuppressed(ReportType typ, const ReportStack *stack) {
+uptr IsSuppressed(ReportType typ, const ReportStack *stack) {
if (g_suppressions == 0 || stack == 0)
- return false;
+ return 0;
SuppressionType stype;
if (typ == ReportTypeRace)
stype = SuppressionRace;
@@ -147,17 +147,17 @@ bool IsSuppressed(ReportType typ, const ReportStack *stack) {
else if (typ == ReportTypeSignalUnsafe)
stype = SuppressionSignal;
else
- return false;
+ return 0;
for (const ReportStack *frame = stack; frame; frame = frame->next) {
for (Suppression *supp = g_suppressions; supp; supp = supp->next) {
if (stype == supp->type &&
(SuppressionMatch(supp->templ, frame->func) ||
SuppressionMatch(supp->templ, frame->file))) {
DPrintf("ThreadSanitizer: matched suppression '%s'\n", supp->templ);
- return true;
+ return frame->pc;
}
}
}
- return false;
+ return 0;
}
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_suppressions.h b/lib/tsan/rtl/tsan_suppressions.h
index 29311c123d6d..61a4cca9d17a 100644
--- a/lib/tsan/rtl/tsan_suppressions.h
+++ b/lib/tsan/rtl/tsan_suppressions.h
@@ -19,14 +19,14 @@ namespace __tsan {
void InitializeSuppressions();
void FinalizeSuppressions();
-bool IsSuppressed(ReportType typ, const ReportStack *stack);
+uptr IsSuppressed(ReportType typ, const ReportStack *stack);
// Exposed for testing.
enum SuppressionType {
SuppressionRace,
SuppressionMutex,
SuppressionThread,
- SuppressionSignal,
+ SuppressionSignal
};
struct Suppression {
diff --git a/lib/tsan/rtl/tsan_symbolize.cc b/lib/tsan/rtl/tsan_symbolize.cc
index f757d070e169..29dfe237ffd9 100644
--- a/lib/tsan/rtl/tsan_symbolize.cc
+++ b/lib/tsan/rtl/tsan_symbolize.cc
@@ -29,14 +29,24 @@ ReportStack *NewReportStackEntry(uptr addr) {
return ent;
}
+// Strip module path to make output shorter.
+static char *StripModuleName(const char *module) {
+ if (module == 0)
+ return 0;
+ const char *short_module_name = internal_strrchr(module, '/');
+ if (short_module_name)
+ short_module_name += 1;
+ else
+ short_module_name = module;
+ return internal_strdup(short_module_name);
+}
+
static ReportStack *NewReportStackEntry(const AddressInfo &info) {
ReportStack *ent = NewReportStackEntry(info.address);
- if (info.module)
- ent->module = internal_strdup(info.module);
+ ent->module = StripModuleName(info.module);
ent->offset = info.module_offset;
- if (info.function) {
+ if (info.function)
ent->func = internal_strdup(info.function);
- }
if (info.file)
ent->file = internal_strdup(info.file);
ent->line = info.line;
@@ -45,12 +55,12 @@ static ReportStack *NewReportStackEntry(const AddressInfo &info) {
}
ReportStack *SymbolizeCode(uptr addr) {
- if (flags()->use_internal_symbolizer) {
+ if (flags()->external_symbolizer_path[0]) {
static const uptr kMaxAddrFrames = 16;
- InternalScopedBuf<AddressInfo> addr_frames(kMaxAddrFrames);
+ InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames);
for (uptr i = 0; i < kMaxAddrFrames; i++)
new(&addr_frames[i]) AddressInfo();
- uptr addr_frames_num = __sanitizer::SymbolizeCode(addr, addr_frames,
+ uptr addr_frames_num = __sanitizer::SymbolizeCode(addr, addr_frames.data(),
kMaxAddrFrames);
if (addr_frames_num == 0)
return NewReportStackEntry(addr);
@@ -71,8 +81,23 @@ ReportStack *SymbolizeCode(uptr addr) {
return SymbolizeCodeAddr2Line(addr);
}
-ReportStack *SymbolizeData(uptr addr) {
- return SymbolizeDataAddr2Line(addr);
+ReportLocation *SymbolizeData(uptr addr) {
+ if (flags()->external_symbolizer_path[0] == 0)
+ return 0;
+ DataInfo info;
+ if (!__sanitizer::SymbolizeData(addr, &info))
+ return 0;
+ ReportLocation *ent = (ReportLocation*)internal_alloc(MBlockReportStack,
+ sizeof(ReportLocation));
+ internal_memset(ent, 0, sizeof(*ent));
+ ent->type = ReportLocationGlobal;
+ ent->module = StripModuleName(info.module);
+ ent->offset = info.module_offset;
+ if (info.name)
+ ent->name = internal_strdup(info.name);
+ ent->addr = info.start;
+ ent->size = info.size;
+ return ent;
}
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_symbolize.h b/lib/tsan/rtl/tsan_symbolize.h
index 115339be38a9..29193043cd70 100644
--- a/lib/tsan/rtl/tsan_symbolize.h
+++ b/lib/tsan/rtl/tsan_symbolize.h
@@ -19,10 +19,9 @@
namespace __tsan {
ReportStack *SymbolizeCode(uptr addr);
-ReportStack *SymbolizeData(uptr addr);
+ReportLocation *SymbolizeData(uptr addr);
ReportStack *SymbolizeCodeAddr2Line(uptr addr);
-ReportStack *SymbolizeDataAddr2Line(uptr addr);
ReportStack *NewReportStackEntry(uptr addr);
diff --git a/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc b/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc
index 5eed977ec278..76926e2b5aaf 100644
--- a/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc
+++ b/lib/tsan/rtl/tsan_symbolize_addr2line_linux.cc
@@ -50,17 +50,17 @@ struct DlIteratePhdrCtx {
static void NOINLINE InitModule(ModuleDesc *m) {
int outfd[2] = {};
if (pipe(&outfd[0])) {
- TsanPrintf("ThreadSanitizer: outfd pipe() failed (%d)\n", errno);
+ Printf("ThreadSanitizer: outfd pipe() failed (%d)\n", errno);
Die();
}
int infd[2] = {};
if (pipe(&infd[0])) {
- TsanPrintf("ThreadSanitizer: infd pipe() failed (%d)\n", errno);
+ Printf("ThreadSanitizer: infd pipe() failed (%d)\n", errno);
Die();
}
int pid = fork();
if (pid == 0) {
- flags()->log_fileno = STDERR_FILENO;
+ __sanitizer_set_report_fd(STDERR_FILENO);
internal_close(STDOUT_FILENO);
internal_close(STDIN_FILENO);
internal_dup2(outfd[0], STDIN_FILENO);
@@ -74,7 +74,7 @@ static void NOINLINE InitModule(ModuleDesc *m) {
execl("/usr/bin/addr2line", "/usr/bin/addr2line", "-Cfe", m->fullname, 0);
_exit(0);
} else if (pid < 0) {
- TsanPrintf("ThreadSanitizer: failed to fork symbolizer\n");
+ Printf("ThreadSanitizer: failed to fork symbolizer\n");
Die();
}
internal_close(outfd[0]);
@@ -85,10 +85,10 @@ static void NOINLINE InitModule(ModuleDesc *m) {
static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
DlIteratePhdrCtx *ctx = (DlIteratePhdrCtx*)arg;
- InternalScopedBuf<char> tmp(128);
+ InternalScopedBuffer<char> tmp(128);
if (ctx->is_first) {
- internal_snprintf(tmp.Ptr(), tmp.Size(), "/proc/%d/exe", GetPid());
- info->dlpi_name = tmp.Ptr();
+ internal_snprintf(tmp.data(), tmp.size(), "/proc/%d/exe", GetPid());
+ info->dlpi_name = tmp.data();
}
ctx->is_first = false;
if (info->dlpi_name == 0 || info->dlpi_name[0] == 0)
@@ -104,11 +104,11 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
m->base = (uptr)info->dlpi_addr;
m->inp_fd = -1;
m->out_fd = -1;
- DPrintf("Module %s %zx\n", m->name, m->base);
+ DPrintf2("Module %s %zx\n", m->name, m->base);
for (int i = 0; i < info->dlpi_phnum; i++) {
const Elf64_Phdr *s = &info->dlpi_phdr[i];
- DPrintf(" Section p_type=%zx p_offset=%zx p_vaddr=%zx p_paddr=%zx"
- " p_filesz=%zx p_memsz=%zx p_flags=%zx p_align=%zx\n",
+ DPrintf2(" Section p_type=%zx p_offset=%zx p_vaddr=%zx p_paddr=%zx"
+ " p_filesz=%zx p_memsz=%zx p_flags=%zx p_align=%zx\n",
(uptr)s->p_type, (uptr)s->p_offset, (uptr)s->p_vaddr,
(uptr)s->p_paddr, (uptr)s->p_filesz, (uptr)s->p_memsz,
(uptr)s->p_flags, (uptr)s->p_align);
@@ -121,7 +121,7 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
sec->end = sec->base + s->p_memsz;
sec->next = ctx->sections;
ctx->sections = sec;
- DPrintf(" Section %zx-%zx\n", sec->base, sec->end);
+ DPrintf2(" Section %zx-%zx\n", sec->base, sec->end);
}
return 0;
}
@@ -155,26 +155,26 @@ ReportStack *SymbolizeCodeAddr2Line(uptr addr) {
char addrstr[32];
internal_snprintf(addrstr, sizeof(addrstr), "%p\n", (void*)offset);
if (0 >= internal_write(m->out_fd, addrstr, internal_strlen(addrstr))) {
- TsanPrintf("ThreadSanitizer: can't write from symbolizer (%d, %d)\n",
+ Printf("ThreadSanitizer: can't write from symbolizer (%d, %d)\n",
m->out_fd, errno);
Die();
}
- InternalScopedBuf<char> func(1024);
- ssize_t len = internal_read(m->inp_fd, func, func.Size() - 1);
+ InternalScopedBuffer<char> func(1024);
+ ssize_t len = internal_read(m->inp_fd, func.data(), func.size() - 1);
if (len <= 0) {
- TsanPrintf("ThreadSanitizer: can't read from symbolizer (%d, %d)\n",
+ Printf("ThreadSanitizer: can't read from symbolizer (%d, %d)\n",
m->inp_fd, errno);
Die();
}
- func.Ptr()[len] = 0;
+ func.data()[len] = 0;
ReportStack *res = NewReportStackEntry(addr);
res->module = internal_strdup(m->name);
res->offset = offset;
- char *pos = (char*)internal_strchr(func, '\n');
+ char *pos = (char*)internal_strchr(func.data(), '\n');
if (pos && func[0] != '?') {
- res->func = (char*)internal_alloc(MBlockReportStack, pos - func + 1);
- internal_memcpy(res->func, func, pos - func);
- res->func[pos - func] = 0;
+ res->func = (char*)internal_alloc(MBlockReportStack, pos - func.data() + 1);
+ internal_memcpy(res->func, func.data(), pos - func.data());
+ res->func[pos - func.data()] = 0;
char *pos2 = (char*)internal_strchr(pos, ':');
if (pos2) {
res->file = (char*)internal_alloc(MBlockReportStack, pos2 - pos - 1 + 1);
diff --git a/lib/tsan/rtl/tsan_sync.cc b/lib/tsan/rtl/tsan_sync.cc
index abb5a2ad298f..b25346ef344f 100644
--- a/lib/tsan/rtl/tsan_sync.cc
+++ b/lib/tsan/rtl/tsan_sync.cc
@@ -17,14 +17,17 @@
namespace __tsan {
-SyncVar::SyncVar(uptr addr)
+SyncVar::SyncVar(uptr addr, u64 uid)
: mtx(MutexTypeSyncVar, StatMtxSyncVar)
, addr(addr)
+ , uid(uid)
, owner_tid(kInvalidTid)
+ , last_lock()
, recursion()
, is_rw()
, is_recursive()
- , is_broken() {
+ , is_broken()
+ , is_linker_init() {
}
SyncTab::Part::Part()
@@ -45,8 +48,61 @@ SyncTab::~SyncTab() {
}
}
+SyncVar* SyncTab::GetOrCreateAndLock(ThreadState *thr, uptr pc,
+ uptr addr, bool write_lock) {
+ return GetAndLock(thr, pc, addr, write_lock, true);
+}
+
+SyncVar* SyncTab::GetIfExistsAndLock(uptr addr, bool write_lock) {
+ return GetAndLock(0, 0, addr, write_lock, false);
+}
+
+SyncVar* SyncTab::Create(ThreadState *thr, uptr pc, uptr addr) {
+ StatInc(thr, StatSyncCreated);
+ void *mem = internal_alloc(MBlockSync, sizeof(SyncVar));
+ const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed);
+ SyncVar *res = new(mem) SyncVar(addr, uid);
+#ifndef TSAN_GO
+ res->creation_stack.ObtainCurrent(thr, pc);
+#endif
+ return res;
+}
+
SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
- uptr addr, bool write_lock) {
+ uptr addr, bool write_lock, bool create) {
+#ifndef TSAN_GO
+ { // NOLINT
+ SyncVar *res = GetJavaSync(thr, pc, addr, write_lock, create);
+ if (res)
+ return res;
+ }
+
+ // Here we ask only PrimaryAllocator, because
+ // SecondaryAllocator::PointerIsMine() is slow and we have fallback on
+ // the hashmap anyway.
+ if (PrimaryAllocator::PointerIsMine((void*)addr)) {
+ MBlock *b = user_mblock(thr, (void*)addr);
+ Lock l(&b->mtx);
+ SyncVar *res = 0;
+ for (res = b->head; res; res = res->next) {
+ if (res->addr == addr)
+ break;
+ }
+ if (res == 0) {
+ if (!create)
+ return 0;
+ res = Create(thr, pc, addr);
+ res->next = b->head;
+ b->head = res;
+ }
+ if (write_lock)
+ res->mtx.Lock();
+ else
+ res->mtx.ReadLock();
+ return res;
+ }
+#endif
+
Part *p = &tab_[PartIdx(addr)];
{
ReadLock l(&p->mtx);
@@ -60,6 +116,8 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
}
}
}
+ if (!create)
+ return 0;
{
Lock l(&p->mtx);
SyncVar *res = p->val;
@@ -68,12 +126,7 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
break;
}
if (res == 0) {
- StatInc(thr, StatSyncCreated);
- void *mem = internal_alloc(MBlockSync, sizeof(SyncVar));
- res = new(mem) SyncVar(addr);
-#ifndef TSAN_GO
- res->creation_stack.ObtainCurrent(thr, pc);
-#endif
+ res = Create(thr, pc, addr);
res->next = p->val;
p->val = res;
}
@@ -86,6 +139,39 @@ SyncVar* SyncTab::GetAndLock(ThreadState *thr, uptr pc,
}
SyncVar* SyncTab::GetAndRemove(ThreadState *thr, uptr pc, uptr addr) {
+#ifndef TSAN_GO
+ { // NOLINT
+ SyncVar *res = GetAndRemoveJavaSync(thr, pc, addr);
+ if (res)
+ return res;
+ }
+ if (PrimaryAllocator::PointerIsMine((void*)addr)) {
+ MBlock *b = user_mblock(thr, (void*)addr);
+ SyncVar *res = 0;
+ {
+ Lock l(&b->mtx);
+ SyncVar **prev = &b->head;
+ res = *prev;
+ while (res) {
+ if (res->addr == addr) {
+ if (res->is_linker_init)
+ return 0;
+ *prev = res->next;
+ break;
+ }
+ prev = &res->next;
+ res = *prev;
+ }
+ }
+ if (res) {
+ StatInc(thr, StatSyncDestroyed);
+ res->mtx.Lock();
+ res->mtx.Unlock();
+ }
+ return res;
+ }
+#endif
+
Part *p = &tab_[PartIdx(addr)];
SyncVar *res = 0;
{
@@ -94,6 +180,8 @@ SyncVar* SyncTab::GetAndRemove(ThreadState *thr, uptr pc, uptr addr) {
res = *prev;
while (res) {
if (res->addr == addr) {
+ if (res->is_linker_init)
+ return 0;
*prev = res->next;
break;
}
@@ -179,15 +267,19 @@ void StackTrace::ObtainCurrent(ThreadState *thr, uptr toppc) {
n_ = thr->shadow_stack_pos - thr->shadow_stack;
if (n_ + !!toppc == 0)
return;
+ uptr start = 0;
if (c_) {
CHECK_NE(s_, 0);
- CHECK_LE(n_ + !!toppc, c_);
+ if (n_ + !!toppc > c_) {
+ start = n_ - c_ + !!toppc;
+ n_ = c_ - !!toppc;
+ }
} else {
s_ = (uptr*)internal_alloc(MBlockStackTrace,
(n_ + !!toppc) * sizeof(s_[0]));
}
for (uptr i = 0; i < n_; i++)
- s_[i] = thr->shadow_stack[i];
+ s_[i] = thr->shadow_stack[start + i];
if (toppc) {
s_[n_] = toppc;
n_++;
diff --git a/lib/tsan/rtl/tsan_sync.h b/lib/tsan/rtl/tsan_sync.h
index 34d3e0b2132f..77749e22ffc2 100644
--- a/lib/tsan/rtl/tsan_sync.h
+++ b/lib/tsan/rtl/tsan_sync.h
@@ -50,23 +50,38 @@ class StackTrace {
};
struct SyncVar {
- explicit SyncVar(uptr addr);
+ explicit SyncVar(uptr addr, u64 uid);
static const int kInvalidTid = -1;
Mutex mtx;
- const uptr addr;
+ uptr addr;
+ const u64 uid; // Globally unique id.
SyncClock clock;
SyncClock read_clock; // Used for rw mutexes only.
StackTrace creation_stack;
int owner_tid; // Set only by exclusive owners.
+ u64 last_lock;
int recursion;
bool is_rw;
bool is_recursive;
bool is_broken;
+ bool is_linker_init;
SyncVar *next; // In SyncTab hashtable.
uptr GetMemoryConsumption();
+ u64 GetId() const {
+ // 47 lsb is addr, then 14 bits is low part of uid, then 3 zero bits.
+ return GetLsb((u64)addr | (uid << 47), 61);
+ }
+ bool CheckId(u64 uid) const {
+ CHECK_EQ(uid, GetLsb(uid, 14));
+ return GetLsb(this->uid, 14) == uid;
+ }
+ static uptr SplitId(u64 id, u64 *uid) {
+ *uid = id >> 47;
+ return (uptr)GetLsb(id, 47);
+ }
};
class SyncTab {
@@ -74,13 +89,15 @@ class SyncTab {
SyncTab();
~SyncTab();
- // If the SyncVar does not exist yet, it is created.
- SyncVar* GetAndLock(ThreadState *thr, uptr pc,
- uptr addr, bool write_lock);
+ SyncVar* GetOrCreateAndLock(ThreadState *thr, uptr pc,
+ uptr addr, bool write_lock);
+ SyncVar* GetIfExistsAndLock(uptr addr, bool write_lock);
// If the SyncVar does not exist, returns 0.
SyncVar* GetAndRemove(ThreadState *thr, uptr pc, uptr addr);
+ SyncVar* Create(ThreadState *thr, uptr pc, uptr addr);
+
uptr GetMemoryConsumption(uptr *nsync);
private:
@@ -94,9 +111,13 @@ class SyncTab {
// FIXME: Implement something more sane.
static const int kPartCount = 1009;
Part tab_[kPartCount];
+ atomic_uint64_t uid_gen_;
int PartIdx(uptr addr);
+ SyncVar* GetAndLock(ThreadState *thr, uptr pc,
+ uptr addr, bool write_lock, bool create);
+
SyncTab(const SyncTab&); // Not implemented.
void operator = (const SyncTab&); // Not implemented.
};
diff --git a/lib/tsan/rtl/tsan_trace.h b/lib/tsan/rtl/tsan_trace.h
index bf15bf5cc239..7df716046567 100644
--- a/lib/tsan/rtl/tsan_trace.h
+++ b/lib/tsan/rtl/tsan_trace.h
@@ -16,12 +16,14 @@
#include "tsan_defs.h"
#include "tsan_mutex.h"
#include "tsan_sync.h"
+#include "tsan_mutexset.h"
namespace __tsan {
-const int kTraceParts = 8;
-const int kTraceSize = 128*1024;
-const int kTracePartSize = kTraceSize / kTraceParts;
+const int kTracePartSizeBits = 14;
+const int kTracePartSize = 1 << kTracePartSizeBits;
+const int kTraceParts = 4 * 1024 * 1024 / kTracePartSize;
+const int kTraceSize = kTracePartSize * kTraceParts;
// Must fit into 3 bits.
enum EventType {
@@ -31,7 +33,7 @@ enum EventType {
EventTypeLock,
EventTypeUnlock,
EventTypeRLock,
- EventTypeRUnlock,
+ EventTypeRUnlock
};
// Represents a thread event (from most significant bit):
@@ -42,13 +44,14 @@ typedef u64 Event;
struct TraceHeader {
StackTrace stack0; // Start stack for the trace.
u64 epoch0; // Start epoch for the trace.
+ MutexSet mset0;
#ifndef TSAN_GO
- uptr stack0buf[kShadowStackSize];
+ uptr stack0buf[kTraceStackSize];
#endif
TraceHeader()
#ifndef TSAN_GO
- : stack0(stack0buf, kShadowStackSize)
+ : stack0(stack0buf, kTraceStackSize)
#else
: stack0()
#endif
@@ -57,7 +60,6 @@ struct TraceHeader {
};
struct Trace {
- Event events[kTraceSize];
TraceHeader headers[kTraceParts];
Mutex mtx;
diff --git a/lib/tsan/rtl/tsan_update_shadow_word_inl.h b/lib/tsan/rtl/tsan_update_shadow_word_inl.h
index c7864ce00af3..2c435556abb2 100644
--- a/lib/tsan/rtl/tsan_update_shadow_word_inl.h
+++ b/lib/tsan/rtl/tsan_update_shadow_word_inl.h
@@ -34,7 +34,7 @@ do {
if (Shadow::TidsAreEqual(old, cur)) {
StatInc(thr, StatShadowSameThread);
if (OldIsInSameSynchEpoch(old, thr)) {
- if (OldIsRWStronger(old, kAccessIsWrite)) {
+ if (OldIsRWNotWeaker(old, kAccessIsWrite)) {
// found a slot that holds effectively the same info
// (that is, same tid, same sync epoch and same size)
StatInc(thr, StatMopSame);
@@ -43,7 +43,7 @@ do {
StoreIfNotYetStored(sp, &store_word);
break;
}
- if (OldIsRWWeaker(old, kAccessIsWrite))
+ if (OldIsRWWeakerOrEqual(old, kAccessIsWrite))
StoreIfNotYetStored(sp, &store_word);
break;
}
diff --git a/lib/tsan/tests/CMakeLists.txt b/lib/tsan/tests/CMakeLists.txt
new file mode 100644
index 000000000000..0fcc6b2b1c8f
--- /dev/null
+++ b/lib/tsan/tests/CMakeLists.txt
@@ -0,0 +1,24 @@
+include_directories(../rtl)
+
+add_custom_target(TsanUnitTests)
+set_target_properties(TsanUnitTests PROPERTIES
+ FOLDER "TSan unittests")
+function(add_tsan_unittest testname)
+ # Build unit tests only on 64-bit Linux.
+ if(UNIX AND NOT APPLE
+ AND CAN_TARGET_x86_64
+ AND CMAKE_SIZEOF_VOID_P EQUAL 8
+ AND NOT LLVM_BUILD_32_BITS)
+ add_unittest(TsanUnitTests ${testname} ${ARGN})
+ # Link with TSan runtime.
+ target_link_libraries(${testname} clang_rt.tsan-x86_64)
+ # Build tests with PIE and debug info.
+ set_property(TARGET ${testname} APPEND_STRING
+ PROPERTY COMPILE_FLAGS " -fPIE -g")
+ set_property(TARGET ${testname} APPEND_STRING
+ PROPERTY LINK_FLAGS " -pie")
+ endif()
+endfunction()
+
+add_subdirectory(rtl)
+add_subdirectory(unit)
diff --git a/lib/tsan/tests/rtl/CMakeLists.txt b/lib/tsan/tests/rtl/CMakeLists.txt
new file mode 100644
index 000000000000..b585660e8b4a
--- /dev/null
+++ b/lib/tsan/tests/rtl/CMakeLists.txt
@@ -0,0 +1,15 @@
+set(TSAN_RTL_TESTS
+ tsan_bench.cc
+ tsan_mop.cc
+ tsan_mutex.cc
+ tsan_posix.cc
+ tsan_string.cc
+ tsan_test.cc
+ tsan_thread.cc
+ )
+
+if(UNIX AND NOT APPLE)
+ list(APPEND TSAN_RTL_TESTS tsan_test_util_linux.cc)
+endif()
+
+add_tsan_unittest(TsanRtlTest ${TSAN_RTL_TESTS})
diff --git a/lib/tsan/rtl_tests/tsan_bench.cc b/lib/tsan/tests/rtl/tsan_bench.cc
index a3cf22f2c626..a3cf22f2c626 100644
--- a/lib/tsan/rtl_tests/tsan_bench.cc
+++ b/lib/tsan/tests/rtl/tsan_bench.cc
diff --git a/lib/tsan/rtl_tests/tsan_mop.cc b/lib/tsan/tests/rtl/tsan_mop.cc
index f21742825050..f21742825050 100644
--- a/lib/tsan/rtl_tests/tsan_mop.cc
+++ b/lib/tsan/tests/rtl/tsan_mop.cc
diff --git a/lib/tsan/rtl_tests/tsan_mutex.cc b/lib/tsan/tests/rtl/tsan_mutex.cc
index 4d9c77961818..4d9c77961818 100644
--- a/lib/tsan/rtl_tests/tsan_mutex.cc
+++ b/lib/tsan/tests/rtl/tsan_mutex.cc
diff --git a/lib/tsan/rtl_tests/tsan_posix.cc b/lib/tsan/tests/rtl/tsan_posix.cc
index 0caedd7207e6..0caedd7207e6 100644
--- a/lib/tsan/rtl_tests/tsan_posix.cc
+++ b/lib/tsan/tests/rtl/tsan_posix.cc
diff --git a/lib/tsan/rtl_tests/tsan_string.cc b/lib/tsan/tests/rtl/tsan_string.cc
index 75adc6c85ee9..c402f7cbd679 100644
--- a/lib/tsan/rtl_tests/tsan_string.cc
+++ b/lib/tsan/tests/rtl/tsan_string.cc
@@ -46,6 +46,9 @@ TEST(ThreadSanitizer, MemcpyRace1) {
t2.Memcpy(data, data2, 10, true);
}
+// The test fails with TSAN_SHADOW_COUNT=2,
+// because the old racy access is evicted.
+#if defined(TSAN_SHADOW_COUNT) && TSAN_SHADOW_COUNT >= 4
TEST(ThreadSanitizer, MemcpyRace2) {
char *data = new char[10];
char *data1 = new char[10];
@@ -54,6 +57,7 @@ TEST(ThreadSanitizer, MemcpyRace2) {
t1.Memcpy(data+5, data1, 1);
t2.Memcpy(data+3, data2, 4, true);
}
+#endif
TEST(ThreadSanitizer, MemcpyRace3) {
char *data = new char[10];
diff --git a/lib/tsan/rtl_tests/tsan_test.cc b/lib/tsan/tests/rtl/tsan_test.cc
index 71641400efee..2184284d39ce 100644
--- a/lib/tsan/rtl_tests/tsan_test.cc
+++ b/lib/tsan/tests/rtl/tsan_test.cc
@@ -28,11 +28,13 @@ TEST(ThreadSanitizer, FuncCall) {
t2.Return();
}
-int main(int argc, char **argv) {
+// We use this function instead of main, as ISO C++ forbids taking the address
+// of main, which we need to pass inside __tsan_func_entry.
+int run_tests(int argc, char **argv) {
TestMutexBeforeInit(); // Mutexes must be usable before __tsan_init();
__tsan_init();
__tsan_func_entry(__builtin_return_address(0));
- __tsan_func_entry((char*)&main + 1);
+ __tsan_func_entry((void*)((intptr_t)&run_tests + 1));
testing::GTEST_FLAG(death_test_style) = "threadsafe";
testing::InitGoogleTest(&argc, argv);
@@ -42,3 +44,7 @@ int main(int argc, char **argv) {
__tsan_func_exit();
return res;
}
+
+int main(int argc, char **argv) {
+ return run_tests(argc, argv);
+}
diff --git a/lib/tsan/rtl_tests/tsan_test_util.h b/lib/tsan/tests/rtl/tsan_test_util.h
index 483a564c8475..483a564c8475 100644
--- a/lib/tsan/rtl_tests/tsan_test_util.h
+++ b/lib/tsan/tests/rtl/tsan_test_util.h
diff --git a/lib/tsan/rtl_tests/tsan_test_util_linux.cc b/lib/tsan/tests/rtl/tsan_test_util_linux.cc
index 5bc393bf6c2e..dce8db90de70 100644
--- a/lib/tsan/rtl_tests/tsan_test_util_linux.cc
+++ b/lib/tsan/tests/rtl/tsan_test_util_linux.cc
@@ -397,7 +397,7 @@ void ScopedThread::VptrUpdate(const MemLoc &vptr,
}
void ScopedThread::Call(void(*pc)()) {
- Event event(Event::CALL, (void*)pc);
+ Event event(Event::CALL, (void*)((uintptr_t)pc));
impl_->send(&event);
}
diff --git a/lib/tsan/rtl_tests/tsan_thread.cc b/lib/tsan/tests/rtl/tsan_thread.cc
index 5646415a79b8..5646415a79b8 100644
--- a/lib/tsan/rtl_tests/tsan_thread.cc
+++ b/lib/tsan/tests/rtl/tsan_thread.cc
diff --git a/lib/tsan/tests/unit/CMakeLists.txt b/lib/tsan/tests/unit/CMakeLists.txt
new file mode 100644
index 000000000000..52ebdb826939
--- /dev/null
+++ b/lib/tsan/tests/unit/CMakeLists.txt
@@ -0,0 +1,14 @@
+set(TSAN_UNIT_TESTS
+ tsan_clock_test.cc
+ tsan_flags_test.cc
+ tsan_mman_test.cc
+ tsan_mutex_test.cc
+ tsan_platform_test.cc
+ tsan_shadow_test.cc
+ tsan_stack_test.cc
+ tsan_suppressions_test.cc
+ tsan_sync_test.cc
+ tsan_vector_test.cc
+ )
+
+add_tsan_unittest(TsanUnitTest ${TSAN_UNIT_TESTS})
diff --git a/lib/tsan/unit_tests/tsan_clock_test.cc b/lib/tsan/tests/unit/tsan_clock_test.cc
index fe886e10bc57..fe886e10bc57 100644
--- a/lib/tsan/unit_tests/tsan_clock_test.cc
+++ b/lib/tsan/tests/unit/tsan_clock_test.cc
diff --git a/lib/tsan/unit_tests/tsan_flags_test.cc b/lib/tsan/tests/unit/tsan_flags_test.cc
index d344cb55b1ba..ffb9c55b605f 100644
--- a/lib/tsan/unit_tests/tsan_flags_test.cc
+++ b/lib/tsan/tests/unit/tsan_flags_test.cc
@@ -19,14 +19,14 @@ namespace __tsan {
TEST(Flags, Basic) {
ScopedInRtl in_rtl;
// At least should not crash.
- Flags f = {};
+ Flags f;
InitializeFlags(&f, 0);
InitializeFlags(&f, "");
}
TEST(Flags, DefaultValues) {
ScopedInRtl in_rtl;
- Flags f = {};
+ Flags f;
f.enable_annotations = false;
f.exitcode = -11;
diff --git a/lib/tsan/unit_tests/tsan_mman_test.cc b/lib/tsan/tests/unit/tsan_mman_test.cc
index 1a9a88f606fc..1a9a88f606fc 100644
--- a/lib/tsan/unit_tests/tsan_mman_test.cc
+++ b/lib/tsan/tests/unit/tsan_mman_test.cc
diff --git a/lib/tsan/unit_tests/tsan_mutex_test.cc b/lib/tsan/tests/unit/tsan_mutex_test.cc
index c39841ddcbb1..c39841ddcbb1 100644
--- a/lib/tsan/unit_tests/tsan_mutex_test.cc
+++ b/lib/tsan/tests/unit/tsan_mutex_test.cc
diff --git a/lib/tsan/tests/unit/tsan_mutexset_test.cc b/lib/tsan/tests/unit/tsan_mutexset_test.cc
new file mode 100644
index 000000000000..da1ae2e49e0c
--- /dev/null
+++ b/lib/tsan/tests/unit/tsan_mutexset_test.cc
@@ -0,0 +1,126 @@
+//===-- tsan_mutexset_test.cc ---------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_mutexset.h"
+#include "gtest/gtest.h"
+
+namespace __tsan {
+
+static void Expect(const MutexSet &mset, uptr i, u64 id, bool write, u64 epoch,
+ int count) {
+ MutexSet::Desc d = mset.Get(i);
+ EXPECT_EQ(id, d.id);
+ EXPECT_EQ(write, d.write);
+ EXPECT_EQ(epoch, d.epoch);
+ EXPECT_EQ(count, d.count);
+}
+
+TEST(MutexSet, Basic) {
+ MutexSet mset;
+ EXPECT_EQ(mset.Size(), (uptr)0);
+
+ mset.Add(1, true, 2);
+ EXPECT_EQ(mset.Size(), (uptr)1);
+ Expect(mset, 0, 1, true, 2, 1);
+ mset.Del(1, true);
+ EXPECT_EQ(mset.Size(), (uptr)0);
+
+ mset.Add(3, true, 4);
+ mset.Add(5, false, 6);
+ EXPECT_EQ(mset.Size(), (uptr)2);
+ Expect(mset, 0, 3, true, 4, 1);
+ Expect(mset, 1, 5, false, 6, 1);
+ mset.Del(3, true);
+ EXPECT_EQ(mset.Size(), (uptr)1);
+ mset.Del(5, false);
+ EXPECT_EQ(mset.Size(), (uptr)0);
+}
+
+TEST(MutexSet, DoubleAdd) {
+ MutexSet mset;
+ mset.Add(1, true, 2);
+ EXPECT_EQ(mset.Size(), (uptr)1);
+ Expect(mset, 0, 1, true, 2, 1);
+
+ mset.Add(1, true, 2);
+ EXPECT_EQ(mset.Size(), (uptr)1);
+ Expect(mset, 0, 1, true, 2, 2);
+
+ mset.Del(1, true);
+ EXPECT_EQ(mset.Size(), (uptr)1);
+ Expect(mset, 0, 1, true, 2, 1);
+
+ mset.Del(1, true);
+ EXPECT_EQ(mset.Size(), (uptr)0);
+}
+
+TEST(MutexSet, DoubleDel) {
+ MutexSet mset;
+ mset.Add(1, true, 2);
+ EXPECT_EQ(mset.Size(), (uptr)1);
+ mset.Del(1, true);
+ EXPECT_EQ(mset.Size(), (uptr)0);
+ mset.Del(1, true);
+ EXPECT_EQ(mset.Size(), (uptr)0);
+}
+
+TEST(MutexSet, Remove) {
+ MutexSet mset;
+ mset.Add(1, true, 2);
+ mset.Add(1, true, 2);
+ mset.Add(3, true, 4);
+ mset.Add(3, true, 4);
+ EXPECT_EQ(mset.Size(), (uptr)2);
+
+ mset.Remove(1);
+ EXPECT_EQ(mset.Size(), (uptr)1);
+ Expect(mset, 0, 3, true, 4, 2);
+}
+
+TEST(MutexSet, Full) {
+ MutexSet mset;
+ for (uptr i = 0; i < MutexSet::kMaxSize; i++) {
+ mset.Add(i, true, i + 1);
+ }
+ EXPECT_EQ(mset.Size(), MutexSet::kMaxSize);
+ for (uptr i = 0; i < MutexSet::kMaxSize; i++) {
+ Expect(mset, i, i, true, i + 1, 1);
+ }
+
+ for (uptr i = 0; i < MutexSet::kMaxSize; i++) {
+ mset.Add(i, true, i + 1);
+ }
+ EXPECT_EQ(mset.Size(), MutexSet::kMaxSize);
+ for (uptr i = 0; i < MutexSet::kMaxSize; i++) {
+ Expect(mset, i, i, true, i + 1, 2);
+ }
+}
+
+TEST(MutexSet, Overflow) {
+ MutexSet mset;
+ for (uptr i = 0; i < MutexSet::kMaxSize; i++) {
+ mset.Add(i, true, i + 1);
+ mset.Add(i, true, i + 1);
+ }
+ mset.Add(100, true, 200);
+ EXPECT_EQ(mset.Size(), MutexSet::kMaxSize);
+ for (uptr i = 0; i < MutexSet::kMaxSize; i++) {
+ if (i == 0)
+ Expect(mset, i, 63, true, 64, 2);
+ else if (i == MutexSet::kMaxSize - 1)
+ Expect(mset, i, 100, true, 200, 1);
+ else
+ Expect(mset, i, i, true, i + 1, 2);
+ }
+}
+
+} // namespace __tsan
diff --git a/lib/tsan/unit_tests/tsan_platform_test.cc b/lib/tsan/tests/unit/tsan_platform_test.cc
index 64c4499fbeae..b43dbb4e4ff3 100644
--- a/lib/tsan/unit_tests/tsan_platform_test.cc
+++ b/lib/tsan/tests/unit/tsan_platform_test.cc
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_libc.h"
#include "tsan_platform.h"
+#include "tsan_rtl.h"
#include "gtest/gtest.h"
namespace __tsan {
diff --git a/lib/tsan/unit_tests/tsan_shadow_test.cc b/lib/tsan/tests/unit/tsan_shadow_test.cc
index 41f9121be0fa..fa9c982c0f6d 100644
--- a/lib/tsan/unit_tests/tsan_shadow_test.cc
+++ b/lib/tsan/tests/unit/tsan_shadow_test.cc
@@ -11,10 +11,41 @@
//
//===----------------------------------------------------------------------===//
#include "tsan_platform.h"
+#include "tsan_rtl.h"
#include "gtest/gtest.h"
namespace __tsan {
+TEST(Shadow, FastState) {
+ Shadow s(FastState(11, 22));
+ EXPECT_EQ(s.tid(), (u64)11);
+ EXPECT_EQ(s.epoch(), (u64)22);
+ EXPECT_EQ(s.GetIgnoreBit(), false);
+ EXPECT_EQ(s.GetFreedAndReset(), false);
+ EXPECT_EQ(s.GetHistorySize(), 0);
+ EXPECT_EQ(s.addr0(), (u64)0);
+ EXPECT_EQ(s.size(), (u64)1);
+ EXPECT_EQ(s.is_write(), false);
+
+ s.IncrementEpoch();
+ EXPECT_EQ(s.epoch(), (u64)23);
+ s.IncrementEpoch();
+ EXPECT_EQ(s.epoch(), (u64)24);
+
+ s.SetIgnoreBit();
+ EXPECT_EQ(s.GetIgnoreBit(), true);
+ s.ClearIgnoreBit();
+ EXPECT_EQ(s.GetIgnoreBit(), false);
+
+ for (int i = 0; i < 8; i++) {
+ s.SetHistorySize(i);
+ EXPECT_EQ(s.GetHistorySize(), i);
+ }
+ s.SetHistorySize(2);
+ s.ClearHistorySize();
+ EXPECT_EQ(s.GetHistorySize(), 0);
+}
+
TEST(Shadow, Mapping) {
static int global;
int stack;
diff --git a/lib/tsan/tests/unit/tsan_stack_test.cc b/lib/tsan/tests/unit/tsan_stack_test.cc
new file mode 100644
index 000000000000..d5392959c48c
--- /dev/null
+++ b/lib/tsan/tests/unit/tsan_stack_test.cc
@@ -0,0 +1,80 @@
+//===-- tsan_stack_test.cc ------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_sync.h"
+#include "tsan_rtl.h"
+#include "gtest/gtest.h"
+#include <string.h>
+
+namespace __tsan {
+
+static void TestStackTrace(StackTrace *trace) {
+ ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0);
+
+ trace->ObtainCurrent(&thr, 0);
+ EXPECT_EQ(trace->Size(), (uptr)0);
+
+ trace->ObtainCurrent(&thr, 42);
+ EXPECT_EQ(trace->Size(), (uptr)1);
+ EXPECT_EQ(trace->Get(0), (uptr)42);
+
+ *thr.shadow_stack_pos++ = 100;
+ *thr.shadow_stack_pos++ = 101;
+ trace->ObtainCurrent(&thr, 0);
+ EXPECT_EQ(trace->Size(), (uptr)2);
+ EXPECT_EQ(trace->Get(0), (uptr)100);
+ EXPECT_EQ(trace->Get(1), (uptr)101);
+
+ trace->ObtainCurrent(&thr, 42);
+ EXPECT_EQ(trace->Size(), (uptr)3);
+ EXPECT_EQ(trace->Get(0), (uptr)100);
+ EXPECT_EQ(trace->Get(1), (uptr)101);
+ EXPECT_EQ(trace->Get(2), (uptr)42);
+}
+
+TEST(StackTrace, Basic) {
+ ScopedInRtl in_rtl;
+ StackTrace trace;
+ TestStackTrace(&trace);
+}
+
+TEST(StackTrace, StaticBasic) {
+ ScopedInRtl in_rtl;
+ uptr buf[10];
+ StackTrace trace1(buf, 10);
+ TestStackTrace(&trace1);
+ StackTrace trace2(buf, 3);
+ TestStackTrace(&trace2);
+}
+
+TEST(StackTrace, StaticTrim) {
+ ScopedInRtl in_rtl;
+ uptr buf[2];
+ StackTrace trace(buf, 2);
+ ThreadState thr(0, 0, 0, 0, 0, 0, 0, 0);
+
+ *thr.shadow_stack_pos++ = 100;
+ *thr.shadow_stack_pos++ = 101;
+ *thr.shadow_stack_pos++ = 102;
+ trace.ObtainCurrent(&thr, 0);
+ EXPECT_EQ(trace.Size(), (uptr)2);
+ EXPECT_EQ(trace.Get(0), (uptr)101);
+ EXPECT_EQ(trace.Get(1), (uptr)102);
+
+ trace.ObtainCurrent(&thr, 42);
+ EXPECT_EQ(trace.Size(), (uptr)2);
+ EXPECT_EQ(trace.Get(0), (uptr)102);
+ EXPECT_EQ(trace.Get(1), (uptr)42);
+}
+
+
+} // namespace __tsan
diff --git a/lib/tsan/unit_tests/tsan_suppressions_test.cc b/lib/tsan/tests/unit/tsan_suppressions_test.cc
index e1e0c12c004c..e1e0c12c004c 100644
--- a/lib/tsan/unit_tests/tsan_suppressions_test.cc
+++ b/lib/tsan/tests/unit/tsan_suppressions_test.cc
diff --git a/lib/tsan/unit_tests/tsan_sync_test.cc b/lib/tsan/tests/unit/tsan_sync_test.cc
index b7605a57a331..dddf0b290883 100644
--- a/lib/tsan/unit_tests/tsan_sync_test.cc
+++ b/lib/tsan/tests/unit/tsan_sync_test.cc
@@ -36,7 +36,7 @@ TEST(Sync, Table) {
uintptr_t addr = rand_r(&seed) % (kRange - 1) + 1;
if (rand_r(&seed) % 2) {
// Get or add.
- SyncVar *v = tab.GetAndLock(thr, pc, addr, true);
+ SyncVar *v = tab.GetOrCreateAndLock(thr, pc, addr, true);
EXPECT_TRUE(golden[addr] == 0 || golden[addr] == v);
EXPECT_EQ(v->addr, addr);
golden[addr] = v;
diff --git a/lib/tsan/unit_tests/tsan_vector_test.cc b/lib/tsan/tests/unit/tsan_vector_test.cc
index cfef6e528ff2..cfef6e528ff2 100644
--- a/lib/tsan/unit_tests/tsan_vector_test.cc
+++ b/lib/tsan/tests/unit/tsan_vector_test.cc
diff --git a/lib/ubsan/CMakeLists.txt b/lib/ubsan/CMakeLists.txt
new file mode 100644
index 000000000000..40d0e897179d
--- /dev/null
+++ b/lib/ubsan/CMakeLists.txt
@@ -0,0 +1,49 @@
+# Build for the undefined behavior sanitizer runtime support library.
+
+set(UBSAN_SOURCES
+ ubsan_diag.cc
+ ubsan_handlers.cc
+ ubsan_handlers_cxx.cc
+ ubsan_type_hash.cc
+ ubsan_value.cc
+ )
+
+include_directories(..)
+
+set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+
+filter_available_targets(UBSAN_SUPPORTED_ARCH
+ x86_64 i386)
+
+set(UBSAN_RUNTIME_LIBRARIES)
+
+if(APPLE)
+ # Build universal binary on APPLE.
+ add_library(clang_rt.ubsan_osx STATIC
+ ${UBSAN_SOURCES}
+ $<TARGET_OBJECTS:RTSanitizerCommon.osx>
+ )
+ set_target_compile_flags(clang_rt.ubsan_osx ${UBSAN_CFLAGS})
+ set_target_properties(clang_rt.ubsan_osx PROPERTIES
+ OSX_ARCHITECTURES "${UBSAN_SUPPORTED_ARCH}")
+ list(APPEND UBSAN_RUNTIME_LIBRARIES clang_rt.ubsan_osx)
+else()
+ # Build separate libraries for each target.
+ foreach(arch ${UBSAN_SUPPORTED_ARCH})
+ add_library(clang_rt.ubsan-${arch} STATIC
+ ${UBSAN_SOURCES}
+ $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+ )
+ set_target_compile_flags(clang_rt.ubsan-${arch}
+ ${UBSAN_CFLAGS} ${TARGET_${arch}_CFLAGS}
+ )
+ list(APPEND UBSAN_RUNTIME_LIBRARIES clang_rt.ubsan-${arch})
+ endforeach()
+endif()
+
+
+set_property(TARGET ${UBSAN_RUNTIME_LIBRARIES} APPEND PROPERTY
+ COMPILE_DEFINITIONS ${UBSAN_COMMON_DEFINITIONS})
+add_clang_compiler_rt_libraries(${UBSAN_RUNTIME_LIBRARIES})
+
+add_subdirectory(lit_tests)
diff --git a/lib/ubsan/Makefile.mk b/lib/ubsan/Makefile.mk
new file mode 100644
index 000000000000..5702e0e752d8
--- /dev/null
+++ b/lib/ubsan/Makefile.mk
@@ -0,0 +1,23 @@
+#===- lib/ubsan/Makefile.mk ---------------------------------*- Makefile -*--===#
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+#===------------------------------------------------------------------------===#
+
+ModuleName := ubsan
+SubDirs :=
+
+Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
+ObjNames := $(Sources:%.cc=%.o)
+
+Implementation := Generic
+
+# FIXME: use automatic dependencies?
+Dependencies := $(wildcard $(Dir)/*.h)
+Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h)
+
+# Define a convenience variable for all the ubsan functions.
+UbsanFunctions := $(Sources:%.cc=%)
diff --git a/lib/ubsan/lit_tests/CMakeLists.txt b/lib/ubsan/lit_tests/CMakeLists.txt
new file mode 100644
index 000000000000..565c523ceb49
--- /dev/null
+++ b/lib/ubsan/lit_tests/CMakeLists.txt
@@ -0,0 +1,22 @@
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+
+if(COMPILER_RT_CAN_EXECUTE_TESTS)
+ # Run UBSan output tests only if we're sure that clang would produce
+ # working binaries.
+ set(UBSAN_TEST_DEPS
+ clang clang-headers FileCheck count not
+ ${UBSAN_RUNTIME_LIBRARIES}
+ )
+ set(UBSAN_TEST_PARAMS
+ ubsan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+ add_lit_testsuite(check-ubsan "Running UndefinedBehaviorSanitizer tests"
+ ${CMAKE_CURRENT_BINARY_DIR}
+ PARAMS ${UBSAN_TEST_PARAMS}
+ DEPENDS ${UBSAN_TEST_DEPS}
+ )
+ set_target_properties(check-ubsan PROPERTIES FOLDER "UBSan unittests")
+endif()
diff --git a/lib/ubsan/lit_tests/Float/cast-overflow.cpp b/lib/ubsan/lit_tests/Float/cast-overflow.cpp
new file mode 100644
index 000000000000..63410dc87140
--- /dev/null
+++ b/lib/ubsan/lit_tests/Float/cast-overflow.cpp
@@ -0,0 +1,98 @@
+// RUN: %clang -fsanitize=float-cast-overflow %s -o %t
+// RUN: %t _
+// RUN: %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-0
+// RUN: %t 1 2>&1 | FileCheck %s --check-prefix=CHECK-1
+// RUN: %t 2 2>&1 | FileCheck %s --check-prefix=CHECK-2
+// RUN: %t 3 2>&1 | FileCheck %s --check-prefix=CHECK-3
+// RUN: %t 4 2>&1 | FileCheck %s --check-prefix=CHECK-4
+// RUN: %t 5 2>&1 | FileCheck %s --check-prefix=CHECK-5
+// RUN: %t 6 2>&1 | FileCheck %s --check-prefix=CHECK-6
+// FIXME: %t 7 2>&1 | FileCheck %s --check-prefix=CHECK-7
+// RUN: %t 8 2>&1 | FileCheck %s --check-prefix=CHECK-8
+// RUN: %t 9 2>&1 | FileCheck %s --check-prefix=CHECK-9
+
+// This test assumes float and double are IEEE-754 single- and double-precision.
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+float Inf;
+float NaN;
+
+int main(int argc, char **argv) {
+ float MaxFloatRepresentableAsInt = 0x7fffff80;
+ (int)MaxFloatRepresentableAsInt; // ok
+ (int)-MaxFloatRepresentableAsInt; // ok
+
+ float MinFloatRepresentableAsInt = -0x7fffffff - 1;
+ (int)MinFloatRepresentableAsInt; // ok
+
+ float MaxFloatRepresentableAsUInt = 0xffffff00u;
+ (unsigned int)MaxFloatRepresentableAsUInt; // ok
+
+#ifdef __SIZEOF_INT128__
+ unsigned __int128 FloatMaxAsUInt128 = -((unsigned __int128)1 << 104);
+ (void)(float)FloatMaxAsUInt128; // ok
+#endif
+
+ // Build a '+Inf'.
+ char InfVal[] = { 0x00, 0x00, 0x80, 0x7f };
+ float Inf;
+ memcpy(&Inf, InfVal, 4);
+
+ // Build a 'NaN'.
+ char NaNVal[] = { 0x01, 0x00, 0x80, 0x7f };
+ float NaN;
+ memcpy(&NaN, NaNVal, 4);
+
+ switch (argv[1][0]) {
+ // FIXME: Produce a source location for these checks and test for it here.
+
+ // Floating point -> integer overflow.
+ case '0':
+ // Note that values between 0x7ffffe00 and 0x80000000 may or may not
+ // successfully round-trip, depending on the rounding mode.
+ // CHECK-0: runtime error: value 2.14748{{.*}} is outside the range of representable values of type 'int'
+ return MaxFloatRepresentableAsInt + 0x80;
+ case '1':
+ // CHECK-1: runtime error: value -2.14748{{.*}} is outside the range of representable values of type 'int'
+ return MinFloatRepresentableAsInt - 0x100;
+ case '2':
+ // CHECK-2: runtime error: value -0.001 is outside the range of representable values of type 'unsigned int'
+ return (unsigned)-0.001;
+ case '3':
+ // CHECK-3: runtime error: value 4.2949{{.*}} is outside the range of representable values of type 'unsigned int'
+ return (unsigned)(MaxFloatRepresentableAsUInt + 0x100);
+
+ case '4':
+ // CHECK-4: runtime error: value {{.*}} is outside the range of representable values of type 'int'
+ return Inf;
+ case '5':
+ // CHECK-5: runtime error: value {{.*}} is outside the range of representable values of type 'int'
+ return NaN;
+
+ // Integer -> floating point overflow.
+ case '6':
+ // CHECK-6: {{runtime error: value 0xffffff00000000000000000000000001 is outside the range of representable values of type 'float'|__int128 not supported}}
+#ifdef __SIZEOF_INT128__
+ return (float)(FloatMaxAsUInt128 + 1);
+#else
+ puts("__int128 not supported");
+ return 0;
+#endif
+ // FIXME: The backend cannot lower __fp16 operations on x86 yet.
+ //case '7':
+ // (__fp16)65504; // ok
+ // // CHECK-7: runtime error: value 65505 is outside the range of representable values of type '__fp16'
+ // return (__fp16)65505;
+
+ // Floating point -> floating point overflow.
+ case '8':
+ // CHECK-8: runtime error: value 1e+39 is outside the range of representable values of type 'float'
+ return (float)1e39;
+ case '9':
+ // CHECK-9: runtime error: value {{.*}} is outside the range of representable values of type 'double'
+ return (double)Inf;
+ }
+}
diff --git a/lib/ubsan/lit_tests/Integer/add-overflow.cpp b/lib/ubsan/lit_tests/Integer/add-overflow.cpp
new file mode 100644
index 000000000000..80543524f51d
--- /dev/null
+++ b/lib/ubsan/lit_tests/Integer/add-overflow.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang -DADD_I32 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I32
+// RUN: %clang -DADD_I64 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I64
+// RUN: %clang -DADD_I128 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I128
+
+#include <stdint.h>
+#include <stdio.h>
+
+int main() {
+ // These promote to 'int'.
+ (void)(int8_t(0x7f) + int8_t(0x7f));
+ (void)(int16_t(0x3fff) + int16_t(0x4000));
+
+#ifdef ADD_I32
+ int32_t k = 0x12345678;
+ k += 0x789abcde;
+ // CHECK-ADD_I32: add-overflow.cpp:[[@LINE-1]]:5: runtime error: signed integer overflow: 305419896 + 2023406814 cannot be represented in type 'int'
+#endif
+
+#ifdef ADD_I64
+ (void)(int64_t(8000000000000000000ll) + int64_t(2000000000000000000ll));
+ // CHECK-ADD_I64: 8000000000000000000 + 2000000000000000000 cannot be represented in type '{{long( long)?}}'
+#endif
+
+#ifdef ADD_I128
+# ifdef __SIZEOF_INT128__
+ (void)((__int128_t(1) << 126) + (__int128_t(1) << 126));
+# else
+ puts("__int128 not supported");
+# endif
+ // CHECK-ADD_I128: {{0x40000000000000000000000000000000 \+ 0x40000000000000000000000000000000 cannot be represented in type '__int128'|__int128 not supported}}
+#endif
+}
diff --git a/lib/ubsan/lit_tests/Integer/div-overflow.cpp b/lib/ubsan/lit_tests/Integer/div-overflow.cpp
new file mode 100644
index 000000000000..dd82427f9d5b
--- /dev/null
+++ b/lib/ubsan/lit_tests/Integer/div-overflow.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s
+
+#include <stdint.h>
+
+int main() {
+ unsigned(0x80000000) / -1;
+
+ // CHECK: div-overflow.cpp:9:23: runtime error: division of -2147483648 by -1 cannot be represented in type 'int'
+ int32_t(0x80000000) / -1;
+}
diff --git a/lib/ubsan/lit_tests/Integer/div-zero.cpp b/lib/ubsan/lit_tests/Integer/div-zero.cpp
new file mode 100644
index 000000000000..b2a839566c5f
--- /dev/null
+++ b/lib/ubsan/lit_tests/Integer/div-zero.cpp
@@ -0,0 +1,15 @@
+// RUN: %clang -fsanitize=integer-divide-by-zero -DDIVIDEND=0 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clang -fsanitize=integer-divide-by-zero -DDIVIDEND=1U %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clang -fsanitize=float-divide-by-zero -DDIVIDEND=1.5 %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clang -fsanitize=integer-divide-by-zero -DDIVIDEND='intmax(123)' %s -o %t && %t 2>&1 | FileCheck %s
+
+#ifdef __SIZEOF_INT128__
+typedef __int128 intmax;
+#else
+typedef long long intmax;
+#endif
+
+int main() {
+ // CHECK: div-zero.cpp:[[@LINE+1]]:12: runtime error: division by zero
+ DIVIDEND / 0;
+}
diff --git a/lib/ubsan/lit_tests/Integer/incdec-overflow.cpp b/lib/ubsan/lit_tests/Integer/incdec-overflow.cpp
new file mode 100644
index 000000000000..48b68b6365c6
--- /dev/null
+++ b/lib/ubsan/lit_tests/Integer/incdec-overflow.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang -DOP=n++ -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clang -DOP=++n -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clang -DOP=m-- -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s
+// RUN: %clang -DOP=--m -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s
+
+#include <stdint.h>
+
+int main() {
+ int n = 0x7ffffffd;
+ n++;
+ n++;
+ int m = -n - 1;
+ // CHECK: incdec-overflow.cpp:15:3: runtime error: signed integer overflow: [[MINUS:-?]]214748364
+ // CHECK: + [[MINUS]]1 cannot be represented in type 'int'
+ OP;
+}
diff --git a/lib/ubsan/lit_tests/Integer/mul-overflow.cpp b/lib/ubsan/lit_tests/Integer/mul-overflow.cpp
new file mode 100644
index 000000000000..8d1e70d6ad48
--- /dev/null
+++ b/lib/ubsan/lit_tests/Integer/mul-overflow.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s
+
+#include <stdint.h>
+
+int main() {
+ // These promote to 'int'.
+ (void)(int8_t(-2) * int8_t(0x7f));
+ (void)(int16_t(0x7fff) * int16_t(0x7fff));
+ (void)(uint16_t(0xffff) * int16_t(0x7fff));
+ (void)(uint16_t(0xffff) * uint16_t(0x8000));
+
+ // CHECK: mul-overflow.cpp:13:27: runtime error: signed integer overflow: 65535 * 32769 cannot be represented in type 'int'
+ (void)(uint16_t(0xffff) * uint16_t(0x8001));
+}
diff --git a/lib/ubsan/lit_tests/Integer/negate-overflow.cpp b/lib/ubsan/lit_tests/Integer/negate-overflow.cpp
new file mode 100644
index 000000000000..2ee4f10115e8
--- /dev/null
+++ b/lib/ubsan/lit_tests/Integer/negate-overflow.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECKS
+// RUN: %clang -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECKU
+
+int main() {
+ // CHECKS-NOT: runtime error
+ // CHECKU: negate-overflow.cpp:[[@LINE+2]]:3: runtime error: negation of 2147483648 cannot be represented in type 'unsigned int'
+ // CHECKU-NOT: cast to an unsigned
+ -unsigned(-0x7fffffff - 1); // ok
+ // CHECKS: negate-overflow.cpp:[[@LINE+2]]:10: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself
+ // CHECKU-NOT: runtime error
+ return -(-0x7fffffff - 1);
+}
diff --git a/lib/ubsan/lit_tests/Integer/no-recover.cpp b/lib/ubsan/lit_tests/Integer/no-recover.cpp
new file mode 100644
index 000000000000..e200feaa79ae
--- /dev/null
+++ b/lib/ubsan/lit_tests/Integer/no-recover.cpp
@@ -0,0 +1,22 @@
+// RUN: %clang -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=RECOVER
+// RUN: %clang -fsanitize=unsigned-integer-overflow -fsanitize-recover %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=RECOVER
+// RUN: %clang -fsanitize=unsigned-integer-overflow -fno-sanitize-recover %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ABORT
+
+#include <stdint.h>
+
+int main() {
+ // These promote to 'int'.
+ (void)(uint8_t(0xff) + uint8_t(0xff));
+ (void)(uint16_t(0xf0fff) + uint16_t(0x0fff));
+ // RECOVER-NOT: runtime error
+ // ABORT-NOT: runtime error
+
+ uint32_t k = 0x87654321;
+ k += 0xedcba987;
+ // RECOVER: no-recover.cpp:[[@LINE-1]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int'
+ // ABORT: no-recover.cpp:[[@LINE-2]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int'
+
+ (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull));
+ // RECOVER: 10000000000000000000 + 9000000000000000000 cannot be represented in type 'unsigned long'
+ // ABORT-NOT: runtime error
+}
diff --git a/lib/ubsan/lit_tests/Integer/shift.cpp b/lib/ubsan/lit_tests/Integer/shift.cpp
new file mode 100644
index 000000000000..19101c53e75e
--- /dev/null
+++ b/lib/ubsan/lit_tests/Integer/shift.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=LSH_OVERFLOW
+// RUN: %clang -DLSH_OVERFLOW -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=LSH_OVERFLOW
+// RUN: %clang -DTOO_LOW -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW
+// RUN: %clang -DTOO_LOW -DOP='>>' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW
+// RUN: %clang -DTOO_LOW -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW
+// RUN: %clang -DTOO_LOW -DOP='>>=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_LOW
+// RUN: %clang -DTOO_HIGH -DOP='<<' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH
+// RUN: %clang -DTOO_HIGH -DOP='>>' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH
+// RUN: %clang -DTOO_HIGH -DOP='<<=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH
+// RUN: %clang -DTOO_HIGH -DOP='>>=' -fsanitize=shift %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=TOO_HIGH
+
+#include <stdint.h>
+
+int main() {
+ int a = 1;
+ unsigned b = 1;
+
+ a <<= 31; // ok in C++11, not ok in C99/C11
+ b <<= 31; // ok
+ b <<= 1; // still ok, unsigned
+
+#ifdef LSH_OVERFLOW
+ // CHECK-LSH_OVERFLOW: shift.cpp:24:5: runtime error: left shift of negative value -2147483648
+ a OP 1;
+#endif
+
+#ifdef TOO_LOW
+ // CHECK-TOO_LOW: shift.cpp:29:5: runtime error: shift exponent -3 is negative
+ a OP (-3);
+#endif
+
+#ifdef TOO_HIGH
+ a = 0;
+ // CHECK-TOO_HIGH: shift.cpp:35:5: runtime error: shift exponent 32 is too large for 32-bit type 'int'
+ a OP 32;
+#endif
+}
diff --git a/lib/ubsan/lit_tests/Integer/sub-overflow.cpp b/lib/ubsan/lit_tests/Integer/sub-overflow.cpp
new file mode 100644
index 000000000000..b43a69bee4e6
--- /dev/null
+++ b/lib/ubsan/lit_tests/Integer/sub-overflow.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang -DSUB_I32 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I32
+// RUN: %clang -DSUB_I64 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I64
+// RUN: %clang -DSUB_I128 -fsanitize=signed-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I128
+
+#include <stdint.h>
+#include <stdio.h>
+
+int main() {
+ // These promote to 'int'.
+ (void)(int8_t(-2) - int8_t(0x7f));
+ (void)(int16_t(-2) - int16_t(0x7fff));
+
+#ifdef SUB_I32
+ (void)(int32_t(-2) - int32_t(0x7fffffff));
+ // CHECK-SUB_I32: sub-overflow.cpp:[[@LINE-1]]:22: runtime error: signed integer overflow: -2 - 2147483647 cannot be represented in type 'int'
+#endif
+
+#ifdef SUB_I64
+ (void)(int64_t(-8000000000000000000ll) - int64_t(2000000000000000000ll));
+ // CHECK-SUB_I64: -8000000000000000000 - 2000000000000000000 cannot be represented in type '{{long( long)?}}'
+#endif
+
+#ifdef SUB_I128
+# ifdef __SIZEOF_INT128__
+ (void)(-(__int128_t(1) << 126) - (__int128_t(1) << 126) - 1);
+# else
+ puts("__int128 not supported");
+# endif
+ // CHECK-SUB_I128: {{0x80000000000000000000000000000000 - 1 cannot be represented in type '__int128'|__int128 not supported}}
+#endif
+}
diff --git a/lib/ubsan/lit_tests/Integer/uadd-overflow.cpp b/lib/ubsan/lit_tests/Integer/uadd-overflow.cpp
new file mode 100644
index 000000000000..0edb10092e2c
--- /dev/null
+++ b/lib/ubsan/lit_tests/Integer/uadd-overflow.cpp
@@ -0,0 +1,32 @@
+// RUN: %clang -DADD_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I32
+// RUN: %clang -DADD_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I64
+// RUN: %clang -DADD_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=ADD_I128
+
+#include <stdint.h>
+#include <stdio.h>
+
+int main() {
+ // These promote to 'int'.
+ (void)(uint8_t(0xff) + uint8_t(0xff));
+ (void)(uint16_t(0xf0fff) + uint16_t(0x0fff));
+
+#ifdef ADD_I32
+ uint32_t k = 0x87654321;
+ k += 0xedcba987;
+ // CHECK-ADD_I32: uadd-overflow.cpp:[[@LINE-1]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int'
+#endif
+
+#ifdef ADD_I64
+ (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull));
+ // CHECK-ADD_I64: 10000000000000000000 + 9000000000000000000 cannot be represented in type 'unsigned long'
+#endif
+
+#ifdef ADD_I128
+# ifdef __SIZEOF_INT128__
+ (void)((__uint128_t(1) << 127) + (__uint128_t(1) << 127));
+# else
+ puts("__int128 not supported");
+# endif
+ // CHECK-ADD_I128: {{0x80000000000000000000000000000000 \+ 0x80000000000000000000000000000000 cannot be represented in type 'unsigned __int128'|__int128 not supported}}
+#endif
+}
diff --git a/lib/ubsan/lit_tests/Integer/uincdec-overflow.cpp b/lib/ubsan/lit_tests/Integer/uincdec-overflow.cpp
new file mode 100644
index 000000000000..6b677ca5bd35
--- /dev/null
+++ b/lib/ubsan/lit_tests/Integer/uincdec-overflow.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang -DOP=n++ -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=INC %s
+// RUN: %clang -DOP=++n -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=INC %s
+// RUN: %clang -DOP=m-- -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=DEC %s
+// RUN: %clang -DOP=--m -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck --check-prefix=DEC %s
+
+#include <stdint.h>
+
+int main() {
+ unsigned n = 0xfffffffd;
+ n++;
+ n++;
+ unsigned m = 0;
+ // CHECK-INC: uincdec-overflow.cpp:15:3: runtime error: unsigned integer overflow: 4294967295 + 1 cannot be represented in type 'unsigned int'
+ // CHECK-DEC: uincdec-overflow.cpp:15:3: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'unsigned int'
+ OP;
+}
diff --git a/lib/ubsan/lit_tests/Integer/umul-overflow.cpp b/lib/ubsan/lit_tests/Integer/umul-overflow.cpp
new file mode 100644
index 000000000000..42cf3a780ed0
--- /dev/null
+++ b/lib/ubsan/lit_tests/Integer/umul-overflow.cpp
@@ -0,0 +1,19 @@
+// RUN: %clang -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s
+
+#include <stdint.h>
+
+int main() {
+ // These promote to 'int'.
+ (void)(int8_t(-2) * int8_t(0x7f));
+ (void)(int16_t(0x7fff) * int16_t(0x7fff));
+ (void)(uint16_t(0xffff) * int16_t(0x7fff));
+ (void)(uint16_t(0xffff) * uint16_t(0x8000));
+
+ // Not an unsigned overflow
+ (void)(uint16_t(0xffff) * uint16_t(0x8001));
+
+ (void)(uint32_t(0xffffffff) * uint32_t(0x2));
+ // CHECK: umul-overflow.cpp:15:31: runtime error: unsigned integer overflow: 4294967295 * 2 cannot be represented in type 'unsigned int'
+
+ return 0;
+}
diff --git a/lib/ubsan/lit_tests/Integer/usub-overflow.cpp b/lib/ubsan/lit_tests/Integer/usub-overflow.cpp
new file mode 100644
index 000000000000..357d662ad63e
--- /dev/null
+++ b/lib/ubsan/lit_tests/Integer/usub-overflow.cpp
@@ -0,0 +1,31 @@
+// RUN: %clang -DSUB_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I32
+// RUN: %clang -DSUB_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I64
+// RUN: %clang -DSUB_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %t 2>&1 | FileCheck %s --check-prefix=SUB_I128
+
+#include <stdint.h>
+#include <stdio.h>
+
+int main() {
+ // These promote to 'int'.
+ (void)(uint8_t(0) - uint8_t(0x7f));
+ (void)(uint16_t(0) - uint16_t(0x7fff));
+
+#ifdef SUB_I32
+ (void)(uint32_t(1) - uint32_t(2));
+ // CHECK-SUB_I32: usub-overflow.cpp:[[@LINE-1]]:22: runtime error: unsigned integer overflow: 1 - 2 cannot be represented in type 'unsigned int'
+#endif
+
+#ifdef SUB_I64
+ (void)(uint64_t(8000000000000000000ll) - uint64_t(9000000000000000000ll));
+ // CHECK-SUB_I64: 8000000000000000000 - 9000000000000000000 cannot be represented in type 'unsigned long'
+#endif
+
+#ifdef SUB_I128
+# ifdef __SIZEOF_INT128__
+ (void)((__uint128_t(1) << 126) - (__uint128_t(1) << 127));
+# else
+ puts("__int128 not supported\n");
+# endif
+ // CHECK-SUB_I128: {{0x40000000000000000000000000000000 - 0x80000000000000000000000000000000 cannot be represented in type 'unsigned __int128'|__int128 not supported}}
+#endif
+}
diff --git a/lib/ubsan/lit_tests/Misc/bool.cpp b/lib/ubsan/lit_tests/Misc/bool.cpp
new file mode 100644
index 000000000000..8fafe7eac053
--- /dev/null
+++ b/lib/ubsan/lit_tests/Misc/bool.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang -fsanitize=bool %s -O3 -o %T/bool.exe && %T/bool.exe 2>&1 | FileCheck %s
+
+unsigned char NotABool = 123;
+
+int main(int argc, char **argv) {
+ bool *p = (bool*)&NotABool;
+
+ // FIXME: Provide a better source location here.
+ // CHECK: bool.exe:0x{{[0-9a-f]*}}: runtime error: load of value 123, which is not a valid value for type 'bool'
+ return *p;
+}
diff --git a/lib/ubsan/lit_tests/Misc/deduplication.cpp b/lib/ubsan/lit_tests/Misc/deduplication.cpp
new file mode 100644
index 000000000000..d9c909f9af4f
--- /dev/null
+++ b/lib/ubsan/lit_tests/Misc/deduplication.cpp
@@ -0,0 +1,25 @@
+// RUN: %clang -fsanitize=undefined %s -o %t && %t 2>&1 | FileCheck %s
+// Verify deduplication works by ensuring only one diag is emitted.
+#include <limits.h>
+#include <stdio.h>
+
+void overflow() {
+ int i = INT_MIN;
+ --i;
+}
+
+int main() {
+ // CHECK: Start
+ fprintf(stderr, "Start\n");
+
+ // CHECK: runtime error
+ // CHECK-NOT: runtime error
+ // CHECK-NOT: runtime error
+ overflow();
+ overflow();
+ overflow();
+
+ // CHECK: End
+ fprintf(stderr, "End\n");
+ return 0;
+}
diff --git a/lib/ubsan/lit_tests/Misc/enum.cpp b/lib/ubsan/lit_tests/Misc/enum.cpp
new file mode 100644
index 000000000000..b363fea3487f
--- /dev/null
+++ b/lib/ubsan/lit_tests/Misc/enum.cpp
@@ -0,0 +1,17 @@
+// RUN: %clang -fsanitize=enum %s -O3 -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-PLAIN
+// RUN: %clang -fsanitize=enum -std=c++11 -DE="class E" %s -O3 -o %t && %t
+// RUN: %clang -fsanitize=enum -std=c++11 -DE="class E : bool" %s -O3 -o %t && %t 2>&1 | FileCheck %s --check-prefix=CHECK-BOOL
+
+enum E { a = 1 } e;
+#undef E
+
+int main(int argc, char **argv) {
+ // memset(&e, 0xff, sizeof(e));
+ for (unsigned char *p = (unsigned char*)&e; p != (unsigned char*)(&e + 1); ++p)
+ *p = 0xff;
+
+ // CHECK-PLAIN: error: load of value 4294967295, which is not a valid value for type 'enum E'
+ // FIXME: Support marshalling and display of enum class values.
+ // CHECK-BOOL: error: load of value <unknown>, which is not a valid value for type 'enum E'
+ return (int)e != -1;
+}
diff --git a/lib/ubsan/lit_tests/Misc/missing_return.cpp b/lib/ubsan/lit_tests/Misc/missing_return.cpp
new file mode 100644
index 000000000000..9997b8386f21
--- /dev/null
+++ b/lib/ubsan/lit_tests/Misc/missing_return.cpp
@@ -0,0 +1,9 @@
+// RUN: %clang -fsanitize=return %s -O3 -o %t && %t 2>&1 | FileCheck %s
+
+// CHECK: missing_return.cpp:4:5: runtime error: execution reached the end of a value-returning function without returning a value
+int f() {
+}
+
+int main(int, char **argv) {
+ return f();
+}
diff --git a/lib/ubsan/lit_tests/Misc/unreachable.cpp b/lib/ubsan/lit_tests/Misc/unreachable.cpp
new file mode 100644
index 000000000000..5ca4e5fd8b0c
--- /dev/null
+++ b/lib/ubsan/lit_tests/Misc/unreachable.cpp
@@ -0,0 +1,6 @@
+// RUN: %clang -fsanitize=unreachable %s -O3 -o %t && %t 2>&1 | FileCheck %s
+
+int main(int, char **argv) {
+ // CHECK: unreachable.cpp:5:3: runtime error: execution reached a __builtin_unreachable() call
+ __builtin_unreachable();
+}
diff --git a/lib/ubsan/lit_tests/Misc/vla.c b/lib/ubsan/lit_tests/Misc/vla.c
new file mode 100644
index 000000000000..2fa88addc0d3
--- /dev/null
+++ b/lib/ubsan/lit_tests/Misc/vla.c
@@ -0,0 +1,11 @@
+// RUN: %clang -fsanitize=vla-bound %s -O3 -o %t
+// RUN: %t 2>&1 | FileCheck %s --check-prefix=CHECK-MINUS-ONE
+// RUN: %t a 2>&1 | FileCheck %s --check-prefix=CHECK-ZERO
+// RUN: %t a b
+
+int main(int argc, char **argv) {
+ // CHECK-MINUS-ONE: vla.c:9:11: runtime error: variable length array bound evaluates to non-positive value -1
+ // CHECK-ZERO: vla.c:9:11: runtime error: variable length array bound evaluates to non-positive value 0
+ int arr[argc - 2];
+ return 0;
+}
diff --git a/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp b/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp
new file mode 100644
index 000000000000..3abacae8be89
--- /dev/null
+++ b/lib/ubsan/lit_tests/TypeCheck/misaligned.cpp
@@ -0,0 +1,73 @@
+// RUN: %clang -fsanitize=alignment %s -O3 -o %t
+// RUN: %t l0 && %t s0 && %t r0 && %t m0 && %t f0 && %t n0
+// RUN: %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --strict-whitespace
+// RUN: %t s1 2>&1 | FileCheck %s --check-prefix=CHECK-STORE
+// RUN: %t r1 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE
+// RUN: %t m1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER
+// RUN: %t f1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN
+// RUN: %t n1 2>&1 | FileCheck %s --check-prefix=CHECK-NEW
+
+#include <new>
+
+struct S {
+ S() {}
+ int f() { return 0; }
+ int k;
+};
+
+int main(int, char **argv) {
+ char c[] __attribute__((aligned(8))) = { 0, 0, 0, 0, 1, 2, 3, 4, 5 };
+
+ // Pointer value may be unspecified here, but behavior is not undefined.
+ int *p = (int*)&c[4 + argv[1][1] - '0'];
+ S *s = (S*)p;
+
+ (void)*p; // ok!
+
+ switch (argv[1][0]) {
+ case 'l':
+ // CHECK-LOAD: misaligned.cpp:[[@LINE+4]]:12: runtime error: load of misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment
+ // CHECK-LOAD-NEXT: [[PTR]]: note: pointer points here
+ // CHECK-LOAD-NEXT: {{^ 00 00 00 01 02 03 04 05}}
+ // CHECK-LOAD-NEXT: {{^ \^}}
+ return *p && 0;
+
+ case 's':
+ // CHECK-STORE: misaligned.cpp:[[@LINE+4]]:5: runtime error: store to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment
+ // CHECK-STORE-NEXT: [[PTR]]: note: pointer points here
+ // CHECK-STORE-NEXT: {{^ 00 00 00 01 02 03 04 05}}
+ // CHECK-STORE-NEXT: {{^ \^}}
+ *p = 1;
+ break;
+
+ case 'r':
+ // CHECK-REFERENCE: misaligned.cpp:[[@LINE+4]]:15: runtime error: reference binding to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment
+ // CHECK-REFERENCE-NEXT: [[PTR]]: note: pointer points here
+ // CHECK-REFERENCE-NEXT: {{^ 00 00 00 01 02 03 04 05}}
+ // CHECK-REFERENCE-NEXT: {{^ \^}}
+ {int &r = *p;}
+ break;
+
+ case 'm':
+ // CHECK-MEMBER: misaligned.cpp:[[@LINE+4]]:15: runtime error: member access within misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment
+ // CHECK-MEMBER-NEXT: [[PTR]]: note: pointer points here
+ // CHECK-MEMBER-NEXT: {{^ 00 00 00 01 02 03 04 05}}
+ // CHECK-MEMBER-NEXT: {{^ \^}}
+ return s->k && 0;
+
+ case 'f':
+ // CHECK-MEMFUN: misaligned.cpp:[[@LINE+4]]:12: runtime error: member call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment
+ // CHECK-MEMFUN-NEXT: [[PTR]]: note: pointer points here
+ // CHECK-MEMFUN-NEXT: {{^ 00 00 00 01 02 03 04 05}}
+ // CHECK-MEMFUN-NEXT: {{^ \^}}
+ return s->f() && 0;
+
+ case 'n':
+ // FIXME: Provide a better source location here.
+ // CHECK-NEW: misaligned{{.*}}:0x{{[0-9a-f]*}}: runtime error: constructor call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment
+ // CHECK-NEW-NEXT: [[PTR]]: note: pointer points here
+ // CHECK-NEW-NEXT: {{^ 00 00 00 01 02 03 04 05}}
+ // CHECK-NEW-NEXT: {{^ \^}}
+ return (new (s) S)->k && 0;
+ }
+}
diff --git a/lib/ubsan/lit_tests/TypeCheck/null.cpp b/lib/ubsan/lit_tests/TypeCheck/null.cpp
new file mode 100644
index 000000000000..f72af28ce160
--- /dev/null
+++ b/lib/ubsan/lit_tests/TypeCheck/null.cpp
@@ -0,0 +1,38 @@
+// RUN: %clang -fsanitize=null %s -O3 -o %t
+// RUN: %t l 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD
+// RUN: %t s 2>&1 | FileCheck %s --check-prefix=CHECK-STORE
+// RUN: %t r 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE
+// RUN: %t m 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER
+// RUN: %t f 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN
+
+struct S {
+ int f() { return 0; }
+ int k;
+};
+
+int main(int, char **argv) {
+ int *p = 0;
+ S *s = 0;
+
+ (void)*p; // ok!
+
+ switch (argv[1][0]) {
+ case 'l':
+ // CHECK-LOAD: null.cpp:22:12: runtime error: load of null pointer of type 'int'
+ return *p;
+ case 's':
+ // CHECK-STORE: null.cpp:25:5: runtime error: store to null pointer of type 'int'
+ *p = 1;
+ break;
+ case 'r':
+ // CHECK-REFERENCE: null.cpp:29:15: runtime error: reference binding to null pointer of type 'int'
+ {int &r = *p;}
+ break;
+ case 'm':
+ // CHECK-MEMBER: null.cpp:33:15: runtime error: member access within null pointer of type 'S'
+ return s->k;
+ case 'f':
+ // CHECK-MEMFUN: null.cpp:36:12: runtime error: member call on null pointer of type 'S'
+ return s->f();
+ }
+}
diff --git a/lib/ubsan/lit_tests/TypeCheck/vptr.cpp b/lib/ubsan/lit_tests/TypeCheck/vptr.cpp
new file mode 100644
index 000000000000..574a7bef9622
--- /dev/null
+++ b/lib/ubsan/lit_tests/TypeCheck/vptr.cpp
@@ -0,0 +1,106 @@
+// RUN: %clang -ccc-cxx -fsanitize=vptr %s -O3 -o %t
+// RUN: %t rT && %t mT && %t fT
+// RUN: %t rU && %t mU && %t fU
+// RUN: %t rS && %t rV && %t oV
+// RUN: %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace
+// RUN: %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace
+// RUN: %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace
+// RUN: %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace
+// RUN: %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --strict-whitespace
+// RUN: %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --strict-whitespace
+
+// FIXME: This test produces linker errors on Darwin.
+// XFAIL: darwin
+
+struct S {
+ S() : a(0) {}
+ ~S() {}
+ int a;
+ int f() { return 0; }
+ virtual int v() { return 0; }
+};
+
+struct T : S {
+ T() : b(0) {}
+ int b;
+ int g() { return 0; }
+ virtual int v() { return 1; }
+};
+
+struct U : S, T { virtual int v() { return 2; } };
+
+int main(int, char **argv) {
+ T t;
+ (void)t.a;
+ (void)t.b;
+ (void)t.f();
+ (void)t.g();
+ (void)t.v();
+ (void)t.S::v();
+
+ U u;
+ (void)u.T::a;
+ (void)u.b;
+ (void)u.T::f();
+ (void)u.g();
+ (void)u.v();
+ (void)u.T::v();
+ (void)((T&)u).S::v();
+
+ T *p = 0;
+ char Buffer[sizeof(U)] = {};
+ switch (argv[1][1]) {
+ case '0':
+ p = reinterpret_cast<T*>(Buffer);
+ break;
+ case 'S':
+ p = reinterpret_cast<T*>(new S);
+ break;
+ case 'T':
+ p = new T;
+ break;
+ case 'U':
+ p = new U;
+ break;
+ case 'V':
+ p = reinterpret_cast<T*>(new U);
+ break;
+ }
+
+ switch (argv[1][0]) {
+ case 'r':
+ // Binding a reference to storage of appropriate size and alignment is OK.
+ {T &r = *p;}
+ break;
+
+ case 'm':
+ // CHECK-MEMBER: vptr.cpp:[[@LINE+5]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
+ // CHECK-MEMBER-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']]
+ // CHECK-MEMBER-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
+ // CHECK-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
+ // CHECK-MEMBER-NEXT: {{^ vptr for}} [[DYN_TYPE]]
+ return p->b;
+
+ // CHECK-NULL-MEMBER: vptr.cpp:[[@LINE-2]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
+ // CHECK-NULL-MEMBER-NEXT: [[PTR]]: note: object has invalid vptr
+ // CHECK-NULL-MEMBER-NEXT: {{^ .. .. .. .. 00 00 00 00 00 00 00 00 }}
+ // CHECK-NULL-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
+ // CHECK-NULL-MEMBER-NEXT: {{^ invalid vptr}}
+
+ case 'f':
+ // CHECK-MEMFUN: vptr.cpp:[[@LINE+5]]:12: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T'
+ // CHECK-MEMFUN-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']]
+ // CHECK-MEMFUN-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }}
+ // CHECK-MEMFUN-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}}
+ // CHECK-MEMFUN-NEXT: {{^ vptr for}} [[DYN_TYPE]]
+ return p->g();
+
+ case 'o':
+ // CHECK-OFFSET: vptr.cpp:[[@LINE+5]]:12: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'U'
+ // CHECK-OFFSET-NEXT: 0x{{[0-9a-f]*}}: note: object is base class subobject at offset {{8|16}} within object of type [[DYN_TYPE:'U']]
+ // CHECK-OFFSET-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. }}
+ // CHECK-OFFSET-NEXT: {{^ \^ ( ~~~~~~~~~~~~)~~~~~~~~~~~ *$}}
+ // CHECK-OFFSET-NEXT: {{^ ( )?vptr for}} 'T' base class of [[DYN_TYPE]]
+ return reinterpret_cast<U*>(p)->v() - 2;
+ }
+}
diff --git a/lib/ubsan/lit_tests/lit.cfg b/lib/ubsan/lit_tests/lit.cfg
new file mode 100644
index 000000000000..9fd3a1aeaa16
--- /dev/null
+++ b/lib/ubsan/lit_tests/lit.cfg
@@ -0,0 +1,65 @@
+# -*- Python -*-
+
+import os
+
+# Setup config name.
+config.name = 'UndefinedBehaviorSanitizer'
+
+# Setup source root.
+config.test_source_root = os.path.dirname(__file__)
+
+def DisplayNoConfigMessage():
+ lit.fatal("No site specific configuration available! " +
+ "Try running your test from the build tree or running " +
+ "make check-ubsan")
+
+# Figure out LLVM source root.
+llvm_src_root = getattr(config, 'llvm_src_root', None)
+if llvm_src_root is None:
+ # We probably haven't loaded the site-specific configuration: the user
+ # is likely trying to run a test file directly, and the site configuration
+ # wasn't created by the build system or we're performing an out-of-tree build.
+ ubsan_site_cfg = lit.params.get('ubsan_site_config', None)
+ if ubsan_site_cfg and os.path.exists(ubsan_site_cfg):
+ lit.load_config(config, ubsan_site_cfg)
+ raise SystemExit
+
+ # Try to guess the location of site-specific configuration using llvm-config
+ # util that can point where the build tree is.
+ llvm_config = lit.util.which("llvm-config", config.environment["PATH"])
+ if not llvm_config:
+ DisplayNoConfigMessage()
+
+ # Validate that llvm-config points to the same source tree.
+ llvm_src_root = lit.util.capture(["llvm-config", "--src-root"]).strip()
+ ubsan_test_src_root = os.path.join(llvm_src_root, "projects", "compiler-rt",
+ "lib", "ubsan", "lit_tests")
+ if (os.path.realpath(ubsan_test_src_root) !=
+ os.path.realpath(config.test_source_root)):
+ DisplayNoConfigMessage()
+
+ # Find out the presumed location of generated site config.
+ llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip()
+ ubsan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt",
+ "lib", "ubsan", "lit_tests", "lit.site.cfg")
+ if not ubsan_site_cfg or not os.path.exists(ubsan_site_cfg):
+ DisplayNoConfigMessage()
+
+ lit.load_config(config, ubsan_site_cfg)
+ raise SystemExit
+
+# Setup attributes common for all compiler-rt projects.
+compiler_rt_lit_cfg = os.path.join(llvm_src_root, "projects", "compiler-rt",
+ "lib", "lit.common.cfg")
+if not compiler_rt_lit_cfg or not os.path.exists(compiler_rt_lit_cfg):
+ lit.fatal("Can't find common compiler-rt lit config at: %r"
+ % compiler_rt_lit_cfg)
+lit.load_config(config, compiler_rt_lit_cfg)
+
+# Default test suffixes.
+config.suffixes = ['.c', '.cc', '.cpp']
+
+# UndefinedBehaviorSanitizer tests are currently supported on
+# Linux and Darwin only.
+if config.host_os not in ['Linux', 'Darwin']:
+ config.unsupported = True
diff --git a/lib/ubsan/lit_tests/lit.site.cfg.in b/lib/ubsan/lit_tests/lit.site.cfg.in
new file mode 100644
index 000000000000..b1c6ccf544ea
--- /dev/null
+++ b/lib/ubsan/lit_tests/lit.site.cfg.in
@@ -0,0 +1,19 @@
+## Autogenerated by LLVM/Clang configuration.
+# Do not edit!
+
+config.clang = "@LLVM_BINARY_DIR@/bin/clang"
+config.host_os = "@HOST_OS@"
+config.llvm_src_root = "@LLVM_SOURCE_DIR@"
+config.llvm_tools_dir = "@LLVM_TOOLS_DIR@"
+config.target_triple = "@TARGET_TRIPLE@"
+
+# LLVM tools dir can be passed in lit parameters, so try to
+# apply substitution.
+try:
+ config.llvm_tools_dir = config.llvm_tools_dir % lit.params
+except KeyError,e:
+ key, = e.args
+ lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key))
+
+# Let the main config do the real work.
+lit.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg")
diff --git a/lib/ubsan/ubsan_diag.cc b/lib/ubsan/ubsan_diag.cc
new file mode 100644
index 000000000000..57c98e669e90
--- /dev/null
+++ b/lib/ubsan/ubsan_diag.cc
@@ -0,0 +1,271 @@
+//===-- ubsan_diag.cc -----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Diagnostic reporting for the UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_diag.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+#include <stdio.h>
+
+using namespace __ubsan;
+
+Location __ubsan::getCallerLocation(uptr CallerLoc) {
+ if (!CallerLoc)
+ return Location();
+
+ uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc);
+
+ AddressInfo Info;
+ if (!SymbolizeCode(Loc, &Info, 1) || !Info.module || !*Info.module)
+ return Location(Loc);
+
+ if (!Info.function)
+ return ModuleLocation(Info.module, Info.module_offset);
+
+ return SourceLocation(Info.file, Info.line, Info.column);
+}
+
+Diag &Diag::operator<<(const TypeDescriptor &V) {
+ return AddArg(V.getTypeName());
+}
+
+Diag &Diag::operator<<(const Value &V) {
+ if (V.getType().isSignedIntegerTy())
+ AddArg(V.getSIntValue());
+ else if (V.getType().isUnsignedIntegerTy())
+ AddArg(V.getUIntValue());
+ else if (V.getType().isFloatTy())
+ AddArg(V.getFloatValue());
+ else
+ AddArg("<unknown>");
+ return *this;
+}
+
+/// Hexadecimal printing for numbers too large for Printf to handle directly.
+static void PrintHex(UIntMax Val) {
+#if HAVE_INT128_T
+ Printf("0x%08x%08x%08x%08x",
+ (unsigned int)(Val >> 96),
+ (unsigned int)(Val >> 64),
+ (unsigned int)(Val >> 32),
+ (unsigned int)(Val));
+#else
+ UNREACHABLE("long long smaller than 64 bits?");
+#endif
+}
+
+static void renderLocation(Location Loc) {
+ switch (Loc.getKind()) {
+ case Location::LK_Source: {
+ SourceLocation SLoc = Loc.getSourceLocation();
+ if (SLoc.isInvalid())
+ RawWrite("<unknown>:");
+ else {
+ Printf("%s:%d:", SLoc.getFilename(), SLoc.getLine());
+ if (SLoc.getColumn())
+ Printf("%d:", SLoc.getColumn());
+ }
+ break;
+ }
+ case Location::LK_Module:
+ Printf("%s:0x%zx:", Loc.getModuleLocation().getModuleName(),
+ Loc.getModuleLocation().getOffset());
+ break;
+ case Location::LK_Memory:
+ Printf("%p:", Loc.getMemoryLocation());
+ break;
+ case Location::LK_Null:
+ RawWrite("<unknown>:");
+ break;
+ }
+}
+
+static void renderText(const char *Message, const Diag::Arg *Args) {
+ for (const char *Msg = Message; *Msg; ++Msg) {
+ if (*Msg != '%') {
+ char Buffer[64];
+ unsigned I;
+ for (I = 0; Msg[I] && Msg[I] != '%' && I != 63; ++I)
+ Buffer[I] = Msg[I];
+ Buffer[I] = '\0';
+ RawWrite(Buffer);
+ Msg += I - 1;
+ } else {
+ const Diag::Arg &A = Args[*++Msg - '0'];
+ switch (A.Kind) {
+ case Diag::AK_String:
+ Printf("%s", A.String);
+ break;
+ case Diag::AK_Mangled: {
+ RawWrite("'");
+ RawWrite(Demangle(A.String));
+ RawWrite("'");
+ break;
+ }
+ case Diag::AK_SInt:
+ // 'long long' is guaranteed to be at least 64 bits wide.
+ if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX)
+ Printf("%lld", (long long)A.SInt);
+ else
+ PrintHex(A.SInt);
+ break;
+ case Diag::AK_UInt:
+ if (A.UInt <= UINT64_MAX)
+ Printf("%llu", (unsigned long long)A.UInt);
+ else
+ PrintHex(A.UInt);
+ break;
+ case Diag::AK_Float: {
+ // FIXME: Support floating-point formatting in sanitizer_common's
+ // printf, and stop using snprintf here.
+ char Buffer[32];
+ snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float);
+ Printf("%s", Buffer);
+ break;
+ }
+ case Diag::AK_Pointer:
+ Printf("%p", A.Pointer);
+ break;
+ }
+ }
+ }
+}
+
+/// Find the earliest-starting range in Ranges which ends after Loc.
+static Range *upperBound(MemoryLocation Loc, Range *Ranges,
+ unsigned NumRanges) {
+ Range *Best = 0;
+ for (unsigned I = 0; I != NumRanges; ++I)
+ if (Ranges[I].getEnd().getMemoryLocation() > Loc &&
+ (!Best ||
+ Best->getStart().getMemoryLocation() >
+ Ranges[I].getStart().getMemoryLocation()))
+ Best = &Ranges[I];
+ return Best;
+}
+
+/// Render a snippet of the address space near a location.
+static void renderMemorySnippet(MemoryLocation Loc,
+ Range *Ranges, unsigned NumRanges,
+ const Diag::Arg *Args) {
+ const unsigned BytesToShow = 32;
+ const unsigned MinBytesNearLoc = 4;
+
+ // Show at least the 8 bytes surrounding Loc.
+ MemoryLocation Min = Loc - MinBytesNearLoc, Max = Loc + MinBytesNearLoc;
+ for (unsigned I = 0; I < NumRanges; ++I) {
+ Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min);
+ Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max);
+ }
+
+ // If we have too many interesting bytes, prefer to show bytes after Loc.
+ if (Max - Min > BytesToShow)
+ Min = __sanitizer::Min(Max - BytesToShow, Loc - MinBytesNearLoc);
+ Max = Min + BytesToShow;
+
+ // Emit data.
+ for (uptr P = Min; P != Max; ++P) {
+ // FIXME: Check that the address is readable before printing it.
+ unsigned char C = *reinterpret_cast<const unsigned char*>(P);
+ Printf("%s%02x", (P % 8 == 0) ? " " : " ", C);
+ }
+ RawWrite("\n");
+
+ // Emit highlights.
+ Range *InRange = upperBound(Min, Ranges, NumRanges);
+ for (uptr P = Min; P != Max; ++P) {
+ char Pad = ' ', Byte = ' ';
+ if (InRange && InRange->getEnd().getMemoryLocation() == P)
+ InRange = upperBound(P, Ranges, NumRanges);
+ if (!InRange && P > Loc)
+ break;
+ if (InRange && InRange->getStart().getMemoryLocation() < P)
+ Pad = '~';
+ if (InRange && InRange->getStart().getMemoryLocation() <= P)
+ Byte = '~';
+ char Buffer[] = { Pad, Pad, P == Loc ? '^' : Byte, Byte, 0 };
+ RawWrite((P % 8 == 0) ? Buffer : &Buffer[1]);
+ }
+ RawWrite("\n");
+
+ // Go over the line again, and print names for the ranges.
+ InRange = 0;
+ unsigned Spaces = 0;
+ for (uptr P = Min; P != Max; ++P) {
+ if (!InRange || InRange->getEnd().getMemoryLocation() == P)
+ InRange = upperBound(P, Ranges, NumRanges);
+ if (!InRange)
+ break;
+
+ Spaces += (P % 8) == 0 ? 2 : 1;
+
+ if (InRange && InRange->getStart().getMemoryLocation() == P) {
+ while (Spaces--)
+ RawWrite(" ");
+ renderText(InRange->getText(), Args);
+ RawWrite("\n");
+ // FIXME: We only support naming one range for now!
+ break;
+ }
+
+ Spaces += 2;
+ }
+
+ // FIXME: Print names for anything we can identify within the line:
+ //
+ // * If we can identify the memory itself as belonging to a particular
+ // global, stack variable, or dynamic allocation, then do so.
+ //
+ // * If we have a pointer-size, pointer-aligned range highlighted,
+ // determine whether the value of that range is a pointer to an
+ // entity which we can name, and if so, print that name.
+ //
+ // This needs an external symbolizer, or (preferably) ASan instrumentation.
+}
+
+Diag::~Diag() {
+ bool UseAnsiColor = PrintsToTty();
+ if (UseAnsiColor)
+ RawWrite("\033[1m");
+
+ renderLocation(Loc);
+
+ switch (Level) {
+ case DL_Error:
+ if (UseAnsiColor)
+ RawWrite("\033[31m");
+ RawWrite(" runtime error: ");
+ if (UseAnsiColor)
+ RawWrite("\033[0;1m");
+ break;
+
+ case DL_Note:
+ if (UseAnsiColor)
+ RawWrite("\033[30m");
+ RawWrite(" note: ");
+ if (UseAnsiColor)
+ RawWrite("\033[0m");
+ break;
+ }
+
+ renderText(Message, Args);
+
+ if (UseAnsiColor)
+ RawWrite("\033[0m");
+
+ RawWrite("\n");
+
+ if (Loc.isMemoryLocation())
+ renderMemorySnippet(Loc.getMemoryLocation(), Ranges, NumRanges, Args);
+}
diff --git a/lib/ubsan/ubsan_diag.h b/lib/ubsan/ubsan_diag.h
new file mode 100644
index 000000000000..16afffdb0a76
--- /dev/null
+++ b/lib/ubsan/ubsan_diag.h
@@ -0,0 +1,202 @@
+//===-- ubsan_diag.h --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Diagnostics emission for Clang's undefined behavior sanitizer.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_DIAG_H
+#define UBSAN_DIAG_H
+
+#include "ubsan_value.h"
+
+namespace __ubsan {
+
+/// \brief A location within a loaded module in the program. These are used when
+/// the location can't be resolved to a SourceLocation.
+class ModuleLocation {
+ const char *ModuleName;
+ uptr Offset;
+
+public:
+ ModuleLocation() : ModuleName(0), Offset(0) {}
+ ModuleLocation(const char *ModuleName, uptr Offset)
+ : ModuleName(ModuleName), Offset(Offset) {}
+ const char *getModuleName() const { return ModuleName; }
+ uptr getOffset() const { return Offset; }
+};
+
+/// A location of some data within the program's address space.
+typedef uptr MemoryLocation;
+
+/// \brief Location at which a diagnostic can be emitted. Either a
+/// SourceLocation, a ModuleLocation, or a MemoryLocation.
+class Location {
+public:
+ enum LocationKind { LK_Null, LK_Source, LK_Module, LK_Memory };
+
+private:
+ LocationKind Kind;
+ // FIXME: In C++11, wrap these in an anonymous union.
+ SourceLocation SourceLoc;
+ ModuleLocation ModuleLoc;
+ MemoryLocation MemoryLoc;
+
+public:
+ Location() : Kind(LK_Null) {}
+ Location(SourceLocation Loc) :
+ Kind(LK_Source), SourceLoc(Loc) {}
+ Location(ModuleLocation Loc) :
+ Kind(LK_Module), ModuleLoc(Loc) {}
+ Location(MemoryLocation Loc) :
+ Kind(LK_Memory), MemoryLoc(Loc) {}
+
+ LocationKind getKind() const { return Kind; }
+
+ bool isSourceLocation() const { return Kind == LK_Source; }
+ bool isModuleLocation() const { return Kind == LK_Module; }
+ bool isMemoryLocation() const { return Kind == LK_Memory; }
+
+ SourceLocation getSourceLocation() const {
+ CHECK(isSourceLocation());
+ return SourceLoc;
+ }
+ ModuleLocation getModuleLocation() const {
+ CHECK(isModuleLocation());
+ return ModuleLoc;
+ }
+ MemoryLocation getMemoryLocation() const {
+ CHECK(isMemoryLocation());
+ return MemoryLoc;
+ }
+};
+
+/// Try to obtain a location for the caller. This might fail, and produce either
+/// an invalid location or a module location for the caller.
+Location getCallerLocation(uptr CallerLoc = GET_CALLER_PC());
+
+/// A diagnostic severity level.
+enum DiagLevel {
+ DL_Error, ///< An error.
+ DL_Note ///< A note, attached to a prior diagnostic.
+};
+
+/// \brief Annotation for a range of locations in a diagnostic.
+class Range {
+ Location Start, End;
+ const char *Text;
+
+public:
+ Range() : Start(), End(), Text() {}
+ Range(MemoryLocation Start, MemoryLocation End, const char *Text)
+ : Start(Start), End(End), Text(Text) {}
+ Location getStart() const { return Start; }
+ Location getEnd() const { return End; }
+ const char *getText() const { return Text; }
+};
+
+/// \brief A mangled C++ name. Really just a strong typedef for 'const char*'.
+class MangledName {
+ const char *Name;
+public:
+ MangledName(const char *Name) : Name(Name) {}
+ const char *getName() const { return Name; }
+};
+
+/// \brief Representation of an in-flight diagnostic.
+///
+/// Temporary \c Diag instances are created by the handler routines to
+/// accumulate arguments for a diagnostic. The destructor emits the diagnostic
+/// message.
+class Diag {
+ /// The location at which the problem occurred.
+ Location Loc;
+
+ /// The diagnostic level.
+ DiagLevel Level;
+
+ /// The message which will be emitted, with %0, %1, ... placeholders for
+ /// arguments.
+ const char *Message;
+
+public:
+ /// Kinds of arguments, corresponding to members of \c Arg's union.
+ enum ArgKind {
+ AK_String, ///< A string argument, displayed as-is.
+ AK_Mangled,///< A C++ mangled name, demangled before display.
+ AK_UInt, ///< An unsigned integer argument.
+ AK_SInt, ///< A signed integer argument.
+ AK_Float, ///< A floating-point argument.
+ AK_Pointer ///< A pointer argument, displayed in hexadecimal.
+ };
+
+ /// An individual diagnostic message argument.
+ struct Arg {
+ Arg() {}
+ Arg(const char *String) : Kind(AK_String), String(String) {}
+ Arg(MangledName MN) : Kind(AK_Mangled), String(MN.getName()) {}
+ Arg(UIntMax UInt) : Kind(AK_UInt), UInt(UInt) {}
+ Arg(SIntMax SInt) : Kind(AK_SInt), SInt(SInt) {}
+ Arg(FloatMax Float) : Kind(AK_Float), Float(Float) {}
+ Arg(const void *Pointer) : Kind(AK_Pointer), Pointer(Pointer) {}
+
+ ArgKind Kind;
+ union {
+ const char *String;
+ UIntMax UInt;
+ SIntMax SInt;
+ FloatMax Float;
+ const void *Pointer;
+ };
+ };
+
+private:
+ static const unsigned MaxArgs = 5;
+ static const unsigned MaxRanges = 1;
+
+ /// The arguments which have been added to this diagnostic so far.
+ Arg Args[MaxArgs];
+ unsigned NumArgs;
+
+ /// The ranges which have been added to this diagnostic so far.
+ Range Ranges[MaxRanges];
+ unsigned NumRanges;
+
+ Diag &AddArg(Arg A) {
+ CHECK(NumArgs != MaxArgs);
+ Args[NumArgs++] = A;
+ return *this;
+ }
+
+ Diag &AddRange(Range A) {
+ CHECK(NumRanges != MaxRanges);
+ Ranges[NumRanges++] = A;
+ return *this;
+ }
+
+ /// \c Diag objects are not copyable.
+ Diag(const Diag &); // NOT IMPLEMENTED
+ Diag &operator=(const Diag &);
+
+public:
+ Diag(Location Loc, DiagLevel Level, const char *Message)
+ : Loc(Loc), Level(Level), Message(Message), NumArgs(0), NumRanges(0) {}
+ ~Diag();
+
+ Diag &operator<<(const char *Str) { return AddArg(Str); }
+ Diag &operator<<(MangledName MN) { return AddArg(MN); }
+ Diag &operator<<(unsigned long long V) { return AddArg(UIntMax(V)); }
+ Diag &operator<<(const void *V) { return AddArg(V); }
+ Diag &operator<<(const TypeDescriptor &V);
+ Diag &operator<<(const Value &V);
+ Diag &operator<<(const Range &R) { return AddRange(R); }
+};
+
+} // namespace __ubsan
+
+#endif // UBSAN_DIAG_H
diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc
new file mode 100644
index 000000000000..1b02aa0fadf3
--- /dev/null
+++ b/lib/ubsan/ubsan_handlers.cc
@@ -0,0 +1,244 @@
+//===-- ubsan_handlers.cc -------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Error logging entry points for the UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_handlers.h"
+#include "ubsan_diag.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+using namespace __sanitizer;
+using namespace __ubsan;
+
+namespace __ubsan {
+ const char *TypeCheckKinds[] = {
+ "load of", "store to", "reference binding to", "member access within",
+ "member call on", "constructor call on"
+ };
+}
+
+static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
+ Location FallbackLoc) {
+ Location Loc = Data->Loc.acquire();
+
+ // Use the SourceLocation from Data to track deduplication, even if 'invalid'
+ if (Loc.getSourceLocation().isDisabled())
+ return;
+ if (Data->Loc.isInvalid())
+ Loc = FallbackLoc;
+
+ if (!Pointer)
+ Diag(Loc, DL_Error, "%0 null pointer of type %1")
+ << TypeCheckKinds[Data->TypeCheckKind] << Data->Type;
+ else if (Data->Alignment && (Pointer & (Data->Alignment - 1)))
+ Diag(Loc, DL_Error, "%0 misaligned address %1 for type %3, "
+ "which requires %2 byte alignment")
+ << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer
+ << Data->Alignment << Data->Type;
+ else
+ Diag(Loc, DL_Error, "%0 address %1 with insufficient space "
+ "for an object of type %2")
+ << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
+ if (Pointer)
+ Diag(Pointer, DL_Note, "pointer points here");
+}
+void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData *Data,
+ ValueHandle Pointer) {
+ handleTypeMismatchImpl(Data, Pointer, getCallerLocation());
+}
+void __ubsan::__ubsan_handle_type_mismatch_abort(TypeMismatchData *Data,
+ ValueHandle Pointer) {
+ handleTypeMismatchImpl(Data, Pointer, getCallerLocation());
+ Die();
+}
+
+/// \brief Common diagnostic emission for various forms of integer overflow.
+template<typename T> static void HandleIntegerOverflow(OverflowData *Data,
+ ValueHandle LHS,
+ const char *Operator,
+ T RHS) {
+ SourceLocation Loc = Data->Loc.acquire();
+ if (Loc.isDisabled())
+ return;
+
+ Diag(Loc, DL_Error, "%0 integer overflow: "
+ "%1 %2 %3 cannot be represented in type %4")
+ << (Data->Type.isSignedIntegerTy() ? "signed" : "unsigned")
+ << Value(Data->Type, LHS) << Operator << RHS << Data->Type;
+}
+
+void __ubsan::__ubsan_handle_add_overflow(OverflowData *Data,
+ ValueHandle LHS, ValueHandle RHS) {
+ HandleIntegerOverflow(Data, LHS, "+", Value(Data->Type, RHS));
+}
+void __ubsan::__ubsan_handle_add_overflow_abort(OverflowData *Data,
+ ValueHandle LHS,
+ ValueHandle RHS) {
+ __ubsan_handle_add_overflow(Data, LHS, RHS);
+ Die();
+}
+
+void __ubsan::__ubsan_handle_sub_overflow(OverflowData *Data,
+ ValueHandle LHS, ValueHandle RHS) {
+ HandleIntegerOverflow(Data, LHS, "-", Value(Data->Type, RHS));
+}
+void __ubsan::__ubsan_handle_sub_overflow_abort(OverflowData *Data,
+ ValueHandle LHS,
+ ValueHandle RHS) {
+ __ubsan_handle_sub_overflow(Data, LHS, RHS);
+ Die();
+}
+
+void __ubsan::__ubsan_handle_mul_overflow(OverflowData *Data,
+ ValueHandle LHS, ValueHandle RHS) {
+ HandleIntegerOverflow(Data, LHS, "*", Value(Data->Type, RHS));
+}
+void __ubsan::__ubsan_handle_mul_overflow_abort(OverflowData *Data,
+ ValueHandle LHS,
+ ValueHandle RHS) {
+ __ubsan_handle_mul_overflow(Data, LHS, RHS);
+ Die();
+}
+
+void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data,
+ ValueHandle OldVal) {
+ SourceLocation Loc = Data->Loc.acquire();
+ if (Loc.isDisabled())
+ return;
+
+ if (Data->Type.isSignedIntegerTy())
+ Diag(Loc, DL_Error,
+ "negation of %0 cannot be represented in type %1; "
+ "cast to an unsigned type to negate this value to itself")
+ << Value(Data->Type, OldVal) << Data->Type;
+ else
+ Diag(Loc, DL_Error,
+ "negation of %0 cannot be represented in type %1")
+ << Value(Data->Type, OldVal) << Data->Type;
+}
+void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data,
+ ValueHandle OldVal) {
+ __ubsan_handle_negate_overflow(Data, OldVal);
+ Die();
+}
+
+void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data,
+ ValueHandle LHS, ValueHandle RHS) {
+ SourceLocation Loc = Data->Loc.acquire();
+ if (Loc.isDisabled())
+ return;
+
+ Value LHSVal(Data->Type, LHS);
+ Value RHSVal(Data->Type, RHS);
+ if (RHSVal.isMinusOne())
+ Diag(Loc, DL_Error,
+ "division of %0 by -1 cannot be represented in type %1")
+ << LHSVal << Data->Type;
+ else
+ Diag(Loc, DL_Error, "division by zero");
+}
+void __ubsan::__ubsan_handle_divrem_overflow_abort(OverflowData *Data,
+ ValueHandle LHS,
+ ValueHandle RHS) {
+ __ubsan_handle_divrem_overflow(Data, LHS, RHS);
+ Die();
+}
+
+void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data,
+ ValueHandle LHS,
+ ValueHandle RHS) {
+ SourceLocation Loc = Data->Loc.acquire();
+ if (Loc.isDisabled())
+ return;
+
+ Value LHSVal(Data->LHSType, LHS);
+ Value RHSVal(Data->RHSType, RHS);
+ if (RHSVal.isNegative())
+ Diag(Loc, DL_Error, "shift exponent %0 is negative") << RHSVal;
+ else if (RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth())
+ Diag(Loc, DL_Error,
+ "shift exponent %0 is too large for %1-bit type %2")
+ << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType;
+ else if (LHSVal.isNegative())
+ Diag(Loc, DL_Error, "left shift of negative value %0") << LHSVal;
+ else
+ Diag(Loc, DL_Error,
+ "left shift of %0 by %1 places cannot be represented in type %2")
+ << LHSVal << RHSVal << Data->LHSType;
+}
+void __ubsan::__ubsan_handle_shift_out_of_bounds_abort(
+ ShiftOutOfBoundsData *Data,
+ ValueHandle LHS,
+ ValueHandle RHS) {
+ __ubsan_handle_shift_out_of_bounds(Data, LHS, RHS);
+ Die();
+}
+
+void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) {
+ Diag(Data->Loc, DL_Error, "execution reached a __builtin_unreachable() call");
+ Die();
+}
+
+void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) {
+ Diag(Data->Loc, DL_Error,
+ "execution reached the end of a value-returning function "
+ "without returning a value");
+ Die();
+}
+
+void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data,
+ ValueHandle Bound) {
+ SourceLocation Loc = Data->Loc.acquire();
+ if (Loc.isDisabled())
+ return;
+
+ Diag(Loc, DL_Error, "variable length array bound evaluates to "
+ "non-positive value %0")
+ << Value(Data->Type, Bound);
+}
+void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data,
+ ValueHandle Bound) {
+ __ubsan_handle_vla_bound_not_positive(Data, Bound);
+ Die();
+}
+
+
+void __ubsan::__ubsan_handle_float_cast_overflow(FloatCastOverflowData *Data,
+ ValueHandle From) {
+ // TODO: Add deduplication once a SourceLocation is generated for this check.
+ Diag(getCallerLocation(), DL_Error,
+ "value %0 is outside the range of representable values of type %2")
+ << Value(Data->FromType, From) << Data->FromType << Data->ToType;
+}
+void __ubsan::__ubsan_handle_float_cast_overflow_abort(
+ FloatCastOverflowData *Data,
+ ValueHandle From) {
+ Diag(getCallerLocation(), DL_Error,
+ "value %0 is outside the range of representable values of type %2")
+ << Value(Data->FromType, From) << Data->FromType << Data->ToType;
+ Die();
+}
+
+void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data,
+ ValueHandle Val) {
+ // TODO: Add deduplication once a SourceLocation is generated for this check.
+ Diag(getCallerLocation(), DL_Error,
+ "load of value %0, which is not a valid value for type %1")
+ << Value(Data->Type, Val) << Data->Type;
+}
+void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data,
+ ValueHandle Val) {
+ Diag(getCallerLocation(), DL_Error,
+ "load of value %0, which is not a valid value for type %1")
+ << Value(Data->Type, Val) << Data->Type;
+ Die();
+}
diff --git a/lib/ubsan/ubsan_handlers.h b/lib/ubsan/ubsan_handlers.h
new file mode 100644
index 000000000000..d6a042481ffa
--- /dev/null
+++ b/lib/ubsan/ubsan_handlers.h
@@ -0,0 +1,108 @@
+//===-- ubsan_handlers.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Entry points to the runtime library for Clang's undefined behavior sanitizer.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_HANDLERS_H
+#define UBSAN_HANDLERS_H
+
+#include "ubsan_value.h"
+
+namespace __ubsan {
+
+struct TypeMismatchData {
+ SourceLocation Loc;
+ const TypeDescriptor &Type;
+ uptr Alignment;
+ unsigned char TypeCheckKind;
+};
+
+#define RECOVERABLE(checkname, ...) \
+ extern "C" SANITIZER_INTERFACE_ATTRIBUTE \
+ void __ubsan_handle_ ## checkname( __VA_ARGS__ ); \
+ extern "C" SANITIZER_INTERFACE_ATTRIBUTE \
+ void __ubsan_handle_ ## checkname ## _abort( __VA_ARGS__ );
+
+/// \brief Handle a runtime type check failure, caused by either a misaligned
+/// pointer, a null pointer, or a pointer to insufficient storage for the
+/// type.
+RECOVERABLE(type_mismatch, TypeMismatchData *Data, ValueHandle Pointer)
+
+struct OverflowData {
+ SourceLocation Loc;
+ const TypeDescriptor &Type;
+};
+
+/// \brief Handle an integer addition overflow.
+RECOVERABLE(add_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS)
+
+/// \brief Handle an integer subtraction overflow.
+RECOVERABLE(sub_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS)
+
+/// \brief Handle an integer multiplication overflow.
+RECOVERABLE(mul_overflow, OverflowData *Data, ValueHandle LHS, ValueHandle RHS)
+
+/// \brief Handle a signed integer overflow for a unary negate operator.
+RECOVERABLE(negate_overflow, OverflowData *Data, ValueHandle OldVal)
+
+/// \brief Handle an INT_MIN/-1 overflow or division by zero.
+RECOVERABLE(divrem_overflow, OverflowData *Data,
+ ValueHandle LHS, ValueHandle RHS)
+
+struct ShiftOutOfBoundsData {
+ SourceLocation Loc;
+ const TypeDescriptor &LHSType;
+ const TypeDescriptor &RHSType;
+};
+
+/// \brief Handle a shift where the RHS is out of bounds or a left shift where
+/// the LHS is negative or overflows.
+RECOVERABLE(shift_out_of_bounds, ShiftOutOfBoundsData *Data,
+ ValueHandle LHS, ValueHandle RHS)
+
+struct UnreachableData {
+ SourceLocation Loc;
+};
+
+/// \brief Handle a __builtin_unreachable which is reached.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __ubsan_handle_builtin_unreachable(UnreachableData *Data);
+/// \brief Handle reaching the end of a value-returning function.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __ubsan_handle_missing_return(UnreachableData *Data);
+
+struct VLABoundData {
+ SourceLocation Loc;
+ const TypeDescriptor &Type;
+};
+
+/// \brief Handle a VLA with a non-positive bound.
+RECOVERABLE(vla_bound_not_positive, VLABoundData *Data, ValueHandle Bound)
+
+struct FloatCastOverflowData {
+ // FIXME: SourceLocation Loc;
+ const TypeDescriptor &FromType;
+ const TypeDescriptor &ToType;
+};
+
+/// \brief Handle overflow in a conversion to or from a floating-point type.
+RECOVERABLE(float_cast_overflow, FloatCastOverflowData *Data, ValueHandle From)
+
+struct InvalidValueData {
+ // FIXME: SourceLocation Loc;
+ const TypeDescriptor &Type;
+};
+
+/// \brief Handle a load of an invalid value for the type.
+RECOVERABLE(load_invalid_value, InvalidValueData *Data, ValueHandle Val)
+
+}
+
+#endif // UBSAN_HANDLERS_H
diff --git a/lib/ubsan/ubsan_handlers_cxx.cc b/lib/ubsan/ubsan_handlers_cxx.cc
new file mode 100644
index 000000000000..dcc1f60078d2
--- /dev/null
+++ b/lib/ubsan/ubsan_handlers_cxx.cc
@@ -0,0 +1,75 @@
+//===-- ubsan_handlers_cxx.cc ---------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Error logging entry points for the UBSan runtime, which are only used for C++
+// compilations. This file is permitted to use language features which require
+// linking against a C++ ABI library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_handlers_cxx.h"
+#include "ubsan_diag.h"
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+using namespace __sanitizer;
+using namespace __ubsan;
+
+namespace __ubsan {
+ extern const char *TypeCheckKinds[];
+}
+
+static void HandleDynamicTypeCacheMiss(
+ DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash,
+ bool Abort) {
+ if (checkDynamicType((void*)Pointer, Data->TypeInfo, Hash))
+ // Just a cache miss. The type matches after all.
+ return;
+
+ SourceLocation Loc = Data->Loc.acquire();
+ if (Loc.isDisabled())
+ return;
+
+ Diag(Loc, DL_Error,
+ "%0 address %1 which does not point to an object of type %2")
+ << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
+
+ // If possible, say what type it actually points to.
+ // FIXME: Demangle the type names.
+ DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer);
+ if (!DTI.isValid())
+ Diag(Pointer, DL_Note, "object has invalid vptr")
+ << MangledName(DTI.getMostDerivedTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
+ else if (!DTI.getOffset())
+ Diag(Pointer, DL_Note, "object is of type %0")
+ << MangledName(DTI.getMostDerivedTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
+ else
+ // FIXME: Find the type at the specified offset, and include that
+ // in the note.
+ Diag(Pointer - DTI.getOffset(), DL_Note,
+ "object is base class subobject at offset %0 within object of type %1")
+ << DTI.getOffset() << MangledName(DTI.getMostDerivedTypeName())
+ << MangledName(DTI.getSubobjectTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr), "vptr for %2 base class of %1");
+
+ if (Abort)
+ Die();
+}
+
+void __ubsan::__ubsan_handle_dynamic_type_cache_miss(
+ DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
+ HandleDynamicTypeCacheMiss(Data, Pointer, Hash, false);
+}
+void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort(
+ DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
+ HandleDynamicTypeCacheMiss(Data, Pointer, Hash, true);
+}
diff --git a/lib/ubsan/ubsan_handlers_cxx.h b/lib/ubsan/ubsan_handlers_cxx.h
new file mode 100644
index 000000000000..cb1bca78b833
--- /dev/null
+++ b/lib/ubsan/ubsan_handlers_cxx.h
@@ -0,0 +1,40 @@
+//===-- ubsan_handlers_cxx.h ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Entry points to the runtime library for Clang's undefined behavior sanitizer,
+// for C++-specific checks. This code is not linked into C binaries.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_HANDLERS_CXX_H
+#define UBSAN_HANDLERS_CXX_H
+
+#include "ubsan_value.h"
+
+namespace __ubsan {
+
+struct DynamicTypeCacheMissData {
+ SourceLocation Loc;
+ const TypeDescriptor &Type;
+ void *TypeInfo;
+ unsigned char TypeCheckKind;
+};
+
+/// \brief Handle a runtime type check failure, caused by an incorrect vptr.
+/// When this handler is called, all we know is that the type was not in the
+/// cache; this does not necessarily imply the existence of a bug.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __ubsan_handle_dynamic_type_cache_miss(
+ DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash);
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __ubsan_handle_dynamic_type_cache_miss_abort(
+ DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash);
+
+}
+
+#endif // UBSAN_HANDLERS_H
diff --git a/lib/ubsan/ubsan_type_hash.cc b/lib/ubsan/ubsan_type_hash.cc
new file mode 100644
index 000000000000..7a9cd28f6ec0
--- /dev/null
+++ b/lib/ubsan/ubsan_type_hash.cc
@@ -0,0 +1,248 @@
+//===-- ubsan_type_hash.cc ------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of a hash table for fast checking of inheritance
+// relationships. This file is only linked into C++ compilations, and is
+// permitted to use language features which require a C++ ABI library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+// The following are intended to be binary compatible with the definitions
+// given in the Itanium ABI. We make no attempt to be ODR-compatible with
+// those definitions, since existing ABI implementations aren't.
+
+namespace std {
+ class type_info {
+ public:
+ virtual ~type_info();
+
+ const char *__type_name;
+ };
+}
+
+namespace __cxxabiv1 {
+
+/// Type info for classes with no bases, and base class for type info for
+/// classes with bases.
+class __class_type_info : public std::type_info {
+ virtual ~__class_type_info();
+};
+
+/// Type info for classes with simple single public inheritance.
+class __si_class_type_info : public __class_type_info {
+public:
+ virtual ~__si_class_type_info();
+
+ const __class_type_info *__base_type;
+};
+
+class __base_class_type_info {
+public:
+ const __class_type_info *__base_type;
+ long __offset_flags;
+
+ enum __offset_flags_masks {
+ __virtual_mask = 0x1,
+ __public_mask = 0x2,
+ __offset_shift = 8
+ };
+};
+
+/// Type info for classes with multiple, virtual, or non-public inheritance.
+class __vmi_class_type_info : public __class_type_info {
+public:
+ virtual ~__vmi_class_type_info();
+
+ unsigned int flags;
+ unsigned int base_count;
+ __base_class_type_info base_info[1];
+};
+
+}
+
+namespace abi = __cxxabiv1;
+
+// We implement a simple two-level cache for type-checking results. For each
+// (vptr,type) pair, a hash is computed. This hash is assumed to be globally
+// unique; if it collides, we will get false negatives, but:
+// * such a collision would have to occur on the *first* bad access,
+// * the probability of such a collision is low (and for a 64-bit target, is
+// negligible), and
+// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs
+// give better coverage.
+//
+// The first caching layer is a small hash table with no chaining; buckets are
+// reused as needed. The second caching layer is a large hash table with open
+// chaining. We can freely evict from either layer since this is just a cache.
+//
+// FIXME: Make these hash table accesses thread-safe. The races here are benign
+// (worst-case, we could miss a bug or see a slowdown) but we should
+// avoid upsetting race detectors.
+
+/// Find a bucket to store the given hash value in.
+static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) {
+ static const unsigned HashTableSize = 65537;
+ static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize] = { 1 };
+
+ unsigned Probe = V & 65535;
+ for (int Tries = 5; Tries; --Tries) {
+ if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V)
+ return &__ubsan_vptr_hash_set[Probe];
+ Probe += ((V >> 16) & 65535) + 1;
+ if (Probe >= HashTableSize)
+ Probe -= HashTableSize;
+ }
+ // FIXME: Pick a random entry from the probe sequence to evict rather than
+ // just taking the first.
+ return &__ubsan_vptr_hash_set[V];
+}
+
+/// A cache of recently-checked hashes. Mini hash table with "random" evictions.
+__ubsan::HashValue
+__ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize] = { 1 };
+
+/// \brief Determine whether \p Derived has a \p Base base class subobject at
+/// offset \p Offset.
+static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
+ const abi::__class_type_info *Base,
+ sptr Offset) {
+ if (Derived == Base)
+ return Offset == 0;
+
+ if (const abi::__si_class_type_info *SI =
+ dynamic_cast<const abi::__si_class_type_info*>(Derived))
+ return isDerivedFromAtOffset(SI->__base_type, Base, Offset);
+
+ const abi::__vmi_class_type_info *VTI =
+ dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
+ if (!VTI)
+ // No base class subobjects.
+ return false;
+
+ // Look for a base class which is derived from \p Base at the right offset.
+ for (unsigned int base = 0; base != VTI->base_count; ++base) {
+ // FIXME: Curtail the recursion if this base can't possibly contain the
+ // given offset.
+ sptr OffsetHere = VTI->base_info[base].__offset_flags >>
+ abi::__base_class_type_info::__offset_shift;
+ if (VTI->base_info[base].__offset_flags &
+ abi::__base_class_type_info::__virtual_mask)
+ // For now, just punt on virtual bases and say 'yes'.
+ // FIXME: OffsetHere is the offset in the vtable of the virtual base
+ // offset. Read the vbase offset out of the vtable and use it.
+ return true;
+ if (isDerivedFromAtOffset(VTI->base_info[base].__base_type,
+ Base, Offset - OffsetHere))
+ return true;
+ }
+
+ return false;
+}
+
+/// \brief Find the derived-most dynamic base class of \p Derived at offset
+/// \p Offset.
+static const abi::__class_type_info *findBaseAtOffset(
+ const abi::__class_type_info *Derived, sptr Offset) {
+ if (!Offset)
+ return Derived;
+
+ if (const abi::__si_class_type_info *SI =
+ dynamic_cast<const abi::__si_class_type_info*>(Derived))
+ return findBaseAtOffset(SI->__base_type, Offset);
+
+ const abi::__vmi_class_type_info *VTI =
+ dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
+ if (!VTI)
+ // No base class subobjects.
+ return 0;
+
+ for (unsigned int base = 0; base != VTI->base_count; ++base) {
+ sptr OffsetHere = VTI->base_info[base].__offset_flags >>
+ abi::__base_class_type_info::__offset_shift;
+ if (VTI->base_info[base].__offset_flags &
+ abi::__base_class_type_info::__virtual_mask)
+ // FIXME: Can't handle virtual bases yet.
+ continue;
+ if (const abi::__class_type_info *Base =
+ findBaseAtOffset(VTI->base_info[base].__base_type,
+ Offset - OffsetHere))
+ return Base;
+ }
+
+ return 0;
+}
+
+namespace {
+
+struct VtablePrefix {
+ /// The offset from the vptr to the start of the most-derived object.
+ /// This should never be greater than zero, and will usually be exactly
+ /// zero.
+ sptr Offset;
+ /// The type_info object describing the most-derived class type.
+ std::type_info *TypeInfo;
+};
+VtablePrefix *getVtablePrefix(void *Object) {
+ VtablePrefix **VptrPtr = reinterpret_cast<VtablePrefix**>(Object);
+ if (!*VptrPtr)
+ return 0;
+ VtablePrefix *Prefix = *VptrPtr - 1;
+ if (Prefix->Offset > 0 || !Prefix->TypeInfo)
+ // This can't possibly be a valid vtable.
+ return 0;
+ return Prefix;
+}
+
+}
+
+bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
+ // A crash anywhere within this function probably means the vptr is corrupted.
+ // FIXME: Perform these checks more cautiously.
+
+ // Check whether this is something we've evicted from the cache.
+ HashValue *Bucket = getTypeCacheHashTableBucket(Hash);
+ if (*Bucket == Hash) {
+ __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
+ return true;
+ }
+
+ VtablePrefix *Vtable = getVtablePrefix(Object);
+ if (!Vtable)
+ return false;
+
+ // Check that this is actually a type_info object for a class type.
+ abi::__class_type_info *Derived =
+ dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo);
+ if (!Derived)
+ return false;
+
+ abi::__class_type_info *Base = (abi::__class_type_info*)Type;
+ if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset))
+ return false;
+
+ // Success. Cache this result.
+ __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
+ *Bucket = Hash;
+ return true;
+}
+
+__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfo(void *Object) {
+ VtablePrefix *Vtable = getVtablePrefix(Object);
+ if (!Vtable)
+ return DynamicTypeInfo(0, 0, 0);
+ const abi::__class_type_info *ObjectType = findBaseAtOffset(
+ static_cast<const abi::__class_type_info*>(Vtable->TypeInfo),
+ -Vtable->Offset);
+ return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset,
+ ObjectType ? ObjectType->__type_name : "<unknown>");
+}
diff --git a/lib/ubsan/ubsan_type_hash.h b/lib/ubsan/ubsan_type_hash.h
new file mode 100644
index 000000000000..58ecd3de9864
--- /dev/null
+++ b/lib/ubsan/ubsan_type_hash.h
@@ -0,0 +1,63 @@
+//===-- ubsan_type_hash.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Hashing of types for Clang's undefined behavior checker.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_TYPE_HASH_H
+#define UBSAN_TYPE_HASH_H
+
+#include "sanitizer_common/sanitizer_common.h"
+
+namespace __ubsan {
+
+typedef uptr HashValue;
+
+/// \brief Information about the dynamic type of an object (extracted from its
+/// vptr).
+class DynamicTypeInfo {
+ const char *MostDerivedTypeName;
+ sptr Offset;
+ const char *SubobjectTypeName;
+
+public:
+ DynamicTypeInfo(const char *MDTN, sptr Offset, const char *STN)
+ : MostDerivedTypeName(MDTN), Offset(Offset), SubobjectTypeName(STN) {}
+
+ /// Determine whether the object had a valid dynamic type.
+ bool isValid() const { return MostDerivedTypeName; }
+ /// Get the name of the most-derived type of the object.
+ const char *getMostDerivedTypeName() const { return MostDerivedTypeName; }
+ /// Get the offset from the most-derived type to this base class.
+ sptr getOffset() const { return Offset; }
+ /// Get the name of the most-derived type at the specified offset.
+ const char *getSubobjectTypeName() const { return SubobjectTypeName; }
+};
+
+/// \brief Get information about the dynamic type of an object.
+DynamicTypeInfo getDynamicTypeInfo(void *Object);
+
+/// \brief Check whether the dynamic type of \p Object has a \p Type subobject
+/// at offset 0.
+/// \return \c true if the type matches, \c false if not.
+bool checkDynamicType(void *Object, void *Type, HashValue Hash);
+
+const unsigned VptrTypeCacheSize = 128;
+
+/// \brief A cache of the results of checkDynamicType. \c checkDynamicType would
+/// return \c true (modulo hash collisions) if
+/// \code
+/// __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] == Hash
+/// \endcode
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+HashValue __ubsan_vptr_type_cache[VptrTypeCacheSize];
+
+} // namespace __ubsan
+
+#endif // UBSAN_TYPE_HASH_H
diff --git a/lib/ubsan/ubsan_value.cc b/lib/ubsan/ubsan_value.cc
new file mode 100644
index 000000000000..f17c58989db9
--- /dev/null
+++ b/lib/ubsan/ubsan_value.cc
@@ -0,0 +1,81 @@
+//===-- ubsan_value.cc ----------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Representation of a runtime value, as marshaled from the generated code to
+// the ubsan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_value.h"
+
+using namespace __ubsan;
+
+SIntMax Value::getSIntValue() const {
+ CHECK(getType().isSignedIntegerTy());
+ if (isInlineInt()) {
+ // Val was zero-extended to ValueHandle. Sign-extend from original width
+ // to SIntMax.
+ const unsigned ExtraBits =
+ sizeof(SIntMax) * 8 - getType().getIntegerBitWidth();
+ return SIntMax(Val) << ExtraBits >> ExtraBits;
+ }
+ if (getType().getIntegerBitWidth() == 64)
+ return *reinterpret_cast<s64*>(Val);
+#if HAVE_INT128_T
+ if (getType().getIntegerBitWidth() == 128)
+ return *reinterpret_cast<s128*>(Val);
+#else
+ if (getType().getIntegerBitWidth() == 128)
+ UNREACHABLE("libclang_rt.ubsan was built without __int128 support");
+#endif
+ UNREACHABLE("unexpected bit width");
+}
+
+UIntMax Value::getUIntValue() const {
+ CHECK(getType().isUnsignedIntegerTy());
+ if (isInlineInt())
+ return Val;
+ if (getType().getIntegerBitWidth() == 64)
+ return *reinterpret_cast<u64*>(Val);
+#if HAVE_INT128_T
+ if (getType().getIntegerBitWidth() == 128)
+ return *reinterpret_cast<u128*>(Val);
+#else
+ if (getType().getIntegerBitWidth() == 128)
+ UNREACHABLE("libclang_rt.ubsan was built without __int128 support");
+#endif
+ UNREACHABLE("unexpected bit width");
+}
+
+UIntMax Value::getPositiveIntValue() const {
+ if (getType().isUnsignedIntegerTy())
+ return getUIntValue();
+ SIntMax Val = getSIntValue();
+ CHECK(Val >= 0);
+ return Val;
+}
+
+/// Get the floating-point value of this object, extended to a long double.
+/// These are always passed by address (our calling convention doesn't allow
+/// them to be passed in floating-point registers, so this has little cost).
+FloatMax Value::getFloatValue() const {
+ CHECK(getType().isFloatTy());
+ switch (getType().getFloatBitWidth()) {
+#if 0
+ // FIXME: OpenCL / NEON 'half' type. LLVM can't lower the conversion
+ // from this to 'long double'.
+ case 16: return *reinterpret_cast<__fp16*>(Val);
+#endif
+ case 32: return *reinterpret_cast<float*>(Val);
+ case 64: return *reinterpret_cast<double*>(Val);
+ case 80: return *reinterpret_cast<long double*>(Val);
+ case 128: return *reinterpret_cast<long double*>(Val);
+ }
+ UNREACHABLE("unexpected floating point bit width");
+}
diff --git a/lib/ubsan/ubsan_value.h b/lib/ubsan/ubsan_value.h
new file mode 100644
index 000000000000..e673f7af1d83
--- /dev/null
+++ b/lib/ubsan/ubsan_value.h
@@ -0,0 +1,195 @@
+//===-- ubsan_value.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Representation of data which is passed from the compiler-generated calls into
+// the ubsan runtime.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_VALUE_H
+#define UBSAN_VALUE_H
+
+// For now, only support linux and darwin. Other platforms should be easy to
+// add, and probably work as-is.
+#if !defined(__linux__) && !defined(__APPLE__)
+#error "UBSan not supported for this platform!"
+#endif
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+// FIXME: Move this out to a config header.
+#if __SIZEOF_INT128__
+typedef __int128 s128;
+typedef unsigned __int128 u128;
+#define HAVE_INT128_T 1
+#else
+#define HAVE_INT128_T 0
+#endif
+
+
+namespace __ubsan {
+
+/// \brief Largest integer types we support.
+#if HAVE_INT128_T
+typedef s128 SIntMax;
+typedef u128 UIntMax;
+#else
+typedef s64 SIntMax;
+typedef u64 UIntMax;
+#endif
+
+/// \brief Largest floating-point type we support.
+typedef long double FloatMax;
+
+/// \brief A description of a source location. This corresponds to Clang's
+/// \c PresumedLoc type.
+class SourceLocation {
+ const char *Filename;
+ u32 Line;
+ u32 Column;
+
+public:
+ SourceLocation() : Filename(), Line(), Column() {}
+ SourceLocation(const char *Filename, unsigned Line, unsigned Column)
+ : Filename(Filename), Line(Line), Column(Column) {}
+
+ /// \brief Determine whether the source location is known.
+ bool isInvalid() const { return !Filename; }
+
+ /// \brief Atomically acquire a copy, disabling original in-place.
+ /// Exactly one call to acquire() returns a copy that isn't disabled.
+ SourceLocation acquire() {
+ u32 OldColumn = __sanitizer::atomic_exchange(
+ (__sanitizer::atomic_uint32_t *)&Column, ~u32(0),
+ __sanitizer::memory_order_relaxed);
+ return SourceLocation(Filename, Line, OldColumn);
+ }
+
+ /// \brief Determine if this Location has been disabled.
+ /// Disabled SourceLocations are invalid to use.
+ bool isDisabled() {
+ return Column == ~u32(0);
+ }
+
+ /// \brief Get the presumed filename for the source location.
+ const char *getFilename() const { return Filename; }
+ /// \brief Get the presumed line number.
+ unsigned getLine() const { return Line; }
+ /// \brief Get the column within the presumed line.
+ unsigned getColumn() const { return Column; }
+};
+
+
+/// \brief A description of a type.
+class TypeDescriptor {
+ /// A value from the \c Kind enumeration, specifying what flavor of type we
+ /// have.
+ u16 TypeKind;
+
+ /// A \c Type-specific value providing information which allows us to
+ /// interpret the meaning of a ValueHandle of this type.
+ u16 TypeInfo;
+
+ /// The name of the type follows, in a format suitable for including in
+ /// diagnostics.
+ char TypeName[1];
+
+public:
+ enum Kind {
+ /// An integer type. Lowest bit is 1 for a signed value, 0 for an unsigned
+ /// value. Remaining bits are log_2(bit width). The value representation is
+ /// the integer itself if it fits into a ValueHandle, and a pointer to the
+ /// integer otherwise.
+ TK_Integer = 0x0000,
+ /// A floating-point type. Low 16 bits are bit width. The value
+ /// representation is a pointer to the floating-point value.
+ TK_Float = 0x0001,
+ /// Any other type. The value representation is unspecified.
+ TK_Unknown = 0xffff
+ };
+
+ const char *getTypeName() const { return TypeName; }
+
+ Kind getKind() const {
+ return static_cast<Kind>(TypeKind);
+ }
+
+ bool isIntegerTy() const { return getKind() == TK_Integer; }
+ bool isSignedIntegerTy() const {
+ return isIntegerTy() && (TypeInfo & 1);
+ }
+ bool isUnsignedIntegerTy() const {
+ return isIntegerTy() && !(TypeInfo & 1);
+ }
+ unsigned getIntegerBitWidth() const {
+ CHECK(isIntegerTy());
+ return 1 << (TypeInfo >> 1);
+ }
+
+ bool isFloatTy() const { return getKind() == TK_Float; }
+ unsigned getFloatBitWidth() const {
+ CHECK(isFloatTy());
+ return TypeInfo;
+ }
+};
+
+/// \brief An opaque handle to a value.
+typedef uptr ValueHandle;
+
+
+/// \brief Representation of an operand value provided by the instrumented code.
+///
+/// This is a combination of a TypeDescriptor (which is emitted as constant data
+/// as an operand to a handler function) and a ValueHandle (which is passed at
+/// runtime when a check failure occurs).
+class Value {
+ /// The type of the value.
+ const TypeDescriptor &Type;
+ /// The encoded value itself.
+ ValueHandle Val;
+
+ /// Is \c Val a (zero-extended) integer?
+ bool isInlineInt() const {
+ CHECK(getType().isIntegerTy());
+ const unsigned InlineBits = sizeof(ValueHandle) * 8;
+ const unsigned Bits = getType().getIntegerBitWidth();
+ return Bits <= InlineBits;
+ }
+
+public:
+ Value(const TypeDescriptor &Type, ValueHandle Val) : Type(Type), Val(Val) {}
+
+ const TypeDescriptor &getType() const { return Type; }
+
+ /// \brief Get this value as a signed integer.
+ SIntMax getSIntValue() const;
+
+ /// \brief Get this value as an unsigned integer.
+ UIntMax getUIntValue() const;
+
+ /// \brief Decode this value, which must be a positive or unsigned integer.
+ UIntMax getPositiveIntValue() const;
+
+ /// Is this an integer with value -1?
+ bool isMinusOne() const {
+ return getType().isSignedIntegerTy() && getSIntValue() == -1;
+ }
+
+ /// Is this a negative integer?
+ bool isNegative() const {
+ return getType().isSignedIntegerTy() && getSIntValue() < 0;
+ }
+
+ /// \brief Get this value as a floating-point quantity.
+ FloatMax getFloatValue() const;
+};
+
+} // namespace __ubsan
+
+#endif // UBSAN_VALUE_H
diff --git a/lib/ucmpdi2.c b/lib/ucmpdi2.c
index 3242bbf08046..40af23613b1f 100644
--- a/lib/ucmpdi2.c
+++ b/lib/ucmpdi2.c
@@ -36,3 +36,16 @@ __ucmpdi2(du_int a, du_int b)
return 2;
return 1;
}
+
+#ifdef __ARM_EABI__
+/* Returns: if (a < b) returns -1
+* if (a == b) returns 0
+* if (a > b) returns 1
+*/
+COMPILER_RT_ABI si_int
+__aeabi_ulcmp(di_int a, di_int b)
+{
+ return __ucmpdi2(a, b) - 1;
+}
+#endif
+
diff --git a/make/AppleBI.mk b/make/AppleBI.mk
index 96f8222c7b5c..b5e702b10e66 100644
--- a/make/AppleBI.mk
+++ b/make/AppleBI.mk
@@ -64,8 +64,7 @@ $(OBJROOT)/libcompiler_rt-%.dylib : $(OBJROOT)/darwin_bni/Release/%/libcompiler_
$(SYMROOT)/libcompiler_rt.dylib: $(foreach arch,$(filter-out armv4t,$(RC_ARCHS)), \
$(OBJROOT)/libcompiler_rt-$(arch).dylib)
$(call GetCNAVar,LIPO,Platform.darwin_bni,Release,) -create $^ -o $@
-
-
+ $(call GetCNAVar,DSYMUTIL,Platform.darwin_bni,Release,) $@
# Copy results to DSTROOT.
diff --git a/make/config.mk b/make/config.mk
index 42fb9a8768f2..6398d058de76 100644
--- a/make/config.mk
+++ b/make/config.mk
@@ -21,6 +21,7 @@ MKDIR := mkdir -p
DATE := date
LIPO := lipo
CP := cp
+DSYMUTIL := dsymutil
VERBOSE := 0
DEBUGMAKE :=
@@ -42,5 +43,5 @@ endif
###
# Common compiler options
-COMMON_CXXFLAGS=-fno-exceptions -fPIC -funwind-tables -I${ProjSrcRoot}/lib
+COMMON_CXXFLAGS=-fno-exceptions -fPIC -funwind-tables -I${ProjSrcRoot}/lib -I${ProjSrcRoot}/include
COMMON_CFLAGS=-fPIC
diff --git a/make/lib_info.mk b/make/lib_info.mk
index 2e85f6402b2c..31850f78f981 100644
--- a/make/lib_info.mk
+++ b/make/lib_info.mk
@@ -53,8 +53,7 @@ $(foreach key,$(SubDirKeys),\
# The names of all the available options.
AvailableOptions := AR ARFLAGS \
- CC CFLAGS FUNCTIONS OPTIMIZED \
+ CC CFLAGS LDFLAGS FUNCTIONS OPTIMIZED \
RANLIB RANLIBFLAGS \
- VISIBILITY_HIDDEN \
- KERNEL_USE \
- STRIP LIPO
+ VISIBILITY_HIDDEN KERNEL_USE \
+ SHARED_LIBRARY SHARED_LIBRARY_SUFFIX STRIP LIPO DSYMUTIL
diff --git a/make/options.mk b/make/options.mk
index f695fc8db717..67197de03f47 100644
--- a/make/options.mk
+++ b/make/options.mk
@@ -23,14 +23,26 @@ OPTIMIZED := 1
# default.
VISIBILITY_HIDDEN := 0
+# Whether the library is being built for kernel use.
+KERNEL_USE := 0
+
+# Whether the library should be built as a shared object.
+SHARED_LIBRARY := 0
+
# Miscellaneous tools.
AR := ar
# FIXME: Remove these pipes once ranlib errors are fixed.
ARFLAGS := cru 2> /dev/null
+
+LDFLAGS :=
+
RANLIB := ranlib
# FIXME: Remove these pipes once ranlib errors are fixed.
RANLIBFLAGS := 2> /dev/null
STRIP := strip
LIPO := lipo
+DSYMUTIL := dsymutil
+
+SHARED_LIBRARY_SUFFIX := so
diff --git a/make/platform/clang_darwin.mk b/make/platform/clang_darwin.mk
index d1788c44903a..fe84a0565929 100644
--- a/make/platform/clang_darwin.mk
+++ b/make/platform/clang_darwin.mk
@@ -44,6 +44,11 @@ UniversalArchs.eprintf := $(call CheckArches,i386,eprintf)
Configs += 10.4
UniversalArchs.10.4 := $(call CheckArches,i386 x86_64,10.4)
+# Configuration for targetting iOS for a couple of functions that didn't
+# make it into libSystem.
+Configs += ios
+UniversalArchs.ios := $(call CheckArches,i386 x86_64 armv7,ios)
+
# Configuration for targetting OSX. These functions may not be in libSystem
# so we should provide our own.
Configs += osx
@@ -51,16 +56,40 @@ UniversalArchs.osx := $(call CheckArches,i386 x86_64,osx)
# Configuration for use with kernel/kexts.
Configs += cc_kext
-UniversalArchs.cc_kext := $(call CheckArches,i386 x86_64,cc_kext)
+UniversalArchs.cc_kext := $(call CheckArches,armv7 i386 x86_64,cc_kext)
+
+# Configuration for use with kernel/kexts for iOS 5.0 and earlier (which used
+# a different code generation strategy).
+Configs += cc_kext_ios5
+UniversalArchs.cc_kext_ios5 := $(call CheckArches,x86_64 armv7,cc_kext_ios5)
# Configurations which define the profiling support functions.
Configs += profile_osx
UniversalArchs.profile_osx := $(call CheckArches,i386 x86_64,profile_osx)
+Configs += profile_ios
+UniversalArchs.profile_ios := $(call CheckArches,i386 x86_64 armv7,profile_ios)
# Configurations which define the ASAN support functions.
Configs += asan_osx
UniversalArchs.asan_osx := $(call CheckArches,i386 x86_64,asan_osx)
+Configs += asan_osx_dynamic
+UniversalArchs.asan_osx_dynamic := $(call CheckArches,i386 x86_64,asan_osx_dynamic)
+
+Configs += ubsan_osx
+UniversalArchs.ubsan_osx := $(call CheckArches,i386 x86_64,ubsan_osx)
+
+# Darwin 10.6 has a bug in cctools that makes it unable to use ranlib on our ARM
+# object files. If we are on that platform, strip out all ARM archs. We still
+# build the libraries themselves so that Clang can find them where it expects
+# them, even though they might not have an expected slice.
+ifneq ($(shell sw_vers -productVersion | grep 10.6),)
+UniversalArchs.ios := $(filter-out armv7, $(UniversalArchs.ios))
+UniversalArchs.cc_kext := $(filter-out armv7, $(UniversalArchs.cc_kext))
+UniversalArchs.cc_kext_ios5 := $(filter-out armv7, $(UniversalArchs.cc_kext_ios5))
+UniversalArchs.profile_ios := $(filter-out armv7, $(UniversalArchs.profile_ios))
+endif
+
# If RC_SUPPORTED_ARCHS is defined, treat it as a list of the architectures we
# are intended to support and limit what we try to build to that.
#
@@ -87,31 +116,79 @@ CFLAGS := -Wall -Werror -O3 -fomit-frame-pointer
# supported deployment target -- nothing in the compiler-rt libraries should
# actually depend on the deployment target.
OSX_DEPLOYMENT_ARGS := -mmacosx-version-min=10.4
+IOS_DEPLOYMENT_ARGS := -miphoneos-version-min=1.0
+IOS6_DEPLOYMENT_ARGS := -miphoneos-version-min=6.0
+IOSSIM_DEPLOYMENT_ARGS := -miphoneos-version-min=1.0
# Use our stub SDK as the sysroot to support more portable building.
OSX_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin
+IOS_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin
+IOS6_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin
+IOSSIM_DEPLOYMENT_ARGS += -isysroot $(ProjSrcRoot)/SDKs/darwin
CFLAGS.eprintf := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
CFLAGS.10.4 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
# FIXME: We can't build ASAN with our stub SDK yet.
CFLAGS.asan_osx := $(CFLAGS) -mmacosx-version-min=10.5 -fno-builtin
-
+CFLAGS.asan_osx_dynamic := \
+ $(CFLAGS) -mmacosx-version-min=10.5 -fno-builtin \
+ -DMAC_INTERPOSE_FUNCTIONS=1
+
+CFLAGS.ubsan_osx := $(CFLAGS) -mmacosx-version-min=10.5 -fno-builtin
+
+CFLAGS.ios.i386 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
+CFLAGS.ios.x86_64 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
+CFLAGS.ios.armv7 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.ios.armv7f := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.ios.armv7k := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.ios.armv7s := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
CFLAGS.osx.i386 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
CFLAGS.osx.x86_64 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
CFLAGS.cc_kext.i386 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
CFLAGS.cc_kext.x86_64 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext.armv7 := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext.armv7f := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext.armv7k := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext.armv7s := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext_ios5.armv7 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext_ios5.armv7f := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext_ios5.armv7k := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.cc_kext_ios5.armv7s := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
CFLAGS.profile_osx.i386 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
CFLAGS.profile_osx.x86_64 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS)
+CFLAGS.profile_ios.i386 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
+CFLAGS.profile_ios.x86_64 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS)
+CFLAGS.profile_ios.armv7 := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.profile_ios.armv7f := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.profile_ios.armv7k := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+CFLAGS.profile_ios.armv7s := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS)
+
+# Configure the asan_osx_dynamic library to be built shared.
+SHARED_LIBRARY.asan_osx_dynamic := 1
+LDFLAGS.asan_osx_dynamic := -framework Foundation -lstdc++
FUNCTIONS.eprintf := eprintf
FUNCTIONS.10.4 := eprintf floatundidf floatundisf floatundixf
+FUNCTIONS.ios := divmodsi4 udivmodsi4 mulosi4 mulodi4 muloti4
+# On x86, the divmod functions reference divsi.
+FUNCTIONS.ios.i386 := $(FUNCTIONS.ios) \
+ divsi3 udivsi3
+FUNCTIONS.ios.x86_64 := $(FUNCTIONS.ios) \
+ divsi3 udivsi3
+
FUNCTIONS.osx := mulosi4 mulodi4 muloti4
FUNCTIONS.profile_osx := GCDAProfiling
+FUNCTIONS.profile_ios := GCDAProfiling
FUNCTIONS.asan_osx := $(AsanFunctions) $(InterceptionFunctions) \
$(SanitizerCommonFunctions)
+FUNCTIONS.asan_osx_dynamic := $(AsanFunctions) $(InterceptionFunctions) \
+ $(SanitizerCommonFunctions) \
+ $(AsanDynamicFunctions)
+
+FUNCTIONS.ubsan_osx := $(UbsanFunctions) $(SanitizerCommonFunctions)
CCKEXT_COMMON_FUNCTIONS := \
absvdi2 \
@@ -224,6 +301,49 @@ CCKEXT_ARM_FUNCTIONS := $(CCKEXT_COMMON_FUNCTIONS) \
unorddf2 \
unordsf2
+CCKEXT_ARMVFP_FUNCTIONS := $(CCKEXT_ARM_FUNCTIONS) \
+ adddf3vfp \
+ addsf3vfp \
+ divdf3vfp \
+ divsf3vfp \
+ eqdf2vfp \
+ eqsf2vfp \
+ extendsfdf2vfp \
+ fixdfsivfp \
+ fixsfsivfp \
+ fixunsdfsivfp \
+ fixunssfsivfp \
+ floatsidfvfp \
+ floatsisfvfp \
+ floatunssidfvfp \
+ floatunssisfvfp \
+ gedf2vfp \
+ gesf2vfp \
+ gtdf2vfp \
+ gtsf2vfp \
+ ledf2vfp \
+ lesf2vfp \
+ ltdf2vfp \
+ ltsf2vfp \
+ muldf3vfp \
+ mulsf3vfp \
+ nedf2vfp \
+ nesf2vfp \
+ subdf3vfp \
+ subsf3vfp \
+ truncdfsf2vfp \
+ unorddf2vfp \
+ unordsf2vfp
+
+FUNCTIONS.cc_kext.armv7 := $(CCKEXT_ARMVFP_FUNCTIONS)
+FUNCTIONS.cc_kext.armv7f := $(CCKEXT_ARMVFP_FUNCTIONS)
+FUNCTIONS.cc_kext.armv7k := $(CCKEXT_ARMVFP_FUNCTIONS)
+FUNCTIONS.cc_kext.armv7s := $(CCKEXT_ARMVFP_FUNCTIONS)
+FUNCTIONS.cc_kext_ios5.armv7 := $(CCKEXT_ARMVFP_FUNCTIONS)
+FUNCTIONS.cc_kext_ios5.armv7f := $(CCKEXT_ARMVFP_FUNCTIONS)
+FUNCTIONS.cc_kext_ios5.armv7k := $(CCKEXT_ARMVFP_FUNCTIONS)
+FUNCTIONS.cc_kext_ios5.armv7s := $(CCKEXT_ARMVFP_FUNCTIONS)
+
CCKEXT_X86_FUNCTIONS := $(CCKEXT_COMMON_FUNCTIONS) \
divxc3 \
fixunsxfdi \
@@ -297,11 +417,30 @@ CCKEXT_MISSING_FUNCTIONS := \
aeabi_fcmpge aeabi_fcmpgt aeabi_fcmple aeabi_fcmplt aeabi_frsub aeabi_idivmod \
aeabi_uidivmod
+FUNCTIONS.cc_kext.armv7 := \
+ $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.armv7))
+FUNCTIONS.cc_kext.armv7f := \
+ $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.armv7f))
+FUNCTIONS.cc_kext.armv7k := \
+ $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.armv7k))
+FUNCTIONS.cc_kext.armv7s := \
+ $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.armv7s))
+FUNCTIONS.cc_kext_ios5.armv7 := \
+ $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext_ios5.armv7))
+FUNCTIONS.cc_kext_ios5.armv7f := \
+ $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext_ios5.armv7f))
+FUNCTIONS.cc_kext_ios5.armv7k := \
+ $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext_ios5.armv7k))
+FUNCTIONS.cc_kext_ios5.armv7s := \
+ $(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext_ios5.armv7s))
FUNCTIONS.cc_kext.i386 := \
$(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.i386))
FUNCTIONS.cc_kext.x86_64 := \
$(filter-out $(CCKEXT_MISSING_FUNCTIONS),$(FUNCTIONS.cc_kext.x86_64))
KERNEL_USE.cc_kext := 1
+KERNEL_USE.cc_kext_ios5 := 1
VISIBILITY_HIDDEN := 1
+
+SHARED_LIBRARY_SUFFIX := dylib
diff --git a/make/platform/clang_linux.mk b/make/platform/clang_linux.mk
index f2b049f8c931..adfe8917de87 100644
--- a/make/platform/clang_linux.mk
+++ b/make/platform/clang_linux.mk
@@ -18,42 +18,63 @@ $(error "unable to infer compiler target triple for $(CC)")
endif
endif
-CompilerTargetArch := $(firstword $(subst -, ,$(CompilerTargetTriple)))
-
# Only define configs if we detected a linux target.
ifneq ($(findstring -linux-,$(CompilerTargetTriple)),)
-# Configurations which just include all the runtime functions.
+# Define configs only if arch in triple is i386 or x86_64
+CompilerTargetArch := $(firstword $(subst -, ,$(CompilerTargetTriple)))
ifeq ($(call contains,i386 x86_64,$(CompilerTargetArch)),true)
-Configs += full-i386 full-x86_64
-Arch.full-i386 := i386
-Arch.full-x86_64 := x86_64
-endif
-# Configuration for profile runtime.
-ifeq ($(call contains,i386 x86_64,$(CompilerTargetArch)),true)
-Configs += profile-i386 profile-x86_64
-Arch.profile-i386 := i386
-Arch.profile-x86_64 := x86_64
+# TryCompile compiler source flags
+# Returns exit code of running a compiler invocation.
+TryCompile = \
+ $(shell \
+ cflags=""; \
+ for flag in $(3); do \
+ cflags="$$cflags $$flag"; \
+ done; \
+ $(1) $$cflags $(2) -o /dev/null > /dev/null 2> /dev/null ; \
+ echo $$?)
+
+test_source = $(ProjSrcRoot)/make/platform/clang_linux_test_input.c
+ifeq ($(CompilerTargetArch),i386)
+ SupportedArches := i386
+ ifeq ($(call TryCompile,$(CC),$(test_source),-m64),0)
+ SupportedArches += x86_64
+ endif
+else
+ SupportedArches := x86_64
+ ifeq ($(call TryCompile,$(CC),$(test_source),-m32),0)
+ SupportedArches += i386
+ endif
endif
-# Configuration for ASAN runtime.
-ifeq ($(CompilerTargetArch),i386)
-Configs += asan-i386
+# Build runtime libraries for i386.
+ifeq ($(call contains,$(SupportedArches),i386),true)
+Configs += full-i386 profile-i386 asan-i386 ubsan-i386
+Arch.full-i386 := i386
+Arch.profile-i386 := i386
Arch.asan-i386 := i386
+Arch.ubsan-i386 := i386
endif
-ifeq ($(CompilerTargetArch),x86_64)
-Configs += asan-x86_64
+
+# Build runtime libraries for x86_64.
+ifeq ($(call contains,$(SupportedArches),x86_64),true)
+Configs += full-x86_64 profile-x86_64 asan-x86_64 tsan-x86_64 ubsan-x86_64
+Arch.full-x86_64 := x86_64
+Arch.profile-x86_64 := x86_64
Arch.asan-x86_64 := x86_64
+Arch.tsan-x86_64 := x86_64
+Arch.ubsan-x86_64 := x86_64
endif
-# Configuration for TSAN runtime.
-ifeq ($(CompilerTargetArch),x86_64)
-Configs += tsan-x86_64
-Arch.tsan-x86_64 := x86_64
+ifneq ($(LLVM_ANDROID_TOOLCHAIN_DIR),)
+Configs += asan-arm-android
+Arch.asan-arm-android := arm-android
endif
endif
+endif
###
@@ -66,6 +87,17 @@ CFLAGS.profile-x86_64 := $(CFLAGS) -m64
CFLAGS.asan-i386 := $(CFLAGS) -m32 -fPIE -fno-builtin
CFLAGS.asan-x86_64 := $(CFLAGS) -m64 -fPIE -fno-builtin
CFLAGS.tsan-x86_64 := $(CFLAGS) -m64 -fPIE -fno-builtin
+CFLAGS.ubsan-i386 := $(CFLAGS) -m32 -fPIE -fno-builtin
+CFLAGS.ubsan-x86_64 := $(CFLAGS) -m64 -fPIE -fno-builtin
+
+SHARED_LIBRARY.asan-arm-android := 1
+ANDROID_COMMON_FLAGS := -target arm-linux-androideabi \
+ --sysroot=$(LLVM_ANDROID_TOOLCHAIN_DIR)/sysroot \
+ -B$(LLVM_ANDROID_TOOLCHAIN_DIR)
+CFLAGS.asan-arm-android := $(CFLAGS) -fPIC -fno-builtin \
+ $(ANDROID_COMMON_FLAGS) -mllvm -arm-enable-ehabi
+LDFLAGS.asan-arm-android := $(LDFLAGS) $(ANDROID_COMMON_FLAGS) -ldl \
+ -Wl,-soname=libclang_rt.asan-arm-android.so
# Use our stub SDK as the sysroot to support more portable building. For now we
# just do this for the non-ASAN modules, because the stub SDK doesn't have
@@ -83,11 +115,17 @@ FUNCTIONS.asan-i386 := $(AsanFunctions) $(InterceptionFunctions) \
$(SanitizerCommonFunctions)
FUNCTIONS.asan-x86_64 := $(AsanFunctions) $(InterceptionFunctions) \
$(SanitizerCommonFunctions)
+FUNCTIONS.asan-arm-android := $(AsanFunctions) $(InterceptionFunctions) \
+ $(SanitizerCommonFunctions)
FUNCTIONS.tsan-x86_64 := $(TsanFunctions) $(InterceptionFunctions) \
- $(SanitizerCommonFunctions)
+ $(SanitizerCommonFunctions)
+FUNCTIONS.ubsan-i386 := $(UbsanFunctions) $(SanitizerCommonFunctions)
+FUNCTIONS.ubsan-x86_64 := $(UbsanFunctions) $(SanitizerCommonFunctions)
# Always use optimized variants.
OPTIMIZED := 1
# We don't need to use visibility hidden on Linux.
VISIBILITY_HIDDEN := 0
+
+SHARED_LIBRARY_SUFFIX := so
diff --git a/make/platform/clang_linux_test_input.c b/make/platform/clang_linux_test_input.c
new file mode 100644
index 000000000000..e65ce9860af4
--- /dev/null
+++ b/make/platform/clang_linux_test_input.c
@@ -0,0 +1,4 @@
+// This file is used to check if we can produce working executables
+// for i386 and x86_64 archs on Linux.
+#include <stdlib.h>
+int main(){}
diff --git a/make/platform/darwin_bni.mk b/make/platform/darwin_bni.mk
index 477e072de0a0..d12cfdff7040 100644
--- a/make/platform/darwin_bni.mk
+++ b/make/platform/darwin_bni.mk
@@ -14,6 +14,7 @@ ifneq (,$(SDKROOT))
RANLIB := $(shell xcrun -sdk $(SDKROOT) -find ranlib)
STRIP := $(shell xcrun -sdk $(SDKROOT) -find strip)
LIPO := $(shell xcrun -sdk $(SDKROOT) -find lipo)
+ DSYMUTIL := $(shell xcrun -sdk $(SDKROOT) -find dsymutil)
endif
ifneq ($(IPHONEOS_DEPLOYMENT_TARGET),)
@@ -111,3 +112,6 @@ FUNCTIONS.armv7 := $(FUNCTIONS) \
nedf2vfp nesf2vfp \
subdf3vfp subsf3vfp truncdfsf2vfp unorddf2vfp unordsf2vfp \
modsi3 umodsi3 udivsi3 divsi3 udivmodsi4 divmodsi4
+
+FUNCTIONS.armv7s := $(FUNCTIONS.armv7)
+
diff --git a/test/Unit/endianness.h b/test/Unit/endianness.h
index 669e6f1736a9..06c53de0bfa9 100644
--- a/test/Unit/endianness.h
+++ b/test/Unit/endianness.h
@@ -51,6 +51,21 @@
/* .. */
+#if defined(__OpenBSD__) || defined(__Bitrig__)
+#include <machine/endian.h>
+
+#if _BYTE_ORDER == _BIG_ENDIAN
+#define _YUGA_LITTLE_ENDIAN 0
+#define _YUGA_BIG_ENDIAN 1
+#elif _BYTE_ORDER == _LITTLE_ENDIAN
+#define _YUGA_LITTLE_ENDIAN 1
+#define _YUGA_BIG_ENDIAN 0
+#endif /* _BYTE_ORDER */
+
+#endif /* OpenBSD and Bitrig. */
+
+/* .. */
+
/* Mac OSX has __BIG_ENDIAN__ or __LITTLE_ENDIAN__ automatically set by the compiler (at least with GCC) */
#if defined(__APPLE__) && defined(__MACH__) || defined(__ellcc__ )