From 476c4db3dc56bee43df384704c75ccc71cfa7a1d Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Tue, 10 Feb 2015 07:45:43 +0000 Subject: [PATCH] Import compiler-rt trunk r228651. https://llvm.org/svn/llvm-project/compiler-rt/trunk@228651 --- CMakeLists.txt | 87 +- cmake/Modules/AddCompilerRT.cmake | 17 +- cmake/Modules/CompilerRTCompile.cmake | 2 +- cmake/config-ix.cmake | 57 +- include/CMakeLists.txt | 1 + include/sanitizer/asan_interface.h | 3 +- include/sanitizer/common_interface_defs.h | 15 +- include/sanitizer/coverage_interface.h | 46 + include/sanitizer/msan_interface.h | 4 +- lib/asan/CMakeLists.txt | 58 +- lib/asan/asan_activation.cc | 140 ++- lib/asan/asan_activation.h | 2 +- lib/asan/asan_activation_flags.inc | 35 + lib/asan/asan_allocator.cc | 909 ++++++++++++++++++ lib/asan/asan_allocator.h | 24 +- lib/asan/asan_allocator2.cc | 792 --------------- lib/asan/asan_debugging.cc | 4 +- lib/asan/asan_fake_stack.cc | 23 +- lib/asan/asan_flags.cc | 141 +++ lib/asan/asan_flags.h | 48 +- lib/asan/asan_flags.inc | 144 +++ lib/asan/asan_globals.cc | 8 +- lib/asan/asan_init_version.h | 6 +- lib/asan/asan_interceptors.cc | 54 +- lib/asan/asan_internal.h | 5 +- lib/asan/asan_linux.cc | 4 - lib/asan/asan_mac.cc | 141 +-- lib/asan/asan_malloc_mac.cc | 79 +- lib/asan/asan_mapping.h | 31 +- lib/asan/asan_poisoning.cc | 19 +- lib/asan/asan_poisoning.h | 11 +- lib/asan/asan_report.cc | 4 +- lib/asan/asan_rtl.cc | 325 +------ lib/asan/asan_stack.cc | 15 + lib/asan/asan_stack.h | 10 +- lib/asan/scripts/asan_device_setup | 3 +- lib/asan/scripts/asan_symbolize.py | 72 +- lib/asan/tests/CMakeLists.txt | 37 +- lib/asan/tests/asan_interface_test.cc | 2 +- lib/asan/tests/asan_noinst_test.cc | 9 - lib/asan/tests/asan_test.cc | 33 +- lib/builtins/CMakeLists.txt | 7 +- lib/builtins/atomic.c | 18 +- lib/builtins/clear_cache.c | 66 +- lib/builtins/gcc_personality_v0.c | 48 +- lib/builtins/int_types.h | 3 +- lib/dfsan/CMakeLists.txt | 1 + lib/dfsan/dfsan.cc | 29 +- lib/dfsan/dfsan.h | 16 +- lib/dfsan/dfsan_custom.cc | 5 +- lib/dfsan/dfsan_flags.inc | 32 + lib/lsan/lsan.cc | 3 + lib/lsan/lsan_allocator.cc | 2 +- lib/lsan/lsan_common.cc | 77 +- lib/lsan/lsan_common.h | 36 +- lib/lsan/lsan_flags.inc | 44 + lib/msan/CMakeLists.txt | 1 + lib/msan/msan.cc | 152 ++- lib/msan/msan.h | 145 ++- lib/msan/msan_allocator.cc | 27 +- lib/msan/msan_flags.h | 20 +- lib/msan/msan_flags.inc | 33 + lib/msan/msan_interceptors.cc | 247 ++--- lib/msan/msan_linux.cc | 123 +-- lib/msan/msan_poisoning.cc | 174 ++++ lib/msan/msan_poisoning.h | 59 ++ lib/msan/msan_report.cc | 21 +- lib/msan/msan_thread.cc | 11 - lib/msan/tests/CMakeLists.txt | 9 +- lib/msan/tests/msan_test.cc | 156 ++- lib/profile/InstrProfilingFile.c | 7 +- lib/sanitizer_common/CMakeLists.txt | 12 +- lib/sanitizer_common/sanitizer_allocator.cc | 9 +- lib/sanitizer_common/sanitizer_allocator.h | 83 +- .../sanitizer_allocator_internal.h | 9 + lib/sanitizer_common/sanitizer_common.cc | 59 +- lib/sanitizer_common/sanitizer_common.h | 84 +- .../sanitizer_common_interceptors.inc | 65 +- .../sanitizer_common_libcdep.cc | 66 ++ .../sanitizer_coverage_libcdep.cc | 345 +++++-- .../sanitizer_coverage_mapping_libcdep.cc | 55 +- .../sanitizer_deadlock_detector.h | 6 +- lib/sanitizer_common/sanitizer_flag_parser.cc | 153 +++ lib/sanitizer_common/sanitizer_flag_parser.h | 121 +++ lib/sanitizer_common/sanitizer_flags.cc | 296 +----- lib/sanitizer_common/sanitizer_flags.h | 74 +- lib/sanitizer_common/sanitizer_flags.inc | 149 +++ .../sanitizer_internal_defs.h | 20 +- lib/sanitizer_common/sanitizer_libc.cc | 17 + lib/sanitizer_common/sanitizer_libc.h | 21 + lib/sanitizer_common/sanitizer_linux.cc | 114 ++- .../sanitizer_linux_libcdep.cc | 60 +- lib/sanitizer_common/sanitizer_list.h | 16 +- lib/sanitizer_common/sanitizer_mac.cc | 17 +- lib/sanitizer_common/sanitizer_mac.h | 1 + lib/sanitizer_common/sanitizer_mutex.h | 6 + .../sanitizer_platform_interceptors.h | 45 +- .../sanitizer_platform_limits_posix.cc | 6 + .../sanitizer_platform_limits_posix.h | 11 +- lib/sanitizer_common/sanitizer_posix.cc | 8 +- .../sanitizer_posix_libcdep.cc | 12 + lib/sanitizer_common/sanitizer_quarantine.h | 18 +- lib/sanitizer_common/sanitizer_stackdepot.cc | 9 +- lib/sanitizer_common/sanitizer_stacktrace.cc | 19 +- lib/sanitizer_common/sanitizer_stacktrace.h | 43 +- .../sanitizer_stacktrace_libcdep.cc | 2 +- .../sanitizer_symbolizer_libbacktrace.cc | 8 +- .../sanitizer_symbolizer_libbacktrace.h | 2 +- .../sanitizer_symbolizer_posix_libcdep.cc | 2 +- .../sanitizer_unwind_posix_libcdep.cc | 6 +- lib/sanitizer_common/sanitizer_win.cc | 110 ++- lib/sanitizer_common/scripts/check_lint.sh | 9 +- lib/sanitizer_common/scripts/sancov.py | 2 +- lib/sanitizer_common/tests/CMakeLists.txt | 25 +- .../tests/sanitizer_allocator_test.cc | 50 +- .../tests/sanitizer_deadlock_detector_test.cc | 2 +- .../tests/sanitizer_flags_test.cc | 104 +- .../tests/sanitizer_libc_test.cc | 70 +- .../tests/sanitizer_printf_test.cc | 13 +- .../tests/sanitizer_procmaps_test.cc | 1 + .../tests/sanitizer_test_utils.h | 6 + lib/tsan/CMakeLists.txt | 17 +- lib/tsan/Makefile.old | 2 +- lib/tsan/check_analyze.sh | 37 +- lib/tsan/dd/dd_rtl.cc | 20 +- lib/tsan/go/build.bat | 2 +- lib/tsan/go/buildgo.sh | 67 +- lib/tsan/go/tsan_go.cc | 7 - lib/tsan/rtl/Makefile.old | 2 +- lib/tsan/rtl/tsan_clock.cc | 8 +- lib/tsan/rtl/tsan_defs.h | 37 +- lib/tsan/rtl/tsan_flags.cc | 101 +- lib/tsan/rtl/tsan_flags.h | 65 +- lib/tsan/rtl/tsan_flags.inc | 78 ++ lib/tsan/rtl/tsan_interceptors.cc | 101 +- lib/tsan/rtl/tsan_interface.cc | 81 +- lib/tsan/rtl/tsan_interface.h | 15 +- lib/tsan/rtl/tsan_interface_java.cc | 30 + lib/tsan/rtl/tsan_interface_java.h | 8 + lib/tsan/rtl/tsan_mman.cc | 13 +- lib/tsan/rtl/tsan_mman.h | 1 + lib/tsan/rtl/tsan_mutex.cc | 18 +- lib/tsan/rtl/tsan_mutex.h | 2 +- lib/tsan/rtl/tsan_platform.h | 3 - lib/tsan/rtl/tsan_platform_linux.cc | 11 +- lib/tsan/rtl/tsan_platform_mac.cc | 2 + lib/tsan/rtl/tsan_rtl.cc | 79 +- lib/tsan/rtl/tsan_rtl.h | 2 +- lib/tsan/rtl/tsan_rtl_report.cc | 7 +- lib/tsan/rtl/tsan_rtl_thread.cc | 11 +- lib/tsan/tests/CMakeLists.txt | 1 + lib/tsan/tests/rtl/tsan_string.cc | 4 - lib/tsan/tests/unit/tsan_clock_test.cc | 4 +- lib/tsan/tests/unit/tsan_mman_test.cc | 2 +- lib/tsan/tests/unit/tsan_mutex_test.cc | 2 +- lib/ubsan/CMakeLists.txt | 5 + lib/ubsan/ubsan_flags.cc | 62 +- lib/ubsan/ubsan_flags.h | 10 +- lib/ubsan/ubsan_flags.inc | 24 + lib/ubsan/ubsan_init.cc | 6 +- lib/ubsan/ubsan_type_hash.cc | 47 +- make/platform/clang_darwin.mk | 34 +- make/platform/darwin_bni.mk | 1 + make/platform/darwin_fat.mk | 56 -- test/asan/CMakeLists.txt | 202 ++-- .../TestCases/Android/coverage-android.cc | 94 +- test/asan/TestCases/Android/lit.local.cfg | 2 +- .../TestCases/Darwin/address-range-limit.mm | 38 + .../TestCases/Darwin/crashlog-stacktraces.c | 43 + .../Darwin/dyld_insert_libraries_reexec.cc | 2 - .../Darwin/dyld_insert_libraries_remove.cc | 39 + .../Darwin/interception-in-shared-lib-test.cc | 6 +- .../Darwin/interface_symbols_darwin.c | 9 + test/asan/TestCases/Darwin/linked-only.cc | 33 + .../Darwin/mixing-global-constructors.cc | 42 + .../TestCases/Darwin/suppressions-darwin.cc | 8 +- .../TestCases/Linux/asan_preload_test-2.cc | 4 +- .../coverage-caller-callee-total-count.cc | 2 +- .../Linux/coverage-direct-activation.cc | 59 ++ .../TestCases/Linux/coverage-direct-large.cc | 24 +- test/asan/TestCases/Linux/coverage-direct.cc | 45 +- .../asan/TestCases/Linux/coverage-disabled.cc | 2 + test/asan/TestCases/Linux/coverage-levels.cc | 15 +- .../Linux/coverage-maybe-open-file.cc | 2 +- .../Linux/coverage-module-unloaded.cc | 2 +- test/asan/TestCases/Linux/coverage-reset.cc | 52 + .../TestCases/Linux/coverage-sandboxing.cc | 2 +- test/asan/TestCases/Linux/coverage-tracing.cc | 48 +- test/asan/TestCases/Linux/coverage.cc | 2 +- test/asan/TestCases/Linux/malloc-in-qsort.cc | 7 +- test/asan/TestCases/Linux/nohugepage_test.cc | 91 ++ .../asan/TestCases/Linux/overflow-in-qsort.cc | 4 +- .../TestCases/Linux/quarantine_size_mb.cc | 24 + .../asan/TestCases/Linux/sized_delete_test.cc | 2 +- .../TestCases/Linux/stack-overflow-sigbus.cc | 64 ++ .../TestCases/Linux/stack-trace-dlclose.cc | 2 +- .../Posix/asan-symbolize-sanity-test.cc | 2 +- .../asan/TestCases/Posix/init-order-dlopen.cc | 4 +- .../large_allocator_unpoisons_on_free.cc | 2 +- test/asan/TestCases/Posix/shared-lib-test.cc | 8 +- .../asan/TestCases/Posix/start-deactivated.cc | 36 +- test/asan/TestCases/Posix/tsd_dtor_leak.cc | 14 +- test/asan/TestCases/Windows/iostream_sbo.cc | 18 + .../Windows/shadow_mapping_failure.cc | 18 + .../TestCases/Windows/thread_suspended.cc | 27 + test/asan/TestCases/allocator_returns_null.cc | 12 +- test/asan/TestCases/asan_options-help.cc | 9 + test/asan/TestCases/deep_call_stack.cc | 3 +- test/asan/TestCases/default_options.cc | 4 +- test/asan/TestCases/dlclose-test.cc | 8 +- .../TestCases/interception_failure_test.cc | 1 + test/asan/TestCases/log-path_test.cc | 4 +- test/asan/TestCases/stack-overflow.cc | 2 +- test/asan/TestCases/suppressions-function.cc | 4 +- .../TestCases/suppressions-interceptor.cc | 2 +- test/asan/TestCases/suppressions-library.cc | 8 +- test/asan/TestCases/zero_page_pc.cc | 8 +- test/asan/Unit/lit.site.cfg.in | 10 +- test/asan/android_commands/android_run.py | 2 +- test/asan/lit.cfg | 16 +- test/builtins/Unit/clear_cache_test.c | 19 +- .../builtins/Unit/enable_execute_stack_test.c | 20 +- test/lit.common.configured.in | 2 +- test/lsan/TestCases/ignore_object.cc | 3 +- test/lsan/TestCases/ignore_object_errors.cc | 4 +- test/lsan/TestCases/leak_check_at_exit.cc | 2 +- .../leak_check_before_thread_started.cc | 3 +- test/lsan/TestCases/suppressions_file.cc | 8 +- test/msan/msan_print_shadow.cc | 6 +- test/msan/origin-store-long.cc | 21 + test/msan/realloc-large-origin.cc | 30 + test/msan/realloc-origin.cc | 21 + test/msan/select_float_origin.cc | 2 +- test/msan/use-after-free.cc | 2 +- test/profile/instrprof-basic.c | 4 +- test/profile/instrprof-reset-counters.c | 2 +- test/profile/instrprof-set-filename.c | 2 +- test/profile/instrprof-without-libc.c | 2 +- .../instrprof-write-file-atexit-explicitly.c | 2 +- test/profile/instrprof-write-file-only.c | 2 +- test/profile/instrprof-write-file.c | 4 +- .../TestCases/Linux/hard_rss_limit_mb_test.cc | 37 + .../sanitizer_set_death_callback_test.cc | 49 + .../TestCases/Linux/sched_getparam.cc | 13 + .../TestCases/Linux/soft_rss_limit_mb_test.cc | 66 ++ .../TestCases/options-help.cc | 8 + .../TestCases/options-include.cc | 21 + .../TestCases/options-invalid.cc | 15 + test/tsan/CMakeLists.txt | 1 + test/tsan/aligned_vs_unaligned_race.cc | 17 +- test/tsan/annotate_happens_before.cc | 57 ++ test/tsan/atomic_free.cc | 7 +- test/tsan/atomic_free2.cc | 7 +- test/tsan/atomic_norace.cc | 11 +- test/tsan/atomic_race.cc | 11 +- test/tsan/atomic_stack.cc | 7 +- test/tsan/benign_race.cc | 8 +- test/tsan/blacklist2.cc | 8 +- test/tsan/cond_cancel.c | 9 +- test/tsan/cond_race.cc | 11 +- test/tsan/deadlock_detector_stress_test.cc | 25 +- test/tsan/deep_stack1.cc | 16 +- test/tsan/fd_close_norace.cc | 8 +- test/tsan/fd_location.cc | 8 +- test/tsan/fd_pipe_race.cc | 8 +- test/tsan/fd_stdout_race.cc | 8 +- test/tsan/fork_deadlock.cc | 8 +- test/tsan/fork_multithreaded.cc | 14 +- test/tsan/free_race.c | 12 +- test/tsan/global_race.cc | 9 +- test/tsan/global_race2.cc | 9 +- test/tsan/global_race3.cc | 9 +- test/tsan/halt_on_error.cc | 8 +- test/tsan/ignore_free.cc | 9 +- test/tsan/ignore_lib0.cc | 2 +- test/tsan/ignore_lib1.cc | 2 +- test/tsan/ignore_lib2.cc | 2 +- test/tsan/ignore_lib3.cc | 2 +- test/tsan/ignore_malloc.cc | 7 +- test/tsan/ignore_race.cc | 8 +- test/tsan/inlined_memcpy_race.cc | 9 +- test/tsan/inlined_memcpy_race2.cc | 9 +- test/tsan/java.h | 8 +- test/tsan/java_finalizer.cc | 4 +- test/tsan/java_lock.cc | 5 +- test/tsan/java_lock_move.cc | 4 +- test/tsan/java_lock_rec.cc | 8 +- test/tsan/java_lock_rec_race.cc | 8 +- test/tsan/java_move_overlap.cc | 4 +- test/tsan/java_move_overlap_race.cc | 4 +- test/tsan/java_race_move.cc | 4 +- test/tsan/java_rwlock.cc | 5 +- test/tsan/java_volatile.cc | 42 + test/tsan/load_shared_lib.cc | 30 +- test/tsan/malloc_stack.cc | 7 +- test/tsan/map32bit.cc | 9 +- test/tsan/memcpy_race.cc | 9 +- test/tsan/mop_with_offset.cc | 9 +- test/tsan/mop_with_offset2.cc | 9 +- test/tsan/mutex_cycle2.c | 8 +- test/tsan/mutexset1.cc | 8 +- test/tsan/mutexset2.cc | 8 +- test/tsan/mutexset3.cc | 8 +- test/tsan/mutexset4.cc | 8 +- test/tsan/mutexset5.cc | 8 +- test/tsan/mutexset6.cc | 8 +- test/tsan/mutexset7.cc | 8 +- test/tsan/mutexset8.cc | 8 +- test/tsan/process_sleep.h | 7 - test/tsan/pthread_atfork_deadlock.c | 8 +- test/tsan/race_on_barrier.c | 9 +- test/tsan/race_on_mutex.c | 13 +- test/tsan/race_on_mutex2.c | 9 +- test/tsan/race_on_puts.cc | 7 +- test/tsan/race_on_read.cc | 20 +- test/tsan/race_on_speculative_load.cc | 11 +- test/tsan/race_on_write.cc | 9 +- test/tsan/race_with_finished_thread.cc | 11 +- .../real_deadlock_detector_stress_test.cc | 186 ++++ test/tsan/restore_stack.cc | 50 + test/tsan/signal_errno.cc | 9 +- test/tsan/signal_malloc.cc | 6 +- test/tsan/signal_recursive.cc | 30 +- test/tsan/signal_reset.cc | 74 ++ test/tsan/signal_sync.cc | 7 +- test/tsan/signal_thread.cc | 52 + test/tsan/signal_write.cc | 2 +- test/tsan/simple_race.c | 8 +- test/tsan/simple_race.cc | 8 +- test/tsan/simple_stack.c | 24 +- test/tsan/simple_stack2.cc | 20 +- test/tsan/sleep_sync.cc | 8 +- test/tsan/sleep_sync2.cc | 8 +- test/tsan/stack_race.cc | 8 +- test/tsan/stack_race2.cc | 8 +- test/tsan/stack_sync_reuse.cc | 65 ++ test/tsan/suppress_same_address.cc | 7 +- test/tsan/suppressions_global.cc | 2 +- test/tsan/suppressions_race.cc | 10 +- test/tsan/suppressions_race2.cc | 10 +- test/tsan/test.h | 31 + test/tsan/thread_detach.c | 8 +- test/tsan/thread_leak3.c | 8 +- test/tsan/thread_leak4.c | 10 +- test/tsan/thread_leak5.c | 8 +- test/tsan/thread_name.cc | 8 +- test/tsan/thread_name2.cc | 9 +- test/tsan/tiny_race.c | 7 +- test/tsan/tls_race.cc | 8 +- test/tsan/tls_race2.cc | 8 +- test/tsan/unaligned_norace.cc | 18 +- test/tsan/unaligned_race.cc | 9 +- test/tsan/vptr_harmful_race.cc | 8 +- test/tsan/vptr_harmful_race2.cc | 8 +- test/tsan/vptr_harmful_race3.cc | 8 +- test/tsan/vptr_harmful_race4.cc | 8 +- test/tsan/write_in_reader_lock.cc | 9 +- test/ubsan/TestCases/Float/cast-overflow.cpp | 1 + test/ubsan/TestCases/Integer/no-recover.cpp | 4 +- test/ubsan/TestCases/Misc/coverage-levels.cc | 38 + test/ubsan/TestCases/Misc/nonnull-arg.cpp | 2 +- test/ubsan/TestCases/TypeCheck/misaligned.cpp | 9 +- .../TestCases/TypeCheck/vptr-virtual-base.cpp | 2 +- test/ubsan/TestCases/TypeCheck/vptr.cpp | 19 +- unittests/lit.common.unit.configured.in | 2 +- 365 files changed, 7553 insertions(+), 4071 deletions(-) create mode 100644 include/sanitizer/coverage_interface.h create mode 100644 lib/asan/asan_activation_flags.inc create mode 100644 lib/asan/asan_allocator.cc delete mode 100644 lib/asan/asan_allocator2.cc create mode 100644 lib/asan/asan_flags.cc create mode 100644 lib/asan/asan_flags.inc create mode 100644 lib/dfsan/dfsan_flags.inc create mode 100644 lib/lsan/lsan_flags.inc create mode 100644 lib/msan/msan_flags.inc create mode 100644 lib/msan/msan_poisoning.cc create mode 100644 lib/msan/msan_poisoning.h create mode 100644 lib/sanitizer_common/sanitizer_flag_parser.cc create mode 100644 lib/sanitizer_common/sanitizer_flag_parser.h create mode 100644 lib/sanitizer_common/sanitizer_flags.inc create mode 100644 lib/tsan/rtl/tsan_flags.inc create mode 100644 lib/ubsan/ubsan_flags.inc delete mode 100644 make/platform/darwin_fat.mk create mode 100644 test/asan/TestCases/Darwin/address-range-limit.mm create mode 100644 test/asan/TestCases/Darwin/crashlog-stacktraces.c create mode 100644 test/asan/TestCases/Darwin/dyld_insert_libraries_remove.cc create mode 100644 test/asan/TestCases/Darwin/linked-only.cc create mode 100644 test/asan/TestCases/Darwin/mixing-global-constructors.cc create mode 100644 test/asan/TestCases/Linux/coverage-direct-activation.cc create mode 100644 test/asan/TestCases/Linux/coverage-reset.cc create mode 100644 test/asan/TestCases/Linux/nohugepage_test.cc create mode 100644 test/asan/TestCases/Linux/quarantine_size_mb.cc create mode 100644 test/asan/TestCases/Linux/stack-overflow-sigbus.cc create mode 100644 test/asan/TestCases/Windows/iostream_sbo.cc create mode 100644 test/asan/TestCases/Windows/shadow_mapping_failure.cc create mode 100644 test/asan/TestCases/Windows/thread_suspended.cc create mode 100644 test/asan/TestCases/asan_options-help.cc create mode 100644 test/msan/origin-store-long.cc create mode 100644 test/msan/realloc-large-origin.cc create mode 100644 test/msan/realloc-origin.cc create mode 100644 test/sanitizer_common/TestCases/Linux/hard_rss_limit_mb_test.cc create mode 100644 test/sanitizer_common/TestCases/Linux/sanitizer_set_death_callback_test.cc create mode 100644 test/sanitizer_common/TestCases/Linux/sched_getparam.cc create mode 100644 test/sanitizer_common/TestCases/Linux/soft_rss_limit_mb_test.cc create mode 100644 test/sanitizer_common/TestCases/options-help.cc create mode 100644 test/sanitizer_common/TestCases/options-include.cc create mode 100644 test/sanitizer_common/TestCases/options-invalid.cc create mode 100644 test/tsan/annotate_happens_before.cc create mode 100644 test/tsan/java_volatile.cc delete mode 100644 test/tsan/process_sleep.h create mode 100644 test/tsan/real_deadlock_detector_stress_test.cc create mode 100644 test/tsan/restore_stack.cc create mode 100644 test/tsan/signal_reset.cc create mode 100644 test/tsan/signal_thread.cc create mode 100644 test/tsan/stack_sync_reuse.cc create mode 100644 test/tsan/test.h create mode 100644 test/ubsan/TestCases/Misc/coverage-levels.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index a6a4ed96105e..f3485853f0f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,7 +104,8 @@ else() # Get some LLVM variables from LLVMConfig. include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake") - set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib) + set(LLVM_LIBRARY_OUTPUT_INTDIR + ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX}) # Find Python interpreter. set(Python_ADDITIONAL_VERSIONS 2.7 2.6 2.5) @@ -162,29 +163,6 @@ set(COMPILER_RT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) # Setup custom SDK sysroots. set(COMPILER_RT_LINUX_SDK_SYSROOT ${COMPILER_RT_SOURCE_DIR}/SDKs/linux) -# 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 (NOT CMAKE_SIZEOF_VOID_P EQUAL 4 AND - NOT CMAKE_SIZEOF_VOID_P EQUAL 8) - message(FATAL_ERROR "Please use architecture with 4 or 8 byte pointers.") -endif() -if (NOT MSVC) - set(TARGET_64_BIT_CFLAGS "-m64") - set(TARGET_32_BIT_CFLAGS "-m32") -else() - set(TARGET_64_BIT_CFLAGS "") - set(TARGET_32_BIT_CFLAGS "") -endif() - -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() - # We support running instrumented tests when we're not cross compiling # and target a UNIX-like system or Windows. # We can run tests on Android even when we are cross-compiling. @@ -199,17 +177,12 @@ option(COMPILER_RT_DEBUG "Build runtimes with full debug info" OFF) # COMPILER_RT_DEBUG_PYBOOL is used by lit.common.configured.in. pythonize_bool(COMPILER_RT_DEBUG) -# We have to support both static and dynamic/shared runtime on Windows. -# Android only works with dynamic runtime. -if(WIN32 OR ANDROID) -option(COMPILER_RT_BUILD_SHARED_ASAN "Build shared version of AddressSanitizer runtime" ON) -else() -option(COMPILER_RT_BUILD_SHARED_ASAN "Build shared version of AddressSanitizer runtime" OFF) -endif() - #================================ # Setup Compiler Flags #================================ +include(CheckIncludeFile) +check_include_file(unwind.h HAVE_UNWIND_H) + include(config-ix) if(MSVC) @@ -238,6 +211,7 @@ append_list_if(COMPILER_RT_HAS_FUNWIND_TABLES_FLAG -funwind-tables SANITIZER_COM append_list_if(COMPILER_RT_HAS_FNO_STACK_PROTECTOR_FLAG -fno-stack-protector SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG -fvisibility=hidden SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_FNO_FUNCTION_SECTIONS_FLAG -fno-function-sections SANITIZER_COMMON_CFLAGS) +append_list_if(COMPILER_RT_HAS_FNO_LTO_FLAG -fno-lto SANITIZER_COMMON_CFLAGS) if(MSVC) # Replace the /MD[d] flags with /MT. @@ -257,14 +231,28 @@ if(MSVC) append_list_if(COMPILER_RT_HAS_GS_FLAG /GS- SANITIZER_COMMON_CFLAGS) endif() +append_list_if(COMPILER_RT_DEBUG -DSANITIZER_DEBUG=1 SANITIZER_COMMON_CFLAGS) + # Build with optimization, unless we're in debug mode. If we're using MSVC, # always respect the optimization flags set by CMAKE_BUILD_TYPE instead. if(NOT COMPILER_RT_DEBUG AND NOT MSVC) list(APPEND SANITIZER_COMMON_CFLAGS -O3) endif() +# Determine if we should restrict stack frame sizes. +# Stack frames on PowerPC and in debug biuld can be much larger than +# anticipated. +# FIXME: Fix all sanitizers and add -Wframe-larger-than to +# SANITIZER_COMMON_FLAGS +if(COMPILER_RT_HAS_WFRAME_LARGER_THAN_FLAG AND NOT COMPILER_RT_DEBUG + AND NOT ${LLVM_NATIVE_ARCH} STREQUAL "PowerPC") + set(SANITIZER_LIMIT_FRAME_SIZE TRUE) +else() + set(SANITIZER_LIMIT_FRAME_SIZE FALSE) +endif() + # Build sanitizer runtimes with debug info. -if(COMPILER_RT_HAS_GLINE_TABLES_ONLY_FLAG) +if(COMPILER_RT_HAS_GLINE_TABLES_ONLY_FLAG AND NOT COMPILER_RT_DEBUG) list(APPEND SANITIZER_COMMON_CFLAGS -gline-tables-only) elseif(COMPILER_RT_HAS_G_FLAG) list(APPEND SANITIZER_COMMON_CFLAGS -g) @@ -283,12 +271,27 @@ append_list_if(COMPILER_RT_HAS_WD4391_FLAG /wd4391 SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_WD4722_FLAG /wd4722 SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_WD4800_FLAG /wd4800 SANITIZER_COMMON_CFLAGS) if(APPLE) - # Obtain the iOS Simulator SDK path from xcodebuild. - execute_process( - COMMAND xcodebuild -version -sdk iphonesimulator Path - OUTPUT_VARIABLE IOSSIM_SDK_DIR - OUTPUT_STRIP_TRAILING_WHITESPACE - ) + macro(find_darwin_sdk_dir var sdk_name) + # Let's first try the internal SDK, otherwise use the public SDK. + execute_process( + COMMAND xcodebuild -version -sdk ${sdk_name}.internal Path + OUTPUT_VARIABLE ${var} + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_FILE /dev/null + ) + if(${var} STREQUAL "") + execute_process( + COMMAND xcodebuild -version -sdk ${sdk_name} Path + OUTPUT_VARIABLE ${var} + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_FILE /dev/null + ) + endif() + endmacro() + + find_darwin_sdk_dir(OSX_SDK_DIR macosx) + find_darwin_sdk_dir(IOSSIM_SDK_DIR iphonesimulator) + string(REGEX MATCH "-mmacosx-version-min=" MACOSX_VERSION_MIN_FLAG "${CMAKE_CXX_FLAGS}") set(SANITIZER_COMMON_SUPPORTED_DARWIN_OS osx) @@ -298,10 +301,12 @@ if(APPLE) set(SANITIZER_MIN_OSX_VERSION 10.7) set(CMAKE_OSX_DEPLOYMENT_TARGET "") # We're setting the flag manually below. - set(DARWIN_osx_CFLAGS -mmacosx-version-min=${SANITIZER_MIN_OSX_VERSION}) + set(DARWIN_osx_CFLAGS -mmacosx-version-min=${SANITIZER_MIN_OSX_VERSION} + -isysroot ${OSX_SDK_DIR} -stdlib=libc++) set(DARWIN_iossim_CFLAGS -mios-simulator-version-min=7.0 -isysroot ${IOSSIM_SDK_DIR}) - set(DARWIN_osx_LINKFLAGS) + set(DARWIN_osx_LINKFLAGS -mmacosx-version-min=${SANITIZER_MIN_OSX_VERSION} + -isysroot ${OSX_SDK_DIR} -stdlib=libc++) set(DARWIN_iossim_LINKFLAGS -Wl,-ios_simulator_version_min,7.0.0 -mios-simulator-version-min=7.0 diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake index 3edd854fdee7..a7782a194847 100644 --- a/cmake/Modules/AddCompilerRT.cmake +++ b/cmake/Modules/AddCompilerRT.cmake @@ -131,10 +131,11 @@ set(COMPILER_RT_GTEST_CFLAGS -I${COMPILER_RT_GTEST_PATH} ) +append_list_if(COMPILER_RT_DEBUG -DSANITIZER_DEBUG=1 COMPILER_RT_TEST_CFLAGS) + if(MSVC) # clang doesn't support exceptions on Windows yet. - list(APPEND COMPILER_RT_TEST_CFLAGS - -D_HAS_EXCEPTIONS=0) + list(APPEND COMPILER_RT_TEST_CFLAGS -D_HAS_EXCEPTIONS=0) # We should teach clang to understand "#pragma intrinsic", see PR19898. list(APPEND COMPILER_RT_TEST_CFLAGS -Wno-undefined-inline) @@ -156,12 +157,20 @@ endif() # using specified link flags. Make executable a part of provided # test_suite. # add_compiler_rt_test( +# SUBDIR # OBJECTS # DEPS # LINK_FLAGS ) macro(add_compiler_rt_test test_suite test_name) - parse_arguments(TEST "OBJECTS;DEPS;LINK_FLAGS" "" ${ARGN}) - set(output_bin "${CMAKE_CURRENT_BINARY_DIR}/${test_name}") + parse_arguments(TEST "SUBDIR;OBJECTS;DEPS;LINK_FLAGS" "" ${ARGN}) + if(TEST_SUBDIR) + set(output_bin "${CMAKE_CURRENT_BINARY_DIR}/${TEST_SUBDIR}/${test_name}") + else() + set(output_bin "${CMAKE_CURRENT_BINARY_DIR}/${test_name}") + endif() + if(MSVC) + set(output_bin "${output_bin}.exe") + endif() # Use host compiler in a standalone build, and just-built Clang otherwise. if(NOT COMPILER_RT_STANDALONE_BUILD) list(APPEND TEST_DEPS clang) diff --git a/cmake/Modules/CompilerRTCompile.cmake b/cmake/Modules/CompilerRTCompile.cmake index af3df8ff4f44..de73ccfc5cb8 100644 --- a/cmake/Modules/CompilerRTCompile.cmake +++ b/cmake/Modules/CompilerRTCompile.cmake @@ -9,7 +9,7 @@ macro(clang_compile object_file source) parse_arguments(SOURCE "CFLAGS;DEPS" "" ${ARGN}) get_filename_component(source_rpath ${source} REALPATH) if(NOT COMPILER_RT_STANDALONE_BUILD) - list(APPEND SOURCE_DEPS clang) + list(APPEND SOURCE_DEPS clang compiler-rt-headers) endif() if (TARGET CompilerRTUnitTestCheckCxx) list(APPEND SOURCE_DEPS CompilerRTUnitTestCheckCxx) diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake index 5f921bf97f5c..c8c01e96b3a1 100644 --- a/cmake/config-ix.cmake +++ b/cmake/config-ix.cmake @@ -1,6 +1,7 @@ include(CheckCXXCompilerFlag) include(CheckLibraryExists) include(CheckSymbolExists) +include(TestBigEndian) # CodeGen options. check_cxx_compiler_flag(-fPIC COMPILER_RT_HAS_FPIC_FLAG) @@ -16,6 +17,8 @@ check_cxx_compiler_flag(-ffreestanding COMPILER_RT_HAS_FFREESTANDING_FLAG) check_cxx_compiler_flag("-Werror -fno-function-sections" COMPILER_RT_HAS_FNO_FUNCTION_SECTIONS_FLAG) check_cxx_compiler_flag(-std=c++11 COMPILER_RT_HAS_STD_CXX11_FLAG) check_cxx_compiler_flag(-ftls-model=initial-exec COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC) +check_cxx_compiler_flag(-fno-lto COMPILER_RT_HAS_FNO_LTO_FLAG) +check_cxx_compiler_flag(-msse3 COMPILER_RT_HAS_MSSE3_FLAG) check_cxx_compiler_flag(/GR COMPILER_RT_HAS_GR_FLAG) check_cxx_compiler_flag(/GS COMPILER_RT_HAS_GS_FLAG) @@ -26,7 +29,7 @@ check_cxx_compiler_flag(/Oy COMPILER_RT_HAS_Oy_FLAG) check_cxx_compiler_flag(-gline-tables-only COMPILER_RT_HAS_GLINE_TABLES_ONLY_FLAG) check_cxx_compiler_flag(-g COMPILER_RT_HAS_G_FLAG) check_cxx_compiler_flag(/Zi COMPILER_RT_HAS_Zi_FLAG) - + # Warnings. check_cxx_compiler_flag(-Wall COMPILER_RT_HAS_WALL_FLAG) check_cxx_compiler_flag(-Werror COMPILER_RT_HAS_WERROR_FLAG) @@ -120,6 +123,13 @@ macro(detect_target_arch) endif() endmacro() +# 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 (NOT CMAKE_SIZEOF_VOID_P EQUAL 4 AND + NOT CMAKE_SIZEOF_VOID_P EQUAL 8) + message(FATAL_ERROR "Please use architecture with 4 or 8 byte pointers.") +endif() + # Generate the COMPILER_RT_SUPPORTED_ARCH list. if(ANDROID) # Can't rely on LLVM_NATIVE_ARCH in cross-compilation. @@ -128,28 +138,34 @@ if(ANDROID) set(COMPILER_RT_OS_SUFFIX "-android") else() if("${LLVM_NATIVE_ARCH}" STREQUAL "X86") - if (NOT MSVC) - test_target_arch(x86_64 ${TARGET_64_BIT_CFLAGS}) + if(NOT MSVC) + test_target_arch(x86_64 "-m64") + test_target_arch(i386 "-m32") + else() + test_target_arch(i386 "") endif() - test_target_arch(i386 ${TARGET_32_BIT_CFLAGS}) elseif("${LLVM_NATIVE_ARCH}" STREQUAL "PowerPC") - test_target_arch(powerpc64 ${TARGET_64_BIT_CFLAGS}) - test_target_arch(powerpc64le ${TARGET_64_BIT_CFLAGS}) + TEST_BIG_ENDIAN(HOST_IS_BIG_ENDIAN) + if(HOST_IS_BIG_ENDIAN) + test_target_arch(powerpc64 "-m64") + else() + test_target_arch(powerpc64le "-m64") + endif() elseif("${LLVM_NATIVE_ARCH}" STREQUAL "Mips") if("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "mipsel|mips64el") # regex for mipsel, mips64el - test_target_arch(mipsel ${TARGET_32_BIT_CFLAGS}) - test_target_arch(mips64el ${TARGET_64_BIT_CFLAGS}) + test_target_arch(mipsel "-m32") + test_target_arch(mips64el "-m64") else() - test_target_arch(mips ${TARGET_32_BIT_CFLAGS}) - test_target_arch(mips64 ${TARGET_64_BIT_CFLAGS}) + test_target_arch(mips "-m32") + test_target_arch(mips64 "-m64") endif() elseif("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "arm") test_target_arch(arm "-march=armv7-a") elseif("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "aarch32") test_target_arch(aarch32 "-march=armv8-a") elseif("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "aarch64") - test_target_arch(aarch64 "-march=aarch64") + test_target_arch(aarch64 "-march=armv8-a") endif() set(COMPILER_RT_OS_SUFFIX "") endif() @@ -168,7 +184,16 @@ function(filter_available_targets out_var) set(${out_var} ${archs} PARENT_SCOPE) endfunction() -# Arhcitectures supported by compiler-rt libraries. +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() + +# Architectures supported by compiler-rt libraries. filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH x86_64 i386 i686 powerpc64 powerpc64le arm aarch64 mips mips64 mipsel mips64el) filter_available_targets(ASAN_SUPPORTED_ARCH @@ -183,7 +208,7 @@ filter_available_targets(MSAN_SUPPORTED_ARCH x86_64 mips64 mips64el) filter_available_targets(PROFILE_SUPPORTED_ARCH x86_64 i386 i686 arm mips mips64 mipsel mips64el aarch64 powerpc64 powerpc64le) filter_available_targets(TSAN_SUPPORTED_ARCH x86_64) -filter_available_targets(UBSAN_SUPPORTED_ARCH x86_64 i386 i686 arm aarch64 mips mipsel) +filter_available_targets(UBSAN_SUPPORTED_ARCH x86_64 i386 i686 arm aarch64 mips mipsel mips64 mips64el) if(ANDROID) set(OS_NAME "Android") @@ -205,6 +230,12 @@ else() set(COMPILER_RT_HAS_ASAN FALSE) endif() +if (OS_NAME MATCHES "Linux|FreeBSD|Windows") + set(COMPILER_RT_ASAN_HAS_STATIC_RUNTIME TRUE) +else() + set(COMPILER_RT_ASAN_HAS_STATIC_RUNTIME FALSE) +endif() + # TODO: Add builtins support. if (COMPILER_RT_HAS_SANITIZER_COMMON AND DFSAN_SUPPORTED_ARCH AND diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 7f8664e09970..ad1437ed15ec 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -2,6 +2,7 @@ set(SANITIZER_HEADERS sanitizer/allocator_interface.h sanitizer/asan_interface.h sanitizer/common_interface_defs.h + sanitizer/coverage_interface.h sanitizer/dfsan_interface.h sanitizer/linux_syscall_hooks.h sanitizer/lsan_interface.h diff --git a/include/sanitizer/asan_interface.h b/include/sanitizer/asan_interface.h index 435391486aaa..7763389ab257 100644 --- a/include/sanitizer/asan_interface.h +++ b/include/sanitizer/asan_interface.h @@ -114,8 +114,7 @@ extern "C" { // Returns the old value. int __asan_set_error_exit_code(int exit_code); - // Sets the callback to be called right before death on error. - // Passing 0 will unset the callback. + // Deprecated. Call __sanitizer_set_death_callback instead. void __asan_set_death_callback(void (*callback)(void)); void __asan_set_error_report_callback(void (*callback)(const char*)); diff --git a/include/sanitizer/common_interface_defs.h b/include/sanitizer/common_interface_defs.h index 9cb5ad88de6a..ef645e527119 100644 --- a/include/sanitizer/common_interface_defs.h +++ b/include/sanitizer/common_interface_defs.h @@ -62,18 +62,6 @@ extern "C" { void __sanitizer_unaligned_store32(void *p, uint32_t x); void __sanitizer_unaligned_store64(void *p, uint64_t x); - // Initialize coverage. - void __sanitizer_cov_init(); - // Record and dump coverage info. - void __sanitizer_cov_dump(); - // Open .sancov.packed in the coverage directory and return the file - // descriptor. Returns -1 on failure, or if coverage dumping is disabled. - // This is intended for use by sandboxing code. - intptr_t __sanitizer_maybe_open_cov_file(const char *name); - // Get the number of total unique covered entities (blocks, edges, calls). - // This can be useful for coverage-directed in-process fuzzers. - uintptr_t __sanitizer_get_total_unique_coverage(); - // Annotate the current state of a contiguous container, such as // std::vector, std::string or similar. // A contiguous container is a container that keeps all of its elements @@ -120,6 +108,9 @@ extern "C" { // Print the stack trace leading to this call. Useful for debugging user code. void __sanitizer_print_stack_trace(); + // Sets the callback to be called right before death on error. + // Passing 0 will unset the callback. + void __sanitizer_set_death_callback(void (*callback)(void)); #ifdef __cplusplus } // extern "C" #endif diff --git a/include/sanitizer/coverage_interface.h b/include/sanitizer/coverage_interface.h new file mode 100644 index 000000000000..88a7e480081d --- /dev/null +++ b/include/sanitizer/coverage_interface.h @@ -0,0 +1,46 @@ +//===-- sanitizer/coverage_interface.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Public interface for sanitizer coverage. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_COVERAG_INTERFACE_H +#define SANITIZER_COVERAG_INTERFACE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + // Initialize coverage. + void __sanitizer_cov_init(); + // Record and dump coverage info. + void __sanitizer_cov_dump(); + // Open .sancov.packed in the coverage directory and return the file + // descriptor. Returns -1 on failure, or if coverage dumping is disabled. + // This is intended for use by sandboxing code. + intptr_t __sanitizer_maybe_open_cov_file(const char *name); + // Get the number of total unique covered entities (blocks, edges, calls). + // This can be useful for coverage-directed in-process fuzzers. + uintptr_t __sanitizer_get_total_unique_coverage(); + + // Reset the basic-block (edge) coverage to the initial state. + // Useful for in-process fuzzing to start collecting coverage from scratch. + // Experimental, will likely not work for multi-threaded process. + void __sanitizer_reset_coverage(); + // Set *data to the array of covered PCs and return the size of that array. + // Some of the entries in *data will be zero. + uintptr_t __sanitizer_get_coverage_guards(uintptr_t **data); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SANITIZER_COVERAG_INTERFACE_H diff --git a/include/sanitizer/msan_interface.h b/include/sanitizer/msan_interface.h index 5be586047ced..c3a19bf345ac 100644 --- a/include/sanitizer/msan_interface.h +++ b/include/sanitizer/msan_interface.h @@ -38,7 +38,9 @@ extern "C" { contents). */ void __msan_unpoison_string(const volatile char *a); - /* Make memory region fully uninitialized (without changing its contents). */ + /* Make memory region fully uninitialized (without changing its contents). + This is a legacy interface that does not update origin information. Use + __msan_allocated_memory() instead. */ void __msan_poison(const volatile void *a, size_t size); /* Make memory region partially uninitialized (without changing its contents). diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt index 47486b7caf89..d4c5c17d36a2 100644 --- a/lib/asan/CMakeLists.txt +++ b/lib/asan/CMakeLists.txt @@ -1,10 +1,11 @@ # Build for the AddressSanitizer runtime support library. set(ASAN_SOURCES - asan_allocator2.cc + asan_allocator.cc asan_activation.cc asan_debugging.cc asan_fake_stack.cc + asan_flags.cc asan_globals.cc asan_interceptors.cc asan_linux.cc @@ -64,8 +65,8 @@ if(APPLE) add_compiler_rt_darwin_object_library(RTAsan ${os} ARCH ${ASAN_SUPPORTED_ARCH} SOURCES ${ASAN_SOURCES} ${ASAN_CXX_SOURCES} - CFLAGS ${ASAN_CFLAGS} - DEFS ${ASAN_COMMON_DEFINITIONS}) + CFLAGS ${ASAN_DYNAMIC_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) endforeach() else() foreach(arch ${ASAN_SUPPORTED_ARCH}) @@ -78,12 +79,10 @@ else() add_compiler_rt_object_library(RTAsan_preinit ${arch} SOURCES ${ASAN_PREINIT_SOURCES} CFLAGS ${ASAN_CFLAGS} DEFS ${ASAN_COMMON_DEFINITIONS}) - if (COMPILER_RT_BUILD_SHARED_ASAN) - add_compiler_rt_object_library(RTAsan_dynamic ${arch} - SOURCES ${ASAN_SOURCES} ${ASAN_CXX_SOURCES} - CFLAGS ${ASAN_DYNAMIC_CFLAGS} - DEFS ${ASAN_DYNAMIC_DEFINITIONS}) - endif() + add_compiler_rt_object_library(RTAsan_dynamic ${arch} + SOURCES ${ASAN_SOURCES} ${ASAN_CXX_SOURCES} + CFLAGS ${ASAN_DYNAMIC_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) endforeach() endif() @@ -97,8 +96,8 @@ if(APPLE) $ $ $ - CFLAGS ${ASAN_CFLAGS} - DEFS ${ASAN_COMMON_DEFINITIONS}) + CFLAGS ${ASAN_DYNAMIC_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) add_dependencies(asan clang_rt.asan_${os}_dynamic) endforeach() else() @@ -128,28 +127,25 @@ else() DEFS ${ASAN_COMMON_DEFINITIONS}) add_dependencies(asan clang_rt.asan_cxx-${arch}) - if (COMPILER_RT_BUILD_SHARED_ASAN) - add_compiler_rt_runtime(clang_rt.asan-preinit-${arch} ${arch} STATIC - SOURCES $ - CFLAGS ${ASAN_CFLAGS} - DEFS ${ASAN_COMMON_DEFINITIONS}) - add_dependencies(asan clang_rt.asan-preinit-${arch}) + add_compiler_rt_runtime(clang_rt.asan-preinit-${arch} ${arch} STATIC + SOURCES $ + CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + add_dependencies(asan clang_rt.asan-preinit-${arch}) - if (WIN32) - set(SHARED_ASAN_NAME clang_rt.asan_dynamic-${arch}${COMPILER_RT_OS_SUFFIX}) - else() - set(SHARED_ASAN_NAME clang_rt.asan-${arch}${COMPILER_RT_OS_SUFFIX}) - endif() - - add_compiler_rt_runtime(clang_rt.asan-dynamic-${arch} ${arch} SHARED - OUTPUT_NAME ${SHARED_ASAN_NAME} - SOURCES $ - ${ASAN_COMMON_RUNTIME_OBJECTS} - CFLAGS ${ASAN_DYNAMIC_CFLAGS} - DEFS ${ASAN_DYNAMIC_DEFINITIONS}) - target_link_libraries(clang_rt.asan-dynamic-${arch} ${ASAN_DYNAMIC_LIBS}) - add_dependencies(asan clang_rt.asan-dynamic-${arch}) + if (WIN32) + set(SHARED_ASAN_NAME clang_rt.asan_dynamic-${arch}${COMPILER_RT_OS_SUFFIX}) + else() + set(SHARED_ASAN_NAME clang_rt.asan-${arch}${COMPILER_RT_OS_SUFFIX}) endif() + add_compiler_rt_runtime(clang_rt.asan-dynamic-${arch} ${arch} SHARED + OUTPUT_NAME ${SHARED_ASAN_NAME} + SOURCES $ + ${ASAN_COMMON_RUNTIME_OBJECTS} + CFLAGS ${ASAN_DYNAMIC_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) + target_link_libraries(clang_rt.asan-dynamic-${arch} ${ASAN_DYNAMIC_LIBS}) + add_dependencies(asan clang_rt.asan-dynamic-${arch}) if (UNIX AND NOT ${arch} STREQUAL "i386" AND NOT ${arch} STREQUAL "i686") add_sanitizer_rt_symbols(clang_rt.asan_cxx-${arch}) diff --git a/lib/asan/asan_activation.cc b/lib/asan/asan_activation.cc index eb4a6db0b85e..3bc01984898d 100644 --- a/lib/asan/asan_activation.cc +++ b/lib/asan/asan_activation.cc @@ -16,40 +16,106 @@ #include "asan_allocator.h" #include "asan_flags.h" #include "asan_internal.h" +#include "asan_poisoning.h" +#include "asan_stack.h" #include "sanitizer_common/sanitizer_flags.h" namespace __asan { static struct AsanDeactivatedFlags { - int quarantine_size; - int max_redzone; + AllocatorOptions allocator_options; int malloc_context_size; bool poison_heap; - bool alloc_dealloc_mismatch; - bool allocator_may_return_null; + bool coverage; + const char *coverage_dir; + + void RegisterActivationFlags(FlagParser *parser, Flags *f, CommonFlags *cf) { +#define ASAN_ACTIVATION_FLAG(Type, Name) \ + RegisterFlag(parser, #Name, "", &f->Name); +#define COMMON_ACTIVATION_FLAG(Type, Name) \ + RegisterFlag(parser, #Name, "", &cf->Name); +#include "asan_activation_flags.inc" +#undef ASAN_ACTIVATION_FLAG +#undef COMMON_ACTIVATION_FLAG + + RegisterIncludeFlag(parser, cf); + } + + void OverrideFromActivationFlags() { + Flags f; + CommonFlags cf; + FlagParser parser; + RegisterActivationFlags(&parser, &f, &cf); + + // Copy the current activation flags. + allocator_options.CopyTo(&f, &cf); + cf.malloc_context_size = malloc_context_size; + f.poison_heap = poison_heap; + cf.coverage = coverage; + cf.coverage_dir = coverage_dir; + cf.verbosity = Verbosity(); + cf.help = false; // this is activation-specific help + + // Check if activation flags need to be overriden. + if (const char *env = GetEnv("ASAN_ACTIVATION_OPTIONS")) { + parser.ParseString(env); + } + + // Override from getprop asan.options. + char buf[100]; + GetExtraActivationFlags(buf, sizeof(buf)); + parser.ParseString(buf); + + SetVerbosity(cf.verbosity); + + if (Verbosity()) ReportUnrecognizedFlags(); + + if (cf.help) parser.PrintFlagDescriptions(); + + allocator_options.SetFrom(&f, &cf); + malloc_context_size = cf.malloc_context_size; + poison_heap = f.poison_heap; + coverage = cf.coverage; + coverage_dir = cf.coverage_dir; + } + + void Print() { + Report( + "quarantine_size_mb %d, max_redzone %d, poison_heap %d, " + "malloc_context_size %d, alloc_dealloc_mismatch %d, " + "allocator_may_return_null %d, coverage %d, coverage_dir %s\n", + allocator_options.quarantine_size_mb, allocator_options.max_redzone, + poison_heap, malloc_context_size, + allocator_options.alloc_dealloc_mismatch, + allocator_options.may_return_null, coverage, coverage_dir); + } } asan_deactivated_flags; static bool asan_is_deactivated; -void AsanStartDeactivated() { +void AsanDeactivate() { + CHECK(!asan_is_deactivated); VReport(1, "Deactivating ASan\n"); - // Save flag values. - asan_deactivated_flags.quarantine_size = flags()->quarantine_size; - asan_deactivated_flags.max_redzone = flags()->max_redzone; - asan_deactivated_flags.poison_heap = flags()->poison_heap; - asan_deactivated_flags.malloc_context_size = - common_flags()->malloc_context_size; - asan_deactivated_flags.alloc_dealloc_mismatch = - flags()->alloc_dealloc_mismatch; - asan_deactivated_flags.allocator_may_return_null = - common_flags()->allocator_may_return_null; - flags()->quarantine_size = 0; - flags()->max_redzone = 16; - flags()->poison_heap = false; - common_flags()->malloc_context_size = 0; - flags()->alloc_dealloc_mismatch = false; - common_flags()->allocator_may_return_null = true; + // Stash runtime state. + GetAllocatorOptions(&asan_deactivated_flags.allocator_options); + asan_deactivated_flags.malloc_context_size = GetMallocContextSize(); + asan_deactivated_flags.poison_heap = CanPoisonMemory(); + asan_deactivated_flags.coverage = common_flags()->coverage; + asan_deactivated_flags.coverage_dir = common_flags()->coverage_dir; + + // Deactivate the runtime. + SetCanPoisonMemory(false); + SetMallocContextSize(1); + ReInitializeCoverage(false, nullptr); + + AllocatorOptions disabled = asan_deactivated_flags.allocator_options; + disabled.quarantine_size_mb = 0; + disabled.min_redzone = 16; // Redzone must be at least 16 bytes long. + disabled.max_redzone = 16; + disabled.alloc_dealloc_mismatch = false; + disabled.may_return_null = true; + ReInitializeAllocator(disabled); asan_is_deactivated = true; } @@ -58,31 +124,19 @@ void AsanActivate() { if (!asan_is_deactivated) return; VReport(1, "Activating ASan\n"); - // Restore flag values. - // FIXME: this is not atomic, and there may be other threads alive. - flags()->quarantine_size = asan_deactivated_flags.quarantine_size; - flags()->max_redzone = asan_deactivated_flags.max_redzone; - flags()->poison_heap = asan_deactivated_flags.poison_heap; - common_flags()->malloc_context_size = - asan_deactivated_flags.malloc_context_size; - flags()->alloc_dealloc_mismatch = - asan_deactivated_flags.alloc_dealloc_mismatch; - common_flags()->allocator_may_return_null = - asan_deactivated_flags.allocator_may_return_null; + asan_deactivated_flags.OverrideFromActivationFlags(); - ParseExtraActivationFlags(); - - ReInitializeAllocator(); + SetCanPoisonMemory(asan_deactivated_flags.poison_heap); + SetMallocContextSize(asan_deactivated_flags.malloc_context_size); + ReInitializeCoverage(asan_deactivated_flags.coverage, + asan_deactivated_flags.coverage_dir); + ReInitializeAllocator(asan_deactivated_flags.allocator_options); asan_is_deactivated = false; - VReport( - 1, - "quarantine_size %d, max_redzone %d, poison_heap %d, " - "malloc_context_size %d, alloc_dealloc_mismatch %d, " - "allocator_may_return_null %d\n", - flags()->quarantine_size, flags()->max_redzone, flags()->poison_heap, - common_flags()->malloc_context_size, flags()->alloc_dealloc_mismatch, - common_flags()->allocator_may_return_null); + if (Verbosity()) { + Report("Activated with flags:\n"); + asan_deactivated_flags.Print(); + } } } // namespace __asan diff --git a/lib/asan/asan_activation.h b/lib/asan/asan_activation.h index dafb840a6042..d5e1ce433001 100644 --- a/lib/asan/asan_activation.h +++ b/lib/asan/asan_activation.h @@ -16,7 +16,7 @@ #define ASAN_ACTIVATION_H namespace __asan { -void AsanStartDeactivated(); +void AsanDeactivate(); void AsanActivate(); } // namespace __asan diff --git a/lib/asan/asan_activation_flags.inc b/lib/asan/asan_activation_flags.inc new file mode 100644 index 000000000000..d4c089ec6538 --- /dev/null +++ b/lib/asan/asan_activation_flags.inc @@ -0,0 +1,35 @@ +//===-- asan_activation_flags.inc -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A subset of ASan (and common) runtime flags supported at activation time. +// +//===----------------------------------------------------------------------===// +#ifndef ASAN_ACTIVATION_FLAG +# error "Define ASAN_ACTIVATION_FLAG prior to including this file!" +#endif + +#ifndef COMMON_ACTIVATION_FLAG +# error "Define COMMON_ACTIVATION_FLAG prior to including this file!" +#endif + +// ASAN_ACTIVATION_FLAG(Type, Name) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +ASAN_ACTIVATION_FLAG(int, redzone) +ASAN_ACTIVATION_FLAG(int, max_redzone) +ASAN_ACTIVATION_FLAG(int, quarantine_size_mb) +ASAN_ACTIVATION_FLAG(bool, alloc_dealloc_mismatch) +ASAN_ACTIVATION_FLAG(bool, poison_heap) + +COMMON_ACTIVATION_FLAG(bool, allocator_may_return_null) +COMMON_ACTIVATION_FLAG(int, malloc_context_size) +COMMON_ACTIVATION_FLAG(bool, coverage) +COMMON_ACTIVATION_FLAG(const char *, coverage_dir) +COMMON_ACTIVATION_FLAG(int, verbosity) +COMMON_ACTIVATION_FLAG(bool, help) diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc new file mode 100644 index 000000000000..fd63ac68c09e --- /dev/null +++ b/lib/asan/asan_allocator.cc @@ -0,0 +1,909 @@ +//===-- asan_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 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. +// +//===----------------------------------------------------------------------===// +#include "asan_allocator.h" + +#include "asan_mapping.h" +#include "asan_poisoning.h" +#include "asan_report.h" +#include "asan_stack.h" +#include "asan_thread.h" +#include "sanitizer_common/sanitizer_allocator_interface.h" +#include "sanitizer_common/sanitizer_flags.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" +#include "lsan/lsan_common.h" + +namespace __asan { + +// 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 = Log2(rz_size) - 4; + CHECK_EQ(rz_size, RZLog2Size(res)); + return res; +} + +static AsanAllocator &get_allocator(); + +// 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 the left redzone is greater than the ChunkHeader size we store a 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 L L L H H U U U U U U +// | ^ +// ---------------------| +// M -- magic value kAllocBegMagic +// B -- address of ChunkHeader pointing to the first 'H' +static const uptr kAllocBegMagic = 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; + u32 lsan_tag : 2; + // 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. + 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); + +// 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 +}; + +struct AsanChunk: ChunkBase { + uptr Beg() { return reinterpret_cast(this) + kChunkHeaderSize; } + uptr UsedSize(bool locked_version = false) { + if (user_requested_size != SizeClassMap::kMaxSize) + return user_requested_size; + return *reinterpret_cast( + get_allocator().GetMetaData(AllocBeg(locked_version))); + } + void *AllocBeg(bool locked_version = false) { + if (from_memalign) { + if (locked_version) + return get_allocator().GetBlockBeginFastLocked( + reinterpret_cast(this)); + return get_allocator().GetBlockBegin(reinterpret_cast(this)); + } + return reinterpret_cast(Beg() - RZLog2Size(rz_log)); + } + bool AddrIsInside(uptr addr, bool locked_version = false) { + return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version)); + } +}; + +struct QuarantineCallback { + explicit QuarantineCallback(AllocatorCache *cache) + : cache_(cache) { + } + + void Recycle(AsanChunk *m) { + CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); + atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed); + 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(m->AllocBeg()); + if (p != m) { + uptr *alloc_magic = reinterpret_cast(p); + CHECK_EQ(alloc_magic[0], kAllocBegMagic); + // Clear the magic value, as allocator internals may overwrite the + // contents of deallocated chunk, confusing GetAsanChunk lookup. + alloc_magic[0] = 0; + CHECK_EQ(alloc_magic[1], reinterpret_cast(m)); + } + + // Statistics. + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.real_frees++; + thread_stats.really_freed += m->UsedSize(); + + get_allocator().Deallocate(cache_, p); + } + + void *Allocate(uptr size) { + return get_allocator().Allocate(cache_, size, 1, false); + } + + void Deallocate(void *p) { + get_allocator().Deallocate(cache_, p); + } + + AllocatorCache *cache_; +}; + +typedef Quarantine AsanQuarantine; +typedef AsanQuarantine::Cache QuarantineCache; + +void AsanMapUnmapCallback::OnMap(uptr p, uptr size) const { + PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic); + // Statistics. + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.mmaps++; + thread_stats.mmaped += size; +} +void AsanMapUnmapCallback::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. + FlushUnneededASanShadowMemory(p, size); + // Statistics. + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.munmaps++; + thread_stats.munmaped += size; +} + +// 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); + return &ms->allocator_cache; +} + +QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) { + CHECK(ms); + CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache)); + return reinterpret_cast(ms->quarantine_cache); +} + +void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) { + quarantine_size_mb = f->quarantine_size_mb; + min_redzone = f->redzone; + max_redzone = f->max_redzone; + may_return_null = cf->allocator_may_return_null; + alloc_dealloc_mismatch = f->alloc_dealloc_mismatch; +} + +void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) { + f->quarantine_size_mb = quarantine_size_mb; + f->redzone = min_redzone; + f->max_redzone = max_redzone; + cf->allocator_may_return_null = may_return_null; + f->alloc_dealloc_mismatch = alloc_dealloc_mismatch; +} + +struct Allocator { + static const uptr kMaxAllowedMallocSize = + FIRST_32_SECOND_64(3UL << 30, 64UL << 30); + static const uptr kMaxThreadLocalQuarantine = + FIRST_32_SECOND_64(1 << 18, 1 << 20); + + AsanAllocator allocator; + AsanQuarantine quarantine; + StaticSpinMutex fallback_mutex; + AllocatorCache fallback_allocator_cache; + QuarantineCache fallback_quarantine_cache; + + // ------------------- Options -------------------------- + atomic_uint16_t min_redzone; + atomic_uint16_t max_redzone; + atomic_uint8_t alloc_dealloc_mismatch; + + // ------------------- Initialization ------------------------ + explicit Allocator(LinkerInitialized) + : quarantine(LINKER_INITIALIZED), + fallback_quarantine_cache(LINKER_INITIALIZED) {} + + void CheckOptions(const AllocatorOptions &options) const { + CHECK_GE(options.min_redzone, 16); + CHECK_GE(options.max_redzone, options.min_redzone); + CHECK_LE(options.max_redzone, 2048); + CHECK(IsPowerOfTwo(options.min_redzone)); + CHECK(IsPowerOfTwo(options.max_redzone)); + } + + void SharedInitCode(const AllocatorOptions &options) { + CheckOptions(options); + quarantine.Init((uptr)options.quarantine_size_mb << 20, + kMaxThreadLocalQuarantine); + atomic_store(&alloc_dealloc_mismatch, options.alloc_dealloc_mismatch, + memory_order_release); + atomic_store(&min_redzone, options.min_redzone, memory_order_release); + atomic_store(&max_redzone, options.max_redzone, memory_order_release); + } + + void Initialize(const AllocatorOptions &options) { + allocator.Init(options.may_return_null); + SharedInitCode(options); + } + + void ReInitialize(const AllocatorOptions &options) { + allocator.SetMayReturnNull(options.may_return_null); + SharedInitCode(options); + } + + void GetOptions(AllocatorOptions *options) const { + options->quarantine_size_mb = quarantine.GetSize() >> 20; + options->min_redzone = atomic_load(&min_redzone, memory_order_acquire); + options->max_redzone = atomic_load(&max_redzone, memory_order_acquire); + options->may_return_null = allocator.MayReturnNull(); + options->alloc_dealloc_mismatch = + atomic_load(&alloc_dealloc_mismatch, memory_order_acquire); + } + + // -------------------- Helper methods. ------------------------- + 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; + u32 min_rz = atomic_load(&min_redzone, memory_order_acquire); + u32 max_rz = atomic_load(&max_redzone, memory_order_acquire); + return Min(Max(rz_log, RZSize2Log(min_rz)), RZSize2Log(max_rz)); + } + + // 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. + sptr 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; + } + + // -------------------- Allocation/Deallocation routines --------------- + void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack, + AllocType alloc_type, bool can_fill) { + if (UNLIKELY(!asan_inited)) + AsanInitFromRtl(); + Flags &fl = *flags(); + CHECK(stack); + const uptr min_alignment = SHADOW_GRANULARITY; + if (alignment < min_alignment) + alignment = min_alignment; + if (size == 0) { + // We'd be happy to avoid allocating memory for zero-size requests, but + // some programs/tests depend on this behavior and assume that malloc + // would not return NULL even for zero-size allocations. Moreover, it + // looks like operator new should never return NULL, and results of + // consecutive "new" calls must be different even if the allocated size + // is zero. + size = 1; + } + CHECK(IsPowerOfTwo(alignment)); + uptr rz_log = ComputeRZLog(size); + uptr rz_size = RZLog2Size(rz_log); + uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment); + 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 allocator.ReturnNullOrDie(); + } + + AsanThread *t = GetCurrentThread(); + void *allocated; + bool check_rss_limit = true; + if (t) { + AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); + allocated = + allocator.Allocate(cache, needed_size, 8, false, check_rss_limit); + } else { + SpinMutexLock l(&fallback_mutex); + AllocatorCache *cache = &fallback_allocator_cache; + allocated = + allocator.Allocate(cache, needed_size, 8, false, check_rss_limit); + } + + if (!allocated) + return allocator.ReturnNullOrDie(); + + if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) { + // Heap poisoning is enabled, but the allocator provides an unpoisoned + // chunk. This is possible if CanPoisonMemory() was false for some + // time, for example, due to flags()->start_disabled. + // Anyway, poison the block before using it for anything else. + uptr allocated_size = allocator.GetActuallyAllocatedSize(allocated); + PoisonShadow((uptr)allocated, allocated_size, kAsanHeapLeftRedzoneMagic); + } + + uptr alloc_beg = reinterpret_cast(allocated); + 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(chunk_beg); + 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 (alloc_beg != chunk_beg) { + CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg); + reinterpret_cast(alloc_beg)[0] = kAllocBegMagic; + reinterpret_cast(alloc_beg)[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(allocator.GetMetaData(allocated)); + meta[0] = size; + meta[1] = chunk_beg; + } + + m->alloc_context_id = StackDepotPut(*stack); + + 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 && CanPoisonMemory()) { + u8 *shadow = + (u8 *)MemToShadow(user_beg + size_rounded_down_to_granularity); + *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0; + } + + AsanStats &thread_stats = 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(user_beg); + if (can_fill && fl.max_malloc_fill_size) { + uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size); + REAL(memset)(res, fl.malloc_fill_byte, fill_size); + } +#if CAN_SANITIZE_LEAKS + m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored + : __lsan::kDirectlyLeaked; +#endif + // Must be the last mutation of metadata in this function. + atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release); + ASAN_MALLOC_HOOK(res, size); + return res; + } + + void AtomicallySetQuarantineFlag(AsanChunk *m, void *ptr, + BufferedStackTrace *stack) { + u8 old_chunk_state = CHUNK_ALLOCATED; + // Flip the chunk_state atomically to avoid race on double-free. + if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state, + CHUNK_QUARANTINE, memory_order_acquire)) + ReportInvalidFree(ptr, old_chunk_state, stack); + CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state); + } + + // Expects the chunk to already be marked as quarantined by using + // AtomicallySetQuarantineFlag. + void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack, + AllocType alloc_type) { + CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); + + if (m->alloc_type != alloc_type) { + if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) { + 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 = GetCurrentThread(); + m->free_tid = t ? t->tid() : 0; + m->free_context_id = StackDepotPut(*stack); + // Poison the region. + PoisonShadow(m->Beg(), + RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), + kAsanHeapFreeMagic); + + AsanStats &thread_stats = 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()); + } + } + + void Deallocate(void *ptr, uptr delete_size, BufferedStackTrace *stack, + AllocType alloc_type) { + uptr p = reinterpret_cast(ptr); + if (p == 0) return; + + uptr chunk_beg = p - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast(chunk_beg); + if (delete_size && flags()->new_delete_type_mismatch && + delete_size != m->UsedSize()) { + ReportNewDeleteSizeMismatch(p, delete_size, stack); + } + ASAN_FREE_HOOK(ptr); + // Must mark the chunk as quarantined before any changes to its metadata. + AtomicallySetQuarantineFlag(m, ptr, stack); + QuarantineChunk(m, ptr, stack, alloc_type); + } + + void *Reallocate(void *old_ptr, uptr new_size, BufferedStackTrace *stack) { + CHECK(old_ptr && new_size); + uptr p = reinterpret_cast(old_ptr); + uptr chunk_beg = p - kChunkHeaderSize; + AsanChunk *m = reinterpret_cast(chunk_beg); + + AsanStats &thread_stats = GetCurrentThreadStats(); + thread_stats.reallocs++; + thread_stats.realloced += new_size; + + void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); + if (new_ptr) { + u8 chunk_state = m->chunk_state; + if (chunk_state != CHUNK_ALLOCATED) + ReportInvalidFree(old_ptr, chunk_state, stack); + CHECK_NE(REAL(memcpy), (void*)0); + uptr memcpy_size = Min(new_size, m->UsedSize()); + // If realloc() races with free(), we may start copying freed memory. + // However, we will report racy double-free later anyway. + REAL(memcpy)(new_ptr, old_ptr, memcpy_size); + Deallocate(old_ptr, 0, stack, FROM_MALLOC); + } + return new_ptr; + } + + void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) + return allocator.ReturnNullOrDie(); + void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); + // If the memory comes from the secondary allocator no need to clear it + // as it comes directly from mmap. + if (ptr && allocator.FromPrimary(ptr)) + REAL(memset)(ptr, 0, nmemb * size); + return ptr; + } + + void ReportInvalidFree(void *ptr, u8 chunk_state, BufferedStackTrace *stack) { + if (chunk_state == CHUNK_QUARANTINE) + ReportDoubleFree((uptr)ptr, stack); + else + ReportFreeNotMalloced((uptr)ptr, stack); + } + + void CommitBack(AsanThreadLocalMallocStorage *ms) { + AllocatorCache *ac = GetAllocatorCache(ms); + quarantine.Drain(GetQuarantineCache(ms), QuarantineCallback(ac)); + allocator.SwallowCache(ac); + } + + // -------------------------- Chunk lookup ---------------------- + + // Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg). + AsanChunk *GetAsanChunk(void *alloc_beg) { + if (!alloc_beg) return 0; + if (!allocator.FromPrimary(alloc_beg)) { + uptr *meta = reinterpret_cast(allocator.GetMetaData(alloc_beg)); + AsanChunk *m = reinterpret_cast(meta[1]); + return m; + } + uptr *alloc_magic = reinterpret_cast(alloc_beg); + if (alloc_magic[0] == kAllocBegMagic) + return reinterpret_cast(alloc_magic[1]); + return reinterpret_cast(alloc_beg); + } + + AsanChunk *GetAsanChunkByAddr(uptr p) { + void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast(p)); + return GetAsanChunk(alloc_beg); + } + + // Allocator must be locked when this function is called. + AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) { + void *alloc_beg = + allocator.GetBlockBeginFastLocked(reinterpret_cast(p)); + return GetAsanChunk(alloc_beg); + } + + 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(); + } + + AsanChunkView FindHeapChunkByAddress(uptr addr) { + AsanChunk *m1 = GetAsanChunkByAddr(addr); + if (!m1) return AsanChunkView(m1); + sptr 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 PrintStats() { + allocator.PrintStats(); + } + + void ForceLock() { + allocator.ForceLock(); + fallback_mutex.Lock(); + } + + void ForceUnlock() { + fallback_mutex.Unlock(); + allocator.ForceUnlock(); + } +}; + +static Allocator instance(LINKER_INITIALIZED); + +static AsanAllocator &get_allocator() { + return instance.allocator; +} + +bool AsanChunkView::IsValid() { + return chunk_ != 0 && chunk_->chunk_state != CHUNK_AVAILABLE; +} +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 StackTrace GetStackTraceFromId(u32 id) { + CHECK(id); + StackTrace res = StackDepotGet(id); + CHECK(res.trace); + return res; +} + +StackTrace AsanChunkView::GetAllocStack() { + return GetStackTraceFromId(chunk_->alloc_context_id); +} + +StackTrace AsanChunkView::GetFreeStack() { + return GetStackTraceFromId(chunk_->free_context_id); +} + +void InitializeAllocator(const AllocatorOptions &options) { + instance.Initialize(options); +} + +void ReInitializeAllocator(const AllocatorOptions &options) { + instance.ReInitialize(options); +} + +void GetAllocatorOptions(AllocatorOptions *options) { + instance.GetOptions(options); +} + +AsanChunkView FindHeapChunkByAddress(uptr addr) { + return instance.FindHeapChunkByAddress(addr); +} + +void AsanThreadLocalMallocStorage::CommitBack() { + instance.CommitBack(this); +} + +void PrintInternalAllocatorStats() { + instance.PrintStats(); +} + +void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack, + AllocType alloc_type) { + return instance.Allocate(size, alignment, stack, alloc_type, true); +} + +void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) { + instance.Deallocate(ptr, 0, stack, alloc_type); +} + +void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack, + AllocType alloc_type) { + instance.Deallocate(ptr, size, stack, alloc_type); +} + +void *asan_malloc(uptr size, BufferedStackTrace *stack) { + return instance.Allocate(size, 8, stack, FROM_MALLOC, true); +} + +void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { + return instance.Calloc(nmemb, size, stack); +} + +void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) { + if (p == 0) + return instance.Allocate(size, 8, stack, FROM_MALLOC, true); + if (size == 0) { + instance.Deallocate(p, 0, stack, FROM_MALLOC); + return 0; + } + return instance.Reallocate(p, size, stack); +} + +void *asan_valloc(uptr size, BufferedStackTrace *stack) { + return instance.Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true); +} + +void *asan_pvalloc(uptr size, BufferedStackTrace *stack) { + uptr PageSize = GetPageSizeCached(); + size = RoundUpTo(size, PageSize); + if (size == 0) { + // pvalloc(0) should allocate one page. + size = PageSize; + } + return instance.Allocate(size, PageSize, stack, FROM_MALLOC, true); +} + +int asan_posix_memalign(void **memptr, uptr alignment, uptr size, + BufferedStackTrace *stack) { + void *ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC, true); + CHECK(IsAligned((uptr)ptr, alignment)); + *memptr = ptr; + return 0; +} + +uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) { + if (ptr == 0) return 0; + uptr usable_size = instance.AllocationSize(reinterpret_cast(ptr)); + if (flags()->check_malloc_usable_size && (usable_size == 0)) { + GET_STACK_TRACE_FATAL(pc, bp); + ReportMallocUsableSizeNotOwned((uptr)ptr, &stack); + } + return usable_size; +} + +uptr asan_mz_size(const void *ptr) { + return instance.AllocationSize(reinterpret_cast(ptr)); +} + +void asan_mz_force_lock() { + instance.ForceLock(); +} + +void asan_mz_force_unlock() { + instance.ForceUnlock(); +} + +void AsanSoftRssLimitExceededCallback(bool exceeded) { + instance.allocator.SetRssLimitIsExceeded(exceeded); +} + +} // namespace __asan + +// --- Implementation of LSan-specific functions --- {{{1 +namespace __lsan { +void LockAllocator() { + __asan::get_allocator().ForceLock(); +} + +void UnlockAllocator() { + __asan::get_allocator().ForceUnlock(); +} + +void GetAllocatorGlobalRange(uptr *begin, uptr *end) { + *begin = (uptr)&__asan::get_allocator(); + *end = *begin + sizeof(__asan::get_allocator()); +} + +uptr PointsIntoChunk(void* p) { + uptr addr = reinterpret_cast(p); + __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(addr); + if (!m) return 0; + uptr chunk = m->Beg(); + if (m->chunk_state != __asan::CHUNK_ALLOCATED) + return 0; + if (m->AddrIsInside(addr, /*locked_version=*/true)) + return chunk; + if (IsSpecialCaseOfOperatorNew0(chunk, m->UsedSize(/*locked_version*/ true), + addr)) + return chunk; + return 0; +} + +uptr GetUserBegin(uptr chunk) { + __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(chunk); + CHECK(m); + return m->Beg(); +} + +LsanMetadata::LsanMetadata(uptr chunk) { + metadata_ = reinterpret_cast(chunk - __asan::kChunkHeaderSize); +} + +bool LsanMetadata::allocated() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->chunk_state == __asan::CHUNK_ALLOCATED; +} + +ChunkTag LsanMetadata::tag() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return static_cast(m->lsan_tag); +} + +void LsanMetadata::set_tag(ChunkTag value) { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + m->lsan_tag = value; +} + +uptr LsanMetadata::requested_size() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->UsedSize(/*locked_version=*/true); +} + +u32 LsanMetadata::stack_trace_id() const { + __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); + return m->alloc_context_id; +} + +void ForEachChunk(ForEachChunkCallback callback, void *arg) { + __asan::get_allocator().ForEachChunk(callback, arg); +} + +IgnoreObjectResult IgnoreObjectLocked(const void *p) { + uptr addr = reinterpret_cast(p); + __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddr(addr); + if (!m) return kIgnoreObjectInvalid; + if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) { + if (m->lsan_tag == kIgnored) + return kIgnoreObjectAlreadyIgnored; + m->lsan_tag = __lsan::kIgnored; + return kIgnoreObjectSuccess; + } else { + return kIgnoreObjectInvalid; + } +} +} // namespace __lsan + +// ---------------------- 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 __sanitizer_get_estimated_allocated_size(uptr size) { + return size; +} + +int __sanitizer_get_ownership(const void *p) { + uptr ptr = reinterpret_cast(p); + return instance.AllocationSize(ptr) > 0; +} + +uptr __sanitizer_get_allocated_size(const void *p) { + if (p == 0) return 0; + uptr ptr = reinterpret_cast(p); + uptr allocated_size = instance.AllocationSize(ptr); + // Die if p is not malloced or if it is already freed. + if (allocated_size == 0) { + GET_STACK_TRACE_FATAL_HERE; + ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack); + } + return allocated_size; +} + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +// Provide default (no-op) implementation of malloc hooks. +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void __sanitizer_malloc_hook(void *ptr, uptr size) { + (void)ptr; + (void)size; +} +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void __sanitizer_free_hook(void *ptr) { + (void)ptr; +} +} // extern "C" +#endif diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h index 6d3a99282a4a..3208d1f950cd 100644 --- a/lib/asan/asan_allocator.h +++ b/lib/asan/asan_allocator.h @@ -9,12 +9,13 @@ // // This file is a part of AddressSanitizer, an address sanity checker. // -// ASan-private header for asan_allocator2.cc. +// ASan-private header for asan_allocator.cc. //===----------------------------------------------------------------------===// #ifndef ASAN_ALLOCATOR_H #define ASAN_ALLOCATOR_H +#include "asan_flags.h" #include "asan_internal.h" #include "asan_interceptors.h" #include "sanitizer_common/sanitizer_allocator.h" @@ -31,8 +32,20 @@ enum AllocType { static const uptr kNumberOfSizeClasses = 255; struct AsanChunk; -void InitializeAllocator(); -void ReInitializeAllocator(); +struct AllocatorOptions { + u32 quarantine_size_mb; + u16 min_redzone; + u16 max_redzone; + u8 may_return_null; + u8 alloc_dealloc_mismatch; + + void SetFrom(const Flags *f, const CommonFlags *cf); + void CopyTo(Flags *f, CommonFlags *cf); +}; + +void InitializeAllocator(const AllocatorOptions &options); +void ReInitializeAllocator(const AllocatorOptions &options); +void GetAllocatorOptions(AllocatorOptions *options); class AsanChunkView { public: @@ -127,12 +140,12 @@ typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, 16, typedef SizeClassAllocatorLocalCache AllocatorCache; typedef LargeMmapAllocator SecondaryAllocator; typedef CombinedAllocator Allocator; + SecondaryAllocator> AsanAllocator; struct AsanThreadLocalMallocStorage { uptr quarantine_cache[16]; - AllocatorCache allocator2_cache; + AllocatorCache allocator_cache; void CommitBack(); private: // These objects are allocated via mmap() and are zero-initialized. @@ -160,6 +173,7 @@ void asan_mz_force_lock(); void asan_mz_force_unlock(); void PrintInternalAllocatorStats(); +void AsanSoftRssLimitExceededCallback(bool exceeded); } // namespace __asan #endif // ASAN_ALLOCATOR_H diff --git a/lib/asan/asan_allocator2.cc b/lib/asan/asan_allocator2.cc deleted file mode 100644 index 52bdcf607f57..000000000000 --- a/lib/asan/asan_allocator2.cc +++ /dev/null @@ -1,792 +0,0 @@ -//===-- 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. -// -//===----------------------------------------------------------------------===// -#include "asan_allocator.h" - -#include "asan_mapping.h" -#include "asan_poisoning.h" -#include "asan_report.h" -#include "asan_stack.h" -#include "asan_thread.h" -#include "sanitizer_common/sanitizer_allocator_interface.h" -#include "sanitizer_common/sanitizer_flags.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" -#include "lsan/lsan_common.h" - -namespace __asan { - -void AsanMapUnmapCallback::OnMap(uptr p, uptr size) const { - PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic); - // Statistics. - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.mmaps++; - thread_stats.mmaped += size; -} -void AsanMapUnmapCallback::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. - FlushUnneededASanShadowMemory(p, size); - // Statistics. - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.munmaps++; - thread_stats.munmaped += size; -} - -// 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); - return &ms->allocator2_cache; -} - -static Allocator allocator; - -static const uptr kMaxAllowedMallocSize = - FIRST_32_SECOND_64(3UL << 30, 64UL << 30); - -static const uptr kMaxThreadLocalQuarantine = - FIRST_32_SECOND_64(1 << 18, 1 << 20); - -// 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 = Log2(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 Min(Max(rz_log, RZSize2Log(flags()->redzone)), - RZSize2Log(flags()->max_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 the left redzone is greater than the ChunkHeader size we store a 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 L L L H H U U U U U U -// | ^ -// ---------------------| -// M -- magic value kAllocBegMagic -// B -- address of ChunkHeader pointing to the first 'H' -static const uptr kAllocBegMagic = 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; - u32 lsan_tag : 2; - // 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. - 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(this) + kChunkHeaderSize; } - uptr UsedSize(bool locked_version = false) { - if (user_requested_size != SizeClassMap::kMaxSize) - return user_requested_size; - return *reinterpret_cast( - allocator.GetMetaData(AllocBeg(locked_version))); - } - void *AllocBeg(bool locked_version = false) { - if (from_memalign) { - if (locked_version) - return allocator.GetBlockBeginFastLocked( - reinterpret_cast(this)); - return allocator.GetBlockBegin(reinterpret_cast(this)); - } - return reinterpret_cast(Beg() - RZLog2Size(rz_log)); - } - bool AddrIsInside(uptr addr, bool locked_version = false) { - return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version)); - } -}; - -bool AsanChunkView::IsValid() { - return chunk_ != 0 && chunk_->chunk_state != CHUNK_AVAILABLE; -} -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 StackTrace GetStackTraceFromId(u32 id) { - CHECK(id); - StackTrace res = StackDepotGet(id); - CHECK(res.trace); - return res; -} - -StackTrace AsanChunkView::GetAllocStack() { - return GetStackTraceFromId(chunk_->alloc_context_id); -} - -StackTrace AsanChunkView::GetFreeStack() { - return GetStackTraceFromId(chunk_->free_context_id); -} - -struct QuarantineCallback; -typedef Quarantine 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(ms->quarantine_cache); -} - -struct QuarantineCallback { - explicit QuarantineCallback(AllocatorCache *cache) - : cache_(cache) { - } - - void Recycle(AsanChunk *m) { - CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); - atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed); - 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(m->AllocBeg()); - if (p != m) { - uptr *alloc_magic = reinterpret_cast(p); - CHECK_EQ(alloc_magic[0], kAllocBegMagic); - // Clear the magic value, as allocator internals may overwrite the - // contents of deallocated chunk, confusing GetAsanChunk lookup. - alloc_magic[0] = 0; - CHECK_EQ(alloc_magic[1], reinterpret_cast(m)); - } - - // Statistics. - AsanStats &thread_stats = 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_; -}; - -void InitializeAllocator() { - allocator.Init(); - quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine); -} - -void ReInitializeAllocator() { - quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine); -} - -static void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack, - AllocType alloc_type, bool can_fill) { - if (UNLIKELY(!asan_inited)) - AsanInitFromRtl(); - Flags &fl = *flags(); - CHECK(stack); - const uptr min_alignment = SHADOW_GRANULARITY; - if (alignment < min_alignment) - alignment = min_alignment; - if (size == 0) { - // We'd be happy to avoid allocating memory for zero-size requests, but - // some programs/tests depend on this behavior and assume that malloc would - // not return NULL even for zero-size allocations. Moreover, it looks like - // operator new should never return NULL, and results of consecutive "new" - // calls must be different even if the allocated size is zero. - size = 1; - } - CHECK(IsPowerOfTwo(alignment)); - uptr rz_log = ComputeRZLog(size); - uptr rz_size = RZLog2Size(rz_log); - uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment); - 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 AllocatorReturnNull(); - } - - AsanThread *t = GetCurrentThread(); - 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); - } - - if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && flags()->poison_heap) { - // Heap poisoning is enabled, but the allocator provides an unpoisoned - // chunk. This is possible if flags()->poison_heap was disabled for some - // time, for example, due to flags()->start_disabled. - // Anyway, poison the block before using it for anything else. - uptr allocated_size = allocator.GetActuallyAllocatedSize(allocated); - PoisonShadow((uptr)allocated, allocated_size, kAsanHeapLeftRedzoneMagic); - } - - uptr alloc_beg = reinterpret_cast(allocated); - 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(chunk_beg); - 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 (alloc_beg != chunk_beg) { - CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg); - reinterpret_cast(alloc_beg)[0] = kAllocBegMagic; - reinterpret_cast(alloc_beg)[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(allocator.GetMetaData(allocated)); - meta[0] = size; - meta[1] = chunk_beg; - } - - m->alloc_context_id = StackDepotPut(*stack); - - 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 && fl.poison_heap) { - u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity); - *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0; - } - - AsanStats &thread_stats = 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(user_beg); - if (can_fill && fl.max_malloc_fill_size) { - uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size); - REAL(memset)(res, fl.malloc_fill_byte, fill_size); - } -#if CAN_SANITIZE_LEAKS - m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored - : __lsan::kDirectlyLeaked; -#endif - // Must be the last mutation of metadata in this function. - atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release); - ASAN_MALLOC_HOOK(res, size); - return res; -} - -static void ReportInvalidFree(void *ptr, u8 chunk_state, - BufferedStackTrace *stack) { - if (chunk_state == CHUNK_QUARANTINE) - ReportDoubleFree((uptr)ptr, stack); - else - ReportFreeNotMalloced((uptr)ptr, stack); -} - -static void AtomicallySetQuarantineFlag(AsanChunk *m, void *ptr, - BufferedStackTrace *stack) { - u8 old_chunk_state = CHUNK_ALLOCATED; - // Flip the chunk_state atomically to avoid race on double-free. - if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state, - CHUNK_QUARANTINE, memory_order_acquire)) - ReportInvalidFree(ptr, old_chunk_state, stack); - CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state); -} - -// Expects the chunk to already be marked as quarantined by using -// AtomicallySetQuarantineFlag. -static void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack, - AllocType alloc_type) { - CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); - - 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 = GetCurrentThread(); - m->free_tid = t ? t->tid() : 0; - m->free_context_id = StackDepotPut(*stack); - // Poison the region. - PoisonShadow(m->Beg(), - RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), - kAsanHeapFreeMagic); - - AsanStats &thread_stats = 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()); - } -} - -static void Deallocate(void *ptr, uptr delete_size, BufferedStackTrace *stack, - AllocType alloc_type) { - uptr p = reinterpret_cast(ptr); - if (p == 0) return; - - uptr chunk_beg = p - kChunkHeaderSize; - AsanChunk *m = reinterpret_cast(chunk_beg); - if (delete_size && flags()->new_delete_type_mismatch && - delete_size != m->UsedSize()) { - ReportNewDeleteSizeMismatch(p, delete_size, stack); - } - ASAN_FREE_HOOK(ptr); - // Must mark the chunk as quarantined before any changes to its metadata. - AtomicallySetQuarantineFlag(m, ptr, stack); - QuarantineChunk(m, ptr, stack, alloc_type); -} - -static void *Reallocate(void *old_ptr, uptr new_size, - BufferedStackTrace *stack) { - CHECK(old_ptr && new_size); - uptr p = reinterpret_cast(old_ptr); - uptr chunk_beg = p - kChunkHeaderSize; - AsanChunk *m = reinterpret_cast(chunk_beg); - - AsanStats &thread_stats = GetCurrentThreadStats(); - thread_stats.reallocs++; - thread_stats.realloced += new_size; - - void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); - if (new_ptr) { - u8 chunk_state = m->chunk_state; - if (chunk_state != CHUNK_ALLOCATED) - ReportInvalidFree(old_ptr, chunk_state, stack); - CHECK_NE(REAL(memcpy), (void*)0); - uptr memcpy_size = Min(new_size, m->UsedSize()); - // If realloc() races with free(), we may start copying freed memory. - // However, we will report racy double-free later anyway. - REAL(memcpy)(new_ptr, old_ptr, memcpy_size); - Deallocate(old_ptr, 0, stack, FROM_MALLOC); - } - return new_ptr; -} - -// Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg). -static AsanChunk *GetAsanChunk(void *alloc_beg) { - if (!alloc_beg) return 0; - if (!allocator.FromPrimary(alloc_beg)) { - uptr *meta = reinterpret_cast(allocator.GetMetaData(alloc_beg)); - AsanChunk *m = reinterpret_cast(meta[1]); - return m; - } - uptr *alloc_magic = reinterpret_cast(alloc_beg); - if (alloc_magic[0] == kAllocBegMagic) - return reinterpret_cast(alloc_magic[1]); - return reinterpret_cast(alloc_beg); -} - -static AsanChunk *GetAsanChunkByAddr(uptr p) { - void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast(p)); - return GetAsanChunk(alloc_beg); -} - -// Allocator must be locked when this function is called. -static AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) { - void *alloc_beg = - allocator.GetBlockBeginFastLocked(reinterpret_cast(p)); - return GetAsanChunk(alloc_beg); -} - -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. - sptr 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); - sptr 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(); -} - -void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack, - AllocType alloc_type) { - return Allocate(size, alignment, stack, alloc_type, true); -} - -void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) { - Deallocate(ptr, 0, stack, alloc_type); -} - -void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack, - AllocType alloc_type) { - Deallocate(ptr, size, stack, alloc_type); -} - -void *asan_malloc(uptr size, BufferedStackTrace *stack) { - return Allocate(size, 8, stack, FROM_MALLOC, true); -} - -void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) - return AllocatorReturnNull(); - void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); - // If the memory comes from the secondary allocator no need to clear it - // as it comes directly from mmap. - if (ptr && allocator.FromPrimary(ptr)) - REAL(memset)(ptr, 0, nmemb * size); - return ptr; -} - -void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) { - if (p == 0) - return Allocate(size, 8, stack, FROM_MALLOC, true); - if (size == 0) { - Deallocate(p, 0, stack, FROM_MALLOC); - return 0; - } - return Reallocate(p, size, stack); -} - -void *asan_valloc(uptr size, BufferedStackTrace *stack) { - return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true); -} - -void *asan_pvalloc(uptr size, BufferedStackTrace *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, true); -} - -int asan_posix_memalign(void **memptr, uptr alignment, uptr size, - BufferedStackTrace *stack) { - void *ptr = Allocate(size, alignment, stack, FROM_MALLOC, true); - CHECK(IsAligned((uptr)ptr, alignment)); - *memptr = ptr; - return 0; -} - -uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) { - if (ptr == 0) return 0; - uptr usable_size = AllocationSize(reinterpret_cast(ptr)); - if (flags()->check_malloc_usable_size && (usable_size == 0)) { - GET_STACK_TRACE_FATAL(pc, bp); - ReportMallocUsableSizeNotOwned((uptr)ptr, &stack); - } - return usable_size; -} - -uptr asan_mz_size(const void *ptr) { - return AllocationSize(reinterpret_cast(ptr)); -} - -void asan_mz_force_lock() { - allocator.ForceLock(); - fallback_mutex.Lock(); -} - -void asan_mz_force_unlock() { - fallback_mutex.Unlock(); - allocator.ForceUnlock(); -} - -} // namespace __asan - -// --- Implementation of LSan-specific functions --- {{{1 -namespace __lsan { -void LockAllocator() { - __asan::allocator.ForceLock(); -} - -void UnlockAllocator() { - __asan::allocator.ForceUnlock(); -} - -void GetAllocatorGlobalRange(uptr *begin, uptr *end) { - *begin = (uptr)&__asan::allocator; - *end = *begin + sizeof(__asan::allocator); -} - -uptr PointsIntoChunk(void* p) { - uptr addr = reinterpret_cast(p); - __asan::AsanChunk *m = __asan::GetAsanChunkByAddrFastLocked(addr); - if (!m) return 0; - uptr chunk = m->Beg(); - if (m->chunk_state != __asan::CHUNK_ALLOCATED) - return 0; - if (m->AddrIsInside(addr, /*locked_version=*/true)) - return chunk; - if (IsSpecialCaseOfOperatorNew0(chunk, m->UsedSize(/*locked_version*/ true), - addr)) - return chunk; - return 0; -} - -uptr GetUserBegin(uptr chunk) { - __asan::AsanChunk *m = - __asan::GetAsanChunkByAddrFastLocked(chunk); - CHECK(m); - return m->Beg(); -} - -LsanMetadata::LsanMetadata(uptr chunk) { - metadata_ = reinterpret_cast(chunk - __asan::kChunkHeaderSize); -} - -bool LsanMetadata::allocated() const { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return m->chunk_state == __asan::CHUNK_ALLOCATED; -} - -ChunkTag LsanMetadata::tag() const { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return static_cast(m->lsan_tag); -} - -void LsanMetadata::set_tag(ChunkTag value) { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - m->lsan_tag = value; -} - -uptr LsanMetadata::requested_size() const { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return m->UsedSize(/*locked_version=*/true); -} - -u32 LsanMetadata::stack_trace_id() const { - __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return m->alloc_context_id; -} - -void ForEachChunk(ForEachChunkCallback callback, void *arg) { - __asan::allocator.ForEachChunk(callback, arg); -} - -IgnoreObjectResult IgnoreObjectLocked(const void *p) { - uptr addr = reinterpret_cast(p); - __asan::AsanChunk *m = __asan::GetAsanChunkByAddr(addr); - if (!m) return kIgnoreObjectInvalid; - if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) { - if (m->lsan_tag == kIgnored) - return kIgnoreObjectAlreadyIgnored; - m->lsan_tag = __lsan::kIgnored; - return kIgnoreObjectSuccess; - } else { - return kIgnoreObjectInvalid; - } -} -} // namespace __lsan - -// ---------------------- 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 __sanitizer_get_estimated_allocated_size(uptr size) { - return size; -} - -int __sanitizer_get_ownership(const void *p) { - uptr ptr = reinterpret_cast(p); - return (AllocationSize(ptr) > 0); -} - -uptr __sanitizer_get_allocated_size(const void *p) { - if (p == 0) return 0; - uptr ptr = reinterpret_cast(p); - uptr allocated_size = AllocationSize(ptr); - // Die if p is not malloced or if it is already freed. - if (allocated_size == 0) { - GET_STACK_TRACE_FATAL_HERE; - ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack); - } - return allocated_size; -} - -#if !SANITIZER_SUPPORTS_WEAK_HOOKS -// Provide default (no-op) implementation of malloc hooks. -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE -void __sanitizer_malloc_hook(void *ptr, uptr size) { - (void)ptr; - (void)size; -} -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE -void __sanitizer_free_hook(void *ptr) { - (void)ptr; -} -} // extern "C" -#endif diff --git a/lib/asan/asan_debugging.cc b/lib/asan/asan_debugging.cc index 2b66dd5265fc..6fc5b690de99 100644 --- a/lib/asan/asan_debugging.cc +++ b/lib/asan/asan_debugging.cc @@ -81,8 +81,8 @@ void AsanLocateAddress(uptr addr, AddressDescription *descr) { GetInfoForHeapAddress(addr, descr); } -uptr AsanGetStack(uptr addr, uptr *trace, uptr size, u32 *thread_id, - bool alloc_stack) { +static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id, + bool alloc_stack) { AsanChunkView chunk = FindHeapChunkByAddress(addr); if (!chunk.IsValid()) return 0; diff --git a/lib/asan/asan_fake_stack.cc b/lib/asan/asan_fake_stack.cc index c95bc11f6cd3..bf4f1eb4c781 100644 --- a/lib/asan/asan_fake_stack.cc +++ b/lib/asan/asan_fake_stack.cc @@ -60,7 +60,7 @@ FakeStack *FakeStack::Create(uptr stack_size_log) { void FakeStack::Destroy(int tid) { PoisonAll(0); - if (common_flags()->verbosity >= 2) { + if (Verbosity() >= 2) { InternalScopedString str(kNumberOfSizeClasses * 50); for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) str.append("%zd: %zd/%zd; ", class_id, hint_position_[class_id], @@ -192,20 +192,19 @@ static FakeStack *GetFakeStackFast() { return GetFakeStack(); } -ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size, uptr real_stack) { +ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size) { FakeStack *fs = GetFakeStackFast(); - if (!fs) return real_stack; + if (!fs) return 0; + uptr local_stack; + uptr real_stack = reinterpret_cast(&local_stack); FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack); - if (!ff) - return real_stack; // Out of fake stack, return the real one. + if (!ff) return 0; // Out of fake stack. uptr ptr = reinterpret_cast(ff); SetShadow(ptr, size, class_id, 0); return ptr; } -ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size, uptr real_stack) { - if (ptr == real_stack) - return; +ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size) { FakeStack::Deallocate(ptr, class_id); SetShadow(ptr, size, class_id, kMagic8); } @@ -216,12 +215,12 @@ ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size, uptr real_stack) { using namespace __asan; #define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \ extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \ - __asan_stack_malloc_##class_id(uptr size, uptr real_stack) { \ - return OnMalloc(class_id, size, real_stack); \ + __asan_stack_malloc_##class_id(uptr size) { \ + return OnMalloc(class_id, size); \ } \ extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \ - uptr ptr, uptr size, uptr real_stack) { \ - OnFree(ptr, class_id, size, real_stack); \ + uptr ptr, uptr size) { \ + OnFree(ptr, class_id, size); \ } DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0) diff --git a/lib/asan/asan_flags.cc b/lib/asan/asan_flags.cc new file mode 100644 index 000000000000..1d82ab0e725f --- /dev/null +++ b/lib/asan/asan_flags.cc @@ -0,0 +1,141 @@ +//===-- asan_flags.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. +// +// ASan flag parsing logic. +//===----------------------------------------------------------------------===// + +#include "asan_activation.h" +#include "asan_flags.h" +#include "asan_interface_internal.h" +#include "asan_stack.h" +#include "lsan/lsan_common.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" + +namespace __asan { + +Flags asan_flags_dont_use_directly; // use via flags(). + +static const char *MaybeCallAsanDefaultOptions() { + return (&__asan_default_options) ? __asan_default_options() : ""; +} + +static const char *MaybeUseAsanDefaultOptionsCompileDefinition() { +#ifdef ASAN_DEFAULT_OPTIONS +// Stringize the macro value. +# define ASAN_STRINGIZE(x) #x +# define ASAN_STRINGIZE_OPTIONS(options) ASAN_STRINGIZE(options) + return ASAN_STRINGIZE_OPTIONS(ASAN_DEFAULT_OPTIONS); +#else + return ""; +#endif +} + +void Flags::SetDefaults() { +#define ASAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "asan_flags.inc" +#undef ASAN_FLAG +} + +void RegisterAsanFlags(FlagParser *parser, Flags *f) { +#define ASAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "asan_flags.inc" +#undef ASAN_FLAG +} + +void InitializeFlags(Flags *f) { + FlagParser parser; + RegisterAsanFlags(&parser, f); + RegisterCommonFlags(&parser); + + SetCommonFlagsDefaults(); + { + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.detect_leaks = CAN_SANITIZE_LEAKS; + cf.external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); + cf.malloc_context_size = kDefaultMallocContextSize; + cf.intercept_tls_get_addr = true; + OverrideCommonFlags(cf); + } + + const int kDefaultQuarantineSizeMb = (ASAN_LOW_MEMORY) ? 1UL << 6 : 1UL << 8; + f->SetDefaults(); + + // Override from compile definition. + const char *compile_def = MaybeUseAsanDefaultOptionsCompileDefinition(); + parser.ParseString(compile_def); + + // Override from user-specified string. + const char *default_options = MaybeCallAsanDefaultOptions(); + parser.ParseString(default_options); + + // Override from command line. + const char *env = GetEnv("ASAN_OPTIONS"); + if (env) parser.ParseString(env); + + // Let activation flags override current settings. On Android they come + // from a system property. On other platforms this is no-op. + if (!flags()->start_deactivated) { + char buf[100]; + GetExtraActivationFlags(buf, sizeof(buf)); + parser.ParseString(buf); + } + + SetVerbosity(common_flags()->verbosity); + + // TODO(eugenis): dump all flags at verbosity>=2? + if (Verbosity()) ReportUnrecognizedFlags(); + + if (common_flags()->help) parser.PrintFlagDescriptions(); + + // Flag validation: + if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) { + Report("%s: detect_leaks is not supported on this platform.\n", + SanitizerToolName); + Die(); + } + // Make "strict_init_order" imply "check_initialization_order". + // TODO(samsonov): Use a single runtime flag for an init-order checker. + if (f->strict_init_order) { + f->check_initialization_order = true; + } + CHECK_LE((uptr)common_flags()->malloc_context_size, kStackTraceMax); + CHECK_LE(f->min_uar_stack_size_log, f->max_uar_stack_size_log); + CHECK_GE(f->redzone, 16); + CHECK_GE(f->max_redzone, f->redzone); + CHECK_LE(f->max_redzone, 2048); + CHECK(IsPowerOfTwo(f->redzone)); + CHECK(IsPowerOfTwo(f->max_redzone)); + + // quarantine_size is deprecated but we still honor it. + // quarantine_size can not be used together with quarantine_size_mb. + if (f->quarantine_size >= 0 && f->quarantine_size_mb >= 0) { + Report("%s: please use either 'quarantine_size' (deprecated) or " + "quarantine_size_mb, but not both\n", SanitizerToolName); + Die(); + } + if (f->quarantine_size >= 0) + f->quarantine_size_mb = f->quarantine_size >> 20; + if (f->quarantine_size_mb < 0) + f->quarantine_size_mb = kDefaultQuarantineSizeMb; +} + +} // namespace __asan + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +const char* __asan_default_options() { return ""; } +} // extern "C" +#endif diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h index 3df4dd3050bc..7653543f6d88 100644 --- a/lib/asan/asan_flags.h +++ b/lib/asan/asan_flags.h @@ -16,6 +16,7 @@ #define ASAN_FLAGS_H #include "sanitizer_common/sanitizer_internal_defs.h" +#include "sanitizer_common/sanitizer_flag_parser.h" // ASan flag values can be defined in four ways: // 1) initialized with default values at startup. @@ -24,55 +25,24 @@ // 3) overriden from string returned by user-specified function // __asan_default_options(). // 4) overriden from env variable ASAN_OPTIONS. +// 5) overriden during ASan activation (for now used on Android only). namespace __asan { struct Flags { - // Flag descriptions are in asan_rtl.cc. - int quarantine_size; - int redzone; - int max_redzone; - bool debug; - int report_globals; - bool check_initialization_order; - bool replace_str; - bool replace_intrin; - bool mac_ignore_invalid_free; - bool detect_stack_use_after_return; - int min_uar_stack_size_log; - int max_uar_stack_size_log; - bool uar_noreserve; - int max_malloc_fill_size, malloc_fill_byte; - int exitcode; - bool allow_user_poisoning; - int sleep_before_dying; - bool check_malloc_usable_size; - bool unmap_shadow_on_exit; - bool abort_on_error; - bool print_stats; - bool print_legend; - bool atexit; - bool allow_reexec; - bool print_full_thread_history; - bool poison_heap; - bool poison_partial; - bool poison_array_cookie; - bool alloc_dealloc_mismatch; - bool new_delete_type_mismatch; - bool strict_memcmp; - bool strict_init_order; - bool start_deactivated; - int detect_invalid_pointer_pairs; - bool detect_container_overflow; - int detect_odr_violation; - bool dump_instruction_bytes; +#define ASAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "asan_flags.inc" +#undef ASAN_FLAG + + void SetDefaults(); }; extern Flags asan_flags_dont_use_directly; inline Flags *flags() { return &asan_flags_dont_use_directly; } -void InitializeFlags(Flags *f, const char *env); +void RegisterAsanFlags(FlagParser *parser, Flags *f); +void InitializeFlags(Flags *f); } // namespace __asan diff --git a/lib/asan/asan_flags.inc b/lib/asan/asan_flags.inc new file mode 100644 index 000000000000..ec9d41980d9c --- /dev/null +++ b/lib/asan/asan_flags.inc @@ -0,0 +1,144 @@ +//===-- asan_flags.inc ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// ASan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef ASAN_FLAG +# error "Define ASAN_FLAG prior to including this file!" +#endif + +// ASAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +ASAN_FLAG(int, quarantine_size, -1, + "Deprecated, please use quarantine_size_mb.") +ASAN_FLAG(int, quarantine_size_mb, -1, + "Size (in Mb) of quarantine used to detect use-after-free " + "errors. Lower value may reduce memory usage but increase the " + "chance of false negatives.") +ASAN_FLAG(int, redzone, 16, + "Minimal size (in bytes) of redzones around heap objects. " + "Requirement: redzone >= 16, is a power of two.") +ASAN_FLAG(int, max_redzone, 2048, + "Maximal size (in bytes) of redzones around heap objects.") +ASAN_FLAG( + bool, debug, false, + "If set, prints some debugging information and does additional checks.") +ASAN_FLAG( + int, report_globals, 1, + "Controls the way to handle globals (0 - don't detect buffer overflow on " + "globals, 1 - detect buffer overflow, 2 - print data about registered " + "globals).") +ASAN_FLAG(bool, check_initialization_order, false, + "If set, attempts to catch initialization order issues.") +ASAN_FLAG( + bool, replace_str, true, + "If set, uses custom wrappers and replacements for libc string functions " + "to find more errors.") +ASAN_FLAG(bool, replace_intrin, true, + "If set, uses custom wrappers for memset/memcpy/memmove intinsics.") +ASAN_FLAG(bool, mac_ignore_invalid_free, false, + "Ignore invalid free() calls to work around some bugs. Used on OS X " + "only.") +ASAN_FLAG(bool, detect_stack_use_after_return, false, + "Enables stack-use-after-return checking at run-time.") +ASAN_FLAG(int, min_uar_stack_size_log, 16, // We can't do smaller anyway. + "Minimum fake stack size log.") +ASAN_FLAG(int, max_uar_stack_size_log, + 20, // 1Mb per size class, i.e. ~11Mb per thread + "Maximum fake stack size log.") +ASAN_FLAG(bool, uar_noreserve, false, + "Use mmap with 'noreserve' flag to allocate fake stack.") +ASAN_FLAG( + int, max_malloc_fill_size, 0x1000, // By default, fill only the first 4K. + "ASan allocator flag. max_malloc_fill_size is the maximal amount of " + "bytes that will be filled with malloc_fill_byte on malloc.") +ASAN_FLAG(int, malloc_fill_byte, 0xbe, + "Value used to fill the newly allocated memory.") +ASAN_FLAG(int, exitcode, ASAN_DEFAULT_FAILURE_EXITCODE, + "Override the program exit status if the tool found an error.") +ASAN_FLAG(bool, allow_user_poisoning, true, + "If set, user may manually mark memory regions as poisoned or " + "unpoisoned.") +ASAN_FLAG( + int, sleep_before_dying, 0, + "Number of seconds to sleep between printing an error report and " + "terminating the program. Useful for debugging purposes (e.g. when one " + "needs to attach gdb).") +ASAN_FLAG(bool, check_malloc_usable_size, true, + "Allows the users to work around the bug in Nvidia drivers prior to " + "295.*.") +ASAN_FLAG(bool, unmap_shadow_on_exit, false, + "If set, explicitly unmaps the (huge) shadow at exit.") +ASAN_FLAG( + bool, abort_on_error, false, + "If set, the tool calls abort() instead of _exit() after printing the " + "error report.") +ASAN_FLAG(bool, print_stats, false, + "Print various statistics after printing an error message or if " + "atexit=1.") +ASAN_FLAG(bool, print_legend, true, "Print the legend for the shadow bytes.") +ASAN_FLAG(bool, atexit, false, + "If set, prints ASan exit stats even after program terminates " + "successfully.") +ASAN_FLAG( + bool, print_full_thread_history, true, + "If set, prints thread creation stacks for the threads involved in the " + "report and their ancestors up to the main thread.") +ASAN_FLAG( + bool, poison_heap, true, + "Poison (or not) the heap memory on [de]allocation. Zero value is useful " + "for benchmarking the allocator or instrumentator.") +ASAN_FLAG(bool, poison_partial, true, + "If true, poison partially addressable 8-byte aligned words " + "(default=true). This flag affects heap and global buffers, but not " + "stack buffers.") +ASAN_FLAG(bool, poison_array_cookie, true, + "Poison (or not) the array cookie after operator new[].") + +// Turn off alloc/dealloc mismatch checker on Mac and Windows for now. +// https://code.google.com/p/address-sanitizer/issues/detail?id=131 +// https://code.google.com/p/address-sanitizer/issues/detail?id=309 +// TODO(glider,timurrrr): Fix known issues and enable this back. +ASAN_FLAG(bool, alloc_dealloc_mismatch, + (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0), + "Report errors on malloc/delete, new/free, new/delete[], etc.") + +ASAN_FLAG(bool, new_delete_type_mismatch, true, + "Report errors on mismatch betwen size of new and delete.") +ASAN_FLAG(bool, strict_memcmp, true, + "If true, assume that memcmp(p1, p2, n) always reads n bytes before " + "comparing p1 and p2.") +ASAN_FLAG( + bool, strict_init_order, false, + "If true, assume that dynamic initializers can never access globals from " + "other modules, even if the latter are already initialized.") +ASAN_FLAG( + bool, start_deactivated, false, + "If true, ASan tweaks a bunch of other flags (quarantine, redzone, heap " + "poisoning) to reduce memory consumption as much as possible, and " + "restores them to original values when the first instrumented module is " + "loaded into the process. This is mainly intended to be used on " + "Android. ") +ASAN_FLAG( + int, detect_invalid_pointer_pairs, 0, + "If non-zero, try to detect operations like <, <=, >, >= and - on " + "invalid pointer pairs (e.g. when pointers belong to different objects). " + "The bigger the value the harder we try.") +ASAN_FLAG( + bool, detect_container_overflow, true, + "If true, honor the container overflow annotations. " + "See https://code.google.com/p/address-sanitizer/wiki/ContainerOverflow") +ASAN_FLAG(int, detect_odr_violation, 2, + "If >=2, detect violation of One-Definition-Rule (ODR); " + "If ==1, detect ODR-violation only if the two variables " + "have different sizes") +ASAN_FLAG(bool, dump_instruction_bytes, false, + "If true, dump 16 bytes starting at the instruction that caused SEGV") diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc index be111d4fb4cf..c4571953c408 100644 --- a/lib/asan/asan_globals.cc +++ b/lib/asan/asan_globals.cc @@ -164,7 +164,7 @@ static void RegisterGlobal(const Global *g) { } } } - if (flags()->poison_heap) + if (CanPoisonMemory()) PoisonRedZones(*g); ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals; l->g = g; @@ -186,7 +186,7 @@ static void UnregisterGlobal(const Global *g) { CHECK(AddrIsInMem(g->beg)); CHECK(AddrIsAlignedByGranularity(g->beg)); CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); - if (flags()->poison_heap) + if (CanPoisonMemory()) PoisonShadowForGlobal(g, 0); // We unpoison the shadow memory for the global but we do not remove it from // the list because that would require O(n^2) time with the current list @@ -249,7 +249,7 @@ void __asan_unregister_globals(__asan_global *globals, uptr n) { // initializer can only touch global variables in the same TU. void __asan_before_dynamic_init(const char *module_name) { if (!flags()->check_initialization_order || - !flags()->poison_heap) + !CanPoisonMemory()) return; bool strict_init_order = flags()->strict_init_order; CHECK(dynamic_init_globals); @@ -275,7 +275,7 @@ void __asan_before_dynamic_init(const char *module_name) { // TU are poisoned. It simply unpoisons all dynamically initialized globals. void __asan_after_dynamic_init() { if (!flags()->check_initialization_order || - !flags()->poison_heap) + !CanPoisonMemory()) return; CHECK(asan_inited); BlockingMutexLock lock(&mu_for_globals); diff --git a/lib/asan/asan_init_version.h b/lib/asan/asan_init_version.h index 77aea81bd298..6cf57c4aa2a8 100644 --- a/lib/asan/asan_init_version.h +++ b/lib/asan/asan_init_version.h @@ -25,8 +25,10 @@ extern "C" { // contains the function PC as the 3-rd field (see // DescribeAddressIfStack). // v3=>v4: added '__asan_global_source_location' to __asan_global. - #define __asan_init __asan_init_v4 - #define __asan_init_name "__asan_init_v4" + // v4=>v5: changed the semantics and format of __asan_stack_malloc_ and + // __asan_stack_free_ functions. + #define __asan_init __asan_init_v5 + #define __asan_init_name "__asan_init_v5" } #endif // ASAN_INIT_VERSION_H diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc index 910cd3addcb0..3dc7ec67a3e5 100644 --- a/lib/asan/asan_interceptors.cc +++ b/lib/asan/asan_interceptors.cc @@ -142,14 +142,17 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ ASAN_READ_RANGE(ctx, ptr, size) #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + ASAN_INTERCEPTOR_ENTER(ctx, func); \ do { \ if (asan_init_is_running) \ return REAL(func)(__VA_ARGS__); \ - ASAN_INTERCEPTOR_ENTER(ctx, func); \ if (SANITIZER_MAC && UNLIKELY(!asan_inited)) \ return REAL(func)(__VA_ARGS__); \ ENSURE_ASAN_INITED(); \ } while (false) +#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \ + do { \ + } while (false) #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ do { \ } while (false) @@ -169,8 +172,9 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) } while (false) #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() -#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res) CovUpdateMapping() -#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CovUpdateMapping() +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \ + CoverageUpdateMapping() +#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CoverageUpdateMapping() #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited) #include "sanitizer_common/sanitizer_common_interceptors.inc" @@ -196,6 +200,12 @@ struct ThreadStartParam { }; static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { +#if SANITIZER_WINDOWS + // FIXME: this is a bandaid fix for PR22025. + AsanThread *t = (AsanThread*)arg; + SetCurrentThread(t); + return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr); +#else ThreadStartParam *param = reinterpret_cast(arg); AsanThread *t = nullptr; while ((t = reinterpret_cast( @@ -203,6 +213,7 @@ static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { internal_sched_yield(); SetCurrentThread(t); return t->ThreadStart(GetTid(), ¶m->is_registered); +#endif } #if ASAN_INTERCEPT_PTHREAD_CREATE @@ -236,22 +247,26 @@ INTERCEPTOR(int, pthread_create, void *thread, } return result; } + +INTERCEPTOR(int, pthread_join, void *t, void **arg) { + return real_pthread_join(t, arg); +} + +DEFINE_REAL_PTHREAD_FUNCTIONS #endif // ASAN_INTERCEPT_PTHREAD_CREATE #if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION #if SANITIZER_ANDROID INTERCEPTOR(void*, bsd_signal, int signum, void *handler) { - if (!AsanInterceptsSignal(signum) || - common_flags()->allow_user_segv_handler) { + if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) { return REAL(bsd_signal)(signum, handler); } return 0; } #else INTERCEPTOR(void*, signal, int signum, void *handler) { - if (!AsanInterceptsSignal(signum) || - common_flags()->allow_user_segv_handler) { + if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) { return REAL(signal)(signum, handler); } return 0; @@ -260,8 +275,7 @@ INTERCEPTOR(void*, signal, int signum, void *handler) { INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact) { - if (!AsanInterceptsSignal(signum) || - common_flags()->allow_user_segv_handler) { + if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) { return REAL(sigaction)(signum, act, oldact); } return 0; @@ -802,23 +816,14 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread, if (flags()->strict_init_order) StopInitOrderChecking(); GET_STACK_TRACE_THREAD; + // FIXME: The CreateThread interceptor is not the same as a pthread_create + // one. This is a bandaid fix for PR22025. bool detached = false; // FIXME: how can we determine it on Windows? - ThreadStartParam param; - atomic_store(¶m.t, 0, memory_order_relaxed); - atomic_store(¶m.is_registered, 0, memory_order_relaxed); - DWORD result = REAL(CreateThread)(security, stack_size, asan_thread_start, - ¶m, thr_flags, tid); - if (result) { - u32 current_tid = GetCurrentTidOrInvalid(); - AsanThread *t = + u32 current_tid = GetCurrentTidOrInvalid(); + AsanThread *t = AsanThread::Create(start_routine, arg, current_tid, &stack, detached); - atomic_store(¶m.t, reinterpret_cast(t), memory_order_release); - // The pthread_create interceptor waits here, so we do the same for - // consistency. - while (atomic_load(¶m.is_registered, memory_order_acquire) == 0) - internal_sched_yield(); - } - return result; + return REAL(CreateThread)(security, stack_size, + asan_thread_start, t, thr_flags, tid); } namespace __asan { @@ -902,6 +907,7 @@ void InitializeAsanInterceptors() { // Intercept threading-related functions #if ASAN_INTERCEPT_PTHREAD_CREATE ASAN_INTERCEPT_FUNC(pthread_create); + ASAN_INTERCEPT_FUNC(pthread_join); #endif // Intercept atexit function. diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h index 65d4a47d3d9e..a8e23ce772ff 100644 --- a/lib/asan/asan_internal.h +++ b/lib/asan/asan_internal.h @@ -94,7 +94,6 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); void AsanOnSIGSEGV(int, void *siginfo, void *context); void MaybeReexec(); -bool AsanInterceptsSignal(int signum); void ReadContextStack(void *context, uptr *stack, uptr *ssize); void AsanPlatformThreadInit(); void StopInitOrderChecking(); @@ -107,10 +106,10 @@ void PlatformTSDDtor(void *tsd); void AppendToErrorMessageBuffer(const char *buffer); -void ParseExtraActivationFlags(); - void *AsanDlSymNext(const char *sym); +void ReserveShadowMemoryRange(uptr beg, uptr end); + // Platform-specific options. #if SANITIZER_MAC bool PlatformHasDifferentMemcpyAndMemmove(); diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc index fdd009c960d8..65605009005f 100644 --- a/lib/asan/asan_linux.cc +++ b/lib/asan/asan_linux.cc @@ -220,10 +220,6 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { #endif } -bool AsanInterceptsSignal(int signum) { - return signum == SIGSEGV && common_flags()->handle_segv; -} - void AsanPlatformThreadInit() { // Nothing here for now. } diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc index ae0fa15b6523..5c2caeae4934 100644 --- a/lib/asan/asan_mac.cc +++ b/lib/asan/asan_mac.cc @@ -102,7 +102,6 @@ void LeakyResetEnv(const char *name, const char *name_value) { } void MaybeReexec() { - if (!flags()->allow_reexec) return; // Make sure the dynamic ASan runtime library is preloaded so that the // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec // ourselves. @@ -113,8 +112,10 @@ void MaybeReexec() { uptr old_env_len = dyld_insert_libraries ? internal_strlen(dyld_insert_libraries) : 0; uptr fname_len = internal_strlen(info.dli_fname); + const char *dylib_name = StripModuleName(info.dli_fname); + uptr dylib_name_len = internal_strlen(dylib_name); if (!dyld_insert_libraries || - !REAL(strstr)(dyld_insert_libraries, StripModuleName(info.dli_fname))) { + !REAL(strstr)(dyld_insert_libraries, dylib_name)) { // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime // library. char program_name[1024]; @@ -140,58 +141,74 @@ void MaybeReexec() { VReport(1, "exec()-ing the program with\n"); VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env); VReport(1, "to enable ASan wrappers.\n"); - VReport(1, "Set ASAN_OPTIONS=allow_reexec=0 to disable this.\n"); execv(program_name, *_NSGetArgv()); - } else { - // DYLD_INSERT_LIBRARIES is set and contains the runtime library. - if (old_env_len == fname_len) { - // It's just the runtime library name - fine to unset the variable. - LeakyResetEnv(kDyldInsertLibraries, NULL); - } else { - uptr env_name_len = internal_strlen(kDyldInsertLibraries); - // Allocate memory to hold the previous env var name, its value, the '=' - // sign and the '\0' char. - char *new_env = (char*)allocator_for_env.Allocate( - old_env_len + 2 + env_name_len); - CHECK(new_env); - internal_memset(new_env, '\0', old_env_len + 2 + env_name_len); - internal_strncpy(new_env, kDyldInsertLibraries, env_name_len); - new_env[env_name_len] = '='; - char *new_env_pos = new_env + env_name_len + 1; - // Iterate over colon-separated pieces of |dyld_insert_libraries|. - char *piece_start = dyld_insert_libraries; - char *piece_end = NULL; - char *old_env_end = dyld_insert_libraries + old_env_len; - do { - if (piece_start[0] == ':') piece_start++; - piece_end = REAL(strchr)(piece_start, ':'); - if (!piece_end) piece_end = dyld_insert_libraries + old_env_len; - if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break; - uptr piece_len = piece_end - piece_start; - - // If the current piece isn't the runtime library name, - // append it to new_env. - if ((piece_len != fname_len) || - (internal_strncmp(piece_start, info.dli_fname, fname_len) != 0)) { - if (new_env_pos != new_env + env_name_len + 1) { - new_env_pos[0] = ':'; - new_env_pos++; - } - internal_strncpy(new_env_pos, piece_start, piece_len); - } - // Move on to the next piece. - new_env_pos += piece_len; - piece_start = piece_end; - } while (piece_start < old_env_end); - - // Can't use setenv() here, because it requires the allocator to be - // initialized. - // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in - // a separate function called after InitializeAllocator(). - LeakyResetEnv(kDyldInsertLibraries, new_env); - } + // We get here only if execv() failed. + Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, " + "which is required for ASan to work. ASan tried to set the " + "environment variable and re-execute itself, but execv() failed, " + "possibly because of sandbox restrictions. Make sure to launch the " + "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env); + CHECK("execv failed" && 0); } + + // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove + // the dylib from the environment variable, because interceptors are installed + // and we don't want our children to inherit the variable. + + uptr env_name_len = internal_strlen(kDyldInsertLibraries); + // Allocate memory to hold the previous env var name, its value, the '=' + // sign and the '\0' char. + char *new_env = (char*)allocator_for_env.Allocate( + old_env_len + 2 + env_name_len); + CHECK(new_env); + internal_memset(new_env, '\0', old_env_len + 2 + env_name_len); + internal_strncpy(new_env, kDyldInsertLibraries, env_name_len); + new_env[env_name_len] = '='; + char *new_env_pos = new_env + env_name_len + 1; + + // Iterate over colon-separated pieces of |dyld_insert_libraries|. + char *piece_start = dyld_insert_libraries; + char *piece_end = NULL; + char *old_env_end = dyld_insert_libraries + old_env_len; + do { + if (piece_start[0] == ':') piece_start++; + piece_end = REAL(strchr)(piece_start, ':'); + if (!piece_end) piece_end = dyld_insert_libraries + old_env_len; + if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break; + uptr piece_len = piece_end - piece_start; + + char *filename_start = + (char *)internal_memrchr(piece_start, '/', piece_len); + uptr filename_len = piece_len; + if (filename_start) { + filename_start += 1; + filename_len = piece_len - (filename_start - piece_start); + } else { + filename_start = piece_start; + } + + // If the current piece isn't the runtime library name, + // append it to new_env. + if ((dylib_name_len != filename_len) || + (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) { + if (new_env_pos != new_env + env_name_len + 1) { + new_env_pos[0] = ':'; + new_env_pos++; + } + internal_strncpy(new_env_pos, piece_start, piece_len); + new_env_pos += piece_len; + } + // Move on to the next piece. + piece_start = piece_end; + } while (piece_start < old_env_end); + + // Can't use setenv() here, because it requires the allocator to be + // initialized. + // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in + // a separate function called after InitializeAllocator(). + if (new_env_pos == new_env + env_name_len + 1) new_env = NULL; + LeakyResetEnv(kDyldInsertLibraries, new_env); } // No-op. Mac does not support static linkage anyway. @@ -205,11 +222,6 @@ void AsanCheckDynamicRTPrereqs() {} // No-op. Mac does not support static linkage anyway. void AsanCheckIncompatibleRT() {} -bool AsanInterceptsSignal(int signum) { - return (signum == SIGSEGV || signum == SIGBUS) && - common_flags()->handle_segv; -} - void AsanPlatformThreadInit() { } @@ -312,7 +324,7 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, dispatch_function_t func) { \ GET_STACK_TRACE_THREAD; \ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \ - if (common_flags()->verbosity >= 2) { \ + if (Verbosity() >= 2) { \ Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \ asan_ctxt, pthread_self()); \ PRINT_CURRENT_STACK(); \ @@ -330,7 +342,7 @@ INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, dispatch_function_t func) { GET_STACK_TRACE_THREAD; asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (common_flags()->verbosity >= 2) { + if (Verbosity() >= 2) { Report("dispatch_after_f: %p\n", asan_ctxt); PRINT_CURRENT_STACK(); } @@ -343,7 +355,7 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, dispatch_function_t func) { GET_STACK_TRACE_THREAD; asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (common_flags()->verbosity >= 2) { + if (Verbosity() >= 2) { Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", asan_ctxt, pthread_self()); PRINT_CURRENT_STACK(); @@ -373,13 +385,6 @@ void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void)); work(); \ } -// Forces the compiler to generate a frame pointer in the function. -#define ENABLE_FRAME_POINTER \ - do { \ - volatile uptr enable_fp; \ - enable_fp = GET_CURRENT_FRAME(); \ - } while (0) - INTERCEPTOR(void, dispatch_async, dispatch_queue_t dq, void(^work)(void)) { ENABLE_FRAME_POINTER; @@ -403,6 +408,10 @@ INTERCEPTOR(void, dispatch_after, INTERCEPTOR(void, dispatch_source_set_cancel_handler, dispatch_source_t ds, void(^work)(void)) { + if (!work) { + REAL(dispatch_source_set_cancel_handler)(ds, work); + return; + } ENABLE_FRAME_POINTER; GET_ASAN_BLOCK(work); REAL(dispatch_source_set_cancel_handler)(ds, asan_block); diff --git a/lib/asan/asan_malloc_mac.cc b/lib/asan/asan_malloc_mac.cc index 79b6dfae10f6..d7a6307c9bdc 100644 --- a/lib/asan/asan_malloc_mac.cc +++ b/lib/asan/asan_malloc_mac.cc @@ -152,13 +152,17 @@ INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) { namespace { -// TODO(glider): the mz_* functions should be united with the Linux wrappers, -// as they are basically copied from there. -size_t mz_size(malloc_zone_t* zone, const void* ptr) { +// TODO(glider): the __asan_mz_* functions should be united with the Linux +// wrappers, as they are basically copied from there. +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +size_t __asan_mz_size(malloc_zone_t* zone, const void* ptr) { return asan_mz_size(ptr); } -void *mz_malloc(malloc_zone_t *zone, size_t size) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__asan_mz_malloc(malloc_zone_t *zone, uptr size) { if (UNLIKELY(!asan_inited)) { CHECK(system_malloc_zone); return malloc_zone_malloc(system_malloc_zone, size); @@ -167,7 +171,9 @@ void *mz_malloc(malloc_zone_t *zone, size_t size) { return asan_malloc(size, &stack); } -void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__asan_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { if (UNLIKELY(!asan_inited)) { // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. const size_t kCallocPoolSize = 1024; @@ -183,7 +189,9 @@ void *mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) { return asan_calloc(nmemb, size, &stack); } -void *mz_valloc(malloc_zone_t *zone, size_t size) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__asan_mz_valloc(malloc_zone_t *zone, size_t size) { if (UNLIKELY(!asan_inited)) { CHECK(system_malloc_zone); return malloc_zone_valloc(system_malloc_zone, size); @@ -210,11 +218,15 @@ void ALWAYS_INLINE free_common(void *context, void *ptr) { } // TODO(glider): the allocation callbacks need to be refactored. -void mz_free(malloc_zone_t *zone, void *ptr) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void __asan_mz_free(malloc_zone_t *zone, void *ptr) { free_common(zone, ptr); } -void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__asan_mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { if (!ptr) { GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); @@ -233,15 +245,16 @@ void *mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) { } } -void mz_destroy(malloc_zone_t* zone) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void __asan_mz_destroy(malloc_zone_t* zone) { // A no-op -- we will not be destroyed! - Report("mz_destroy() called -- ignoring\n"); + Report("__asan_mz_destroy() called -- ignoring\n"); } - // from AvailabilityMacros.h -#if defined(MAC_OS_X_VERSION_10_6) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 -void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { +extern "C" +SANITIZER_INTERFACE_ATTRIBUTE +void *__asan_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { if (UNLIKELY(!asan_inited)) { CHECK(system_malloc_zone); return malloc_zone_memalign(system_malloc_zone, align, size); @@ -252,12 +265,12 @@ void *mz_memalign(malloc_zone_t *zone, size_t align, size_t size) { // This function is currently unused, and we build with -Werror. #if 0 -void mz_free_definite_size(malloc_zone_t* zone, void *ptr, size_t size) { +void __asan_mz_free_definite_size( + malloc_zone_t* zone, void *ptr, size_t size) { // TODO(glider): check that |size| is valid. UNIMPLEMENTED(); } #endif -#endif kern_return_t mi_enumerator(task_t task, void *, unsigned type_mask, vm_address_t zone_address, @@ -299,13 +312,10 @@ void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) { internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t)); } -#if defined(MAC_OS_X_VERSION_10_6) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 boolean_t mi_zone_locked(malloc_zone_t *zone) { // UNIMPLEMENTED(); return false; } -#endif } // unnamed namespace @@ -324,32 +334,25 @@ void ReplaceSystemMalloc() { asan_introspection.force_lock = &mi_force_lock; asan_introspection.force_unlock = &mi_force_unlock; asan_introspection.statistics = &mi_statistics; + asan_introspection.zone_locked = &mi_zone_locked; internal_memset(&asan_zone, 0, sizeof(malloc_zone_t)); - // Start with a version 4 zone which is used for OS X 10.4 and 10.5. - asan_zone.version = 4; + // Use version 6 for OSX >= 10.6. + asan_zone.version = 6; asan_zone.zone_name = "asan"; - asan_zone.size = &mz_size; - asan_zone.malloc = &mz_malloc; - asan_zone.calloc = &mz_calloc; - asan_zone.valloc = &mz_valloc; - asan_zone.free = &mz_free; - asan_zone.realloc = &mz_realloc; - asan_zone.destroy = &mz_destroy; + asan_zone.size = &__asan_mz_size; + asan_zone.malloc = &__asan_mz_malloc; + asan_zone.calloc = &__asan_mz_calloc; + asan_zone.valloc = &__asan_mz_valloc; + asan_zone.free = &__asan_mz_free; + asan_zone.realloc = &__asan_mz_realloc; + asan_zone.destroy = &__asan_mz_destroy; asan_zone.batch_malloc = 0; asan_zone.batch_free = 0; - asan_zone.introspect = &asan_introspection; - - // from AvailabilityMacros.h -#if defined(MAC_OS_X_VERSION_10_6) && \ - MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - // Switch to version 6 on OSX 10.6 to support memalign. - asan_zone.version = 6; asan_zone.free_definite_size = 0; - asan_zone.memalign = &mz_memalign; - asan_introspection.zone_locked = &mi_zone_locked; -#endif + asan_zone.memalign = &__asan_mz_memalign; + asan_zone.introspect = &asan_introspection; // Register the ASan zone. malloc_zone_register(&asan_zone); diff --git a/lib/asan/asan_mapping.h b/lib/asan/asan_mapping.h index 2746754152b6..5cb011d683d3 100644 --- a/lib/asan/asan_mapping.h +++ b/lib/asan/asan_mapping.h @@ -59,13 +59,20 @@ // || `[0x20000000, 0x23ffffff]` || LowShadow || // || `[0x00000000, 0x1fffffff]` || LowMem || // -// Default Linux/MIPS mapping: +// Default Linux/MIPS32 mapping: // || `[0x2aaa0000, 0xffffffff]` || HighMem || // || `[0x0fff4000, 0x2aa9ffff]` || HighShadow || // || `[0x0bff4000, 0x0fff3fff]` || ShadowGap || // || `[0x0aaa0000, 0x0bff3fff]` || LowShadow || // || `[0x00000000, 0x0aa9ffff]` || LowMem || // +// Default Linux/MIPS64 mapping: +// || `[0x4000000000, 0xffffffffff]` || HighMem || +// || `[0x2800000000, 0x3fffffffff]` || HighShadow || +// || `[0x2400000000, 0x27ffffffff]` || ShadowGap || +// || `[0x2000000000, 0x23ffffffff]` || LowShadow || +// || `[0x0000000000, 0x1fffffffff]` || LowMem || +// // Shadow mapping on FreeBSD/x86-64 with SHADOW_OFFSET == 0x400000000000: // || `[0x500000000000, 0x7fffffffffff]` || HighMem || // || `[0x4a0000000000, 0x4fffffffffff]` || HighShadow || @@ -79,6 +86,15 @@ // || `[0x48000000, 0x4bffffff]` || ShadowGap || // || `[0x40000000, 0x47ffffff]` || LowShadow || // || `[0x00000000, 0x3fffffff]` || LowMem || +// +// Default Windows/i386 mapping: +// (the exact location of HighShadow/HighMem may vary depending +// on WoW64, /LARGEADDRESSAWARE, etc). +// || `[0x50000000, 0xffffffff]` || HighMem || +// || `[0x3a000000, 0x4fffffff]` || HighShadow || +// || `[0x36000000, 0x39ffffff]` || ShadowGap || +// || `[0x30000000, 0x35ffffff]` || LowShadow || +// || `[0x00000000, 0x2fffffff]` || LowMem || static const u64 kDefaultShadowScale = 3; static const u64 kDefaultShadowOffset32 = 1ULL << 29; // 0x20000000 @@ -87,10 +103,11 @@ static const u64 kDefaultShadowOffset64 = 1ULL << 44; static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G. static const u64 kAArch64_ShadowOffset64 = 1ULL << 36; static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000; -static const u64 kMIPS64_ShadowOffset64 = 1ULL << 36; +static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37; static const u64 kPPC64_ShadowOffset64 = 1ULL << 41; static const u64 kFreeBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000 static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000 +static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 #define SHADOW_SCALE kDefaultShadowScale #if SANITIZER_ANDROID @@ -101,12 +118,12 @@ static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000 # define SHADOW_OFFSET kMIPS32_ShadowOffset32 # elif SANITIZER_FREEBSD # define SHADOW_OFFSET kFreeBSD_ShadowOffset32 +# elif SANITIZER_IOS +# define SHADOW_OFFSET kIosShadowOffset32 +# elif SANITIZER_WINDOWS +# define SHADOW_OFFSET kWindowsShadowOffset32 # else -# if SANITIZER_IOS -# define SHADOW_OFFSET kIosShadowOffset32 -# else -# define SHADOW_OFFSET kDefaultShadowOffset32 -# endif +# define SHADOW_OFFSET kDefaultShadowOffset32 # endif # else # if defined(__aarch64__) diff --git a/lib/asan/asan_poisoning.cc b/lib/asan/asan_poisoning.cc index 1c6e92f69c65..e2b1f4dc4d5e 100644 --- a/lib/asan/asan_poisoning.cc +++ b/lib/asan/asan_poisoning.cc @@ -15,13 +15,24 @@ #include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" +#include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_flags.h" namespace __asan { +static atomic_uint8_t can_poison_memory; + +void SetCanPoisonMemory(bool value) { + atomic_store(&can_poison_memory, value, memory_order_release); +} + +bool CanPoisonMemory() { + return atomic_load(&can_poison_memory, memory_order_acquire); +} + void PoisonShadow(uptr addr, uptr size, u8 value) { - if (!flags()->poison_heap) return; + if (!CanPoisonMemory()) return; CHECK(AddrIsAlignedByGranularity(addr)); CHECK(AddrIsInMem(addr)); CHECK(AddrIsAlignedByGranularity(addr + size)); @@ -34,7 +45,7 @@ void PoisonShadowPartialRightRedzone(uptr addr, uptr size, uptr redzone_size, u8 value) { - if (!flags()->poison_heap) return; + if (!CanPoisonMemory()) return; CHECK(AddrIsAlignedByGranularity(addr)); CHECK(AddrIsInMem(addr)); FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value); @@ -63,10 +74,10 @@ void FlushUnneededASanShadowMemory(uptr p, uptr size) { void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) { uptr end = ptr + size; - if (common_flags()->verbosity) { + if (Verbosity()) { Printf("__asan_%spoison_intra_object_redzone [%p,%p) %zd\n", poison ? "" : "un", ptr, end, size); - if (common_flags()->verbosity >= 2) + if (Verbosity() >= 2) PRINT_CURRENT_STACK(); } CHECK(size); diff --git a/lib/asan/asan_poisoning.h b/lib/asan/asan_poisoning.h index feda1a984544..3fc94649fb39 100644 --- a/lib/asan/asan_poisoning.h +++ b/lib/asan/asan_poisoning.h @@ -19,6 +19,10 @@ namespace __asan { +// Enable/disable memory poisoning. +void SetCanPoisonMemory(bool value); +bool CanPoisonMemory(); + // Poisons the shadow memory for "size" bytes starting from "addr". void PoisonShadow(uptr addr, uptr size, u8 value); @@ -34,7 +38,7 @@ void PoisonShadowPartialRightRedzone(uptr addr, // performance-critical code with care. ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, u8 value) { - DCHECK(flags()->poison_heap); + DCHECK(CanPoisonMemory()); uptr shadow_beg = MEM_TO_SHADOW(aligned_beg); uptr shadow_end = MEM_TO_SHADOW( aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1; @@ -60,15 +64,14 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, if (page_end != shadow_end) { REAL(memset)((void *)page_end, 0, shadow_end - page_end); } - void *res = MmapFixedNoReserve(page_beg, page_end - page_beg); - CHECK_EQ(page_beg, res); + ReserveShadowMemoryRange(page_beg, page_end - 1); } } } ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone( uptr aligned_addr, uptr size, uptr redzone_size, u8 value) { - DCHECK(flags()->poison_heap); + DCHECK(CanPoisonMemory()); bool poison_partial = flags()->poison_partial; u8 *shadow = (u8*)MEM_TO_SHADOW(aligned_addr); for (uptr i = 0; i < redzone_size; i += SHADOW_GRANULARITY, shadow++) { diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc index 0fb50276186b..8706d5decc0b 100644 --- a/lib/asan/asan_report.cc +++ b/lib/asan/asan_report.cc @@ -53,7 +53,7 @@ void AppendToErrorMessageBuffer(const char *buffer) { 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; + error_message_buffer_pos += Min(remaining, length); } } @@ -937,6 +937,8 @@ using namespace __asan; // NOLINT void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, uptr access_size) { + ENABLE_FRAME_POINTER; + // Determine the error type. const char *bug_descr = "unknown-crash"; if (AddrIsInMem(addr)) { diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index 34fb111d5607..0b2a23d40379 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -56,8 +56,6 @@ static void AsanDie() { } if (common_flags()->coverage) __sanitizer_cov_dump(); - if (death_callback) - death_callback(); if (flags()->abort_on_error) Abort(); internal__exit(flags()->exitcode); @@ -72,265 +70,9 @@ static void AsanCheckFailed(const char *file, int line, const char *cond, Die(); } -// -------------------------- Flags ------------------------- {{{1 -static const int kDefaultMallocContextSize = 30; - -Flags asan_flags_dont_use_directly; // use via flags(). - -static const char *MaybeCallAsanDefaultOptions() { - return (&__asan_default_options) ? __asan_default_options() : ""; -} - -static const char *MaybeUseAsanDefaultOptionsCompileDefinition() { -#ifdef ASAN_DEFAULT_OPTIONS -// Stringize the macro value. -# define ASAN_STRINGIZE(x) #x -# define ASAN_STRINGIZE_OPTIONS(options) ASAN_STRINGIZE(options) - return ASAN_STRINGIZE_OPTIONS(ASAN_DEFAULT_OPTIONS); -#else - return ""; -#endif -} - -static void ParseFlagsFromString(Flags *f, const char *str) { - CommonFlags *cf = common_flags(); - ParseCommonFlagsFromString(cf, str); - CHECK((uptr)cf->malloc_context_size <= kStackTraceMax); - // Please write meaningful flag descriptions when adding new flags. - ParseFlag(str, &f->quarantine_size, "quarantine_size", - "Size (in bytes) of quarantine used to detect use-after-free " - "errors. Lower value may reduce memory usage but increase the " - "chance of false negatives."); - ParseFlag(str, &f->redzone, "redzone", - "Minimal size (in bytes) of redzones around heap objects. " - "Requirement: redzone >= 16, is a power of two."); - ParseFlag(str, &f->max_redzone, "max_redzone", - "Maximal size (in bytes) of redzones around heap objects."); - CHECK_GE(f->redzone, 16); - CHECK_GE(f->max_redzone, f->redzone); - CHECK_LE(f->max_redzone, 2048); - CHECK(IsPowerOfTwo(f->redzone)); - CHECK(IsPowerOfTwo(f->max_redzone)); - - ParseFlag(str, &f->debug, "debug", - "If set, prints some debugging information and does additional checks."); - ParseFlag(str, &f->report_globals, "report_globals", - "Controls the way to handle globals (0 - don't detect buffer overflow on " - "globals, 1 - detect buffer overflow, 2 - print data about registered " - "globals)."); - - ParseFlag(str, &f->check_initialization_order, - "check_initialization_order", - "If set, attempts to catch initialization order issues."); - - ParseFlag(str, &f->replace_str, "replace_str", - "If set, uses custom wrappers and replacements for libc string functions " - "to find more errors."); - - ParseFlag(str, &f->replace_intrin, "replace_intrin", - "If set, uses custom wrappers for memset/memcpy/memmove intinsics."); - ParseFlag(str, &f->mac_ignore_invalid_free, "mac_ignore_invalid_free", - "Ignore invalid free() calls to work around some bugs. Used on OS X " - "only."); - ParseFlag(str, &f->detect_stack_use_after_return, - "detect_stack_use_after_return", - "Enables stack-use-after-return checking at run-time."); - ParseFlag(str, &f->min_uar_stack_size_log, "min_uar_stack_size_log", - "Minimum fake stack size log."); - ParseFlag(str, &f->max_uar_stack_size_log, "max_uar_stack_size_log", - "Maximum fake stack size log."); - ParseFlag(str, &f->uar_noreserve, "uar_noreserve", - "Use mmap with 'norserve' flag to allocate fake stack."); - ParseFlag(str, &f->max_malloc_fill_size, "max_malloc_fill_size", - "ASan allocator flag. max_malloc_fill_size is the maximal amount of " - "bytes that will be filled with malloc_fill_byte on malloc."); - ParseFlag(str, &f->malloc_fill_byte, "malloc_fill_byte", - "Value used to fill the newly allocated memory."); - ParseFlag(str, &f->exitcode, "exitcode", - "Override the program exit status if the tool found an error."); - ParseFlag(str, &f->allow_user_poisoning, "allow_user_poisoning", - "If set, user may manually mark memory regions as poisoned or " - "unpoisoned."); - ParseFlag(str, &f->sleep_before_dying, "sleep_before_dying", - "Number of seconds to sleep between printing an error report and " - "terminating the program. Useful for debugging purposes (e.g. when one " - "needs to attach gdb)."); - - ParseFlag(str, &f->check_malloc_usable_size, "check_malloc_usable_size", - "Allows the users to work around the bug in Nvidia drivers prior to " - "295.*."); - - ParseFlag(str, &f->unmap_shadow_on_exit, "unmap_shadow_on_exit", - "If set, explicitly unmaps the (huge) shadow at exit."); - ParseFlag(str, &f->abort_on_error, "abort_on_error", - "If set, the tool calls abort() instead of _exit() after printing the " - "error report."); - ParseFlag(str, &f->print_stats, "print_stats", - "Print various statistics after printing an error message or if " - "atexit=1."); - ParseFlag(str, &f->print_legend, "print_legend", - "Print the legend for the shadow bytes."); - ParseFlag(str, &f->atexit, "atexit", - "If set, prints ASan exit stats even after program terminates " - "successfully."); - - ParseFlag(str, &f->allow_reexec, "allow_reexec", - "Allow the tool to re-exec the program. This may interfere badly with " - "the debugger."); - - ParseFlag(str, &f->print_full_thread_history, - "print_full_thread_history", - "If set, prints thread creation stacks for the threads involved in the " - "report and their ancestors up to the main thread."); - - ParseFlag(str, &f->poison_heap, "poison_heap", - "Poison (or not) the heap memory on [de]allocation. Zero value is useful " - "for benchmarking the allocator or instrumentator."); - - ParseFlag(str, &f->poison_array_cookie, "poison_array_cookie", - "Poison (or not) the array cookie after operator new[]."); - - ParseFlag(str, &f->poison_partial, "poison_partial", - "If true, poison partially addressable 8-byte aligned words " - "(default=true). This flag affects heap and global buffers, but not " - "stack buffers."); - - ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch", - "Report errors on malloc/delete, new/free, new/delete[], etc."); - - ParseFlag(str, &f->new_delete_type_mismatch, "new_delete_type_mismatch", - "Report errors on mismatch betwen size of new and delete."); - - ParseFlag(str, &f->strict_memcmp, "strict_memcmp", - "If true, assume that memcmp(p1, p2, n) always reads n bytes before " - "comparing p1 and p2."); - - ParseFlag(str, &f->strict_init_order, "strict_init_order", - "If true, assume that dynamic initializers can never access globals from " - "other modules, even if the latter are already initialized."); - - ParseFlag(str, &f->start_deactivated, "start_deactivated", - "If true, ASan tweaks a bunch of other flags (quarantine, redzone, heap " - "poisoning) to reduce memory consumption as much as possible, and " - "restores them to original values when the first instrumented module is " - "loaded into the process. This is mainly intended to be used on " - "Android. "); - - ParseFlag(str, &f->detect_invalid_pointer_pairs, - "detect_invalid_pointer_pairs", - "If non-zero, try to detect operations like <, <=, >, >= and - on " - "invalid pointer pairs (e.g. when pointers belong to different objects). " - "The bigger the value the harder we try."); - - ParseFlag(str, &f->detect_container_overflow, - "detect_container_overflow", - "If true, honor the container overflow annotations. " - "See https://code.google.com/p/address-sanitizer/wiki/ContainerOverflow"); - - ParseFlag(str, &f->detect_odr_violation, "detect_odr_violation", - "If >=2, detect violation of One-Definition-Rule (ODR); " - "If ==1, detect ODR-violation only if the two variables " - "have different sizes"); - - ParseFlag(str, &f->dump_instruction_bytes, "dump_instruction_bytes", - "If true, dump 16 bytes starting at the instruction that caused SEGV"); -} - -void InitializeFlags(Flags *f, const char *env) { - CommonFlags *cf = common_flags(); - SetCommonFlagsDefaults(cf); - cf->detect_leaks = CAN_SANITIZE_LEAKS; - cf->external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); - cf->malloc_context_size = kDefaultMallocContextSize; - cf->intercept_tls_get_addr = true; - cf->coverage = false; - - internal_memset(f, 0, sizeof(*f)); - f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28; - f->redzone = 16; - f->max_redzone = 2048; - f->debug = false; - f->report_globals = 1; - f->check_initialization_order = false; - f->replace_str = true; - f->replace_intrin = true; - f->mac_ignore_invalid_free = false; - f->detect_stack_use_after_return = false; // Also needs the compiler flag. - f->min_uar_stack_size_log = 16; // We can't do smaller anyway. - f->max_uar_stack_size_log = 20; // 1Mb per size class, i.e. ~11Mb per thread. - f->uar_noreserve = false; - f->max_malloc_fill_size = 0x1000; // By default, fill only the first 4K. - f->malloc_fill_byte = 0xbe; - f->exitcode = ASAN_DEFAULT_FAILURE_EXITCODE; - f->allow_user_poisoning = true; - f->sleep_before_dying = 0; - f->check_malloc_usable_size = true; - f->unmap_shadow_on_exit = false; - f->abort_on_error = false; - f->print_stats = false; - f->print_legend = true; - f->atexit = false; - f->allow_reexec = true; - f->print_full_thread_history = true; - f->poison_heap = true; - f->poison_array_cookie = true; - f->poison_partial = true; - // Turn off alloc/dealloc mismatch checker on Mac and Windows for now. - // https://code.google.com/p/address-sanitizer/issues/detail?id=131 - // https://code.google.com/p/address-sanitizer/issues/detail?id=309 - // TODO(glider,timurrrr): Fix known issues and enable this back. - f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0); - f->new_delete_type_mismatch = true; - f->strict_memcmp = true; - f->strict_init_order = false; - f->start_deactivated = false; - f->detect_invalid_pointer_pairs = 0; - f->detect_container_overflow = true; - f->detect_odr_violation = 2; - f->dump_instruction_bytes = false; - - // Override from compile definition. - ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefinition()); - - // Override from user-specified string. - ParseFlagsFromString(f, MaybeCallAsanDefaultOptions()); - VReport(1, "Using the defaults from __asan_default_options: %s\n", - MaybeCallAsanDefaultOptions()); - - // Override from command line. - ParseFlagsFromString(f, env); - if (common_flags()->help) { - PrintFlagDescriptions(); - } - - if (!CAN_SANITIZE_LEAKS && cf->detect_leaks) { - Report("%s: detect_leaks is not supported on this platform.\n", - SanitizerToolName); - cf->detect_leaks = false; - } - - // Make "strict_init_order" imply "check_initialization_order". - // TODO(samsonov): Use a single runtime flag for an init-order checker. - if (f->strict_init_order) { - f->check_initialization_order = true; - } -} - -// Parse flags that may change between startup and activation. -// On Android they come from a system property. -// On other platforms this is no-op. -void ParseExtraActivationFlags() { - char buf[100]; - GetExtraActivationFlags(buf, sizeof(buf)); - ParseFlagsFromString(flags(), buf); - if (buf[0] != '\0') - VReport(1, "Extra activation flags: %s\n", buf); -} - // -------------------------- Globals --------------------- {{{1 int asan_inited; bool asan_init_is_running; -void (*death_callback)(void); #if !ASAN_FIXED_MAPPING uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; @@ -344,7 +86,8 @@ void ShowStatsAndAbort() { // ---------------------- mmap -------------------- {{{1 // Reserve memory range [beg, end]. -static void ReserveShadowMemoryRange(uptr beg, uptr end) { +// We need to use inclusive range because end+1 may not be representable. +void ReserveShadowMemoryRange(uptr beg, uptr end) { CHECK_EQ((beg % GetPageSizeCached()), 0); CHECK_EQ(((end + 1) % GetPageSizeCached()), 0); uptr size = end - beg + 1; @@ -355,6 +98,10 @@ static void ReserveShadowMemoryRange(uptr beg, uptr end) { "Perhaps you're using ulimit -v\n", size); Abort(); } + if (common_flags()->no_huge_pages_for_shadow) + NoHugePagesInRegion(beg, size); + if (common_flags()->use_madv_dontdump) + DontDumpShadowMemory(beg, size); } // --------------- LowLevelAllocateCallbac ---------- {{{1 @@ -500,7 +247,13 @@ static void InitializeHighMemEnd() { } static void ProtectGap(uptr a, uptr size) { - CHECK_EQ(a, (uptr)Mprotect(a, size)); + void *res = Mprotect(a, size); + if (a == (uptr)res) + return; + Report("ERROR: Failed to protect the shadow gap. " + "ASan cannot proceed correctly. ABORTING.\n"); + DumpProcessMap(); + Die(); } static void PrintAddressSpaceLayout() { @@ -539,7 +292,7 @@ static void PrintAddressSpaceLayout() { Printf("\n"); Printf("redzone=%zu\n", (uptr)flags()->redzone); Printf("max_redzone=%zu\n", (uptr)flags()->max_redzone); - Printf("quarantine_size=%zuM\n", (uptr)flags()->quarantine_size >> 20); + Printf("quarantine_size_mb=%zuM\n", (uptr)flags()->quarantine_size_mb); Printf("malloc_context_size=%zu\n", (uptr)common_flags()->malloc_context_size); @@ -561,8 +314,10 @@ static void AsanInitInternal() { // 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); + InitializeFlags(flags()); + + SetCanPoisonMemory(flags()->poison_heap); + SetMallocContextSize(common_flags()->malloc_context_size); InitializeHighMemEnd(); @@ -574,20 +329,11 @@ static void AsanInitInternal() { SetCheckFailedCallback(AsanCheckFailed); SetPrintfAndReportCallback(AppendToErrorMessageBuffer); - if (!flags()->start_deactivated) - ParseExtraActivationFlags(); - __sanitizer_set_report_path(common_flags()->log_path); + + // Enable UAR detection, if required. __asan_option_detect_stack_use_after_return = flags()->detect_stack_use_after_return; - CHECK_LE(flags()->min_uar_stack_size_log, flags()->max_uar_stack_size_log); - - if (options) { - VReport(1, "Parsed ASAN_OPTIONS: %s\n", options); - } - - if (flags()->start_deactivated) - AsanStartDeactivated(); // Re-exec ourselves if we need to set additional env or command line args. MaybeReexec(); @@ -618,8 +364,7 @@ static void AsanInitInternal() { } #endif - if (common_flags()->verbosity) - PrintAddressSpaceLayout(); + if (Verbosity()) PrintAddressSpaceLayout(); DisableCoreDumperIfNecessary(); @@ -649,6 +394,8 @@ static void AsanInitInternal() { } else { Report("Shadow memory range interleaves with an existing memory mapping. " "ASan cannot proceed correctly. ABORTING.\n"); + Report("ASan shadow was supposed to be located in the [%p-%p] range.\n", + shadow_start, kHighShadowEnd); DumpProcessMap(); Die(); } @@ -656,7 +403,12 @@ static void AsanInitInternal() { AsanTSDInit(PlatformTSDDtor); InstallDeadlySignalHandlers(AsanOnSIGSEGV); - InitializeAllocator(); + AllocatorOptions allocator_options; + allocator_options.SetFrom(flags(), common_flags()); + InitializeAllocator(allocator_options); + + MaybeStartBackgroudThread(); + SetSoftRssLimitExceededCallback(AsanSoftRssLimitExceededCallback); // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited // should be set to 1 prior to initializing the threads. @@ -666,10 +418,12 @@ static void AsanInitInternal() { if (flags()->atexit) Atexit(asan_atexit); - if (common_flags()->coverage) { - __sanitizer_cov_init(); - Atexit(__sanitizer_cov_dump); - } + InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); + + // Now that ASan runtime is (mostly) initialized, deactivate it if + // necessary, so that it can be re-activated when requested. + if (flags()->start_deactivated) + AsanDeactivate(); // interceptors InitTlsSize(); @@ -724,13 +478,6 @@ static AsanInitializer asan_initializer; // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT -#if !SANITIZER_SUPPORTS_WEAK_HOOKS -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_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; @@ -764,7 +511,7 @@ void NOINLINE __asan_handle_no_return() { } void NOINLINE __asan_set_death_callback(void (*callback)(void)) { - death_callback = callback; + SetUserDieCallback(callback); } // Initialize as requested from instrumented application code. diff --git a/lib/asan/asan_stack.cc b/lib/asan/asan_stack.cc index 8188f3b5b6e9..cf7a587fa65a 100644 --- a/lib/asan/asan_stack.cc +++ b/lib/asan/asan_stack.cc @@ -13,6 +13,21 @@ //===----------------------------------------------------------------------===// #include "asan_internal.h" #include "asan_stack.h" +#include "sanitizer_common/sanitizer_atomic.h" + +namespace __asan { + +static atomic_uint32_t malloc_context_size; + +void SetMallocContextSize(u32 size) { + atomic_store(&malloc_context_size, size, memory_order_release); +} + +u32 GetMallocContextSize() { + return atomic_load(&malloc_context_size, memory_order_acquire); +} + +} // namespace __asan // ------------------ Interface -------------- {{{1 diff --git a/lib/asan/asan_stack.h b/lib/asan/asan_stack.h index a995256212e1..122967a152f8 100644 --- a/lib/asan/asan_stack.h +++ b/lib/asan/asan_stack.h @@ -21,6 +21,11 @@ namespace __asan { +static const u32 kDefaultMallocContextSize = 30; + +void SetMallocContextSize(u32 size); +u32 GetMallocContextSize(); + // 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. @@ -93,9 +98,8 @@ void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth, #define GET_STACK_TRACE_THREAD \ GET_STACK_TRACE(kStackTraceMax, true) -#define GET_STACK_TRACE_MALLOC \ - GET_STACK_TRACE(common_flags()->malloc_context_size, \ - common_flags()->fast_unwind_on_malloc) +#define GET_STACK_TRACE_MALLOC \ + GET_STACK_TRACE(GetMallocContextSize(), common_flags()->fast_unwind_on_malloc) #define GET_STACK_TRACE_FREE GET_STACK_TRACE_MALLOC diff --git a/lib/asan/scripts/asan_device_setup b/lib/asan/scripts/asan_device_setup index a620f51b0836..c143b9fab13c 100755 --- a/lib/asan/scripts/asan_device_setup +++ b/lib/asan/scripts/asan_device_setup @@ -87,6 +87,7 @@ if [[ x$device != x ]]; then fi echo '>> Remounting /system rw' +$ADB wait-for-device $ADB root $ADB wait-for-device $ADB remount @@ -184,7 +185,7 @@ cp "$ASAN_RT_PATH/$ASAN_RT" "$TMPDIR/" ASAN_OPTIONS=start_deactivated=1,alloc_dealloc_mismatch=0 # On Android-L not allowing user segv handler breaks some applications. -if $ADB shell 'echo $LD_PRELOAD' | grep libsigchain.so >&/dev/null; then +if [[ PRE_L -eq 0 ]]; then ASAN_OPTIONS="$ASAN_OPTIONS,allow_user_segv_handler=1" fi diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py index 5fca136b6950..59fceaaed814 100755 --- a/lib/asan/scripts/asan_symbolize.py +++ b/lib/asan/scripts/asan_symbolize.py @@ -11,11 +11,9 @@ import bisect import getopt import os -import pty import re import subprocess import sys -import termios symbolizers = {} DEBUG = False @@ -171,6 +169,9 @@ class UnbufferedLineConverter(object): output. Uses pty to trick the child into providing unbuffered output. """ def __init__(self, args, close_stderr=False): + # Local imports so that the script can start on Windows. + import pty + import termios pid, fd = pty.fork() if pid == 0: # We're the child. Transfer control to command. @@ -341,17 +342,23 @@ def symbolize(self, addr, binary, offset): class SymbolizationLoop(object): def __init__(self, binary_name_filter=None, dsym_hint_producer=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.dsym_hint_producer = dsym_hint_producer - self.system = os.uname()[0] - if self.system not in ['Linux', 'Darwin', 'FreeBSD']: - raise Exception('Unknown system') - self.llvm_symbolizers = {} - self.last_llvm_symbolizer = None - self.dsym_hints = set([]) - self.frame_no = 0 + if sys.platform == 'win32': + # ASan on Windows uses dbghelp.dll to symbolize in-process, which works + # even in sandboxed processes. Nothing needs to be done here. + self.process_line = self.process_line_echo + else: + # 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.dsym_hint_producer = dsym_hint_producer + self.system = os.uname()[0] + if self.system not in ['Linux', 'Darwin', 'FreeBSD']: + raise Exception('Unknown system') + self.llvm_symbolizers = {} + self.last_llvm_symbolizer = None + self.dsym_hints = set([]) + self.frame_no = 0 + self.process_line = self.process_line_posix def symbolize_address(self, addr, binary, offset): # On non-Darwin (i.e. on platforms without .dSYM debug info) always use @@ -366,12 +373,12 @@ def symbolize_address(self, addr, binary, offset): # 3. otherwise create a new symbolizer and pass all currently known # .dSYM hints to it. if not binary in self.llvm_symbolizers: - use_last_symbolizer = True + use_new_symbolizer = True if self.system == 'Darwin' and self.dsym_hint_producer: dsym_hints_for_binary = set(self.dsym_hint_producer(binary)) - use_last_symbolizer = bool(dsym_hints_for_binary - self.dsym_hints) + use_new_symbolizer = bool(dsym_hints_for_binary - self.dsym_hints) self.dsym_hints |= dsym_hints_for_binary - if self.last_llvm_symbolizer and use_last_symbolizer: + if self.last_llvm_symbolizer and not use_new_symbolizer: self.llvm_symbolizers[binary] = self.last_llvm_symbolizer else: self.last_llvm_symbolizer = LLVMSymbolizerFactory( @@ -405,14 +412,14 @@ def get_symbolized_lines(self, symbolized_lines): def process_logfile(self): self.frame_no = 0 - while True: - line = logfile.readline() - if not line: - break + for line in logfile: processed = self.process_line(line) print '\n'.join(processed) - def process_line(self, line): + def process_line_echo(self, line): + return [line.rstrip()] + + def process_line_posix(self, line): self.current_line = line.rstrip() #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) stack_trace_line_format = ( @@ -437,20 +444,23 @@ def process_line(self, line): if __name__ == '__main__': - parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, - description='ASan symbolization script', - epilog='''Example of use: - asan_symbolize.py -c "$HOME/opt/cross/bin/arm-linux-gnueabi-" -s "$HOME/SymbolFiles" < asan.log''') + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description='ASan symbolization script', + epilog='Example of use:\n' + 'asan_symbolize.py -c "$HOME/opt/cross/bin/arm-linux-gnueabi-" ' + '-s "$HOME/SymbolFiles" < asan.log') parser.add_argument('path_to_cut', nargs='*', - help='pattern to be cut from the result file path ') + help='pattern to be cut from the result file path ') parser.add_argument('-d','--demangle', action='store_true', - help='demangle function names') + help='demangle function names') parser.add_argument('-s', metavar='SYSROOT', - help='set path to sysroot for sanitized binaries') + help='set path to sysroot for sanitized binaries') parser.add_argument('-c', metavar='CROSS_COMPILE', - help='set prefix for binutils') - parser.add_argument('-l','--logfile', default=sys.stdin, type=argparse.FileType('r'), - help='set log file name to parse, default is stdin') + help='set prefix for binutils') + parser.add_argument('-l','--logfile', default=sys.stdin, + type=argparse.FileType('r'), + help='set log file name to parse, default is stdin') args = parser.parse_args() if args.path_to_cut: fix_filename_patterns = args.path_to_cut diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt index 7b363714e8a5..513d1282f8a6 100644 --- a/lib/asan/tests/CMakeLists.txt +++ b/lib/asan/tests/CMakeLists.txt @@ -30,7 +30,8 @@ set(ASAN_UNITTEST_COMMON_CFLAGS -fno-rtti -O2 -Wno-format - -Werror=sign-compare) + -Werror=sign-compare + -Wno-non-virtual-dtor) append_list_if(COMPILER_RT_HAS_WVARIADIC_MACROS_FLAG -Wno-variadic-macros ASAN_UNITTEST_COMMON_CFLAGS) # -gline-tables-only must be enough for ASan, so use it if possible. @@ -46,6 +47,11 @@ list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -DASAN_HAS_EXCEPTIONS=1 -DASAN_UAR=0) +if(APPLE) + list(APPEND ASAN_UNITTEST_COMMON_CFLAGS ${DARWIN_osx_CFLAGS}) + list(APPEND ASAN_UNITTEST_COMMON_LINKFLAGS ${DARWIN_osx_LINKFLAGS}) +endif() + set(ASAN_BLACKLIST_FILE "${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore") set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS ${ASAN_UNITTEST_COMMON_CFLAGS} @@ -117,7 +123,7 @@ endmacro() # Link ASan unit test for a given architecture from a set # of objects in with given linker flags. macro(add_asan_test test_suite test_name arch kind) - parse_arguments(TEST "OBJECTS;LINKFLAGS" "WITH_TEST_RUNTIME" ${ARGN}) + parse_arguments(TEST "OBJECTS;LINKFLAGS;SUBDIR" "WITH_TEST_RUNTIME" ${ARGN}) get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) set(TEST_DEPS ${TEST_OBJECTS}) if(NOT COMPILER_RT_STANDALONE_BUILD) @@ -132,6 +138,7 @@ macro(add_asan_test test_suite test_name arch kind) endif() endif() add_compiler_rt_test(${test_suite} ${test_name} + SUBDIR ${TEST_SUBDIR} OBJECTS ${TEST_OBJECTS} DEPS ${TEST_DEPS} LINK_FLAGS ${TEST_LINKFLAGS} @@ -141,6 +148,11 @@ endmacro() # Main AddressSanitizer unit tests. add_custom_target(AsanUnitTests) set_target_properties(AsanUnitTests PROPERTIES FOLDER "ASan unit tests") +# AddressSanitizer unit tests with dynamic runtime (on platforms where it's +# not the default). +add_custom_target(AsanDynamicUnitTests) +set_target_properties(AsanDynamicUnitTests + PROPERTIES FOLDER "ASan unit tests with dynamic runtime") # ASan benchmarks (not actively used now). add_custom_target(AsanBenchmarks) set_target_properties(AsanBenchmarks PROPERTIES FOLDER "Asan benchmarks") @@ -182,11 +194,15 @@ macro(add_asan_tests_for_arch_and_kind arch kind) asan_compile(ASAN_INST_TEST_OBJECTS asan_mac_test_helpers.mm ${arch} ${kind} ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} -ObjC ${ARGN}) endif() - add_asan_test(AsanUnitTests "Asan-${arch}${kind}-Test" ${arch} ${kind} + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/default") + add_asan_test(AsanUnitTests "Asan-${arch}${kind}-Test" + ${arch} ${kind} SUBDIR "default" OBJECTS ${ASAN_INST_TEST_OBJECTS} LINKFLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}) - if(COMPILER_RT_BUILD_SHARED_ASAN) - add_asan_test(AsanUnitTests "Asan-${arch}${kind}-Dynamic-Test" ${arch} ${kind} + if(COMPILER_RT_ASAN_HAS_STATIC_RUNTIME) + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dynamic") + add_asan_test(AsanDynamicUnitTests "Asan-${arch}${kind}-Dynamic-Test" + ${arch} ${kind} SUBDIR "dynamic" OBJECTS ${ASAN_INST_TEST_OBJECTS} LINKFLAGS ${ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS}) endif() @@ -220,7 +236,8 @@ macro(add_asan_tests_for_arch_and_kind arch kind) asan_compile(ASAN_NOINST_TEST_OBJECTS ${src} ${arch} ${kind} ${ASAN_UNITTEST_COMMON_CFLAGS} ${ARGN}) endforeach() - add_asan_test(AsanUnitTests "Asan-${arch}${kind}-Noinst-Test" ${arch} ${kind} + add_asan_test(AsanUnitTests "Asan-${arch}${kind}-Noinst-Test" + ${arch} ${kind} SUBDIR "default" OBJECTS ${ASAN_NOINST_TEST_OBJECTS} LINKFLAGS ${ASAN_UNITTEST_NOINST_LINKFLAGS} WITH_TEST_RUNTIME) @@ -231,14 +248,10 @@ macro(add_asan_tests_for_arch_and_kind arch kind) asan_compile(ASAN_BENCHMARKS_OBJECTS ${src} ${arch} ${kind} ${ASAN_UNITTEST_INSTRUMENTED_CFLAGS} ${ARGN}) endforeach() - add_asan_test(AsanBenchmarks "Asan-${arch}${kind}-Benchmark" ${arch} ${kind} + add_asan_test(AsanBenchmarks "Asan-${arch}${kind}-Benchmark" + ${arch} ${kind} SUBDIR "default" OBJECTS ${ASAN_BENCHMARKS_OBJECTS} LINKFLAGS ${ASAN_UNITTEST_INSTRUMENTED_LINKFLAGS}) - if(COMPILER_RT_BUILD_SHARED_ASAN) - add_asan_test(AsanBenchmarks "Asan-${arch}${kind}-Dynamic-Benchmark" ${arch} ${kind} - OBJECTS ${ASAN_BENCHMARKS_OBJECTS} - LINKFLAGS ${ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS}) - endif() endmacro() if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT ANDROID) diff --git a/lib/asan/tests/asan_interface_test.cc b/lib/asan/tests/asan_interface_test.cc index 50fdf1119f0b..a34c8528eae0 100644 --- a/lib/asan/tests/asan_interface_test.cc +++ b/lib/asan/tests/asan_interface_test.cc @@ -87,7 +87,7 @@ TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) { } TEST(AddressSanitizerInterface, GetHeapSizeTest) { - // asan_allocator2 does not keep huge chunks in free list, but unmaps them. + // ASan allocator 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 << 28) + 1; // 256M diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc index bb6af45bddf9..c469d62a6766 100644 --- a/lib/asan/tests/asan_noinst_test.cc +++ b/lib/asan/tests/asan_noinst_test.cc @@ -31,15 +31,6 @@ // in this test. The static runtime library is linked explicitly (without // -fsanitize=address), thus the interceptors do not work correctly on OS X. -#if !defined(_WIN32) -extern "C" { -// Set specific ASan options for uninstrumented unittest. -const char* __asan_default_options() { - return "allow_reexec=0"; -} -} // extern "C" -#endif - // Make sure __asan_init is called before any test case is run. struct AsanInitCaller { AsanInitCaller() { __asan_init(); } diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc index 67bcbaca1e40..952b05e21fe2 100644 --- a/lib/asan/tests/asan_test.cc +++ b/lib/asan/tests/asan_test.cc @@ -603,7 +603,8 @@ NOINLINE void SigLongJmpFunc1(sigjmp_buf buf) { } #if !defined(__ANDROID__) && !defined(__arm__) && \ - !defined(__powerpc64__) && !defined(__powerpc__) + !defined(__powerpc64__) && !defined(__powerpc__) && \ + !defined(__aarch64__) // Does not work on Power and ARM: // https://code.google.com/p/address-sanitizer/issues/detail?id=185 TEST(AddressSanitizer, BuiltinLongJmpTest) { @@ -1284,3 +1285,33 @@ TEST(AddressSanitizer, pthread_getschedparam) { ASSERT_EQ(0, res); } #endif + +#if SANITIZER_TEST_HAS_PRINTF_L +static int vsnprintf_l_wrapper(char *s, size_t n, + locale_t l, const char *format, ...) { + va_list va; + va_start(va, format); + int res = vsnprintf_l(s, n , l, format, va); + va_end(va); + return res; +} + +TEST(AddressSanitizer, snprintf_l) { + char buff[5]; + // Check that snprintf_l() works fine with Asan. + int res = snprintf_l(buff, 5, + _LIBCPP_GET_C_LOCALE, "%s", "snprintf_l()"); + EXPECT_EQ(12, res); + // Check that vsnprintf_l() works fine with Asan. + res = vsnprintf_l_wrapper(buff, 5, + _LIBCPP_GET_C_LOCALE, "%s", "vsnprintf_l()"); + EXPECT_EQ(13, res); + + EXPECT_DEATH(snprintf_l(buff, 10, + _LIBCPP_GET_C_LOCALE, "%s", "snprintf_l()"), + "AddressSanitizer: stack-buffer-overflow"); + EXPECT_DEATH(vsnprintf_l_wrapper(buff, 10, + _LIBCPP_GET_C_LOCALE, "%s", "vsnprintf_l()"), + "AddressSanitizer: stack-buffer-overflow"); +} +#endif diff --git a/lib/builtins/CMakeLists.txt b/lib/builtins/CMakeLists.txt index 999faa86739d..4d102c615003 100644 --- a/lib/builtins/CMakeLists.txt +++ b/lib/builtins/CMakeLists.txt @@ -79,7 +79,6 @@ set(GENERIC_SOURCES floatuntidf.c floatuntisf.c floatuntixf.c - gcc_personality_v0.c int_util.c lshrdi3.c lshrti3.c @@ -137,6 +136,12 @@ set(GENERIC_SOURCES umodsi3.c umodti3.c) +if (HAVE_UNWIND_H) + set(GENERIC_SOURCES + ${GENERIC_SOURCES} + gcc_personality_v0.c) +endif () + set(x86_64_SOURCES x86_64/floatdidf.c x86_64/floatdisf.c diff --git a/lib/builtins/atomic.c b/lib/builtins/atomic.c index 02429a653d2b..35c8837dcecf 100644 --- a/lib/builtins/atomic.c +++ b/lib/builtins/atomic.c @@ -28,20 +28,14 @@ #include #include +#include "assembly.h" + // Clang objects if you redefine a builtin. This little hack allows us to // define a function with the same name as an intrinsic. -#if __APPLE__ -// mach-o has extra leading underscore -#pragma redefine_extname __atomic_load_c ___atomic_load -#pragma redefine_extname __atomic_store_c ___atomic_store -#pragma redefine_extname __atomic_exchange_c ___atomic_exchange -#pragma redefine_extname __atomic_compare_exchange_c ___atomic_compare_exchange -#else -#pragma redefine_extname __atomic_load_c __atomic_load -#pragma redefine_extname __atomic_store_c __atomic_store -#pragma redefine_extname __atomic_exchange_c __atomic_exchange -#pragma redefine_extname __atomic_compare_exchange_c __atomic_compare_exchange -#endif +#pragma redefine_extname __atomic_load_c SYMBOL_NAME(__atomic_load) +#pragma redefine_extname __atomic_store_c SYMBOL_NAME(__atomic_store) +#pragma redefine_extname __atomic_exchange_c SYMBOL_NAME(__atomic_exchange) +#pragma redefine_extname __atomic_compare_exchange_c SYMBOL_NAME(__atomic_compare_exchange) /// Number of locks. This allocates one page on 32-bit platforms, two on /// 64-bit. This can be specified externally if a different trade between diff --git a/lib/builtins/clear_cache.c b/lib/builtins/clear_cache.c index 86e68afad3b7..61b1e9bbb5c0 100644 --- a/lib/builtins/clear_cache.c +++ b/lib/builtins/clear_cache.c @@ -13,12 +13,62 @@ #if __APPLE__ #include #endif +#if defined(__FreeBSD__) && defined(__arm__) + #include + #include +#endif + #if defined(__NetBSD__) && defined(__arm__) #include #endif #if defined(__ANDROID__) && defined(__mips__) #include + #include + #ifdef __LP64__ + /* + * clear_mips_cache - Invalidates instruction cache for Mips. + */ + static void clear_mips_cache(const void* Addr, size_t Size) { + asm volatile ( + ".set push\n" + ".set noreorder\n" + ".set noat\n" + "beq %[Size], $zero, 20f\n" /* If size == 0, branch around. */ + "nop\n" + "daddu %[Size], %[Addr], %[Size]\n" /* Calculate end address + 1 */ + "rdhwr $v0, $1\n" /* Get step size for SYNCI. + $1 is $HW_SYNCI_Step */ + "beq $v0, $zero, 20f\n" /* If no caches require + synchronization, branch + around. */ + "nop\n" + "10:\n" + "synci 0(%[Addr])\n" /* Synchronize all caches around + address. */ + "daddu %[Addr], %[Addr], $v0\n" /* Add step size. */ + "sltu $at, %[Addr], %[Size]\n" /* Compare current with end + address. */ + "bne $at, $zero, 10b\n" /* Branch if more to do. */ + "nop\n" + "sync\n" /* Clear memory hazards. */ + "20:\n" + "bal 30f\n" + "nop\n" + "30:\n" + "daddiu $ra, $ra, 12\n" /* $ra has a value of $pc here. + Add offset of 12 to point to the + instruction after the last nop. + */ + "jr.hb $ra\n" /* Return, clearing instruction + hazards. */ + "nop\n" + ".set pop\n" + : [Addr] "+r"(Addr), [Size] "+r"(Size) + :: "at", "ra", "v0", "memory" + ); + } + #endif #endif #if defined(__ANDROID__) && defined(__arm__) @@ -39,7 +89,7 @@ void __clear_cache(void *start, void *end) { * so there is nothing to do */ #elif defined(__arm__) && !defined(__APPLE__) - #if defined(__NetBSD__) + #if defined(__FreeBSD__) || defined(__NetBSD__) struct arm_sync_icache_args arg; arg.addr = (uintptr_t)start; @@ -47,7 +97,7 @@ void __clear_cache(void *start, void *end) { sysarch(ARM_SYNC_ICACHE, &arg); #elif defined(__ANDROID__) - const register int start_reg __asm("r0") = (int) (intptr_t) start; + register int start_reg __asm("r0") = (int) (intptr_t) start; const register int end_reg __asm("r1") = (int) (intptr_t) end; const register int flags __asm("r2") = 0; const register int syscall_nr __asm("r7") = __ARM_NR_cacheflush; @@ -62,7 +112,17 @@ void __clear_cache(void *start, void *end) { #elif defined(__ANDROID__) && defined(__mips__) const uintptr_t start_int = (uintptr_t) start; const uintptr_t end_int = (uintptr_t) end; - _flush_cache(start, (end_int - start_int), BCACHE); + #ifdef __LP64__ + // Call synci implementation for short address range. + const uintptr_t address_range_limit = 256; + if ((end_int - start_int) <= address_range_limit) { + clear_mips_cache(start, (end_int - start_int)); + } else { + syscall(__NR_cacheflush, start, (end_int - start_int), BCACHE); + } + #else + syscall(__NR_cacheflush, start, (end_int - start_int), BCACHE); + #endif #elif defined(__aarch64__) && !defined(__APPLE__) uint64_t xstart = (uint64_t)(uintptr_t) start; uint64_t xend = (uint64_t)(uintptr_t) end; diff --git a/lib/builtins/gcc_personality_v0.c b/lib/builtins/gcc_personality_v0.c index 869f4178e853..4b95cfd43b05 100644 --- a/lib/builtins/gcc_personality_v0.c +++ b/lib/builtins/gcc_personality_v0.c @@ -11,47 +11,7 @@ #include "int_lib.h" -/* - * _Unwind_* stuff based on C++ ABI public documentation - * http://refspecs.freestandards.org/abi-eh-1.21.html - */ - -typedef enum { - _URC_NO_REASON = 0, - _URC_FOREIGN_EXCEPTION_CAUGHT = 1, - _URC_FATAL_PHASE2_ERROR = 2, - _URC_FATAL_PHASE1_ERROR = 3, - _URC_NORMAL_STOP = 4, - _URC_END_OF_STACK = 5, - _URC_HANDLER_FOUND = 6, - _URC_INSTALL_CONTEXT = 7, - _URC_CONTINUE_UNWIND = 8 -} _Unwind_Reason_Code; - -typedef enum { - _UA_SEARCH_PHASE = 1, - _UA_CLEANUP_PHASE = 2, - _UA_HANDLER_FRAME = 4, - _UA_FORCE_UNWIND = 8, - _UA_END_OF_STACK = 16 -} _Unwind_Action; - -typedef struct _Unwind_Context* _Unwind_Context_t; - -struct _Unwind_Exception { - uint64_t exception_class; - void (*exception_cleanup)(_Unwind_Reason_Code reason, - struct _Unwind_Exception* exc); - uintptr_t private_1; - uintptr_t private_2; -}; - -COMPILER_RT_ABI const uint8_t* _Unwind_GetLanguageSpecificData(_Unwind_Context_t c); -COMPILER_RT_ABI void _Unwind_SetGR(_Unwind_Context_t c, int i, uintptr_t n); -COMPILER_RT_ABI void _Unwind_SetIP(_Unwind_Context_t, uintptr_t new_value); -COMPILER_RT_ABI uintptr_t _Unwind_GetIP(_Unwind_Context_t context); -COMPILER_RT_ABI uintptr_t _Unwind_GetRegionStart(_Unwind_Context_t context); - +#include /* * Pointer encodings documented at: @@ -185,12 +145,12 @@ static uintptr_t readEncodedPointer(const uint8_t** data, uint8_t encoding) COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_sj0(int version, _Unwind_Action actions, uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject, - _Unwind_Context_t context) + struct _Unwind_Context *context) #else COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject, - _Unwind_Context_t context) + struct _Unwind_Context *context) #endif { /* Since C does not have catch clauses, there is nothing to do during */ @@ -199,7 +159,7 @@ __gcc_personality_v0(int version, _Unwind_Action actions, return _URC_CONTINUE_UNWIND; /* There is nothing to do if there is no LSDA for this frame. */ - const uint8_t* lsda = _Unwind_GetLanguageSpecificData(context); + const uint8_t* lsda = (uint8_t*)_Unwind_GetLanguageSpecificData(context); if ( lsda == (uint8_t*) 0 ) return _URC_CONTINUE_UNWIND; diff --git a/lib/builtins/int_types.h b/lib/builtins/int_types.h index 5107f71550ac..aedae14b2046 100644 --- a/lib/builtins/int_types.h +++ b/lib/builtins/int_types.h @@ -56,7 +56,8 @@ typedef union }s; } udwords; -#if __LP64__ +/* MIPS64 issue: PR 20098 */ +#if defined(__LP64__) && !(defined(__mips__) && defined(__clang__)) #define CRT_HAS_128BIT #endif diff --git a/lib/dfsan/CMakeLists.txt b/lib/dfsan/CMakeLists.txt index 257a15a93301..24ea876f210d 100644 --- a/lib/dfsan/CMakeLists.txt +++ b/lib/dfsan/CMakeLists.txt @@ -6,6 +6,7 @@ set(DFSAN_RTL_SOURCES dfsan_custom.cc dfsan_interceptors.cc) set(DFSAN_COMMON_CFLAGS ${SANITIZER_COMMON_CFLAGS}) +append_no_rtti_flag(DFSAN_COMMON_CFLAGS) # Prevent clang from generating libc calls. append_list_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding DFSAN_COMMON_CFLAGS) diff --git a/lib/dfsan/dfsan.cc b/lib/dfsan/dfsan.cc index 941edc5eba7a..dd0ea61142b8 100644 --- a/lib/dfsan/dfsan.cc +++ b/lib/dfsan/dfsan.cc @@ -22,6 +22,7 @@ #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_libc.h" #include "dfsan/dfsan.h" @@ -256,7 +257,7 @@ dfsan_read_label(const void *addr, uptr size) { return __dfsan_union_load(shadow_for(addr), size); } -SANITIZER_INTERFACE_ATTRIBUTE +extern "C" SANITIZER_INTERFACE_ATTRIBUTE const struct dfsan_label_info *dfsan_get_label_info(dfsan_label label) { return &__dfsan_label_info[label]; } @@ -310,16 +311,24 @@ dfsan_dump_labels(int fd) { } } -static void InitializeFlags(Flags &f, const char *env) { - f.warn_unimplemented = true; - f.warn_nonzero_labels = false; - f.strict_data_dependencies = true; - f.dump_labels_at_exit = ""; +void Flags::SetDefaults() { +#define DFSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "dfsan_flags.inc" +#undef DFSAN_FLAG +} - ParseFlag(env, &f.warn_unimplemented, "warn_unimplemented", ""); - ParseFlag(env, &f.warn_nonzero_labels, "warn_nonzero_labels", ""); - ParseFlag(env, &f.strict_data_dependencies, "strict_data_dependencies", ""); - ParseFlag(env, &f.dump_labels_at_exit, "dump_labels_at_exit", ""); +void RegisterDfsanFlags(FlagParser *parser, Flags *f) { +#define DFSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "dfsan_flags.inc" +#undef DFSAN_FLAG +} + +static void InitializeFlags(Flags &f, const char *env) { + FlagParser parser; + RegisterDfsanFlags(&parser, &f); + f.SetDefaults(); + parser.ParseString(env); } static void dfsan_fini() { diff --git a/lib/dfsan/dfsan.h b/lib/dfsan/dfsan.h index bc38be08c9cd..ceba3533a233 100644 --- a/lib/dfsan/dfsan.h +++ b/lib/dfsan/dfsan.h @@ -56,17 +56,11 @@ inline const dfsan_label *shadow_for(const void *ptr) { } struct Flags { - // Whether to warn on unimplemented functions. - bool warn_unimplemented; - // Whether to warn on non-zero labels. - bool warn_nonzero_labels; - // Whether to propagate labels only when there is an obvious data dependency - // (e.g., when comparing strings, ignore the fact that the output of the - // comparison might be data-dependent on the content of the strings). This - // applies only to the custom functions defined in 'custom.c'. - bool strict_data_dependencies; - // The path of the file where to dump the labels when the program terminates. - const char* dump_labels_at_exit; +#define DFSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "dfsan_flags.inc" +#undef DFSAN_FLAG + + void SetDefaults(); }; extern Flags flags_data; diff --git a/lib/dfsan/dfsan_custom.cc b/lib/dfsan/dfsan_custom.cc index 839a399faae0..318ecd6fb317 100644 --- a/lib/dfsan/dfsan_custom.cc +++ b/lib/dfsan/dfsan_custom.cc @@ -314,11 +314,12 @@ static void unpoison(const void *ptr, uptr size) { SANITIZER_INTERFACE_ATTRIBUTE void * __dfsw_dlopen(const char *filename, int flag, dfsan_label filename_label, dfsan_label flag_label, dfsan_label *ret_label) { - link_map *map = (link_map *)dlopen(filename, flag); + void *handle = dlopen(filename, flag); + link_map *map = GET_LINK_MAP_BY_DLOPEN_HANDLE(handle); if (map) ForEachMappedRegion(map, unpoison); *ret_label = 0; - return (void *)map; + return handle; } struct pthread_create_info { diff --git a/lib/dfsan/dfsan_flags.inc b/lib/dfsan/dfsan_flags.inc new file mode 100644 index 000000000000..24fbfcb9e46f --- /dev/null +++ b/lib/dfsan/dfsan_flags.inc @@ -0,0 +1,32 @@ +//===-- dfsan_flags.inc -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// DFSan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef DFSAN_FLAG +# error "Define DFSAN_FLAG prior to including this file!" +#endif + +// DFSAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +DFSAN_FLAG(bool, warn_unimplemented, true, + "Whether to warn on unimplemented functions.") +DFSAN_FLAG(bool, warn_nonzero_labels, false, + "Whether to warn on unimplemented functions.") +DFSAN_FLAG( + bool, strict_data_dependencies, true, + "Whether to propagate labels only when there is an obvious data dependency" + "(e.g., when comparing strings, ignore the fact that the output of the" + "comparison might be data-dependent on the content of the strings). This" + "applies only to the custom functions defined in 'custom.c'.") +DFSAN_FLAG(const char *, dump_labels_at_exit, "", "The path of the file where " + "to dump the labels when the " + "program terminates.") diff --git a/lib/lsan/lsan.cc b/lib/lsan/lsan.cc index 1598fcac4a76..1509b2a7d342 100644 --- a/lib/lsan/lsan.cc +++ b/lib/lsan/lsan.cc @@ -52,6 +52,9 @@ extern "C" void __lsan_init() { if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) Atexit(DoLeakCheck); + + InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); + lsan_inited = true; lsan_init_is_running = false; } diff --git a/lib/lsan/lsan_allocator.cc b/lib/lsan/lsan_allocator.cc index 8be2a2ad9224..96a2b0428a61 100644 --- a/lib/lsan/lsan_allocator.cc +++ b/lib/lsan/lsan_allocator.cc @@ -47,7 +47,7 @@ static Allocator allocator; static THREADLOCAL AllocatorCache cache; void InitializeAllocator() { - allocator.Init(); + allocator.InitLinkerInitialized(common_flags()->allocator_may_return_null); } void AllocatorThreadFinish() { diff --git a/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc index c2ba52e46911..7b3cd1639761 100644 --- a/lib/lsan/lsan_common.cc +++ b/lib/lsan/lsan_common.cc @@ -16,6 +16,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_stackdepot.h" @@ -36,52 +37,50 @@ bool DisabledInThisThread() { return disable_counter > 0; } Flags lsan_flags; +void Flags::SetDefaults() { +#define LSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "lsan_flags.inc" +#undef LSAN_FLAG +} + +static void RegisterLsanFlags(FlagParser *parser, Flags *f) { +#define LSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "lsan_flags.inc" +#undef LSAN_FLAG +} + static void InitializeFlags(bool standalone) { Flags *f = flags(); - // Default values. - f->report_objects = false; - f->resolution = 0; - f->max_leaks = 0; - f->exitcode = 23; - f->use_registers = true; - f->use_globals = true; - f->use_stacks = true; - f->use_tls = true; - f->use_root_regions = true; - f->use_unaligned = false; - f->use_poisoned = false; - f->log_pointers = false; - f->log_threads = false; + FlagParser parser; + RegisterLsanFlags(&parser, f); + RegisterCommonFlags(&parser); - const char *options = GetEnv("LSAN_OPTIONS"); - if (options) { - ParseFlag(options, &f->use_registers, "use_registers", ""); - ParseFlag(options, &f->use_globals, "use_globals", ""); - ParseFlag(options, &f->use_stacks, "use_stacks", ""); - ParseFlag(options, &f->use_tls, "use_tls", ""); - ParseFlag(options, &f->use_root_regions, "use_root_regions", ""); - ParseFlag(options, &f->use_unaligned, "use_unaligned", ""); - ParseFlag(options, &f->use_poisoned, "use_poisoned", ""); - ParseFlag(options, &f->report_objects, "report_objects", ""); - ParseFlag(options, &f->resolution, "resolution", ""); - CHECK_GE(&f->resolution, 0); - ParseFlag(options, &f->max_leaks, "max_leaks", ""); - CHECK_GE(&f->max_leaks, 0); - ParseFlag(options, &f->log_pointers, "log_pointers", ""); - ParseFlag(options, &f->log_threads, "log_threads", ""); - ParseFlag(options, &f->exitcode, "exitcode", ""); - } + f->SetDefaults(); // Set defaults for common flags (only in standalone mode) and parse // them from LSAN_OPTIONS. - CommonFlags *cf = common_flags(); if (standalone) { - SetCommonFlagsDefaults(cf); - cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); - cf->malloc_context_size = 30; - cf->detect_leaks = true; + SetCommonFlagsDefaults(); + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); + cf.malloc_context_size = 30; + cf.detect_leaks = true; + OverrideCommonFlags(cf); } - ParseCommonFlagsFromString(cf, options); + + bool help_before = common_flags()->help; + + const char *options = GetEnv("LSAN_OPTIONS"); + parser.ParseString(options); + + SetVerbosity(common_flags()->verbosity); + + if (Verbosity()) ReportUnrecognizedFlags(); + + if (!help_before && common_flags()->help) + parser.PrintFlagDescriptions(); } #define LOG_POINTERS(...) \ @@ -367,7 +366,7 @@ static void CollectLeaksCb(uptr chunk, void *arg) { LsanMetadata m(chunk); if (!m.allocated()) return; if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) { - uptr resolution = flags()->resolution; + u32 resolution = flags()->resolution; u32 stack_trace_id = 0; if (resolution > 0) { StackTrace stack = StackDepotGet(m.stack_trace_id()); diff --git a/lib/lsan/lsan_common.h b/lib/lsan/lsan_common.h index 86ff12da6e0f..64cbef38c7a6 100644 --- a/lib/lsan/lsan_common.h +++ b/lib/lsan/lsan_common.h @@ -38,40 +38,14 @@ enum ChunkTag { }; struct Flags { +#define LSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "lsan_flags.inc" +#undef LSAN_FLAG + + void SetDefaults(); uptr pointer_alignment() const { return use_unaligned ? 1 : sizeof(uptr); } - - // Print addresses of leaked objects after main leak report. - bool report_objects; - // Aggregate two objects into one leak if this many stack frames match. If - // zero, the entire stack trace must match. - int resolution; - // The number of leaks reported. - int max_leaks; - // If nonzero kill the process with this exit code upon finding leaks. - int exitcode; - - // Flags controlling the root set of reachable memory. - // Global variables (.data and .bss). - bool use_globals; - // Thread stacks. - bool use_stacks; - // Thread registers. - bool use_registers; - // TLS and thread-specific storage. - bool use_tls; - // Regions added via __lsan_register_root_region(). - bool use_root_regions; - - // Consider unaligned pointers valid. - bool use_unaligned; - // Consider pointers found in poisoned memory to be valid. - bool use_poisoned; - - // Debug logging. - bool log_pointers; - bool log_threads; }; extern Flags lsan_flags; diff --git a/lib/lsan/lsan_flags.inc b/lib/lsan/lsan_flags.inc new file mode 100644 index 000000000000..7f00acbf9119 --- /dev/null +++ b/lib/lsan/lsan_flags.inc @@ -0,0 +1,44 @@ +//===-- lsan_flags.inc ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// LSan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef LSAN_FLAG +# error "Define LSAN_FLAG prior to including this file!" +#endif + +// LSAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +LSAN_FLAG(bool, report_objects, false, + "Print addresses of leaked objects after main leak report.") +LSAN_FLAG( + int, resolution, 0, + "Aggregate two objects into one leak if this many stack frames match. If " + "zero, the entire stack trace must match.") +LSAN_FLAG(int, max_leaks, 0, "The number of leaks reported.") +LSAN_FLAG(int, exitcode, 23, + "If nonzero kill the process with this exit code upon finding leaks.") + +// Flags controlling the root set of reachable memory. +LSAN_FLAG(bool, use_globals, true, + "Root set: include global variables (.data and .bss)") +LSAN_FLAG(bool, use_stacks, true, "Root set: include thread stacks") +LSAN_FLAG(bool, use_registers, true, "Root set: include thread registers") +LSAN_FLAG(bool, use_tls, true, + "Root set: include TLS and thread-specific storage") +LSAN_FLAG(bool, use_root_regions, true, + "Root set: include regions added via __lsan_register_root_region().") + +LSAN_FLAG(bool, use_unaligned, false, "Consider unaligned pointers valid.") +LSAN_FLAG(bool, use_poisoned, false, + "Consider pointers found in poisoned memory to be valid.") +LSAN_FLAG(bool, log_pointers, false, "Debug logging") +LSAN_FLAG(bool, log_threads, false, "Debug logging") diff --git a/lib/msan/CMakeLists.txt b/lib/msan/CMakeLists.txt index 90d9face1731..ccf47fc45cf3 100644 --- a/lib/msan/CMakeLists.txt +++ b/lib/msan/CMakeLists.txt @@ -10,6 +10,7 @@ set(MSAN_RTL_SOURCES msan_new_delete.cc msan_report.cc msan_thread.cc + msan_poisoning.cc ) set(MSAN_RTL_CFLAGS ${SANITIZER_COMMON_CFLAGS}) diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc index 853e448fab8e..4adcc9eb8e6a 100644 --- a/lib/msan/msan.cc +++ b/lib/msan/msan.cc @@ -16,16 +16,17 @@ #include "msan_chained_origin_depot.h" #include "msan_origin.h" #include "msan_thread.h" +#include "msan_poisoning.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.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 "sanitizer_common/sanitizer_stackdepot.h" - // ACHTUNG! No system header includes in this file. using namespace __sanitizer; @@ -96,19 +97,78 @@ static const char *StackOriginDescr[kNumStackOriginDescrs]; static uptr StackOriginPC[kNumStackOriginDescrs]; static atomic_uint32_t NumStackOriginDescrs; -static void ParseFlagsFromString(Flags *f, const char *str) { - CommonFlags *cf = common_flags(); - ParseCommonFlagsFromString(cf, 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->poison_in_free, "poison_in_free", ""); - ParseFlag(str, &f->exit_code, "exit_code", ""); +void Flags::SetDefaults() { +#define MSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "msan_flags.inc" +#undef MSAN_FLAG +} + +// keep_going is an old name for halt_on_error, +// and it has inverse meaning. +class FlagHandlerKeepGoing : public FlagHandlerBase { + bool *halt_on_error_; + + public: + explicit FlagHandlerKeepGoing(bool *halt_on_error) + : halt_on_error_(halt_on_error) {} + bool Parse(const char *value) { + bool tmp; + FlagHandler h(&tmp); + if (!h.Parse(value)) return false; + *halt_on_error_ = !tmp; + return true; + } +}; + +void RegisterMsanFlags(FlagParser *parser, Flags *f) { +#define MSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "msan_flags.inc" +#undef MSAN_FLAG + + FlagHandlerKeepGoing *fh_keep_going = new (FlagParser::Alloc) // NOLINT + FlagHandlerKeepGoing(&f->halt_on_error); + parser->RegisterHandler("keep_going", fh_keep_going, + "deprecated, use halt_on_error"); +} + +static void InitializeFlags(Flags *f, const char *options) { + FlagParser parser; + RegisterMsanFlags(&parser, f); + RegisterCommonFlags(&parser); + + SetCommonFlagsDefaults(); + { + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.external_symbolizer_path = GetEnv("MSAN_SYMBOLIZER_PATH"); + cf.malloc_context_size = 20; + cf.handle_ioctl = true; + // FIXME: test and enable. + cf.check_printf = false; + cf.intercept_tls_get_addr = true; + OverrideCommonFlags(cf); + } + + f->SetDefaults(); + + // Override from user-specified string. + if (__msan_default_options) + parser.ParseString(__msan_default_options()); + + parser.ParseString(options); + + SetVerbosity(common_flags()->verbosity); + + if (Verbosity()) ReportUnrecognizedFlags(); + + if (common_flags()->help) parser.PrintFlagDescriptions(); + + // Check flag values: if (f->exit_code < 0 || f->exit_code > 127) { Printf("Exit code not in [0, 128) range: %d\n", f->exit_code); Die(); } - ParseFlag(str, &f->origin_history_size, "origin_history_size", ""); if (f->origin_history_size < 0 || f->origin_history_size > Origin::kMaxDepth) { Printf( @@ -117,8 +177,6 @@ static void ParseFlagsFromString(Flags *f, const char *str) { f->origin_history_size, Origin::kMaxDepth); Die(); } - ParseFlag(str, &f->origin_history_per_stack_limit, - "origin_history_per_stack_limit", ""); // Limiting to kStackDepotMaxUseCount / 2 to avoid overflow in // StackDepotHandle::inc_use_count_unsafe. if (f->origin_history_per_stack_limit < 0 || @@ -129,51 +187,7 @@ static void ParseFlagsFromString(Flags *f, const char *str) { f->origin_history_per_stack_limit, kStackDepotMaxUseCount / 2); Die(); } - - ParseFlag(str, &f->report_umrs, "report_umrs", ""); - ParseFlag(str, &f->wrap_signals, "wrap_signals", ""); - ParseFlag(str, &f->print_stats, "print_stats", ""); - ParseFlag(str, &f->atexit, "atexit", ""); - ParseFlag(str, &f->store_context_size, "store_context_size", ""); if (f->store_context_size < 1) f->store_context_size = 1; - - // keep_going is an old name for halt_on_error, - // and it has inverse meaning. - f->halt_on_error = !f->halt_on_error; - ParseFlag(str, &f->halt_on_error, "keep_going", ""); - f->halt_on_error = !f->halt_on_error; - ParseFlag(str, &f->halt_on_error, "halt_on_error", ""); -} - -static void InitializeFlags(Flags *f, const char *options) { - CommonFlags *cf = common_flags(); - SetCommonFlagsDefaults(cf); - cf->external_symbolizer_path = GetEnv("MSAN_SYMBOLIZER_PATH"); - cf->malloc_context_size = 20; - cf->handle_ioctl = true; - // FIXME: test and enable. - cf->check_printf = false; - cf->intercept_tls_get_addr = true; - - internal_memset(f, 0, sizeof(*f)); - f->poison_heap_with_zeroes = false; - f->poison_stack_with_zeroes = false; - f->poison_in_malloc = true; - f->poison_in_free = true; - f->exit_code = 77; - f->origin_history_size = Origin::kMaxDepth; - f->origin_history_per_stack_limit = 20000; - f->report_umrs = true; - f->wrap_signals = true; - f->print_stats = false; - f->atexit = false; - f->halt_on_error = !&__msan_keep_going; - f->store_context_size = 20; - - // Override from user-specified string. - if (__msan_default_options) - ParseFlagsFromString(f, __msan_default_options()); - ParseFlagsFromString(f, options); } void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp, @@ -259,6 +273,7 @@ u32 ChainOrigin(u32 id, StackTrace *stack) { return id; Origin o = Origin::FromRawId(id); + stack->tag = StackTrace::TAG_UNKNOWN; Origin chained = Origin::CreateChainedOrigin(o, stack); return chained.raw_id(); } @@ -338,7 +353,6 @@ void __msan_init() { const char *msan_options = GetEnv("MSAN_OPTIONS"); InitializeFlags(&msan_flags, msan_options); - if (common_flags()->help) PrintFlagDescriptions(); __sanitizer_set_report_path(common_flags()->log_path); InitializeInterceptors(); @@ -372,10 +386,7 @@ void __msan_init() { Symbolizer::GetOrInit()->AddHooks(EnterSymbolizer, ExitSymbolizer); - if (common_flags()->coverage) { - __sanitizer_cov_init(); - Atexit(__sanitizer_cov_dump); - } + InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); MsanTSDInit(MsanTSDDtor); @@ -485,24 +496,7 @@ void __msan_load_unpoisoned(void *src, uptr size, void *dst) { } void __msan_set_origin(const 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_get_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 to 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; + if (__msan_get_track_origins()) SetOrigin(a, size, origin); } // 'descr' is created at compile time and contains '----' in the beginning. diff --git a/lib/msan/msan.h b/lib/msan/msan.h index 9bc1e4c6a447..ed18f21d0282 100644 --- a/lib/msan/msan.h +++ b/lib/msan/msan.h @@ -25,90 +25,90 @@ # define MSAN_REPLACE_OPERATORS_NEW_AND_DELETE 1 #endif -/* -C/C++ on FreeBSD -0000 0000 0000 - 00ff ffff ffff: Low memory: main binary, MAP_32BIT mappings and modules -0100 0000 0000 - 0fff ffff ffff: Bad1 -1000 0000 0000 - 30ff ffff ffff: Shadow -3100 0000 0000 - 37ff ffff ffff: Bad2 -3800 0000 0000 - 58ff ffff ffff: Origins -5900 0000 0000 - 5fff ffff ffff: Bad3 -6000 0000 0000 - 7fff ffff ffff: High memory: heap, modules and main thread stack +struct MappingDesc { + uptr start; + uptr end; + enum Type { + INVALID, APP, SHADOW, ORIGIN + } type; + const char *name; +}; -C/C++ on Linux/PIE -0000 0000 0000 - 1fff ffff ffff: Bad1 -2000 0000 0000 - 3fff ffff ffff: Shadow -4000 0000 0000 - 5fff ffff ffff: Origins -6000 0000 0000 - 7fff ffff ffff: Main memory - -C/C++ on Mips -0000 0000 0000 - 009f ffff ffff: Bad1 -00a0 0000 0000 - 00bf ffff ffff: Shadow -00c0 0000 0000 - 00df ffff ffff: Origins -00e0 0000 0000 - 00ff ffff ffff: Main memory -*/ #if SANITIZER_LINUX && defined(__mips64) -const uptr kLowMemBeg = 0; -const uptr kLowMemSize = 0; -const uptr kHighMemBeg = 0x00e000000000; -const uptr kHighMemSize = 0x002000000000; -const uptr kShadowBeg = 0x00a000000000; -const uptr kShadowSize = 0x002000000000; -const uptr kOriginsBeg = 0x00c000000000; -# define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x4000000000ULL) + +// Everything is above 0x00e000000000. +const MappingDesc kMemoryLayout[] = { + {0x000000000000ULL, 0x00a000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x00a000000000ULL, 0x00c000000000ULL, MappingDesc::SHADOW, "shadow"}, + {0x00c000000000ULL, 0x00e000000000ULL, MappingDesc::ORIGIN, "origin"}, + {0x00e000000000ULL, 0x010000000000ULL, MappingDesc::APP, "app"}}; + +#define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x4000000000ULL) +#define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x002000000000) + #elif SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 64 -const uptr kLowMemBeg = 0x000000000000; -const uptr kLowMemSize = 0x010000000000; -const uptr kHighMemBeg = 0x600000000000; -const uptr kHighMemSize = 0x200000000000; -const uptr kShadowBeg = 0x100000000000; -const uptr kShadowSize = 0x210000000000; -const uptr kOriginsBeg = 0x380000000000; + +// Low memory: main binary, MAP_32BIT mappings and modules +// High memory: heap, modules and main thread stack +const MappingDesc kMemoryLayout[] = { + {0x000000000000ULL, 0x010000000000ULL, MappingDesc::APP, "low memory"}, + {0x010000000000ULL, 0x100000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x100000000000ULL, 0x310000000000ULL, MappingDesc::SHADOW, "shadow"}, + {0x310000000000ULL, 0x380000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x380000000000ULL, 0x590000000000ULL, MappingDesc::ORIGIN, "origin"}, + {0x590000000000ULL, 0x600000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x600000000000ULL, 0x800000000000ULL, MappingDesc::APP, "high memory"}}; + // Maps low and high app ranges to contiguous space with zero base: // Low: 0000 0000 0000 - 00ff ffff ffff -> 2000 0000 0000 - 20ff ffff ffff // High: 6000 0000 0000 - 7fff ffff ffff -> 0000 0000 0000 - 1fff ffff ffff -# define LINEARIZE_MEM(mem) \ - (((uptr)(mem) & ~0xc00000000000ULL) ^ 0x200000000000ULL) -# define MEM_TO_SHADOW(mem) (LINEARIZE_MEM((mem)) + 0x100000000000ULL) +#define LINEARIZE_MEM(mem) \ + (((uptr)(mem) & ~0xc00000000000ULL) ^ 0x200000000000ULL) +#define MEM_TO_SHADOW(mem) (LINEARIZE_MEM((mem)) + 0x100000000000ULL) +#define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x280000000000) + #elif SANITIZER_LINUX && SANITIZER_WORDSIZE == 64 -const uptr kLowMemBeg = 0; -const uptr kLowMemSize = 0; -const uptr kHighMemBeg = 0x600000000000; -const uptr kHighMemSize = 0x200000000000; -const uptr kShadowBeg = 0x200000000000; -const uptr kShadowSize = 0x200000000000; -const uptr kOriginsBeg = 0x400000000000; -# define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x400000000000ULL) + +// Requries PIE binary and ASLR enabled. +// Main thread stack and DSOs at 0x7f0000000000 (sometimes 0x7e0000000000). +// Heap at 0x600000000000. +const MappingDesc kMemoryLayout[] = { + {0x000000000000ULL, 0x200000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x200000000000ULL, 0x400000000000ULL, MappingDesc::SHADOW, "shadow"}, + {0x400000000000ULL, 0x600000000000ULL, MappingDesc::ORIGIN, "origin"}, + {0x600000000000ULL, 0x800000000000ULL, MappingDesc::APP, "app"}}; + +#define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x400000000000ULL) +#define SHADOW_TO_ORIGIN(mem) (((uptr)(mem)) + 0x200000000000ULL) + #else #error "Unsupported platform" #endif -const uptr kBad1Beg = kLowMemBeg + kLowMemSize; -const uptr kBad1Size = kShadowBeg - kBad1Beg; - -const uptr kBad2Beg = kShadowBeg + kShadowSize; -const uptr kBad2Size = kOriginsBeg - kBad2Beg; - -const uptr kOriginsSize = kShadowSize; - -const uptr kBad3Beg = kOriginsBeg + kOriginsSize; -const uptr kBad3Size = kHighMemBeg - kBad3Beg; - -#define SHADOW_TO_ORIGIN(shadow) \ - (((uptr)(shadow)) + (kOriginsBeg - kShadowBeg)) +const uptr kMemoryLayoutSize = sizeof(kMemoryLayout) / sizeof(kMemoryLayout[0]); #define MEM_TO_ORIGIN(mem) (SHADOW_TO_ORIGIN(MEM_TO_SHADOW((mem)))) -#define MEM_IS_APP(mem) \ - ((kLowMemSize > 0 && (uptr)(mem) < kLowMemSize) || \ - (uptr)(mem) >= kHighMemBeg) +#ifndef __clang__ +__attribute__((optimize("unroll-loops"))) +#endif +inline bool addr_is_type(uptr addr, MappingDesc::Type mapping_type) { +// It is critical for performance that this loop is unrolled (because then it is +// simplified into just a few constant comparisons). +#ifdef __clang__ +#pragma unroll +#endif + for (unsigned i = 0; i < kMemoryLayoutSize; ++i) + if (kMemoryLayout[i].type == mapping_type && + addr >= kMemoryLayout[i].start && addr < kMemoryLayout[i].end) + return true; + return false; +} -#define MEM_IS_SHADOW(mem) \ - ((uptr)(mem) >= kShadowBeg && (uptr)(mem) < kShadowBeg + kShadowSize) - -#define MEM_IS_ORIGIN(mem) \ - ((uptr)(mem) >= kOriginsBeg && (uptr)(mem) < kOriginsBeg + kOriginsSize) +#define MEM_IS_APP(mem) addr_is_type((uptr)(mem), MappingDesc::APP) +#define MEM_IS_SHADOW(mem) addr_is_type((uptr)(mem), MappingDesc::SHADOW) +#define MEM_IS_ORIGIN(mem) addr_is_type((uptr)(mem), MappingDesc::ORIGIN) // These constants must be kept in sync with the ones in MemorySanitizer.cc. const int kMsanParamTlsSize = 800; @@ -125,6 +125,7 @@ char *GetProcSelfMaps(); void InitializeInterceptors(); void MsanAllocatorThreadFinish(); +void *MsanCalloc(StackTrace *stack, uptr nmemb, uptr size); void *MsanReallocate(StackTrace *stack, void *oldp, uptr size, uptr alignment, bool zeroise); void MsanDeallocate(StackTrace *stack, void *ptr); @@ -162,16 +163,12 @@ void ReportUMRInsideAddressRange(const char *what, const void *start, uptr size, void UnpoisonParam(uptr n); void UnpoisonThreadLocalState(); -u32 GetOriginIfPoisoned(uptr a, uptr size); -void SetOriginIfPoisoned(uptr addr, uptr src_shadow, uptr size, u32 src_origin); -void CopyOrigin(void *dst, const void *src, uptr size, StackTrace *stack); -void MovePoison(void *dst, const void *src, uptr size, StackTrace *stack); -void CopyPoison(void *dst, const void *src, uptr size, StackTrace *stack); - // Returns a "chained" origin id, pointing to the given stack trace followed by // the previous origin id. u32 ChainOrigin(u32 id, StackTrace *stack); +const int STACK_TRACE_TAG_POISON = StackTrace::TAG_CUSTOM + 1; + #define GET_MALLOC_STACK_TRACE \ BufferedStackTrace stack; \ if (__msan_get_track_origins() && msan_inited) \ diff --git a/lib/msan/msan_allocator.cc b/lib/msan/msan_allocator.cc index f21d71409ce2..698b6cddd30b 100644 --- a/lib/msan/msan_allocator.cc +++ b/lib/msan/msan_allocator.cc @@ -18,6 +18,7 @@ #include "msan_allocator.h" #include "msan_origin.h" #include "msan_thread.h" +#include "msan_poisoning.h" namespace __msan { @@ -73,7 +74,7 @@ static inline void Init() { if (inited) return; __msan_init(); inited = true; // this must happen before any threads are created. - allocator.Init(); + allocator.Init(common_flags()->allocator_may_return_null); } AllocatorCache *GetAllocatorCache(MsanThreadLocalMallocStorage *ms) { @@ -92,7 +93,7 @@ static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment, if (size > kMaxAllowedMallocSize) { Report("WARNING: MemorySanitizer failed to allocate %p bytes\n", (void *)size); - return AllocatorReturnNull(); + return allocator.ReturnNullOrDie(); } MsanThread *t = GetCurrentThread(); void *allocated; @@ -112,6 +113,7 @@ static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment, } else if (flags()->poison_in_malloc) { __msan_poison(allocated, size); if (__msan_get_track_origins()) { + stack->tag = StackTrace::TAG_ALLOC; Origin o = Origin::CreateHeapOrigin(stack); __msan_set_origin(allocated, size, o.raw_id()); } @@ -132,6 +134,7 @@ void MsanDeallocate(StackTrace *stack, void *p) { if (flags()->poison_in_free) { __msan_poison(p, size); if (__msan_get_track_origins()) { + stack->tag = StackTrace::TAG_DEALLOC; Origin o = Origin::CreateHeapOrigin(stack); __msan_set_origin(p, size, o.raw_id()); } @@ -147,6 +150,13 @@ void MsanDeallocate(StackTrace *stack, void *p) { } } +void *MsanCalloc(StackTrace *stack, uptr nmemb, uptr size) { + Init(); + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) + return allocator.ReturnNullOrDie(); + return MsanReallocate(stack, 0, nmemb * size, sizeof(u64), true); +} + void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, uptr alignment, bool zeroise) { if (!old_p) @@ -161,15 +171,22 @@ void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, 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); + if (new_size > old_size) { + if (zeroise) { + __msan_clear_and_unpoison((char *)old_p + old_size, + new_size - old_size); + } else if (flags()->poison_in_malloc) { + stack->tag = StackTrace::TAG_ALLOC; + PoisonMemory((char *)old_p + old_size, new_size - old_size, stack); + } + } 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); + CopyMemory(new_p, old_p, memcpy_size, stack); MsanDeallocate(stack, old_p); } return new_p; diff --git a/lib/msan/msan_flags.h b/lib/msan/msan_flags.h index 9b93f118a985..4fc6d172a04a 100644 --- a/lib/msan/msan_flags.h +++ b/lib/msan/msan_flags.h @@ -9,28 +9,18 @@ // // 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 origin_history_size; - int origin_history_per_stack_limit; - bool poison_heap_with_zeroes; // default: false - bool poison_stack_with_zeroes; // default: false - bool poison_in_malloc; // default: true - bool poison_in_free; // default: true - bool report_umrs; - bool wrap_signals; - bool print_stats; - bool halt_on_error; - bool atexit; - int store_context_size; // like malloc_context_size, but for uninit stores +#define MSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "msan_flags.inc" +#undef MSAN_FLAG + + void SetDefaults(); }; Flags *flags(); diff --git a/lib/msan/msan_flags.inc b/lib/msan/msan_flags.inc new file mode 100644 index 000000000000..cb58ffc4aba7 --- /dev/null +++ b/lib/msan/msan_flags.inc @@ -0,0 +1,33 @@ +//===-- msan_flags.inc ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// MSan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef MSAN_FLAG +# error "Define MSAN_FLAG prior to including this file!" +#endif + +// MSAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +MSAN_FLAG(int, exit_code, 77, "") +MSAN_FLAG(int, origin_history_size, Origin::kMaxDepth, "") +MSAN_FLAG(int, origin_history_per_stack_limit, 20000, "") +MSAN_FLAG(bool, poison_heap_with_zeroes, false, "") +MSAN_FLAG(bool, poison_stack_with_zeroes, false, "") +MSAN_FLAG(bool, poison_in_malloc, true, "") +MSAN_FLAG(bool, poison_in_free, true, "") +MSAN_FLAG(bool, report_umrs, true, "") +MSAN_FLAG(bool, wrap_signals, true, "") +MSAN_FLAG(bool, print_stats, false, "") +MSAN_FLAG(bool, halt_on_error, !&__msan_keep_going, "") +MSAN_FLAG(bool, atexit, false, "") +MSAN_FLAG(int, store_context_size, 20, + "Like malloc_context_size, but for uninit stores.") diff --git a/lib/msan/msan_interceptors.cc b/lib/msan/msan_interceptors.cc index bbdf18e162a3..4a243941b8a3 100644 --- a/lib/msan/msan_interceptors.cc +++ b/lib/msan/msan_interceptors.cc @@ -20,6 +20,7 @@ #include "msan_chained_origin_depot.h" #include "msan_origin.h" #include "msan_thread.h" +#include "msan_poisoning.h" #include "sanitizer_common/sanitizer_platform_limits_posix.h" #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_interface.h" @@ -290,7 +291,7 @@ INTERCEPTOR(char *, strcpy, char *dest, const char *src) { // NOLINT GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); char *res = REAL(strcpy)(dest, src); // NOLINT - CopyPoison(dest, src, n + 1, &stack); + CopyShadowAndOrigin(dest, src, n + 1, &stack); return res; } @@ -301,7 +302,7 @@ INTERCEPTOR(char *, strncpy, char *dest, const char *src, SIZE_T n) { // NOLINT if (copy_size < n) copy_size++; // trailing \0 char *res = REAL(strncpy)(dest, src, n); // NOLINT - CopyPoison(dest, src, copy_size, &stack); + CopyShadowAndOrigin(dest, src, copy_size, &stack); __msan_unpoison(dest + copy_size, n - copy_size); return res; } @@ -311,16 +312,18 @@ INTERCEPTOR(char *, stpcpy, char *dest, const char *src) { // NOLINT GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); char *res = REAL(stpcpy)(dest, src); // NOLINT - CopyPoison(dest, src, n + 1, &stack); + CopyShadowAndOrigin(dest, src, n + 1, &stack); return res; } INTERCEPTOR(char *, strdup, char *src) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; + // On FreeBSD strdup() leverages strlen(). + InterceptorScope interceptor_scope; SIZE_T n = REAL(strlen)(src); char *res = REAL(strdup)(src); - CopyPoison(res, src, n + 1, &stack); + CopyShadowAndOrigin(res, src, n + 1, &stack); return res; } @@ -330,7 +333,7 @@ INTERCEPTOR(char *, __strdup, char *src) { GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); char *res = REAL(__strdup)(src); - CopyPoison(res, src, n + 1, &stack); + CopyShadowAndOrigin(res, src, n + 1, &stack); return res; } #define MSAN_MAYBE_INTERCEPT___STRDUP INTERCEPT_FUNCTION(__strdup) @@ -341,9 +344,11 @@ INTERCEPTOR(char *, __strdup, char *src) { INTERCEPTOR(char *, strndup, char *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; + // On FreeBSD strndup() leverages strnlen(). + InterceptorScope interceptor_scope; SIZE_T copy_size = REAL(strnlen)(src, n); char *res = REAL(strndup)(src, n); - CopyPoison(res, src, copy_size, &stack); + CopyShadowAndOrigin(res, src, copy_size, &stack); __msan_unpoison(res + copy_size, 1); // \0 return res; } @@ -354,7 +359,7 @@ INTERCEPTOR(char *, __strndup, char *src, SIZE_T n) { GET_STORE_STACK_TRACE; SIZE_T copy_size = REAL(strnlen)(src, n); char *res = REAL(__strndup)(src, n); - CopyPoison(res, src, copy_size, &stack); + CopyShadowAndOrigin(res, src, copy_size, &stack); __msan_unpoison(res + copy_size, 1); // \0 return res; } @@ -377,7 +382,7 @@ INTERCEPTOR(char *, strcat, char *dest, const char *src) { // NOLINT SIZE_T src_size = REAL(strlen)(src); SIZE_T dest_size = REAL(strlen)(dest); char *res = REAL(strcat)(dest, src); // NOLINT - CopyPoison(dest + dest_size, src, src_size + 1, &stack); + CopyShadowAndOrigin(dest + dest_size, src, src_size + 1, &stack); return res; } @@ -387,7 +392,7 @@ INTERCEPTOR(char *, strncat, char *dest, const char *src, SIZE_T n) { // NOLINT SIZE_T dest_size = REAL(strlen)(dest); SIZE_T copy_size = REAL(strnlen)(src, n); char *res = REAL(strncat)(dest, src, n); // NOLINT - CopyPoison(dest + dest_size, src, copy_size, &stack); + CopyShadowAndOrigin(dest + dest_size, src, copy_size, &stack); __msan_unpoison(dest + dest_size + copy_size, 1); // \0 return res; } @@ -576,7 +581,8 @@ INTERCEPTOR(wchar_t *, wcscpy, wchar_t *dest, const wchar_t *src) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; wchar_t *res = REAL(wcscpy)(dest, src); - CopyPoison(dest, src, sizeof(wchar_t) * (REAL(wcslen)(src) + 1), &stack); + CopyShadowAndOrigin(dest, src, sizeof(wchar_t) * (REAL(wcslen)(src) + 1), + &stack); return res; } @@ -585,7 +591,7 @@ INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; wchar_t *res = REAL(wmemcpy)(dest, src, n); - CopyPoison(dest, src, n * sizeof(wchar_t), &stack); + CopyShadowAndOrigin(dest, src, n * sizeof(wchar_t), &stack); return res; } @@ -593,7 +599,7 @@ INTERCEPTOR(wchar_t *, wmempcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; wchar_t *res = REAL(wmempcpy)(dest, src, n); - CopyPoison(dest, src, n * sizeof(wchar_t), &stack); + CopyShadowAndOrigin(dest, src, n * sizeof(wchar_t), &stack); return res; } @@ -609,7 +615,7 @@ INTERCEPTOR(wchar_t *, wmemmove, wchar_t *dest, const wchar_t *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; wchar_t *res = REAL(wmemmove)(dest, src, n); - MovePoison(dest, src, n * sizeof(wchar_t), &stack); + MoveShadowAndOrigin(dest, src, n * sizeof(wchar_t), &stack); return res; } @@ -699,7 +705,15 @@ INTERCEPTOR(int, __fxstat64, int magic, int fd, void *buf) { #define MSAN_MAYBE_INTERCEPT___FXSTAT64 #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_FREEBSD +INTERCEPTOR(int, fstatat, int fd, char *pathname, void *buf, int flags) { + ENSURE_MSAN_INITED(); + int res = REAL(fstatat)(fd, pathname, buf, flags); + if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); + return res; +} +# define MSAN_INTERCEPT_FSTATAT INTERCEPT_FUNCTION(fstatat) +#else INTERCEPTOR(int, __fxstatat, int magic, int fd, char *pathname, void *buf, int flags) { ENSURE_MSAN_INITED(); @@ -707,9 +721,7 @@ INTERCEPTOR(int, __fxstatat, int magic, int fd, char *pathname, void *buf, if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); return res; } -#define MSAN_MAYBE_INTERCEPT___FXSTATAT INTERCEPT_FUNCTION(__fxstatat) -#else -#define MSAN_MAYBE_INTERCEPT___FXSTATAT +# define MSAN_INTERCEPT_FSTATAT INTERCEPT_FUNCTION(__fxstatat) #endif #if !SANITIZER_FREEBSD @@ -725,7 +737,16 @@ INTERCEPTOR(int, __fxstatat64, int magic, int fd, char *pathname, void *buf, #define MSAN_MAYBE_INTERCEPT___FXSTATAT64 #endif -#if !SANITIZER_FREEBSD +#if SANITIZER_FREEBSD +INTERCEPTOR(int, stat, char *path, void *buf) { + ENSURE_MSAN_INITED(); + int res = REAL(stat)(path, buf); + if (!res) + __msan_unpoison(buf, __sanitizer::struct_stat_sz); + return res; +} +# define MSAN_INTERCEPT_STAT INTERCEPT_FUNCTION(stat) +#else INTERCEPTOR(int, __xstat, int magic, char *path, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__xstat)(magic, path, buf); @@ -733,9 +754,7 @@ INTERCEPTOR(int, __xstat, int magic, char *path, void *buf) { __msan_unpoison(buf, __sanitizer::struct_stat_sz); return res; } -#define MSAN_MAYBE_INTERCEPT___XSTAT INTERCEPT_FUNCTION(__xstat) -#else -#define MSAN_MAYBE_INTERCEPT___XSTAT +# define MSAN_INTERCEPT_STAT INTERCEPT_FUNCTION(__xstat) #endif #if !SANITIZER_FREEBSD @@ -849,14 +868,29 @@ INTERCEPTOR(int, getrlimit64, int resource, void *rlim) { #define MSAN_MAYBE_INTERCEPT_GETRLIMIT64 #endif -INTERCEPTOR(int, uname, void *utsname) { +#if SANITIZER_FREEBSD +// FreeBSD's define uname() as +// static __inline int uname(struct utsname *name) { +// return __xuname(SYS_NMLN, (void*)name); +// } +INTERCEPTOR(int, __xuname, int size, void *utsname) { ENSURE_MSAN_INITED(); - int res = REAL(uname)(utsname); - if (!res) { + int res = REAL(__xuname)(size, utsname); + if (!res) __msan_unpoison(utsname, __sanitizer::struct_utsname_sz); - } return res; } +#define MSAN_INTERCEPT_UNAME INTERCEPT_FUNCTION(__xuname) +#else +INTERCEPTOR(int, uname, struct utsname *utsname) { + ENSURE_MSAN_INITED(); + int res = REAL(uname)(utsname); + if (!res) + __msan_unpoison(utsname, __sanitizer::struct_utsname_sz); + return res; +} +#define MSAN_INTERCEPT_UNAME INTERCEPT_FUNCTION(uname) +#endif INTERCEPTOR(int, gethostname, char *name, SIZE_T len) { ENSURE_MSAN_INITED(); @@ -918,17 +952,15 @@ INTERCEPTOR(SSIZE_T, recvfrom, int fd, void *buf, SIZE_T len, int flags, __msan_unpoison(buf, res); if (srcaddr) { SIZE_T sz = *addrlen; - __msan_unpoison(srcaddr, (sz < srcaddr_sz) ? sz : srcaddr_sz); + __msan_unpoison(srcaddr, Min(sz, srcaddr_sz)); } } return res; } INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) { - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) - return AllocatorReturnNull(); GET_MALLOC_STACK_TRACE; - if (!msan_inited) { + if (UNLIKELY(!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]; @@ -939,7 +971,7 @@ INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) { CHECK(allocated < kCallocPoolSize); return mem; } - return MsanReallocate(&stack, 0, nmemb * size, sizeof(u64), true); + return MsanCalloc(&stack, nmemb, size); } INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) { @@ -952,13 +984,11 @@ INTERCEPTOR(void *, malloc, SIZE_T size) { return MsanReallocate(&stack, 0, size, sizeof(u64), false); } -void __msan_allocated_memory(const void* data, uptr size) { +void __msan_allocated_memory(const void *data, uptr size) { GET_MALLOC_STACK_TRACE; - if (flags()->poison_in_malloc) - __msan_poison(data, size); - if (__msan_get_track_origins()) { - Origin o = Origin::CreateHeapOrigin(&stack); - __msan_set_origin(data, size, o.raw_id()); + if (flags()->poison_in_malloc) { + stack.tag = STACK_TRACE_TAG_POISON; + PoisonMemory(data, size, &stack); } } @@ -1328,6 +1358,9 @@ int OnExit() { InterceptorScope interceptor_scope; \ __msan_unpoison(__errno_location(), sizeof(int)); /* NOLINT */ \ ENSURE_MSAN_INITED(); +#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \ + do { \ + } while (false) #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ do { \ } while (false) @@ -1345,8 +1378,11 @@ int OnExit() { } while (false) // FIXME #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() -#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, map) \ - if (map) ForEachMappedRegion((link_map *)map, __msan_unpoison); +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \ + do { \ + link_map *map = GET_LINK_MAP_BY_DLOPEN_HANDLE((handle)); \ + if (map) ForEachMappedRegion(map, __msan_unpoison); \ + } while (false) #include "sanitizer_common/sanitizer_common_interceptors.inc" @@ -1360,53 +1396,26 @@ int OnExit() { #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) __msan_unpoison(p, s) #include "sanitizer_common/sanitizer_common_syscalls.inc" -static void PoisonShadow(uptr ptr, uptr size, u8 value) { - uptr PageSize = GetPageSizeCached(); - uptr shadow_beg = MEM_TO_SHADOW(ptr); - uptr shadow_end = MEM_TO_SHADOW(ptr + size); - if (value || - shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) { - REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); - } else { - uptr page_beg = RoundUpTo(shadow_beg, PageSize); - uptr page_end = RoundDownTo(shadow_end, PageSize); - - if (page_beg >= page_end) { - REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg); - } else { - if (page_beg != shadow_beg) { - REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg); - } - if (page_end != shadow_end) { - REAL(memset)((void *)page_end, 0, shadow_end - page_end); - } - MmapFixedNoReserve(page_beg, page_end - page_beg); - } - } -} - // These interface functions reside here so that they can use // REAL(memset), etc. void __msan_unpoison(const void *a, uptr size) { if (!MEM_IS_APP(a)) return; - PoisonShadow((uptr)a, size, 0); + SetShadow(a, size, 0); } void __msan_poison(const void *a, uptr size) { if (!MEM_IS_APP(a)) return; - PoisonShadow((uptr)a, size, - __msan::flags()->poison_heap_with_zeroes ? 0 : -1); + SetShadow(a, size, __msan::flags()->poison_heap_with_zeroes ? 0 : -1); } void __msan_poison_stack(void *a, uptr size) { if (!MEM_IS_APP(a)) return; - PoisonShadow((uptr)a, size, - __msan::flags()->poison_stack_with_zeroes ? 0 : -1); + SetShadow(a, size, __msan::flags()->poison_stack_with_zeroes ? 0 : -1); } void __msan_clear_and_unpoison(void *a, uptr size) { REAL(memset)(a, 0, size); - PoisonShadow((uptr)a, size, 0); + SetShadow(a, size, 0); } void *__msan_memcpy(void *dest, const void *src, SIZE_T n) { @@ -1415,7 +1424,7 @@ void *__msan_memcpy(void *dest, const void *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; void *res = REAL(memcpy)(dest, src, n); - CopyPoison(dest, src, n, &stack); + CopyShadowAndOrigin(dest, src, n, &stack); return res; } @@ -1434,7 +1443,7 @@ void *__msan_memmove(void *dest, const void *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; void *res = REAL(memmove)(dest, src, n); - MovePoison(dest, src, n, &stack); + MoveShadowAndOrigin(dest, src, n, &stack); return res; } @@ -1445,96 +1454,6 @@ void __msan_unpoison_string(const char* s) { namespace __msan { -u32 GetOriginIfPoisoned(uptr addr, uptr size) { - unsigned char *s = (unsigned char *)MEM_TO_SHADOW(addr); - for (uptr i = 0; i < size; ++i) - if (s[i]) - return *(u32 *)SHADOW_TO_ORIGIN(((uptr)s + i) & ~3UL); - return 0; -} - -void SetOriginIfPoisoned(uptr addr, uptr src_shadow, uptr size, - u32 src_origin) { - uptr dst_s = MEM_TO_SHADOW(addr); - uptr src_s = src_shadow; - uptr src_s_end = src_s + size; - - for (; src_s < src_s_end; ++dst_s, ++src_s) - if (*(u8 *)src_s) *(u32 *)SHADOW_TO_ORIGIN(dst_s &~3UL) = src_origin; -} - -void CopyOrigin(void *dst, const void *src, uptr size, StackTrace *stack) { - if (!__msan_get_track_origins()) return; - if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) return; - - uptr d = (uptr)dst; - uptr beg = d & ~3UL; - // Copy left unaligned origin if that memory is poisoned. - if (beg < d) { - u32 o = GetOriginIfPoisoned((uptr)src, d - beg); - if (o) { - if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack); - *(u32 *)MEM_TO_ORIGIN(beg) = o; - } - beg += 4; - } - - uptr end = (d + size) & ~3UL; - // If both ends fall into the same 4-byte slot, we are done. - if (end < beg) return; - - // Copy right unaligned origin if that memory is poisoned. - if (end < d + size) { - u32 o = GetOriginIfPoisoned((uptr)src + (end - d), (d + size) - end); - if (o) { - if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack); - *(u32 *)MEM_TO_ORIGIN(end) = o; - } - } - - if (beg < end) { - // Align src up. - uptr s = ((uptr)src + 3) & ~3UL; - // FIXME: factor out to msan_copy_origin_aligned - if (__msan_get_track_origins() > 1) { - u32 *src = (u32 *)MEM_TO_ORIGIN(s); - u32 *src_s = (u32 *)MEM_TO_SHADOW(s); - u32 *src_end = (u32 *)MEM_TO_ORIGIN(s + (end - beg)); - u32 *dst = (u32 *)MEM_TO_ORIGIN(beg); - u32 src_o = 0; - u32 dst_o = 0; - for (; src < src_end; ++src, ++src_s, ++dst) { - if (!*src_s) continue; - if (*src != src_o) { - src_o = *src; - dst_o = ChainOrigin(src_o, stack); - } - *dst = dst_o; - } - } else { - REAL(memcpy)((void *)MEM_TO_ORIGIN(beg), (void *)MEM_TO_ORIGIN(s), - end - beg); - } - } -} - -void MovePoison(void *dst, const void *src, uptr size, StackTrace *stack) { - if (!MEM_IS_APP(dst)) return; - if (!MEM_IS_APP(src)) return; - if (src == dst) return; - REAL(memmove)((void *)MEM_TO_SHADOW((uptr)dst), - (void *)MEM_TO_SHADOW((uptr)src), size); - CopyOrigin(dst, src, size, stack); -} - -void CopyPoison(void *dst, const void *src, uptr size, StackTrace *stack) { - if (!MEM_IS_APP(dst)) return; - if (!MEM_IS_APP(src)) return; - REAL(memcpy)((void *)MEM_TO_SHADOW((uptr)dst), - (void *)MEM_TO_SHADOW((uptr)src), size); - CopyOrigin(dst, src, size, stack); -} - void InitializeInterceptors() { static int inited = 0; CHECK_EQ(inited, 0); @@ -1617,8 +1536,8 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(gettimeofday); INTERCEPT_FUNCTION(fcvt); MSAN_MAYBE_INTERCEPT___FXSTAT; - MSAN_MAYBE_INTERCEPT___FXSTATAT; - MSAN_MAYBE_INTERCEPT___XSTAT; + MSAN_INTERCEPT_FSTATAT; + MSAN_INTERCEPT_STAT; MSAN_MAYBE_INTERCEPT___LXSTAT; MSAN_MAYBE_INTERCEPT___FXSTAT64; MSAN_MAYBE_INTERCEPT___FXSTATAT64; @@ -1631,7 +1550,7 @@ void InitializeInterceptors() { MSAN_MAYBE_INTERCEPT_FGETS_UNLOCKED; INTERCEPT_FUNCTION(getrlimit); MSAN_MAYBE_INTERCEPT_GETRLIMIT64; - INTERCEPT_FUNCTION(uname); + MSAN_INTERCEPT_UNAME; INTERCEPT_FUNCTION(gethostname); MSAN_MAYBE_INTERCEPT_EPOLL_WAIT; MSAN_MAYBE_INTERCEPT_EPOLL_PWAIT; diff --git a/lib/msan/msan_linux.cc b/lib/msan/msan_linux.cc index 0b67b531d51c..6c185165fc50 100644 --- a/lib/msan/msan_linux.cc +++ b/lib/msan/msan_linux.cc @@ -64,41 +64,45 @@ static bool ProtectMemoryRange(uptr beg, uptr size) { return true; } +static void CheckMemoryLayoutSanity() { + uptr prev_end = 0; + for (unsigned i = 0; i < kMemoryLayoutSize; ++i) { + uptr start = kMemoryLayout[i].start; + uptr end = kMemoryLayout[i].end; + MappingDesc::Type type = kMemoryLayout[i].type; + CHECK_LT(start, end); + CHECK_EQ(prev_end, start); + CHECK(addr_is_type(start, type)); + CHECK(addr_is_type((start + end) / 2, type)); + CHECK(addr_is_type(end - 1, type)); + if (type == MappingDesc::APP) { + uptr addr = start; + CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr))); + CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr))); + CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr))); + + addr = (start + end) / 2; + CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr))); + CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr))); + CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr))); + + addr = end - 1; + CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(addr))); + CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(addr))); + CHECK_EQ(MEM_TO_ORIGIN(addr), SHADOW_TO_ORIGIN(MEM_TO_SHADOW(addr))); + } + prev_end = end; + } +} + bool InitShadow(bool map_shadow, bool init_origins) { // Let user know mapping parameters first. VPrintf(1, "__msan_init %p\n", &__msan_init); - ReportMapRange("Low Memory ", kLowMemBeg, kLowMemSize); - ReportMapRange("Bad1 ", kBad1Beg, kBad1Size); - ReportMapRange("Shadow ", kShadowBeg, kShadowSize); - ReportMapRange("Bad2 ", kBad2Beg, kBad2Size); - ReportMapRange("Origins ", kOriginsBeg, kOriginsSize); - ReportMapRange("Bad3 ", kBad3Beg, kBad3Size); - ReportMapRange("High Memory", kHighMemBeg, kHighMemSize); + for (unsigned i = 0; i < kMemoryLayoutSize; ++i) + VPrintf(1, "%s: %zx - %zx\n", kMemoryLayout[i].name, kMemoryLayout[i].start, + kMemoryLayout[i].end - 1); - // Check mapping sanity (the invariant). - CHECK_EQ(kLowMemBeg, 0); - CHECK_EQ(kBad1Beg, kLowMemBeg + kLowMemSize); - CHECK_EQ(kShadowBeg, kBad1Beg + kBad1Size); - CHECK_GT(kShadowSize, 0); - CHECK_GE(kShadowSize, kLowMemSize + kHighMemSize); - CHECK_EQ(kBad2Beg, kShadowBeg + kShadowSize); - CHECK_EQ(kOriginsBeg, kBad2Beg + kBad2Size); - CHECK_EQ(kOriginsSize, kShadowSize); - CHECK_EQ(kBad3Beg, kOriginsBeg + kOriginsSize); - CHECK_EQ(kHighMemBeg, kBad3Beg + kBad3Size); - CHECK_GT(kHighMemSize, 0); - CHECK_GE(kHighMemBeg + kHighMemSize, kHighMemBeg); // Tests for no overflow. - - if (kLowMemSize > 0) { - CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(kLowMemBeg))); - CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(kLowMemBeg + kLowMemSize - 1))); - CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(kLowMemBeg))); - CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(kLowMemBeg + kLowMemSize - 1))); - } - CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(kHighMemBeg))); - CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(kHighMemBeg + kHighMemSize - 1))); - CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(kHighMemBeg))); - CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(kHighMemBeg + kHighMemSize - 1))); + CheckMemoryLayoutSanity(); if (!MEM_IS_APP(&__msan_init)) { Printf("FATAL: Code %p is out of application range. Non-PIE build?\n", @@ -106,29 +110,23 @@ bool InitShadow(bool map_shadow, bool init_origins) { return false; } - if (!CheckMemoryRangeAvailability(kShadowBeg, kShadowSize) || - (init_origins && - !CheckMemoryRangeAvailability(kOriginsBeg, kOriginsSize)) || - !CheckMemoryRangeAvailability(kBad1Beg, kBad1Size) || - !CheckMemoryRangeAvailability(kBad2Beg, kBad2Size) || - !CheckMemoryRangeAvailability(kBad3Beg, kBad3Size)) { - return false; + for (unsigned i = 0; i < kMemoryLayoutSize; ++i) { + uptr start = kMemoryLayout[i].start; + uptr end = kMemoryLayout[i].end; + uptr size= end - start; + MappingDesc::Type type = kMemoryLayout[i].type; + if ((map_shadow && type == MappingDesc::SHADOW) || + (init_origins && type == MappingDesc::ORIGIN)) { + if (!CheckMemoryRangeAvailability(start, size)) return false; + if ((uptr)MmapFixedNoReserve(start, size) != start) return false; + if (common_flags()->use_madv_dontdump) + DontDumpShadowMemory(start, size); + } else if (type == MappingDesc::INVALID) { + if (!CheckMemoryRangeAvailability(start, size)) return false; + if (!ProtectMemoryRange(start, size)) return false; + } } - if (!ProtectMemoryRange(kBad1Beg, kBad1Size) || - !ProtectMemoryRange(kBad2Beg, kBad2Size) || - !ProtectMemoryRange(kBad3Beg, kBad3Size)) { - return false; - } - - if (map_shadow) { - void *shadow = MmapFixedNoReserve(kShadowBeg, kShadowSize); - if (shadow != (void*)kShadowBeg) return false; - } - if (init_origins) { - void *origins = MmapFixedNoReserve(kOriginsBeg, kOriginsSize); - if (origins != (void*)kOriginsBeg) return false; - } return true; } @@ -137,7 +135,7 @@ void MsanDie() { __sanitizer_cov_dump(); if (death_callback) death_callback(); - _exit(flags()->exit_code); + internal__exit(flags()->exit_code); } static void MsanAtExit(void) { @@ -157,20 +155,26 @@ void InstallAtExitHandler() { static pthread_key_t tsd_key; static bool tsd_key_inited = false; + void MsanTSDInit(void (*destructor)(void *tsd)) { CHECK(!tsd_key_inited); tsd_key_inited = true; CHECK_EQ(0, pthread_key_create(&tsd_key, destructor)); } -void *MsanTSDGet() { - CHECK(tsd_key_inited); - return pthread_getspecific(tsd_key); +static THREADLOCAL MsanThread* msan_current_thread; + +MsanThread *GetCurrentThread() { + return msan_current_thread; } -void MsanTSDSet(void *tsd) { +void SetCurrentThread(MsanThread *t) { + // Make sure we do not reset the current MsanThread. + CHECK_EQ(0, msan_current_thread); + msan_current_thread = t; + // Make sure that MsanTSDDtor gets called at the end. CHECK(tsd_key_inited); - pthread_setspecific(tsd_key, tsd); + pthread_setspecific(tsd_key, (void *)t); } void MsanTSDDtor(void *tsd) { @@ -180,6 +184,9 @@ void MsanTSDDtor(void *tsd) { CHECK_EQ(0, pthread_setspecific(tsd_key, tsd)); return; } + msan_current_thread = nullptr; + // Make sure that signal handler can not see a stale current thread pointer. + atomic_signal_fence(memory_order_seq_cst); MsanThread::TSDDtor(tsd); } diff --git a/lib/msan/msan_poisoning.cc b/lib/msan/msan_poisoning.cc new file mode 100644 index 000000000000..96411fdbc31b --- /dev/null +++ b/lib/msan/msan_poisoning.cc @@ -0,0 +1,174 @@ +//===-- msan_poisoning.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 MemorySanitizer. +// +//===----------------------------------------------------------------------===// + +#include "msan_poisoning.h" + +#include "interception/interception.h" +#include "msan_origin.h" +#include "sanitizer_common/sanitizer_common.h" + +DECLARE_REAL(void *, memset, void *dest, int c, uptr n) +DECLARE_REAL(void *, memcpy, void *dest, const void *src, uptr n) +DECLARE_REAL(void *, memmove, void *dest, const void *src, uptr n) + +namespace __msan { + +u32 GetOriginIfPoisoned(uptr addr, uptr size) { + unsigned char *s = (unsigned char *)MEM_TO_SHADOW(addr); + for (uptr i = 0; i < size; ++i) + if (s[i]) return *(u32 *)SHADOW_TO_ORIGIN(((uptr)s + i) & ~3UL); + return 0; +} + +void SetOriginIfPoisoned(uptr addr, uptr src_shadow, uptr size, + u32 src_origin) { + uptr dst_s = MEM_TO_SHADOW(addr); + uptr src_s = src_shadow; + uptr src_s_end = src_s + size; + + for (; src_s < src_s_end; ++dst_s, ++src_s) + if (*(u8 *)src_s) *(u32 *)SHADOW_TO_ORIGIN(dst_s & ~3UL) = src_origin; +} + +void CopyOrigin(const void *dst, const void *src, uptr size, + StackTrace *stack) { + if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) return; + + uptr d = (uptr)dst; + uptr beg = d & ~3UL; + // Copy left unaligned origin if that memory is poisoned. + if (beg < d) { + u32 o = GetOriginIfPoisoned((uptr)src, d - beg); + if (o) { + if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack); + *(u32 *)MEM_TO_ORIGIN(beg) = o; + } + beg += 4; + } + + uptr end = (d + size) & ~3UL; + // If both ends fall into the same 4-byte slot, we are done. + if (end < beg) return; + + // Copy right unaligned origin if that memory is poisoned. + if (end < d + size) { + u32 o = GetOriginIfPoisoned((uptr)src + (end - d), (d + size) - end); + if (o) { + if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack); + *(u32 *)MEM_TO_ORIGIN(end) = o; + } + } + + if (beg < end) { + // Align src up. + uptr s = ((uptr)src + 3) & ~3UL; + // FIXME: factor out to msan_copy_origin_aligned + if (__msan_get_track_origins() > 1) { + u32 *src = (u32 *)MEM_TO_ORIGIN(s); + u32 *src_s = (u32 *)MEM_TO_SHADOW(s); + u32 *src_end = (u32 *)MEM_TO_ORIGIN(s + (end - beg)); + u32 *dst = (u32 *)MEM_TO_ORIGIN(beg); + u32 src_o = 0; + u32 dst_o = 0; + for (; src < src_end; ++src, ++src_s, ++dst) { + if (!*src_s) continue; + if (*src != src_o) { + src_o = *src; + dst_o = ChainOrigin(src_o, stack); + } + *dst = dst_o; + } + } else { + REAL(memcpy)((void *)MEM_TO_ORIGIN(beg), (void *)MEM_TO_ORIGIN(s), + end - beg); + } + } +} + +void MoveShadowAndOrigin(const void *dst, const void *src, uptr size, + StackTrace *stack) { + if (!MEM_IS_APP(dst)) return; + if (!MEM_IS_APP(src)) return; + if (src == dst) return; + REAL(memmove)((void *)MEM_TO_SHADOW((uptr)dst), + (void *)MEM_TO_SHADOW((uptr)src), size); + if (__msan_get_track_origins()) CopyOrigin(dst, src, size, stack); +} + +void CopyShadowAndOrigin(const void *dst, const void *src, uptr size, + StackTrace *stack) { + if (!MEM_IS_APP(dst)) return; + if (!MEM_IS_APP(src)) return; + REAL(memcpy)((void *)MEM_TO_SHADOW((uptr)dst), + (void *)MEM_TO_SHADOW((uptr)src), size); + if (__msan_get_track_origins()) CopyOrigin(dst, src, size, stack); +} + +void CopyMemory(void *dst, const void *src, uptr size, StackTrace *stack) { + REAL(memcpy)(dst, src, size); + CopyShadowAndOrigin(dst, src, size, stack); +} + +void SetShadow(const void *ptr, uptr size, u8 value) { + uptr PageSize = GetPageSizeCached(); + uptr shadow_beg = MEM_TO_SHADOW(ptr); + uptr shadow_end = MEM_TO_SHADOW((uptr)ptr + size); + if (value || + shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) { + REAL(memset)((void *)shadow_beg, value, shadow_end - shadow_beg); + } else { + uptr page_beg = RoundUpTo(shadow_beg, PageSize); + uptr page_end = RoundDownTo(shadow_end, PageSize); + + if (page_beg >= page_end) { + REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg); + } else { + if (page_beg != shadow_beg) { + REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg); + } + if (page_end != shadow_end) { + REAL(memset)((void *)page_end, 0, shadow_end - page_end); + } + MmapFixedNoReserve(page_beg, page_end - page_beg); + } + } +} + +void SetOrigin(const void *dst, 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. + uptr x = MEM_TO_ORIGIN((uptr)dst); + 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 to 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; +} + +void PoisonMemory(const void *dst, uptr size, StackTrace *stack) { + SetShadow(dst, size, (u8)-1); + + if (__msan_get_track_origins()) { + Origin o = Origin::CreateHeapOrigin(stack); + SetOrigin(dst, size, o.raw_id()); + } +} + +} // namespace __msan diff --git a/lib/msan/msan_poisoning.h b/lib/msan/msan_poisoning.h new file mode 100644 index 000000000000..edacbeeab0a6 --- /dev/null +++ b/lib/msan/msan_poisoning.h @@ -0,0 +1,59 @@ +//===-- msan_poisoning.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. +// +//===----------------------------------------------------------------------===// + +#ifndef MSAN_POISONING_H +#define MSAN_POISONING_H + +#include "msan.h" + +namespace __msan { + +// Return origin for the first poisoned byte in the memory range, or 0. +u32 GetOriginIfPoisoned(uptr addr, uptr size); + +// Walk [addr, addr+size) app memory region, copying origin tags from the +// corresponding positions in [src_origin, src_origin+size) where the +// corresponding shadow in [src_shadow, src_shadow+size) is non-zero. +void SetOriginIfPoisoned(uptr addr, uptr src_shadow, uptr size, u32 src_origin); + +// Copy origin from src (app address) to dst (app address), creating chained +// origin ids as necessary, without overriding origin for fully initialized +// quads. +void CopyOrigin(const void *dst, const void *src, uptr size, StackTrace *stack); + +// memmove() shadow and origin. Dst and src are application addresses. +// See CopyOrigin() for the origin copying logic. +void MoveShadowAndOrigin(const void *dst, const void *src, uptr size, + StackTrace *stack); + +// memcpy() shadow and origin. Dst and src are application addresses. +// See CopyOrigin() for the origin copying logic. +void CopyShadowAndOrigin(const void *dst, const void *src, uptr size, + StackTrace *stack); + +// memcpy() app memory, and do "the right thing" to the corresponding shadow and +// origin regions. +void CopyMemory(void *dst, const void *src, uptr size, StackTrace *stack); + +// Fill shadow will value. Ptr is an application address. +void SetShadow(const void *ptr, uptr size, u8 value); + +// Set origin for the memory region. +void SetOrigin(const void *dst, uptr size, u32 origin); + +// Mark memory region uninitialized, with origins. +void PoisonMemory(const void *dst, uptr size, StackTrace *stack); + +} // namespace __msan + +#endif // MSAN_POISONING_H diff --git a/lib/msan/msan_report.cc b/lib/msan/msan_report.cc index 717c4a958c8f..33c28b2fba0e 100644 --- a/lib/msan/msan_report.cc +++ b/lib/msan/msan_report.cc @@ -75,8 +75,23 @@ static void DescribeOrigin(u32 id) { DescribeStackOrigin(so, pc); } else { StackTrace stack = o.getStackTraceForHeapOrigin(); - Printf(" %sUninitialized value was created by a heap allocation%s\n", - d.Origin(), d.End()); + switch (stack.tag) { + case StackTrace::TAG_ALLOC: + Printf(" %sUninitialized value was created by a heap allocation%s\n", + d.Origin(), d.End()); + break; + case StackTrace::TAG_DEALLOC: + Printf(" %sUninitialized value was created by a heap deallocation%s\n", + d.Origin(), d.End()); + break; + case STACK_TRACE_TAG_POISON: + Printf(" %sMemory was marked as uninitialized%s\n", d.Origin(), + d.End()); + break; + default: + Printf(" %sUninitialized value was created%s\n", d.Origin(), d.End()); + break; + } stack.Print(); } } @@ -255,7 +270,7 @@ void ReportUMRInsideAddressRange(const char *what, const void *start, uptr size, Printf("%sUninitialized bytes in %s%s%s at offset %zu inside [%p, %zu)%s\n", d.Warning(), d.Name(), what, d.Warning(), offset, start, size, d.End()); - if (__sanitizer::common_flags()->verbosity > 0) + if (__sanitizer::Verbosity()) DescribeMemoryRange(start, size); } diff --git a/lib/msan/msan_thread.cc b/lib/msan/msan_thread.cc index f29a4b053a36..e15a247c6bb8 100644 --- a/lib/msan/msan_thread.cc +++ b/lib/msan/msan_thread.cc @@ -79,15 +79,4 @@ thread_return_t MsanThread::ThreadStart() { return res; } -MsanThread *GetCurrentThread() { - return reinterpret_cast(MsanTSDGet()); -} - -void SetCurrentThread(MsanThread *t) { - // Make sure we do not reset the current MsanThread. - CHECK_EQ(0, MsanTSDGet()); - MsanTSDSet(t); - CHECK_EQ(t, MsanTSDGet()); -} - } // namespace __msan diff --git a/lib/msan/tests/CMakeLists.txt b/lib/msan/tests/CMakeLists.txt index 53e1b549b70d..e008bd329cb4 100644 --- a/lib/msan/tests/CMakeLists.txt +++ b/lib/msan/tests/CMakeLists.txt @@ -19,6 +19,7 @@ set(MSAN_UNITTEST_HEADERS ) set(MSAN_UNITTEST_COMMON_CFLAGS -I${COMPILER_RT_LIBCXX_PATH}/include + ${COMPILER_RT_TEST_CFLAGS} ${COMPILER_RT_GTEST_CFLAGS} -I${COMPILER_RT_SOURCE_DIR}/include -I${COMPILER_RT_SOURCE_DIR}/lib @@ -135,9 +136,9 @@ endmacro() # We should only build MSan unit tests if we can build instrumented libcxx. if(COMPILER_RT_CAN_EXECUTE_TESTS AND COMPILER_RT_HAS_LIBCXX_SOURCES) - if(CAN_TARGET_x86_64) - add_msan_tests_for_arch(x86_64 "") - add_msan_tests_for_arch(x86_64 "-with-call" + foreach(arch ${MSAN_SUPPORTED_ARCH}) + add_msan_tests_for_arch(${arch} "") + add_msan_tests_for_arch(${arch} "-with-call" -mllvm -msan-instrumentation-with-call-threshold=0) - endif() + endforeach() endif() diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc index 554265da6aa9..1c5fc5f7f1e3 100644 --- a/lib/msan/tests/msan_test.cc +++ b/lib/msan/tests/msan_test.cc @@ -21,13 +21,25 @@ #include "sanitizer/allocator_interface.h" #include "sanitizer/msan_interface.h" +#if defined(__FreeBSD__) +# define _KERNEL // To declare 'shminfo' structure. +# include +# undef _KERNEL +extern "C" { +// doesn't declare these functions in _KERNEL mode. +void *shmat(int, const void *, int); +int shmget(key_t, size_t, int); +int shmctl(int, int, struct shmid_ds *); +int shmdt(const void *); +} +#endif + #include #include #include #include #include #include -#include #include #include @@ -43,20 +55,31 @@ #include #include #include -#include #include #include -#include #include #include #include #include #include -#include -#include #include #include +#if !defined(__FreeBSD__) +# include +# include +# include +# include +# include +#else +# include +# include +# include +# include +# define f_namelen f_namemax // FreeBSD names this statfs field so. +# define cpu_set_t cpuset_t +#endif + #if defined(__i386__) || defined(__x86_64__) # include # define MSAN_HAS_M128 1 @@ -68,6 +91,19 @@ # include #endif +// On FreeBSD procfs is not enabled by default. +#if defined(__FreeBSD__) +# define FILE_TO_READ "/bin/cat" +# define DIR_TO_READ "/bin" +# define SUBFILE_TO_READ "cat" +# define SYMLINK_TO_READ "/usr/bin/tar" +#else +# define FILE_TO_READ "/proc/self/stat" +# define DIR_TO_READ "/proc/self" +# define SUBFILE_TO_READ "stat" +# define SYMLINK_TO_READ "/proc/self/exe" +#endif + static const size_t kPageSize = 4096; typedef unsigned char U1; @@ -493,10 +529,9 @@ static char *DynRetTestStr; TEST(MemorySanitizer, DynRet) { ReturnPoisoned(); - EXPECT_NOT_POISONED(clearenv()); + EXPECT_NOT_POISONED(atoi("0")); } - TEST(MemorySanitizer, DynRet1) { ReturnPoisoned(); } @@ -551,7 +586,7 @@ TEST(MemorySanitizer, strerror) { TEST(MemorySanitizer, strerror_r) { errno = 0; char buf[1000]; - char *res = strerror_r(EINVAL, buf, sizeof(buf)); + char *res = (char*) (size_t) strerror_r(EINVAL, buf, sizeof(buf)); ASSERT_EQ(0, errno); if (!res) res = buf; // POSIX version success. EXPECT_NOT_POISONED(strlen(res)); @@ -559,7 +594,7 @@ TEST(MemorySanitizer, strerror_r) { TEST(MemorySanitizer, fread) { char *x = new char[32]; - FILE *f = fopen("/proc/self/stat", "r"); + FILE *f = fopen(FILE_TO_READ, "r"); ASSERT_TRUE(f != NULL); fread(x, 1, 32, f); EXPECT_NOT_POISONED(x[0]); @@ -571,7 +606,7 @@ TEST(MemorySanitizer, fread) { TEST(MemorySanitizer, read) { char *x = new char[32]; - int fd = open("/proc/self/stat", O_RDONLY); + int fd = open(FILE_TO_READ, O_RDONLY); ASSERT_GT(fd, 0); int sz = read(fd, x, 32); ASSERT_EQ(sz, 32); @@ -584,7 +619,7 @@ TEST(MemorySanitizer, read) { TEST(MemorySanitizer, pread) { char *x = new char[32]; - int fd = open("/proc/self/stat", O_RDONLY); + int fd = open(FILE_TO_READ, O_RDONLY); ASSERT_GT(fd, 0); int sz = pread(fd, x, 32, 0); ASSERT_EQ(sz, 32); @@ -602,11 +637,11 @@ TEST(MemorySanitizer, readv) { iov[0].iov_len = 5; iov[1].iov_base = buf + 10; iov[1].iov_len = 2000; - int fd = open("/proc/self/stat", O_RDONLY); + int fd = open(FILE_TO_READ, O_RDONLY); ASSERT_GT(fd, 0); int sz = readv(fd, iov, 2); ASSERT_GE(sz, 0); - ASSERT_LT(sz, 5 + 2000); + ASSERT_LE(sz, 5 + 2000); ASSERT_GT((size_t)sz, iov[0].iov_len); EXPECT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[1]); @@ -626,11 +661,11 @@ TEST(MemorySanitizer, preadv) { iov[0].iov_len = 5; iov[1].iov_base = buf + 10; iov[1].iov_len = 2000; - int fd = open("/proc/self/stat", O_RDONLY); + int fd = open(FILE_TO_READ, O_RDONLY); ASSERT_GT(fd, 0); int sz = preadv(fd, iov, 2, 3); ASSERT_GE(sz, 0); - ASSERT_LT(sz, 5 + 2000); + ASSERT_LE(sz, 5 + 2000); ASSERT_GT((size_t)sz, iov[0].iov_len); EXPECT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[1]); @@ -652,15 +687,14 @@ TEST(MemorySanitizer, DISABLED_ioctl) { TEST(MemorySanitizer, readlink) { char *x = new char[1000]; - readlink("/proc/self/exe", x, 1000); + readlink(SYMLINK_TO_READ, x, 1000); EXPECT_NOT_POISONED(x[0]); delete [] x; } - TEST(MemorySanitizer, stat) { struct stat* st = new struct stat; - int res = stat("/proc/self/stat", st); + int res = stat(FILE_TO_READ, st); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st->st_dev); EXPECT_NOT_POISONED(st->st_mode); @@ -669,9 +703,9 @@ TEST(MemorySanitizer, stat) { TEST(MemorySanitizer, fstatat) { struct stat* st = new struct stat; - int dirfd = open("/proc/self", O_RDONLY); + int dirfd = open(DIR_TO_READ, O_RDONLY); ASSERT_GT(dirfd, 0); - int res = fstatat(dirfd, "stat", st, 0); + int res = fstatat(dirfd, SUBFILE_TO_READ, st, 0); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st->st_dev); EXPECT_NOT_POISONED(st->st_mode); @@ -763,6 +797,8 @@ TEST(MemorySanitizer, poll) { close(pipefd[1]); } +// There is no ppoll() on FreeBSD. +#if !defined (__FreeBSD__) TEST(MemorySanitizer, ppoll) { int* pipefd = new int[2]; int res = pipe(pipefd); @@ -787,6 +823,7 @@ TEST(MemorySanitizer, ppoll) { close(pipefd[0]); close(pipefd[1]); } +#endif TEST(MemorySanitizer, poll_positive) { int* pipefd = new int[2]; @@ -851,8 +888,11 @@ TEST(MemorySanitizer, accept) { res = fcntl(connect_socket, F_SETFL, O_NONBLOCK); ASSERT_EQ(0, res); res = connect(connect_socket, (struct sockaddr *)&sai, sizeof(sai)); - ASSERT_EQ(-1, res); - ASSERT_EQ(EINPROGRESS, errno); + // On FreeBSD this connection completes immediately. + if (res != 0) { + ASSERT_EQ(-1, res); + ASSERT_EQ(EINPROGRESS, errno); + } __msan_poison(&sai, sizeof(sai)); int new_sock = accept(listen_socket, (struct sockaddr *)&sai, &sz); @@ -973,7 +1013,6 @@ TEST(MemorySanitizer, recvmsg) { ASSERT_EQ(0, res); ASSERT_EQ(sizeof(client_sai), sz); - const char *s = "message text"; struct iovec iov; iov.iov_base = (void *)s; @@ -1125,12 +1164,15 @@ TEST(MemorySanitizer, getcwd_gnu) { free(res); } +// There's no get_current_dir_name() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, get_current_dir_name) { char* res = get_current_dir_name(); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(res[0]); free(res); } +#endif TEST(MemorySanitizer, shmctl) { int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); @@ -1141,6 +1183,8 @@ TEST(MemorySanitizer, shmctl) { ASSERT_GT(res, -1); EXPECT_NOT_POISONED(ds); + // FreeBSD does not support shmctl(IPC_INFO) and shmctl(SHM_INFO). +#if !defined(__FreeBSD__) struct shminfo si; res = shmctl(id, IPC_INFO, (struct shmid_ds *)&si); ASSERT_GT(res, -1); @@ -1150,6 +1194,7 @@ TEST(MemorySanitizer, shmctl) { res = shmctl(id, SHM_INFO, (struct shmid_ds *)&s_i); ASSERT_GT(res, -1); EXPECT_NOT_POISONED(s_i); +#endif res = shmctl(id, IPC_RMID, 0); ASSERT_GT(res, -1); @@ -1157,7 +1202,7 @@ TEST(MemorySanitizer, shmctl) { TEST(MemorySanitizer, shmat) { void *p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); ASSERT_NE(MAP_FAILED, p); ((char *)p)[10] = *GetPoisoned(); @@ -1183,6 +1228,8 @@ TEST(MemorySanitizer, shmat) { ASSERT_GT(res, -1); } +// There's no random_r() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, random_r) { int32_t x; char z[64]; @@ -1198,6 +1245,7 @@ TEST(MemorySanitizer, random_r) { ASSERT_EQ(0, res); EXPECT_NOT_POISONED(x); } +#endif TEST(MemorySanitizer, confstr) { char buf[3]; @@ -1215,6 +1263,16 @@ TEST(MemorySanitizer, confstr) { ASSERT_EQ(res, strlen(buf2) + 1); } +TEST(MemorySanitizer, opendir) { + DIR *dir = opendir("."); + closedir(dir); + + char name[10] = "."; + __msan_poison(name, sizeof(name)); + EXPECT_UMR(dir = opendir(name)); + closedir(dir); +} + TEST(MemorySanitizer, readdir) { DIR *dir = opendir("."); struct dirent *d = readdir(dir); @@ -1251,6 +1309,8 @@ TEST(MemorySanitizer, realpath_null) { free(res); } +// There's no canonicalize_file_name() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, canonicalize_file_name) { const char* relpath = "."; char* res = canonicalize_file_name(relpath); @@ -1258,6 +1318,7 @@ TEST(MemorySanitizer, canonicalize_file_name) { EXPECT_NOT_POISONED(res[0]); free(res); } +#endif extern char **environ; @@ -1655,26 +1716,35 @@ TEST(MemorySanitizer, modfl) { EXPECT_NOT_POISONED(y); } +// There's no sincos() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, sincos) { double s, c; sincos(0.2, &s, &c); EXPECT_NOT_POISONED(s); EXPECT_NOT_POISONED(c); } +#endif +// There's no sincosf() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, sincosf) { float s, c; sincosf(0.2, &s, &c); EXPECT_NOT_POISONED(s); EXPECT_NOT_POISONED(c); } +#endif +// There's no sincosl() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, sincosl) { long double s, c; sincosl(0.2, &s, &c); EXPECT_NOT_POISONED(s); EXPECT_NOT_POISONED(c); } +#endif TEST(MemorySanitizer, remquo) { int quo; @@ -1729,13 +1799,18 @@ TEST(MemorySanitizer, lgammaf_r) { EXPECT_NOT_POISONED(sgn); } +// There's no lgammal_r() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, lgammal_r) { int sgn; long double res = lgammal_r(1.1, &sgn); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(sgn); } +#endif +// There's no drand48_r() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, drand48_r) { struct drand48_data buf; srand48_r(0, &buf); @@ -1743,7 +1818,10 @@ TEST(MemorySanitizer, drand48_r) { drand48_r(&buf, &d); EXPECT_NOT_POISONED(d); } +#endif +// There's no lrand48_r() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, lrand48_r) { struct drand48_data buf; srand48_r(0, &buf); @@ -1751,6 +1829,7 @@ TEST(MemorySanitizer, lrand48_r) { lrand48_r(&buf, &d); EXPECT_NOT_POISONED(d); } +#endif TEST(MemorySanitizer, sprintf) { // NOLINT char buff[10]; @@ -2015,6 +2094,8 @@ TEST(MemorySanitizer, localtime_r) { EXPECT_NE(0U, strlen(time.tm_zone)); } +// There's no getmntent() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, getmntent) { FILE *fp = setmntent("/etc/fstab", "r"); struct mntent *mnt = getmntent(fp); @@ -2027,7 +2108,10 @@ TEST(MemorySanitizer, getmntent) { EXPECT_NOT_POISONED(mnt->mnt_passno); fclose(fp); } +#endif +// There's no getmntent_r() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, getmntent_r) { FILE *fp = setmntent("/etc/fstab", "r"); struct mntent mntbuf; @@ -2042,6 +2126,7 @@ TEST(MemorySanitizer, getmntent_r) { EXPECT_NOT_POISONED(mnt->mnt_passno); fclose(fp); } +#endif TEST(MemorySanitizer, ether) { const char *asc = "11:22:33:44:55:66"; @@ -2813,12 +2898,15 @@ TEST(MemorySanitizer, dlopenFailed) { #endif // MSAN_TEST_DISABLE_DLOPEN +// There's no sched_getaffinity() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, sched_getaffinity) { cpu_set_t mask; int res = sched_getaffinity(getpid(), sizeof(mask), &mask); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(mask); } +#endif TEST(MemorySanitizer, scanf) { const char *input = "42 hello"; @@ -3048,11 +3136,14 @@ TEST(MemorySanitizer, posix_memalign) { free(p); } +// There's no memalign() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, memalign) { void *p = memalign(4096, 13); EXPECT_EQ(0U, (uintptr_t)p % kPageSize); free(p); } +#endif TEST(MemorySanitizer, valloc) { void *a = valloc(100); @@ -3060,6 +3151,8 @@ TEST(MemorySanitizer, valloc) { free(a); } +// There's no pvalloc() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, pvalloc) { void *p = pvalloc(kPageSize + 100); EXPECT_EQ(0U, (uintptr_t)p % kPageSize); @@ -3071,6 +3164,7 @@ TEST(MemorySanitizer, pvalloc) { EXPECT_EQ(kPageSize, __sanitizer_get_allocated_size(p)); free(p); } +#endif TEST(MemorySanitizer, inet_pton) { const char *s = "1:0:0:0:0:0:0:8"; @@ -3114,12 +3208,15 @@ TEST(MemorySanitizer, gethostname) { EXPECT_NOT_POISONED(strlen(buf)); } +// There's no sysinfo() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, sysinfo) { struct sysinfo info; int res = sysinfo(&info); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(info); } +#endif TEST(MemorySanitizer, getpwuid) { struct passwd *p = getpwuid(0); // root @@ -3207,6 +3304,8 @@ TEST(MemorySanitizer, getpwent_r) { EXPECT_NOT_POISONED(pwdres); } +// There's no fgetpwent() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, fgetpwent) { FILE *fp = fopen("/etc/passwd", "r"); struct passwd *p = fgetpwent(fp); @@ -3217,6 +3316,7 @@ TEST(MemorySanitizer, fgetpwent) { EXPECT_NOT_POISONED(p->pw_uid); fclose(fp); } +#endif TEST(MemorySanitizer, getgrent) { setgrent(); @@ -3228,6 +3328,8 @@ TEST(MemorySanitizer, getgrent) { EXPECT_NOT_POISONED(p->gr_gid); } +// There's no fgetgrent() on FreeBSD. +#if !defined(__FreeBSD__) TEST(MemorySanitizer, fgetgrent) { FILE *fp = fopen("/etc/group", "r"); struct group *grp = fgetgrent(fp); @@ -3242,6 +3344,7 @@ TEST(MemorySanitizer, fgetgrent) { } fclose(fp); } +#endif TEST(MemorySanitizer, getgrent_r) { struct group grp; @@ -3597,7 +3700,7 @@ TEST(MemorySanitizer, UnalignedStore64_precise2) { EXPECT_POISONED_O(x[11], originx3); } -#if defined(__clang__) +#if (defined(__x86_64__) && defined(__clang__)) namespace { typedef U1 V16x8 __attribute__((__vector_size__(16))); typedef U2 V8x16 __attribute__((__vector_size__(16))); @@ -4116,7 +4219,8 @@ TEST(MemorySanitizer, LargeAllocatorUnpoisonsOnFree) { // Allocate the page that was released to the OS in free() with the real mmap, // bypassing the interceptor. - char *q = (char *)real_mmap(p, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + char *q = (char *)real_mmap(p, 4096, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); ASSERT_NE((char *)0, q); ASSERT_TRUE(q <= p); diff --git a/lib/profile/InstrProfilingFile.c b/lib/profile/InstrProfilingFile.c index 5aef3904b56d..346665fd5b3e 100644 --- a/lib/profile/InstrProfilingFile.c +++ b/lib/profile/InstrProfilingFile.c @@ -11,6 +11,7 @@ #include #include #include +#include #define UNCONST(ptr) ((void *)(uintptr_t)(ptr)) @@ -175,7 +176,11 @@ int __llvm_profile_write_file(void) { return -1; /* Write the file. */ - return writeFileWithName(__llvm_profile_CurrentFilename); + int rc = writeFileWithName(__llvm_profile_CurrentFilename); + if (rc && getenv("LLVM_PROFILE_VERBOSE_ERRORS")) + fprintf(stderr, "LLVM Profile: Failed to write file \"%s\": %s\n", + __llvm_profile_CurrentFilename, strerror(errno)); + return rc; } static void writeFileWithoutReturn(void) { diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt index fe4418cd3cfa..86697e7f7c40 100644 --- a/lib/sanitizer_common/CMakeLists.txt +++ b/lib/sanitizer_common/CMakeLists.txt @@ -7,6 +7,7 @@ set(SANITIZER_SOURCES sanitizer_deadlock_detector1.cc sanitizer_deadlock_detector2.cc sanitizer_flags.cc + sanitizer_flag_parser.cc sanitizer_libc.cc sanitizer_libignore.cc sanitizer_linux.cc @@ -63,7 +64,9 @@ set(SANITIZER_HEADERS sanitizer_common_syscalls.inc sanitizer_deadlock_detector.h sanitizer_deadlock_detector_interface.h + sanitizer_flag_parser.h sanitizer_flags.h + sanitizer_flags.inc sanitizer_internal_defs.h sanitizer_lfstack.h sanitizer_libc.h @@ -105,11 +108,10 @@ endif() set(SANITIZER_CFLAGS ${SANITIZER_COMMON_CFLAGS}) append_no_rtti_flag(SANITIZER_CFLAGS) -# Stack frames on PowerPC are much larger than anticipated. -if(NOT ${LLVM_NATIVE_ARCH} STREQUAL "PowerPC") - append_list_if(COMPILER_RT_HAS_WFRAME_LARGER_THAN_FLAG -Wframe-larger-than=512 SANITIZER_CFLAGS) -endif() -append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors SANITIZER_CFLAGS) +append_list_if(SANITIZER_LIMIT_FRAME_SIZE -Wframe-larger-than=512 + SANITIZER_CFLAGS) +append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors + SANITIZER_CFLAGS) add_custom_target(sanitizer_common) set(SANITIZER_RUNTIME_LIBRARIES) diff --git a/lib/sanitizer_common/sanitizer_allocator.cc b/lib/sanitizer_common/sanitizer_allocator.cc index 47509f83665b..03b3e83153de 100644 --- a/lib/sanitizer_common/sanitizer_allocator.cc +++ b/lib/sanitizer_common/sanitizer_allocator.cc @@ -14,7 +14,6 @@ #include "sanitizer_allocator.h" #include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" -#include "sanitizer_flags.h" namespace __sanitizer { @@ -61,7 +60,7 @@ InternalAllocator *internal_allocator() { SpinMutexLock l(&internal_alloc_init_mu); if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) == 0) { - internal_allocator_instance->Init(); + internal_allocator_instance->Init(/* may_return_null*/ false); atomic_store(&internal_allocator_initialized, 1, memory_order_release); } } @@ -140,14 +139,12 @@ bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) { return (max / size) < n; } -void *AllocatorReturnNull() { - if (common_flags()->allocator_may_return_null) - return 0; +void NORETURN ReportAllocatorCannotReturnNull() { Report("%s's allocator is terminating the process instead of returning 0\n", SanitizerToolName); Report("If you don't like this behavior set allocator_may_return_null=1\n"); CHECK(0); - return 0; + Die(); } } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_allocator.h b/lib/sanitizer_common/sanitizer_allocator.h index 23218016b791..b5105f8c2555 100644 --- a/lib/sanitizer_common/sanitizer_allocator.h +++ b/lib/sanitizer_common/sanitizer_allocator.h @@ -23,8 +23,8 @@ namespace __sanitizer { -// Depending on allocator_may_return_null either return 0 or crash. -void *AllocatorReturnNull(); +// Prints error message and kills the program. +void NORETURN ReportAllocatorCannotReturnNull(); // SizeClassMap maps allocation sizes into size classes and back. // Class 0 corresponds to size 0. @@ -211,6 +211,7 @@ class AllocatorStats { void Init() { internal_memset(this, 0, sizeof(*this)); } + void InitLinkerInitialized() {} void Add(AllocatorStat i, uptr v) { v += atomic_load(&stats_[i], memory_order_relaxed); @@ -240,11 +241,14 @@ class AllocatorStats { // Global stats, used for aggregation and querying. class AllocatorGlobalStats : public AllocatorStats { public: - void Init() { - internal_memset(this, 0, sizeof(*this)); + void InitLinkerInitialized() { next_ = this; prev_ = this; } + void Init() { + internal_memset(this, 0, sizeof(*this)); + InitLinkerInitialized(); + } void Register(AllocatorStats *s) { SpinMutexLock l(&mu_); @@ -1002,9 +1006,14 @@ struct SizeClassAllocatorLocalCache { template class LargeMmapAllocator { public: - void Init() { - internal_memset(this, 0, sizeof(*this)); + void InitLinkerInitialized(bool may_return_null) { page_size_ = GetPageSizeCached(); + atomic_store(&may_return_null_, may_return_null, memory_order_relaxed); + } + + void Init(bool may_return_null) { + internal_memset(this, 0, sizeof(*this)); + InitLinkerInitialized(may_return_null); } void *Allocate(AllocatorStats *stat, uptr size, uptr alignment) { @@ -1012,7 +1021,9 @@ class LargeMmapAllocator { uptr map_size = RoundUpMapSize(size); if (alignment > page_size_) map_size += alignment; - if (map_size < size) return AllocatorReturnNull(); // Overflow. + // Overflow. + if (map_size < size) + return ReturnNullOrDie(); uptr map_beg = reinterpret_cast( MmapOrDie(map_size, "LargeMmapAllocator")); CHECK(IsAligned(map_beg, page_size_)); @@ -1048,6 +1059,16 @@ class LargeMmapAllocator { return reinterpret_cast(res); } + void *ReturnNullOrDie() { + if (atomic_load(&may_return_null_, memory_order_acquire)) + return 0; + ReportAllocatorCannotReturnNull(); + } + + void SetMayReturnNull(bool may_return_null) { + atomic_store(&may_return_null_, may_return_null, memory_order_release); + } + void Deallocate(AllocatorStats *stat, void *p) { Header *h = GetHeader(p); { @@ -1226,6 +1247,7 @@ class LargeMmapAllocator { struct Stats { uptr n_allocs, n_frees, currently_allocated, max_allocated, by_size_log[64]; } stats; + atomic_uint8_t may_return_null_; SpinMutex mutex_; }; @@ -1239,19 +1261,32 @@ template // NOLINT class CombinedAllocator { public: - void Init() { + void InitCommon(bool may_return_null) { primary_.Init(); - secondary_.Init(); + atomic_store(&may_return_null_, may_return_null, memory_order_relaxed); + } + + void InitLinkerInitialized(bool may_return_null) { + secondary_.InitLinkerInitialized(may_return_null); + stats_.InitLinkerInitialized(); + InitCommon(may_return_null); + } + + void Init(bool may_return_null) { + secondary_.Init(may_return_null); stats_.Init(); + InitCommon(may_return_null); } void *Allocate(AllocatorCache *cache, uptr size, uptr alignment, - bool cleared = false) { + bool cleared = false, bool check_rss_limit = false) { // Returning 0 on malloc(0) may break a lot of code. if (size == 0) size = 1; if (size + alignment < size) - return AllocatorReturnNull(); + return ReturnNullOrDie(); + if (check_rss_limit && RssLimitIsExceeded()) + return ReturnNullOrDie(); if (alignment > 8) size = RoundUpTo(size, alignment); void *res; @@ -1267,6 +1302,30 @@ class CombinedAllocator { return res; } + bool MayReturnNull() const { + return atomic_load(&may_return_null_, memory_order_acquire); + } + + void *ReturnNullOrDie() { + if (MayReturnNull()) + return 0; + ReportAllocatorCannotReturnNull(); + } + + void SetMayReturnNull(bool may_return_null) { + secondary_.SetMayReturnNull(may_return_null); + atomic_store(&may_return_null_, may_return_null, memory_order_release); + } + + bool RssLimitIsExceeded() { + return atomic_load(&rss_limit_is_exceeded_, memory_order_acquire); + } + + void SetRssLimitIsExceeded(bool rss_limit_is_exceeded) { + atomic_store(&rss_limit_is_exceeded_, rss_limit_is_exceeded, + memory_order_release); + } + void Deallocate(AllocatorCache *cache, void *p) { if (!p) return; if (primary_.PointerIsMine(p)) @@ -1379,6 +1438,8 @@ class CombinedAllocator { PrimaryAllocator primary_; SecondaryAllocator secondary_; AllocatorGlobalStats stats_; + atomic_uint8_t may_return_null_; + atomic_uint8_t rss_limit_is_exceeded_; }; // Returns true if calloc(size, n) should return 0 due to overflow in size*n. diff --git a/lib/sanitizer_common/sanitizer_allocator_internal.h b/lib/sanitizer_common/sanitizer_allocator_internal.h index 4409fd65bf31..9b9cfd0b5931 100644 --- a/lib/sanitizer_common/sanitizer_allocator_internal.h +++ b/lib/sanitizer_common/sanitizer_allocator_internal.h @@ -49,6 +49,15 @@ void *InternalAlloc(uptr size, InternalAllocatorCache *cache = 0); void InternalFree(void *p, InternalAllocatorCache *cache = 0); InternalAllocator *internal_allocator(); +enum InternalAllocEnum { + INTERNAL_ALLOC +}; + } // namespace __sanitizer +inline void *operator new(__sanitizer::operator_new_size_type size, + InternalAllocEnum) { + return InternalAlloc(size); +} + #endif // SANITIZER_ALLOCATOR_INTERNAL_H diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc index c3740f24a366..489081e0760b 100644 --- a/lib/sanitizer_common/sanitizer_common.cc +++ b/lib/sanitizer_common/sanitizer_common.cc @@ -12,13 +12,17 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common.h" +#include "sanitizer_allocator_internal.h" #include "sanitizer_flags.h" #include "sanitizer_libc.h" +#include "sanitizer_placement_new.h" namespace __sanitizer { const char *SanitizerToolName = "SanitizerTool"; +atomic_uint32_t current_verbosity; + uptr GetPageSizeCached() { static uptr PageSize; if (!PageSize) @@ -94,19 +98,23 @@ uptr stoptheworld_tracer_pid = 0; // writing to the same log file. uptr stoptheworld_tracer_ppid = 0; -static DieCallbackType DieCallback; +static DieCallbackType InternalDieCallback, UserDieCallback; void SetDieCallback(DieCallbackType callback) { - DieCallback = callback; + InternalDieCallback = callback; +} +void SetUserDieCallback(DieCallbackType callback) { + UserDieCallback = callback; } DieCallbackType GetDieCallback() { - return DieCallback; + return InternalDieCallback; } void NORETURN Die() { - if (DieCallback) { - DieCallback(); - } + if (UserDieCallback) + UserDieCallback(); + if (InternalDieCallback) + InternalDieCallback(); internal__exit(1); } @@ -125,8 +133,8 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond, Die(); } -uptr ReadFileToBuffer(const char *file_name, char **buff, - uptr *buff_size, uptr max_len) { +uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, + uptr max_len, int *errno_p) { uptr PageSize = GetPageSizeCached(); uptr kMinFileLen = PageSize; uptr read_len = 0; @@ -135,7 +143,7 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, // The files we usually open are not seekable, so try different buffer sizes. for (uptr size = kMinFileLen; size <= max_len; size *= 2) { uptr openrv = OpenFile(file_name, /*write*/ false); - if (internal_iserror(openrv)) return 0; + if (internal_iserror(openrv, errno_p)) return 0; fd_t fd = openrv; UnmapOrDie(*buff, *buff_size); *buff = (char*)MmapOrDie(size, __func__); @@ -145,6 +153,10 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, bool reached_eof = false; while (read_len + PageSize <= size) { uptr just_read = internal_read(fd, *buff + read_len, PageSize); + if (internal_iserror(just_read, errno_p)) { + UnmapOrDie(*buff, *buff_size); + return 0; + } if (just_read == 0) { reached_eof = true; break; @@ -233,20 +245,28 @@ void ReportErrorSummary(const char *error_type, const char *file, LoadedModule::LoadedModule(const char *module_name, uptr base_address) { full_name_ = internal_strdup(module_name); base_address_ = base_address; - n_ranges_ = 0; + ranges_.clear(); +} + +void LoadedModule::clear() { + InternalFree(full_name_); + while (!ranges_.empty()) { + AddressRange *r = ranges_.front(); + ranges_.pop_front(); + InternalFree(r); + } } void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) { - CHECK_LT(n_ranges_, kMaxNumberOfAddressRanges); - ranges_[n_ranges_].beg = beg; - ranges_[n_ranges_].end = end; - exec_[n_ranges_] = executable; - n_ranges_++; + void *mem = InternalAlloc(sizeof(AddressRange)); + AddressRange *r = new(mem) AddressRange(beg, end, executable); + ranges_.push_back(r); } bool LoadedModule::containsAddress(uptr address) const { - for (uptr i = 0; i < n_ranges_; i++) { - if (ranges_[i].beg <= address && address < ranges_[i].end) + for (Iterator iter = ranges(); iter.hasNext();) { + const AddressRange *r = iter.next(); + if (r->beg <= address && address < r->end) return true; } return false; @@ -280,4 +300,9 @@ void __sanitizer_set_report_path(const char *path) { void __sanitizer_report_error_summary(const char *error_summary) { Printf("%s\n", error_summary); } + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_set_death_callback(void (*callback)(void)) { + SetUserDieCallback(callback); +} } // extern "C" diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h index c00dce66bb07..720cd73a43c5 100644 --- a/lib/sanitizer_common/sanitizer_common.h +++ b/lib/sanitizer_common/sanitizer_common.h @@ -16,10 +16,11 @@ #ifndef SANITIZER_COMMON_H #define SANITIZER_COMMON_H +#include "sanitizer_flags.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" +#include "sanitizer_list.h" #include "sanitizer_mutex.h" -#include "sanitizer_flags.h" namespace __sanitizer { struct StackTrace; @@ -40,6 +41,14 @@ const uptr kMaxThreadStackSize = 1 << 30; // 1Gb extern const char *SanitizerToolName; // Can be changed by the tool. +extern atomic_uint32_t current_verbosity; +INLINE void SetVerbosity(int verbosity) { + atomic_store(¤t_verbosity, verbosity, memory_order_relaxed); +} +INLINE int Verbosity() { + return atomic_load(¤t_verbosity, memory_order_relaxed); +} + uptr GetPageSize(); uptr GetPageSizeCached(); uptr GetMmapGranularity(); @@ -67,6 +76,8 @@ void FlushUnneededShadowMemory(uptr addr, uptr size); void IncreaseTotalMmap(uptr size); void DecreaseTotalMmap(uptr size); uptr GetRSS(); +void NoHugePagesInRegion(uptr addr, uptr length); +void DontDumpShadowMemory(uptr addr, uptr length); // InternalScopedBuffer can be used instead of large stack arrays to // keep frame size low. @@ -135,11 +146,11 @@ void Report(const char *format, ...); void SetPrintfAndReportCallback(void (*callback)(const char *)); #define VReport(level, ...) \ do { \ - if ((uptr)common_flags()->verbosity >= (level)) Report(__VA_ARGS__); \ + if ((uptr)Verbosity() >= (level)) Report(__VA_ARGS__); \ } while (0) #define VPrintf(level, ...) \ do { \ - if ((uptr)common_flags()->verbosity >= (level)) Printf(__VA_ARGS__); \ + if ((uptr)Verbosity() >= (level)) Printf(__VA_ARGS__); \ } while (0) // Can be used to prevent mixing error reports from different sanitizers. @@ -179,8 +190,8 @@ uptr OpenFile(const char *filename, bool write); // The resulting buffer is mmaped and stored in '*buff'. // The size of the mmaped region is stored in '*buff_size', // Returns the number of read bytes or 0 if file can not be opened. -uptr ReadFileToBuffer(const char *file_name, char **buff, - uptr *buff_size, uptr max_len); +uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, + uptr max_len, int *errno_p = nullptr); // Maps given file to virtual memory, and returns pointer to it // (or NULL if the mapping failes). Stores the size of mmaped region // in '*buff_size'. @@ -214,10 +225,13 @@ void PrepareForSandboxing(__sanitizer_sandbox_arguments *args); void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args); void SetSandboxingCallback(void (*f)()); -void CovUpdateMapping(uptr caller_pc = 0); +void CoverageUpdateMapping(); void CovBeforeFork(); void CovAfterFork(int child_pid); +void InitializeCoverage(bool enabled, const char *coverage_dir); +void ReInitializeCoverage(bool enabled, const char *coverage_dir); + void InitTlsSize(); uptr GetTlsSize(); @@ -245,11 +259,18 @@ bool SanitizerGetThreadName(char *name, int max_len); // to do tool-specific job. typedef void (*DieCallbackType)(void); void SetDieCallback(DieCallbackType); +void SetUserDieCallback(DieCallbackType); DieCallbackType GetDieCallback(); typedef void (*CheckFailedCallbackType)(const char *, int, const char *, u64, u64); void SetCheckFailedCallback(CheckFailedCallbackType callback); +// Callback will be called if soft_rss_limit_mb is given and the limit is +// exceeded (exceeded==true) or if rss went down below the limit +// (exceeded==false). +// The callback should be registered once at the tool init time. +void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)); + // Functions related to signal handling. typedef void (*SignalHandlerType)(int, void *, void *); bool IsDeadlySignal(int signum); @@ -376,14 +397,14 @@ INLINE int ToLower(int c) { // small vectors. // WARNING: The current implementation supports only POD types. template -class InternalMmapVector { +class InternalMmapVectorNoCtor { public: - explicit InternalMmapVector(uptr initial_capacity) { + void Initialize(uptr initial_capacity) { capacity_ = Max(initial_capacity, (uptr)1); size_ = 0; - data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVector"); + data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVectorNoCtor"); } - ~InternalMmapVector() { + void Destroy() { UnmapOrDie(data_, capacity_ * sizeof(T)); } T &operator[](uptr i) { @@ -434,15 +455,24 @@ class InternalMmapVector { UnmapOrDie(old_data, capacity_ * sizeof(T)); capacity_ = new_capacity; } - // Disallow evil constructors. - InternalMmapVector(const InternalMmapVector&); - void operator=(const InternalMmapVector&); T *data_; uptr capacity_; uptr size_; }; +template +class InternalMmapVector : public InternalMmapVectorNoCtor { + public: + explicit InternalMmapVector(uptr initial_capacity) { + InternalMmapVectorNoCtor::Initialize(initial_capacity); + } + ~InternalMmapVector() { InternalMmapVectorNoCtor::Destroy(); } + // Disallow evil constructors. + InternalMmapVector(const InternalMmapVector&); + void operator=(const InternalMmapVector&); +}; + // HeapSort for arrays and InternalMmapVector. template void InternalSort(Container *v, uptr size, Compare comp) { @@ -501,28 +531,30 @@ uptr InternalBinarySearch(const Container &v, uptr first, uptr last, class LoadedModule { public: LoadedModule(const char *module_name, uptr base_address); + void clear(); void addAddressRange(uptr beg, uptr end, bool executable); bool containsAddress(uptr address) const; const char *full_name() const { return full_name_; } uptr base_address() const { return base_address_; } - uptr n_ranges() const { return n_ranges_; } - uptr address_range_start(int i) const { return ranges_[i].beg; } - uptr address_range_end(int i) const { return ranges_[i].end; } - bool address_range_executable(int i) const { return exec_[i]; } - - private: struct AddressRange { + AddressRange *next; uptr beg; uptr end; + bool executable; + + AddressRange(uptr beg, uptr end, bool executable) + : next(nullptr), beg(beg), end(end), executable(executable) {} }; - char *full_name_; + + typedef IntrusiveList::ConstIterator Iterator; + Iterator ranges() const { return Iterator(&ranges_); } + + private: + char *full_name_; // Owned. uptr base_address_; - static const uptr kMaxNumberOfAddressRanges = 6; - AddressRange ranges_[kMaxNumberOfAddressRanges]; - bool exec_[kMaxNumberOfAddressRanges]; - uptr n_ranges_; + IntrusiveList ranges_; }; // OS-dependent function that fills array with descriptions of at most @@ -556,6 +588,10 @@ INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; } INLINE void SanitizerInitializeUnwinder() {} #endif +void *internal_start_thread(void(*func)(void*), void *arg); +void internal_join_thread(void *th); +void MaybeStartBackgroudThread(); + // Make the compiler think that something is going on there. // Use this inside a loop that looks like memset/memcpy/etc to prevent the // compiler from recognising it and turning it into an actual call to diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc index 274e87c3d67d..87c33e186320 100644 --- a/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -17,6 +17,7 @@ // COMMON_INTERCEPTOR_READ_RANGE // COMMON_INTERCEPTOR_WRITE_RANGE // COMMON_INTERCEPTOR_INITIALIZE_RANGE +// COMMON_INTERCEPTOR_DIR_ACQUIRE // COMMON_INTERCEPTOR_FD_ACQUIRE // COMMON_INTERCEPTOR_FD_RELEASE // COMMON_INTERCEPTOR_FD_ACCESS @@ -43,6 +44,8 @@ #if SANITIZER_FREEBSD #define pthread_setname_np pthread_set_name_np +#define inet_aton __inet_aton +#define inet_pton __inet_pton #endif #ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE @@ -82,7 +85,7 @@ #endif #ifndef COMMON_INTERCEPTOR_LIBRARY_LOADED -#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, map) {} +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) {} #endif #ifndef COMMON_INTERCEPTOR_LIBRARY_UNLOADED @@ -915,6 +918,16 @@ INTERCEPTOR(int, vsnprintf, char *str, SIZE_T size, const char *format, va_list ap) VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf, str, size, format, ap) +#if SANITIZER_INTERCEPT_PRINTF_L +INTERCEPTOR(int, vsnprintf_l, char *str, SIZE_T size, void *loc, + const char *format, va_list ap) +VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf_l, str, size, loc, format, ap) + +INTERCEPTOR(int, snprintf_l, char *str, SIZE_T size, void *loc, + const char *format, ...) +FORMAT_INTERCEPTOR_IMPL(snprintf_l, vsnprintf_l, str, size, loc, format) +#endif // SANITIZER_INTERCEPT_PRINTF_L + INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) VSPRINTF_INTERCEPTOR_IMPL(vsprintf, str, format, ap) @@ -991,6 +1004,14 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size, #define INIT_PRINTF #endif +#if SANITIZER_INTERCEPT_PRINTF_L +#define INIT_PRINTF_L \ + COMMON_INTERCEPT_FUNCTION(snprintf_l); \ + COMMON_INTERCEPT_FUNCTION(vsnprintf_l); +#else +#define INIT_PRINTF_L +#endif + #if SANITIZER_INTERCEPT_ISOC99_PRINTF #define INIT_ISOC99_PRINTF \ COMMON_INTERCEPT_FUNCTION(__isoc99_printf); \ @@ -1007,8 +1028,12 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size, #if SANITIZER_INTERCEPT_IOCTL #include "sanitizer_common_interceptors_ioctl.inc" -INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) { +INTERCEPTOR(int, ioctl, int d, unsigned long request, ...) { void *ctx; + va_list ap; + va_start(ap, request); + void *arg = va_arg(ap, void *); + va_end(ap); COMMON_INTERCEPTOR_ENTER(ctx, ioctl, d, request, arg); CHECK(ioctl_initialized); @@ -1017,6 +1042,10 @@ INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) { // This effectively disables ioctl handling in TSan. if (!common_flags()->handle_ioctl) return REAL(ioctl)(d, request, arg); + // Although request is unsigned long, the rest of the interceptor uses it + // as just "unsigned" to save space, because we know that all values fit in + // "unsigned" - they are compile-time constants. + const ioctl_desc *desc = ioctl_lookup(request); ioctl_desc decoded_desc; if (!desc) { @@ -2139,6 +2168,16 @@ INTERCEPTOR(int, sysinfo, void *info) { #endif #if SANITIZER_INTERCEPT_READDIR +INTERCEPTOR(__sanitizer_dirent *, opendir, const char *path) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, opendir, path); + COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + __sanitizer_dirent *res = REAL(opendir)(path); + if (res != 0) + COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path); + return res; +} + INTERCEPTOR(__sanitizer_dirent *, readdir, void *dirp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir, dirp); @@ -2167,6 +2206,7 @@ INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry, } #define INIT_READDIR \ + COMMON_INTERCEPT_FUNCTION(opendir); \ COMMON_INTERCEPT_FUNCTION(readdir); \ COMMON_INTERCEPT_FUNCTION(readdir_r); #else @@ -2560,6 +2600,19 @@ INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) { #define INIT_SCHED_GETAFFINITY #endif +#if SANITIZER_INTERCEPT_SCHED_GETPARAM +INTERCEPTOR(int, sched_getparam, int pid, void *param) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sched_getparam, pid, param); + int res = REAL(sched_getparam)(pid, param); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, param, struct_sched_param_sz); + return res; +} +#define INIT_SCHED_GETPARAM COMMON_INTERCEPT_FUNCTION(sched_getparam); +#else +#define INIT_SCHED_GETPARAM +#endif + #if SANITIZER_INTERCEPT_STRERROR INTERCEPTOR(char *, strerror, int errnum) { void *ctx; @@ -3868,6 +3921,12 @@ INTERCEPTOR(__sanitizer_clock_t, times, void *tms) { #if SANITIZER_INTERCEPT_TLS_GET_ADDR #define INIT_TLS_GET_ADDR COMMON_INTERCEPT_FUNCTION(__tls_get_addr) +// If you see any crashes around this functions, there are 2 known issues with +// it: 1. __tls_get_addr can be called with mis-aligned stack due to: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066 +// 2. It can be called recursively if sanitizer code uses __tls_get_addr +// to access thread local variables (it should not happen normally, +// because sanitizers use initial-exec tls model). INTERCEPTOR(void *, __tls_get_addr, void *arg) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr, arg); @@ -4762,6 +4821,7 @@ static void InitializeCommonInterceptors() { INIT_SCANF; INIT_ISOC99_SCANF; INIT_PRINTF; + INIT_PRINTF_L; INIT_ISOC99_PRINTF; INIT_FREXP; INIT_FREXPF_FREXPL; @@ -4812,6 +4872,7 @@ static void InitializeCommonInterceptors() { INIT_CANONICALIZE_FILE_NAME; INIT_CONFSTR; INIT_SCHED_GETAFFINITY; + INIT_SCHED_GETPARAM; INIT_STRERROR; INIT_STRERROR_R; INIT_XPG_STRERROR_R; diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc index 20c1d5a78987..e357e1cbbfc9 100644 --- a/lib/sanitizer_common/sanitizer_common_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc @@ -13,6 +13,7 @@ #include "sanitizer_common.h" #include "sanitizer_flags.h" +#include "sanitizer_stackdepot.h" #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" @@ -59,6 +60,71 @@ void ReportErrorSummary(const char *error_type, StackTrace *stack) { #endif } +static void (*SoftRssLimitExceededCallback)(bool exceeded); +void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) { + CHECK_EQ(SoftRssLimitExceededCallback, nullptr); + SoftRssLimitExceededCallback = Callback; +} + +void BackgroundThread(void *arg) { + uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb; + uptr soft_rss_limit_mb = common_flags()->soft_rss_limit_mb; + uptr prev_reported_rss = 0; + uptr prev_reported_stack_depot_size = 0; + bool reached_soft_rss_limit = false; + while (true) { + SleepForMillis(100); + uptr current_rss_mb = GetRSS() >> 20; + if (Verbosity()) { + // If RSS has grown 10% since last time, print some information. + if (prev_reported_rss * 11 / 10 < current_rss_mb) { + Printf("%s: RSS: %zdMb\n", SanitizerToolName, current_rss_mb); + prev_reported_rss = current_rss_mb; + } + // If stack depot has grown 10% since last time, print it too. + StackDepotStats *stack_depot_stats = StackDepotGetStats(); + if (prev_reported_stack_depot_size * 11 / 10 < + stack_depot_stats->allocated) { + Printf("%s: StackDepot: %zd ids; %zdM allocated\n", + SanitizerToolName, + stack_depot_stats->n_uniq_ids, + stack_depot_stats->allocated >> 20); + prev_reported_stack_depot_size = stack_depot_stats->allocated; + } + } + // Check RSS against the limit. + if (hard_rss_limit_mb && hard_rss_limit_mb < current_rss_mb) { + Report("%s: hard rss limit exhausted (%zdMb vs %zdMb)\n", + SanitizerToolName, hard_rss_limit_mb, current_rss_mb); + DumpProcessMap(); + Die(); + } + if (soft_rss_limit_mb) { + if (soft_rss_limit_mb < current_rss_mb && !reached_soft_rss_limit) { + reached_soft_rss_limit = true; + Report("%s: soft rss limit exhausted (%zdMb vs %zdMb)\n", + SanitizerToolName, soft_rss_limit_mb, current_rss_mb); + if (SoftRssLimitExceededCallback) + SoftRssLimitExceededCallback(true); + } else if (soft_rss_limit_mb >= current_rss_mb && + reached_soft_rss_limit) { + reached_soft_rss_limit = false; + if (SoftRssLimitExceededCallback) + SoftRssLimitExceededCallback(false); + } + } + } +} + +void MaybeStartBackgroudThread() { + if (!SANITIZER_LINUX) return; // Need to implement/test on other platforms. + // Start the background thread if one of the rss limits is given. + if (!common_flags()->hard_rss_limit_mb && + !common_flags()->soft_rss_limit_mb) return; + if (!real_pthread_create) return; // Can't spawn the thread anyway. + internal_start_thread(BackgroundThread, nullptr); +} + } // namespace __sanitizer void NOINLINE diff --git a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc index 3fa8a18696f8..e8f42f68a42f 100644 --- a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc @@ -12,14 +12,16 @@ // // Compiler instrumentation: // For every interesting basic block the compiler injects the following code: -// if (Guard) { +// if (Guard < 0) { // __sanitizer_cov(&Guard); // } +// At the module start up time __sanitizer_cov_module_init sets the guards +// to consecutive negative numbers (-1, -2, -3, ...). // It's fine to call __sanitizer_cov more than once for a given block. // // Run-time: // - __sanitizer_cov(): record that we've executed the PC (GET_CALLER_PC). -// and atomically set Guard to 1. +// and atomically set Guard to -Guard. // - __sanitizer_cov_dump: dump the coverage data to disk. // For every module of the current process that has coverage data // this will create a file module_name.PID.sancov. The file format is simple: @@ -56,23 +58,32 @@ static atomic_uintptr_t coverage_counter; static bool cov_sandboxed = false; static int cov_fd = kInvalidFd; static unsigned int cov_max_block_size = 0; +static bool coverage_enabled = false; +static const char *coverage_dir; namespace __sanitizer { class CoverageData { public: void Init(); + void Enable(); + void Disable(); + void ReInit(); void BeforeFork(); void AfterFork(int child_pid); void Extend(uptr npcs); - void Add(uptr pc, u8 *guard); + void Add(uptr pc, u32 *guard); void IndirCall(uptr caller, uptr callee, uptr callee_cache[], uptr cache_size); void DumpCallerCalleePairs(); void DumpTrace(); ALWAYS_INLINE - void TraceBasicaBlock(uptr *cache); + void TraceBasicBlock(s32 *id); + + void InitializeGuardArray(s32 *guards); + void InitializeGuards(s32 *guards, uptr n); + void ReinitializeGuards(); uptr *data(); uptr size(); @@ -80,7 +91,7 @@ class CoverageData { private: // Maximal size pc array may ever grow. // We MmapNoReserve this space to ensure that the array is contiguous. - static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27); + static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << 24, 1 << 27); // The amount file mapping for the pc array is grown by. static const uptr kPcArrayMmapSize = 64 * 1024; @@ -96,45 +107,41 @@ class CoverageData { // Descriptor of the file mapped pc array. int pc_fd; + // Vector of coverage guard arrays, protected by mu. + InternalMmapVectorNoCtor guard_array_vec; + // Caller-Callee (cc) array, size and current index. static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24); uptr **cc_array; atomic_uintptr_t cc_array_index; atomic_uintptr_t cc_array_size; - // Tracing (tr) pc and event arrays, their size and current index. + // Tracing event array, size and current pointer. // We record all events (basic block entries) in a global buffer of u32 - // values. Each such value is an index in the table of TracedPc objects. + // values. Each such value is the index in pc_array. // So far the tracing is highly experimental: // - not thread-safe; // - does not support long traces; // - not tuned for performance. - struct TracedPc { - uptr pc; - const char *module_name; - uptr module_offset; - }; static const uptr kTrEventArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 30); u32 *tr_event_array; uptr tr_event_array_size; - uptr tr_event_array_index; + u32 *tr_event_pointer; static const uptr kTrPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27); - TracedPc *tr_pc_array; - uptr tr_pc_array_size; - uptr tr_pc_array_index; StaticSpinMutex mu; void DirectOpen(); - void ReInit(); }; static CoverageData coverage_data; +void CovUpdateMapping(const char *path, uptr caller_pc = 0); + void CoverageData::DirectOpen() { InternalScopedString path(kMaxPathLength); internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.raw", - common_flags()->coverage_dir, internal_getpid()); + coverage_dir, internal_getpid()); pc_fd = OpenFile(path.data(), true); if (internal_iserror(pc_fd)) { Report(" Coverage: failed to open %s for writing\n", path.data()); @@ -142,19 +149,23 @@ void CoverageData::DirectOpen() { } pc_array_mapped_size = 0; - CovUpdateMapping(); + CovUpdateMapping(coverage_dir); } void CoverageData::Init() { + pc_fd = kInvalidFd; +} + +void CoverageData::Enable() { + if (pc_array) + return; pc_array = reinterpret_cast( MmapNoReserveOrDie(sizeof(uptr) * kPcArrayMaxSize, "CovInit")); - pc_fd = kInvalidFd; + atomic_store(&pc_array_index, 0, memory_order_relaxed); if (common_flags()->coverage_direct) { atomic_store(&pc_array_size, 0, memory_order_relaxed); - atomic_store(&pc_array_index, 0, memory_order_relaxed); } else { atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed); - atomic_store(&pc_array_index, 0, memory_order_relaxed); } cc_array = reinterpret_cast(MmapNoReserveOrDie( @@ -162,30 +173,72 @@ void CoverageData::Init() { atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed); atomic_store(&cc_array_index, 0, memory_order_relaxed); - tr_event_array = reinterpret_cast( - MmapNoReserveOrDie(sizeof(tr_event_array[0]) * kTrEventArrayMaxSize, - "CovInit::tr_event_array")); + // Allocate tr_event_array with a guard page at the end. + tr_event_array = reinterpret_cast(MmapNoReserveOrDie( + sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + GetMmapGranularity(), + "CovInit::tr_event_array")); + Mprotect(reinterpret_cast(&tr_event_array[kTrEventArrayMaxSize]), + GetMmapGranularity()); tr_event_array_size = kTrEventArrayMaxSize; - tr_event_array_index = 0; + tr_event_pointer = tr_event_array; +} - tr_pc_array = reinterpret_cast(MmapNoReserveOrDie( - sizeof(tr_pc_array[0]) * kTrEventArrayMaxSize, "CovInit::tr_pc_array")); - tr_pc_array_size = kTrEventArrayMaxSize; - tr_pc_array_index = 0; +void CoverageData::InitializeGuardArray(s32 *guards) { + Enable(); // Make sure coverage is enabled at this point. + s32 n = guards[0]; + for (s32 j = 1; j <= n; j++) { + uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed); + guards[j] = -static_cast(idx + 1); + } +} + +void CoverageData::Disable() { + if (pc_array) { + internal_munmap(pc_array, sizeof(uptr) * kPcArrayMaxSize); + pc_array = nullptr; + } + if (cc_array) { + internal_munmap(cc_array, sizeof(uptr *) * kCcArrayMaxSize); + cc_array = nullptr; + } + if (tr_event_array) { + internal_munmap(tr_event_array, + sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + + GetMmapGranularity()); + tr_event_array = nullptr; + tr_event_pointer = nullptr; + } + if (pc_fd != kInvalidFd) { + internal_close(pc_fd); + pc_fd = kInvalidFd; + } +} + +void CoverageData::ReinitializeGuards() { + // Assuming single thread. + atomic_store(&pc_array_index, 0, memory_order_relaxed); + for (uptr i = 0; i < guard_array_vec.size(); i++) + InitializeGuardArray(guard_array_vec[i]); } void CoverageData::ReInit() { - internal_munmap(pc_array, sizeof(uptr) * kPcArrayMaxSize); - if (pc_fd != kInvalidFd) internal_close(pc_fd); - if (common_flags()->coverage_direct) { - // In memory-mapped mode we must extend the new file to the known array - // size. - uptr size = atomic_load(&pc_array_size, memory_order_relaxed); - Init(); - if (size) Extend(size); - } else { - Init(); + Disable(); + if (coverage_enabled) { + if (common_flags()->coverage_direct) { + // In memory-mapped mode we must extend the new file to the known array + // size. + uptr size = atomic_load(&pc_array_size, memory_order_relaxed); + Enable(); + if (size) Extend(size); + if (coverage_enabled) CovUpdateMapping(coverage_dir); + } else { + Enable(); + } } + // Re-initialize the guards. + // We are single-threaded now, no need to grab any lock. + CHECK_EQ(atomic_load(&pc_array_index, memory_order_relaxed), 0); + ReinitializeGuards(); } void CoverageData::BeforeFork() { @@ -203,15 +256,16 @@ void CoverageData::Extend(uptr npcs) { if (!common_flags()->coverage_direct) return; SpinMutexLock l(&mu); - if (pc_fd == kInvalidFd) DirectOpen(); - CHECK_NE(pc_fd, kInvalidFd); - uptr size = atomic_load(&pc_array_size, memory_order_relaxed); size += npcs * sizeof(uptr); - if (size > pc_array_mapped_size) { + if (coverage_enabled && size > pc_array_mapped_size) { + if (pc_fd == kInvalidFd) DirectOpen(); + CHECK_NE(pc_fd, kInvalidFd); + uptr new_mapped_size = pc_array_mapped_size; while (size > new_mapped_size) new_mapped_size += kPcArrayMmapSize; + CHECK_LE(new_mapped_size, sizeof(uptr) * kPcArrayMaxSize); // Extend the file and map the new space at the end of pc_array. uptr res = internal_ftruncate(pc_fd, new_mapped_size); @@ -220,29 +274,45 @@ void CoverageData::Extend(uptr npcs) { Printf("failed to extend raw coverage file: %d\n", err); Die(); } - void *p = MapWritableFileToMemory(pc_array + pc_array_mapped_size, + + uptr next_map_base = ((uptr)pc_array) + pc_array_mapped_size; + void *p = MapWritableFileToMemory((void *)next_map_base, new_mapped_size - pc_array_mapped_size, pc_fd, pc_array_mapped_size); - CHECK_EQ(p, pc_array + pc_array_mapped_size); + CHECK_EQ((uptr)p, next_map_base); pc_array_mapped_size = new_mapped_size; } atomic_store(&pc_array_size, size, memory_order_release); } -// Atomically add the pc to the vector. The atomically set the guard to 1. -// If the function is called more than once for a given PC it will -// be inserted multiple times, which is fine. -void CoverageData::Add(uptr pc, u8 *guard) { +void CoverageData::InitializeGuards(s32 *guards, uptr n) { + // The array 'guards' has n+1 elements, we use the element zero + // to store 'n'. + CHECK_LT(n, 1 << 30); + guards[0] = static_cast(n); + InitializeGuardArray(guards); + SpinMutexLock l(&mu); + guard_array_vec.push_back(guards); +} + +// If guard is negative, atomically set it to -guard and store the PC in +// pc_array. +void CoverageData::Add(uptr pc, u32 *guard) { + atomic_uint32_t *atomic_guard = reinterpret_cast(guard); + s32 guard_value = atomic_load(atomic_guard, memory_order_relaxed); + if (guard_value >= 0) return; + + atomic_store(atomic_guard, -guard_value, memory_order_relaxed); if (!pc_array) return; - uptr idx = atomic_fetch_add(&pc_array_index, 1, memory_order_relaxed); + + uptr idx = -guard_value - 1; + if (idx >= atomic_load(&pc_array_index, memory_order_acquire)) + return; // May happen after fork when pc_array_index becomes 0. CHECK_LT(idx * sizeof(uptr), atomic_load(&pc_array_size, memory_order_acquire)); pc_array[idx] = pc; atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed); - // Set the guard. - atomic_uint8_t *atomic_guard = reinterpret_cast(guard); - atomic_store(atomic_guard, 1, memory_order_relaxed); } // Registers a pair caller=>callee. @@ -338,18 +408,19 @@ static void CovWritePacked(int pid, const char *module, const void *blob, // If packed = true and name == 0: ... // If packed = true and name != 0: .. (name is // user-supplied). -static int CovOpenFile(bool packed, const char* name) { +static int CovOpenFile(bool packed, const char *name, + const char *extension = "sancov") { InternalScopedString path(kMaxPathLength); if (!packed) { CHECK(name); - path.append("%s/%s.%zd.sancov", common_flags()->coverage_dir, name, - internal_getpid()); + path.append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(), + extension); } else { if (!name) - path.append("%s/%zd.sancov.packed", common_flags()->coverage_dir, - internal_getpid()); + path.append("%s/%zd.%s.packed", coverage_dir, internal_getpid(), + extension); else - path.append("%s/%s.sancov.packed", common_flags()->coverage_dir, name); + path.append("%s/%s.%s.packed", coverage_dir, name, extension); } uptr fd = OpenFile(path.data(), true); if (internal_iserror(fd)) { @@ -361,23 +432,18 @@ static int CovOpenFile(bool packed, const char* name) { // Dump trace PCs and trace events into two separate files. void CoverageData::DumpTrace() { - uptr max_idx = tr_event_array_index; + uptr max_idx = tr_event_pointer - tr_event_array; if (!max_idx) return; auto sym = Symbolizer::GetOrInit(); if (!sym) return; InternalScopedString out(32 << 20); - for (uptr i = 0; i < max_idx; i++) { - u32 pc_idx = tr_event_array[i]; - TracedPc *t = &tr_pc_array[pc_idx]; - if (!t->module_name) { - const char *module_name = ""; - uptr module_address = 0; - sym->GetModuleNameAndOffsetForPC(t->pc, &module_name, &module_address); - t->module_name = internal_strdup(module_name); - t->module_offset = module_address; - out.append("%s 0x%zx\n", t->module_name, t->module_offset); - } + for (uptr i = 0, n = size(); i < n; i++) { + const char *module_name = ""; + uptr module_address = 0; + sym->GetModuleNameAndOffsetForPC(pc_array[i], &module_name, + &module_address); + out.append("%s 0x%zx\n", module_name, module_address); } int fd = CovOpenFile(false, "trace-points"); if (fd < 0) return; @@ -386,10 +452,21 @@ void CoverageData::DumpTrace() { fd = CovOpenFile(false, "trace-events"); if (fd < 0) return; - internal_write(fd, tr_event_array, max_idx * sizeof(tr_event_array[0])); + uptr bytes_to_write = max_idx * sizeof(tr_event_array[0]); + u8 *event_bytes = reinterpret_cast(tr_event_array); + // The trace file could be huge, and may not be written with a single syscall. + while (bytes_to_write) { + uptr actually_written = internal_write(fd, event_bytes, bytes_to_write); + if (actually_written <= bytes_to_write) { + bytes_to_write -= actually_written; + event_bytes += actually_written; + } else { + break; + } + } internal_close(fd); - VReport(1, " CovDump: Trace: %zd PCs written\n", tr_pc_array_index); - VReport(1, " CovDump: Trace: %zd Events written\n", tr_event_array_index); + VReport(1, " CovDump: Trace: %zd PCs written\n", size()); + VReport(1, " CovDump: Trace: %zd Events written\n", max_idx); } // This function dumps the caller=>callee pairs into a file as a sequence of @@ -434,28 +511,45 @@ void CoverageData::DumpCallerCalleePairs() { // Record the current PC into the event buffer. // Every event is a u32 value (index in tr_pc_array_index) so we compute // it once and then cache in the provided 'cache' storage. -void CoverageData::TraceBasicaBlock(uptr *cache) { - CHECK(common_flags()->coverage); - uptr idx = *cache; - if (!idx) { - CHECK_LT(tr_pc_array_index, kTrPcArrayMaxSize); - idx = tr_pc_array_index++; - TracedPc *t = &tr_pc_array[idx]; - t->pc = GET_CALLER_PC(); - *cache = idx; - CHECK_LT(idx, 1U << 31); +// +// This function will eventually be inlined by the compiler. +void CoverageData::TraceBasicBlock(s32 *id) { + // Will trap here if + // 1. coverage is not enabled at run-time. + // 2. The array tr_event_array is full. + *tr_event_pointer = static_cast(*id - 1); + tr_event_pointer++; +} + +static void CovDumpAsBitSet() { + if (!common_flags()->coverage_bitset) return; + if (!coverage_data.size()) return; + int fd = CovOpenFile(/* packed */false, "combined", "bitset-sancov"); + if (fd < 0) return; + uptr n = coverage_data.size(); + uptr n_set_bits = 0; + InternalScopedBuffer out(n); + for (uptr i = 0; i < n; i++) { + uptr pc = coverage_data.data()[i]; + out[i] = pc ? '1' : '0'; + if (pc) + n_set_bits++; } - CHECK_LT(tr_event_array_index, tr_event_array_size); - tr_event_array[tr_event_array_index] = static_cast(idx); - tr_event_array_index++; + internal_write(fd, out.data(), n); + internal_close(fd); + VReport(1, " CovDump: bitset of %zd bits written, %zd bits are set\n", n, + n_set_bits); } // Dump the coverage on disk. static void CovDump() { - if (!common_flags()->coverage || common_flags()->coverage_direct) return; + if (!coverage_enabled || common_flags()->coverage_direct) return; #if !SANITIZER_WINDOWS if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed)) return; + CovDumpAsBitSet(); + coverage_data.DumpTrace(); + if (!common_flags()->coverage_pcs) return; uptr size = coverage_data.size(); InternalMmapVector offsets(size); uptr *vb = coverage_data.data(); @@ -491,8 +585,8 @@ static void CovDump() { } else { // One file per module per process. path.clear(); - path.append("%s/%s.%zd.sancov", common_flags()->coverage_dir, - module_name, internal_getpid()); + path.append("%s/%s.%zd.sancov", coverage_dir, module_name, + internal_getpid()); int fd = CovOpenFile(false /* packed */, module_name); if (fd > 0) { internal_write(fd, offsets.data(), offsets.size() * sizeof(u32)); @@ -506,13 +600,12 @@ static void CovDump() { if (cov_fd >= 0) internal_close(cov_fd); coverage_data.DumpCallerCalleePairs(); - coverage_data.DumpTrace(); #endif // !SANITIZER_WINDOWS } void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) { if (!args) return; - if (!common_flags()->coverage) return; + if (!coverage_enabled) return; cov_sandboxed = args->coverage_sandboxed; if (!cov_sandboxed) return; cov_fd = args->coverage_fd; @@ -524,7 +617,7 @@ void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) { int MaybeOpenCovFile(const char *name) { CHECK(name); - if (!common_flags()->coverage) return -1; + if (!coverage_enabled) return -1; return CovOpenFile(true /* packed */, name); } @@ -536,28 +629,60 @@ void CovAfterFork(int child_pid) { coverage_data.AfterFork(child_pid); } +void InitializeCoverage(bool enabled, const char *dir) { + if (coverage_enabled) + return; // May happen if two sanitizer enable coverage in the same process. + coverage_enabled = enabled; + coverage_dir = dir; + coverage_data.Init(); + if (enabled) coverage_data.Enable(); +#if !SANITIZER_WINDOWS + if (!common_flags()->coverage_direct) Atexit(__sanitizer_cov_dump); +#endif +} + +void ReInitializeCoverage(bool enabled, const char *dir) { + coverage_enabled = enabled; + coverage_dir = dir; + coverage_data.ReInit(); +} + +void CoverageUpdateMapping() { + if (coverage_enabled) + CovUpdateMapping(coverage_dir); +} + } // namespace __sanitizer extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u8 *guard) { +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u32 *guard) { coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()), guard); } +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_with_check(u32 *guard) { + atomic_uint32_t *atomic_guard = reinterpret_cast(guard); + if (__sanitizer::atomic_load(atomic_guard, memory_order_relaxed)) + __sanitizer_cov(guard); +} SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_indir_call16(uptr callee, uptr callee_cache16[]) { coverage_data.IndirCall(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()), callee, callee_cache16, 16); } -SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() { + coverage_enabled = true; + coverage_dir = common_flags()->coverage_dir; coverage_data.Init(); } -SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(uptr npcs) { - if (!common_flags()->coverage || !common_flags()->coverage_direct) return; - if (SANITIZER_ANDROID) { +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); } +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(s32 *guards, + uptr npcs) { + coverage_data.InitializeGuards(guards, npcs); + if (!common_flags()->coverage_direct) return; + if (SANITIZER_ANDROID && coverage_enabled) { // dlopen/dlclose interceptors do not work on Android, so we rely on // Extend() calls to update .sancov.map. - CovUpdateMapping(GET_CALLER_PC()); + CovUpdateMapping(coverage_dir, GET_CALLER_PC()); } coverage_data.Extend(npcs); } @@ -571,11 +696,23 @@ uptr __sanitizer_get_total_unique_coverage() { } SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_cov_trace_func_enter(uptr *cache) { - coverage_data.TraceBasicaBlock(cache); +void __sanitizer_cov_trace_func_enter(s32 *id) { + coverage_data.TraceBasicBlock(id); } SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_cov_trace_basic_block(uptr *cache) { - coverage_data.TraceBasicaBlock(cache); +void __sanitizer_cov_trace_basic_block(s32 *id) { + coverage_data.TraceBasicBlock(id); +} +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_reset_coverage() { + coverage_data.ReinitializeGuards(); + internal_bzero_aligned16( + coverage_data.data(), + RoundUpTo(coverage_data.size() * sizeof(coverage_data.data()[0]), 16)); +} +SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_get_coverage_guards(uptr **data) { + *data = coverage_data.data(); + return coverage_data.size(); } } // extern "C" diff --git a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc index fe72d06a2fb1..6b5e91fbc018 100644 --- a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc @@ -62,8 +62,8 @@ struct CachedMapping { static CachedMapping cached_mapping; static StaticSpinMutex mapping_mu; -void CovUpdateMapping(uptr caller_pc) { - if (!common_flags()->coverage || !common_flags()->coverage_direct) return; +void CovUpdateMapping(const char *coverage_dir, uptr caller_pc) { + if (!common_flags()->coverage_direct) return; SpinMutexLock l(&mapping_mu); @@ -71,36 +71,41 @@ void CovUpdateMapping(uptr caller_pc) { return; InternalScopedString text(kMaxTextSize); - InternalScopedBuffer modules(kMaxNumberOfModules); - CHECK(modules.data()); - int n_modules = GetListOfModules(modules.data(), kMaxNumberOfModules, - /* filter */ 0); - text.append("%d\n", sizeof(uptr) * 8); - for (int i = 0; i < n_modules; ++i) { - const char *module_name = StripModuleName(modules[i].full_name()); - for (unsigned j = 0; j < modules[i].n_ranges(); ++j) { - if (modules[i].address_range_executable(j)) { - uptr start = modules[i].address_range_start(j); - uptr end = modules[i].address_range_end(j); - uptr base = modules[i].base_address(); - text.append("%zx %zx %zx %s\n", start, end, base, module_name); - if (caller_pc && caller_pc >= start && caller_pc < end) - cached_mapping.SetModuleRange(start, end); + { + InternalScopedBuffer modules(kMaxNumberOfModules); + CHECK(modules.data()); + int n_modules = GetListOfModules(modules.data(), kMaxNumberOfModules, + /* filter */ 0); + + text.append("%d\n", sizeof(uptr) * 8); + for (int i = 0; i < n_modules; ++i) { + const char *module_name = StripModuleName(modules[i].full_name()); + uptr base = modules[i].base_address(); + for (auto iter = modules[i].ranges(); iter.hasNext();) { + const auto *range = iter.next(); + if (range->executable) { + uptr start = range->beg; + uptr end = range->end; + text.append("%zx %zx %zx %s\n", start, end, base, module_name); + if (caller_pc && caller_pc >= start && caller_pc < end) + cached_mapping.SetModuleRange(start, end); + } } + modules[i].clear(); } } int err; - InternalScopedString tmp_path(64 + - internal_strlen(common_flags()->coverage_dir)); + InternalScopedString tmp_path(64 + internal_strlen(coverage_dir)); uptr res = internal_snprintf((char *)tmp_path.data(), tmp_path.size(), - "%s/%zd.sancov.map.tmp", common_flags()->coverage_dir, - internal_getpid()); + "%s/%zd.sancov.map.tmp", coverage_dir, + internal_getpid()); CHECK_LE(res, tmp_path.size()); uptr map_fd = OpenFile(tmp_path.data(), true); - if (internal_iserror(map_fd)) { - Report(" Coverage: failed to open %s for writing\n", tmp_path.data()); + if (internal_iserror(map_fd, &err)) { + Report(" Coverage: failed to open %s for writing: %d\n", tmp_path.data(), + err); Die(); } @@ -111,9 +116,9 @@ void CovUpdateMapping(uptr caller_pc) { } internal_close(map_fd); - InternalScopedString path(64 + internal_strlen(common_flags()->coverage_dir)); + InternalScopedString path(64 + internal_strlen(coverage_dir)); res = internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.map", - common_flags()->coverage_dir, internal_getpid()); + coverage_dir, internal_getpid()); CHECK_LE(res, path.size()); res = internal_rename(tmp_path.data(), path.data()); if (internal_iserror(res, &err)) { diff --git a/lib/sanitizer_common/sanitizer_deadlock_detector.h b/lib/sanitizer_common/sanitizer_deadlock_detector.h index 90e1cc4eb597..86d5743e9794 100644 --- a/lib/sanitizer_common/sanitizer_deadlock_detector.h +++ b/lib/sanitizer_common/sanitizer_deadlock_detector.h @@ -50,6 +50,8 @@ class DeadlockDetectorTLS { if (epoch_ == current_epoch) return; bv_.clear(); epoch_ = current_epoch; + n_recursive_locks = 0; + n_all_locks_ = 0; } uptr getEpoch() const { return epoch_; } @@ -83,7 +85,8 @@ class DeadlockDetectorTLS { } } // Printf("remLock: %zx %zx\n", lock_id, epoch_); - CHECK(bv_.clearBit(lock_id)); + if (!bv_.clearBit(lock_id)) + return; // probably addLock happened before flush if (n_all_locks_) { for (sptr i = n_all_locks_ - 1; i >= 0; i--) { if (all_locks_with_contexts_[i].lock == static_cast(lock_id)) { @@ -175,6 +178,7 @@ class DeadlockDetector { recycled_nodes_.clear(); available_nodes_.setAll(); g_.clear(); + n_edges_ = 0; return getAvailableNode(data); } diff --git a/lib/sanitizer_common/sanitizer_flag_parser.cc b/lib/sanitizer_common/sanitizer_flag_parser.cc new file mode 100644 index 000000000000..d125002daf4c --- /dev/null +++ b/lib/sanitizer_common/sanitizer_flag_parser.cc @@ -0,0 +1,153 @@ +//===-- sanitizer_flag_parser.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_flag_parser.h" + +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_flags.h" +#include "sanitizer_flag_parser.h" + +namespace __sanitizer { + +LowLevelAllocator FlagParser::Alloc; + +class UnknownFlags { + static const int kMaxUnknownFlags = 20; + const char *unknown_flags_[kMaxUnknownFlags]; + int n_unknown_flags_; + + public: + void Add(const char *name) { + CHECK_LT(n_unknown_flags_, kMaxUnknownFlags); + unknown_flags_[n_unknown_flags_++] = name; + } + + void Report() { + if (!n_unknown_flags_) return; + Printf("WARNING: found %d unrecognized flag(s):\n", n_unknown_flags_); + for (int i = 0; i < n_unknown_flags_; ++i) + Printf(" %s\n", unknown_flags_[i]); + n_unknown_flags_ = 0; + } +}; + +UnknownFlags unknown_flags; + +void ReportUnrecognizedFlags() { + unknown_flags.Report(); +} + +char *FlagParser::ll_strndup(const char *s, uptr n) { + uptr len = internal_strnlen(s, n); + char *s2 = (char*)Alloc.Allocate(len + 1); + internal_memcpy(s2, s, len); + s2[len] = 0; + return s2; +} + +void FlagParser::PrintFlagDescriptions() { + Printf("Available flags for %s:\n", SanitizerToolName); + for (int i = 0; i < n_flags_; ++i) + Printf("\t%s\n\t\t- %s\n", flags_[i].name, flags_[i].desc); +} + +void FlagParser::fatal_error(const char *err) { + Printf("ERROR: %s\n", err); + Die(); +} + +bool FlagParser::is_space(char c) { + return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' || + c == '\r'; +} + +void FlagParser::skip_whitespace() { + while (is_space(buf_[pos_])) ++pos_; +} + +void FlagParser::parse_flag() { + uptr name_start = pos_; + while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_; + if (buf_[pos_] != '=') fatal_error("expected '='"); + char *name = ll_strndup(buf_ + name_start, pos_ - name_start); + + uptr value_start = ++pos_; + char *value; + if (buf_[pos_] == '\'' || buf_[pos_] == '"') { + char quote = buf_[pos_++]; + while (buf_[pos_] != 0 && buf_[pos_] != quote) ++pos_; + if (buf_[pos_] == 0) fatal_error("unterminated string"); + value = ll_strndup(buf_ + value_start + 1, pos_ - value_start - 1); + ++pos_; // consume the closing quote + } else { + while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_; + if (buf_[pos_] != 0 && !is_space(buf_[pos_])) + fatal_error("expected separator or eol"); + value = ll_strndup(buf_ + value_start, pos_ - value_start); + } + + bool res = run_handler(name, value); + if (!res) fatal_error("Flag parsing failed."); +} + +void FlagParser::parse_flags() { + while (true) { + skip_whitespace(); + if (buf_[pos_] == 0) break; + parse_flag(); + } + + // Do a sanity check for certain flags. + if (common_flags_dont_use.malloc_context_size < 1) + common_flags_dont_use.malloc_context_size = 1; +} + +void FlagParser::ParseString(const char *s) { + if (!s) return; + // Backup current parser state to allow nested ParseString() calls. + const char *old_buf_ = buf_; + uptr old_pos_ = pos_; + buf_ = s; + pos_ = 0; + + parse_flags(); + + buf_ = old_buf_; + pos_ = old_pos_; +} + +bool FlagParser::run_handler(const char *name, const char *value) { + for (int i = 0; i < n_flags_; ++i) { + if (internal_strcmp(name, flags_[i].name) == 0) + return flags_[i].handler->Parse(value); + } + // Unrecognized flag. This is not a fatal error, we may print a warning later. + unknown_flags.Add(name); + return true; +} + +void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler, + const char *desc) { + CHECK_LT(n_flags_, kMaxFlags); + flags_[n_flags_].name = name; + flags_[n_flags_].desc = desc; + flags_[n_flags_].handler = handler; + ++n_flags_; +} + +FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) { + flags_ = (Flag *)Alloc.Allocate(sizeof(Flag) * kMaxFlags); +} + +} // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_flag_parser.h b/lib/sanitizer_common/sanitizer_flag_parser.h new file mode 100644 index 000000000000..87afb8238c01 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_flag_parser.h @@ -0,0 +1,121 @@ +//===-- sanitizer_flag_parser.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/AddressSanitizer runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_FLAG_REGISTRY_H +#define SANITIZER_FLAG_REGISTRY_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_libc.h" +#include "sanitizer_common.h" + +namespace __sanitizer { + +class FlagHandlerBase { + public: + virtual bool Parse(const char *value) { return false; } +}; + +template +class FlagHandler : public FlagHandlerBase { + T *t_; + + public: + explicit FlagHandler(T *t) : t_(t) {} + bool Parse(const char *value); +}; + +template <> +inline bool FlagHandler::Parse(const char *value) { + if (internal_strcmp(value, "0") == 0 || + internal_strcmp(value, "no") == 0 || + internal_strcmp(value, "false") == 0) { + *t_ = false; + return true; + } + if (internal_strcmp(value, "1") == 0 || + internal_strcmp(value, "yes") == 0 || + internal_strcmp(value, "true") == 0) { + *t_ = true; + return true; + } + Printf("ERROR: Invalid value for bool option: '%s'\n", value); + return false; +} + +template <> +inline bool FlagHandler::Parse(const char *value) { + *t_ = internal_strdup(value); + return true; +} + +template <> +inline bool FlagHandler::Parse(const char *value) { + char *value_end; + *t_ = internal_simple_strtoll(value, &value_end, 10); + bool ok = *value_end == 0; + if (!ok) Printf("ERROR: Invalid value for int option: '%s'\n", value); + return ok; +} + +template <> +inline bool FlagHandler::Parse(const char *value) { + char *value_end; + *t_ = internal_simple_strtoll(value, &value_end, 10); + bool ok = *value_end == 0; + if (!ok) Printf("ERROR: Invalid value for uptr option: '%s'\n", value); + return ok; +} + +class FlagParser { + static const int kMaxFlags = 200; + struct Flag { + const char *name; + const char *desc; + FlagHandlerBase *handler; + } *flags_; + int n_flags_; + + const char *buf_; + uptr pos_; + + public: + FlagParser(); + void RegisterHandler(const char *name, FlagHandlerBase *handler, + const char *desc); + void ParseString(const char *s); + void PrintFlagDescriptions(); + + static LowLevelAllocator Alloc; + + private: + void fatal_error(const char *err); + bool is_space(char c); + void skip_whitespace(); + void parse_flags(); + void parse_flag(); + bool run_handler(const char *name, const char *value); + char *ll_strndup(const char *s, uptr n); +}; + +template +static void RegisterFlag(FlagParser *parser, const char *name, const char *desc, + T *var) { + FlagHandler *fh = new (FlagParser::Alloc) FlagHandler(var); // NOLINT + parser->RegisterHandler(name, fh, desc); +} + +void ReportUnrecognizedFlags(); + +} // namespace __sanitizer + +#endif // SANITIZER_FLAG_REGISTRY_H diff --git a/lib/sanitizer_common/sanitizer_flags.cc b/lib/sanitizer_common/sanitizer_flags.cc index 40b6ec067150..a2965351cf2a 100644 --- a/lib/sanitizer_common/sanitizer_flags.cc +++ b/lib/sanitizer_common/sanitizer_flags.cc @@ -16,6 +16,7 @@ #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_list.h" +#include "sanitizer_flag_parser.h" namespace __sanitizer { @@ -34,274 +35,53 @@ IntrusiveList flag_descriptions; # define SANITIZER_NEEDS_SEGV 1 #endif -void SetCommonFlagsDefaults(CommonFlags *f) { - f->symbolize = true; - f->external_symbolizer_path = 0; - f->allow_addr2line = false; - f->strip_path_prefix = ""; - f->fast_unwind_on_check = false; - f->fast_unwind_on_fatal = false; - f->fast_unwind_on_malloc = true; - f->handle_ioctl = false; - f->malloc_context_size = 1; - f->log_path = "stderr"; - f->verbosity = 0; - f->detect_leaks = true; - f->leak_check_at_exit = true; - f->allocator_may_return_null = false; - f->print_summary = true; - f->check_printf = true; - // TODO(glider): tools may want to set different defaults for handle_segv. - f->handle_segv = SANITIZER_NEEDS_SEGV; - f->allow_user_segv_handler = false; - f->use_sigaltstack = true; - f->detect_deadlocks = false; - f->clear_shadow_mmap_threshold = 64 * 1024; - f->color = "auto"; - f->legacy_pthread_cond = false; - f->intercept_tls_get_addr = false; - f->coverage = false; - f->coverage_direct = SANITIZER_ANDROID; - f->coverage_dir = "."; - f->full_address_space = false; - f->suppressions = ""; - f->print_suppressions = true; - f->disable_coredump = (SANITIZER_WORDSIZE == 64); - f->symbolize_inline_frames = true; - f->stack_trace_format = "DEFAULT"; +void CommonFlags::SetDefaults() { +#define COMMON_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "sanitizer_flags.inc" +#undef COMMON_FLAG } -void ParseCommonFlagsFromString(CommonFlags *f, const char *str) { - ParseFlag(str, &f->symbolize, "symbolize", - "If set, use the online symbolizer from common sanitizer runtime to turn " - "virtual addresses to file/line locations."); - ParseFlag(str, &f->external_symbolizer_path, "external_symbolizer_path", - "Path to external symbolizer. If empty, the tool will search $PATH for " - "the symbolizer."); - ParseFlag(str, &f->allow_addr2line, "allow_addr2line", - "If set, allows online symbolizer to run addr2line binary to symbolize " - "stack traces (addr2line will only be used if llvm-symbolizer binary is " - "unavailable."); - ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix", - "Strips this prefix from file paths in error reports."); - ParseFlag(str, &f->fast_unwind_on_check, "fast_unwind_on_check", - "If available, use the fast frame-pointer-based unwinder on " - "internal CHECK failures."); - ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal", - "If available, use the fast frame-pointer-based unwinder on fatal " - "errors."); - ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc", - "If available, use the fast frame-pointer-based unwinder on " - "malloc/free."); - ParseFlag(str, &f->handle_ioctl, "handle_ioctl", - "Intercept and handle ioctl requests."); - ParseFlag(str, &f->malloc_context_size, "malloc_context_size", - "Max number of stack frames kept for each allocation/deallocation."); - ParseFlag(str, &f->log_path, "log_path", - "Write logs to \"log_path.pid\". The special values are \"stdout\" and " - "\"stderr\". The default is \"stderr\"."); - ParseFlag(str, &f->verbosity, "verbosity", - "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output)."); - ParseFlag(str, &f->detect_leaks, "detect_leaks", - "Enable memory leak detection."); - ParseFlag(str, &f->leak_check_at_exit, "leak_check_at_exit", - "Invoke leak checking in an atexit handler. Has no effect if " - "detect_leaks=false, or if __lsan_do_leak_check() is called before the " - "handler has a chance to run."); - ParseFlag(str, &f->allocator_may_return_null, "allocator_may_return_null", - "If false, the allocator will crash instead of returning 0 on " - "out-of-memory."); - ParseFlag(str, &f->print_summary, "print_summary", - "If false, disable printing error summaries in addition to error " - "reports."); - ParseFlag(str, &f->check_printf, "check_printf", - "Check printf arguments."); - ParseFlag(str, &f->handle_segv, "handle_segv", - "If set, registers the tool's custom SEGV handler (both SIGBUS and " - "SIGSEGV on OSX)."); - ParseFlag(str, &f->allow_user_segv_handler, "allow_user_segv_handler", - "If set, allows user to register a SEGV handler even if the tool " - "registers one."); - ParseFlag(str, &f->use_sigaltstack, "use_sigaltstack", - "If set, uses alternate stack for signal handling."); - ParseFlag(str, &f->detect_deadlocks, "detect_deadlocks", - "If set, deadlock detection is enabled."); - ParseFlag(str, &f->clear_shadow_mmap_threshold, - "clear_shadow_mmap_threshold", - "Large shadow regions are zero-filled using mmap(NORESERVE) instead of " - "memset(). This is the threshold size in bytes."); - ParseFlag(str, &f->color, "color", - "Colorize reports: (always|never|auto)."); - ParseFlag(str, &f->legacy_pthread_cond, "legacy_pthread_cond", - "Enables support for dynamic libraries linked with libpthread 2.2.5."); - ParseFlag(str, &f->intercept_tls_get_addr, "intercept_tls_get_addr", - "Intercept __tls_get_addr."); - ParseFlag(str, &f->help, "help", "Print the flag descriptions."); - ParseFlag(str, &f->mmap_limit_mb, "mmap_limit_mb", - "Limit the amount of mmap-ed memory (excluding shadow) in Mb; " - "not a user-facing flag, used mosly for testing the tools"); - ParseFlag(str, &f->coverage, "coverage", - "If set, coverage information will be dumped at program shutdown (if the " - "coverage instrumentation was enabled at compile time)."); - ParseFlag(str, &f->coverage_direct, "coverage_direct", - "If set, coverage information will be dumped directly to a memory " - "mapped file. This way data is not lost even if the process is " - "suddenly killed."); - ParseFlag(str, &f->coverage_dir, "coverage_dir", - "Target directory for coverage dumps. Defaults to the current " - "directory."); - ParseFlag(str, &f->full_address_space, "full_address_space", - "Sanitize complete address space; " - "by default kernel area on 32-bit platforms will not be sanitized"); - ParseFlag(str, &f->suppressions, "suppressions", "Suppressions file name."); - ParseFlag(str, &f->print_suppressions, "print_suppressions", - "Print matched suppressions at exit."); - ParseFlag(str, &f->disable_coredump, "disable_coredump", - "Disable core dumping. By default, disable_core=1 on 64-bit to avoid " - "dumping a 16T+ core file. Ignored on OSes that don't dump core by" - "default and for sanitizers that don't reserve lots of virtual memory."); - ParseFlag(str, &f->symbolize_inline_frames, "symbolize_inline_frames", - "Print inlined frames in stacktraces. Defaults to true."); - ParseFlag(str, &f->stack_trace_format, "stack_trace_format", - "Format string used to render stack frames. " - "See sanitizer_stacktrace_printer.h for the format description. " - "Use DEFAULT to get default format."); - - // Do a sanity check for certain flags. - if (f->malloc_context_size < 1) - f->malloc_context_size = 1; +void CommonFlags::CopyFrom(const CommonFlags &other) { + internal_memcpy(this, &other, sizeof(*this)); } -static bool GetFlagValue(const char *env, const char *name, - const char **value, int *value_length) { - if (env == 0) - return false; - const char *pos = 0; - for (;;) { - pos = internal_strstr(env, name); - if (pos == 0) +class FlagHandlerInclude : public FlagHandlerBase { + static const uptr kMaxIncludeSize = 1 << 15; + FlagParser *parser_; + + public: + explicit FlagHandlerInclude(FlagParser *parser) : parser_(parser) {} + bool Parse(const char *value) { + char *data; + uptr data_mapped_size; + int err; + uptr len = + ReadFileToBuffer(value, &data, &data_mapped_size, + Max(kMaxIncludeSize, GetPageSizeCached()), &err); + if (!len) { + Printf("Failed to read options from '%s': error %d\n", value, err); return false; - const char *name_end = pos + internal_strlen(name); - if ((pos != env && - ((pos[-1] >= 'a' && pos[-1] <= 'z') || pos[-1] == '_')) || - *name_end != '=') { - // Seems to be middle of another flag name or value. - env = pos + 1; - continue; } - pos = name_end; - break; + parser_->ParseString(data); + UnmapOrDie(data, data_mapped_size); + return true; } - const char *end; - if (pos[0] != '=') { - end = pos; - } else { - pos += 1; - if (pos[0] == '"') { - pos += 1; - end = internal_strchr(pos, '"'); - } else if (pos[0] == '\'') { - pos += 1; - end = internal_strchr(pos, '\''); - } else { - // Read until the next space or colon. - end = pos + internal_strcspn(pos, " :"); - } - if (end == 0) - end = pos + internal_strlen(pos); - } - *value = pos; - *value_length = end - pos; - return true; +}; + +void RegisterIncludeFlag(FlagParser *parser, CommonFlags *cf) { + FlagHandlerInclude *fh_include = + new (FlagParser::Alloc) FlagHandlerInclude(parser); // NOLINT + parser->RegisterHandler("include", fh_include, + "read more options from the given file"); } -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 RegisterCommonFlags(FlagParser *parser, CommonFlags *cf) { +#define COMMON_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &cf->Name); +#include "sanitizer_flags.inc" +#undef COMMON_FLAG -static LowLevelAllocator allocator_for_flags; - -// The linear scan is suboptimal, but the number of flags is relatively small. -bool FlagInDescriptionList(const char *name) { - IntrusiveList::Iterator it(&flag_descriptions); - while (it.hasNext()) { - if (!internal_strcmp(it.next()->name, name)) return true; - } - return false; -} - -void AddFlagDescription(const char *name, const char *description) { - if (FlagInDescriptionList(name)) return; - FlagDescription *new_description = new(allocator_for_flags) FlagDescription; - new_description->name = name; - new_description->description = description; - flag_descriptions.push_back(new_description); -} - -// TODO(glider): put the descriptions inside CommonFlags. -void PrintFlagDescriptions() { - IntrusiveList::Iterator it(&flag_descriptions); - Printf("Available flags for %s:\n", SanitizerToolName); - while (it.hasNext()) { - FlagDescription *descr = it.next(); - Printf("\t%s\n\t\t- %s\n", descr->name, descr->description); - } -} - -void ParseFlag(const char *env, bool *flag, - const char *name, const char *descr) { - const char *value; - int value_length; - AddFlagDescription(name, descr); - if (!GetFlagValue(env, name, &value, &value_length)) - return; - if (StartsWith(value, value_length, "0") || - StartsWith(value, value_length, "no") || - StartsWith(value, value_length, "false")) - *flag = false; - if (StartsWith(value, value_length, "1") || - StartsWith(value, value_length, "yes") || - StartsWith(value, value_length, "true")) - *flag = true; -} - -void ParseFlag(const char *env, int *flag, - const char *name, const char *descr) { - const char *value; - int value_length; - AddFlagDescription(name, descr); - if (!GetFlagValue(env, name, &value, &value_length)) - return; - *flag = static_cast(internal_atoll(value)); -} - -void ParseFlag(const char *env, uptr *flag, - const char *name, const char *descr) { - const char *value; - int value_length; - AddFlagDescription(name, descr); - if (!GetFlagValue(env, name, &value, &value_length)) - return; - *flag = static_cast(internal_atoll(value)); -} - -void ParseFlag(const char *env, const char **flag, - const char *name, const char *descr) { - const char *value; - int value_length; - AddFlagDescription(name, descr); - if (!GetFlagValue(env, name, &value, &value_length)) - return; - // 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; + RegisterIncludeFlag(parser, cf); } } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_flags.h b/lib/sanitizer_common/sanitizer_flags.h index 4791397a5761..fda6d710757e 100644 --- a/lib/sanitizer_common/sanitizer_flags.h +++ b/lib/sanitizer_common/sanitizer_flags.h @@ -18,62 +18,38 @@ namespace __sanitizer { -void ParseFlag(const char *env, bool *flag, - const char *name, const char *descr); -void ParseFlag(const char *env, int *flag, - const char *name, const char *descr); -void ParseFlag(const char *env, uptr *flag, - const char *name, const char *descr); -void ParseFlag(const char *env, const char **flag, - const char *name, const char *descr); - struct CommonFlags { - bool symbolize; - const char *external_symbolizer_path; - bool allow_addr2line; - const char *strip_path_prefix; - bool fast_unwind_on_check; - bool fast_unwind_on_fatal; - bool fast_unwind_on_malloc; - bool handle_ioctl; - int malloc_context_size; - const char *log_path; - int verbosity; - bool detect_leaks; - bool leak_check_at_exit; - bool allocator_may_return_null; - bool print_summary; - bool check_printf; - bool handle_segv; - bool allow_user_segv_handler; - bool use_sigaltstack; - bool detect_deadlocks; - uptr clear_shadow_mmap_threshold; - const char *color; - bool legacy_pthread_cond; - bool intercept_tls_get_addr; - bool help; - uptr mmap_limit_mb; - bool coverage; - bool coverage_direct; - const char *coverage_dir; - bool full_address_space; - const char *suppressions; - bool print_suppressions; - bool disable_coredump; - bool symbolize_inline_frames; - const char *stack_trace_format; +#define COMMON_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "sanitizer_flags.inc" +#undef COMMON_FLAG + + void SetDefaults(); + void CopyFrom(const CommonFlags &other); }; -inline CommonFlags *common_flags() { - extern CommonFlags common_flags_dont_use; +// Functions to get/set global CommonFlags shared by all sanitizer runtimes: +extern CommonFlags common_flags_dont_use; +inline const CommonFlags *common_flags() { return &common_flags_dont_use; } -void SetCommonFlagsDefaults(CommonFlags *f); -void ParseCommonFlagsFromString(CommonFlags *f, const char *str); -void PrintFlagDescriptions(); +inline void SetCommonFlagsDefaults() { + common_flags_dont_use.SetDefaults(); +} +// This function can only be used to setup tool-specific overrides for +// CommonFlags defaults. Generally, it should only be used right after +// SetCommonFlagsDefaults(), but before ParseCommonFlagsFromString(), and +// only during the flags initialization (i.e. before they are used for +// the first time). +inline void OverrideCommonFlags(const CommonFlags &cf) { + common_flags_dont_use.CopyFrom(cf); +} + +class FlagParser; +void RegisterCommonFlags(FlagParser *parser, + CommonFlags *cf = &common_flags_dont_use); +void RegisterIncludeFlag(FlagParser *parser, CommonFlags *cf); } // namespace __sanitizer #endif // SANITIZER_FLAGS_H diff --git a/lib/sanitizer_common/sanitizer_flags.inc b/lib/sanitizer_common/sanitizer_flags.inc new file mode 100644 index 000000000000..e8c8a87537d7 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_flags.inc @@ -0,0 +1,149 @@ +//===-- sanitizer_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 describes common flags available in all sanitizers. +// +//===----------------------------------------------------------------------===// +#ifndef COMMON_FLAG +#error "Define COMMON_FLAG prior to including this file!" +#endif + +// COMMON_FLAG(Type, Name, DefaultValue, Description) +// Supported types: bool, const char *, int, uptr. +// Default value must be a compile-time constant. +// Description must be a string literal. + +COMMON_FLAG( + bool, symbolize, true, + "If set, use the online symbolizer from common sanitizer runtime to turn " + "virtual addresses to file/line locations.") +COMMON_FLAG( + const char *, external_symbolizer_path, 0, + "Path to external symbolizer. If empty, the tool will search $PATH for " + "the symbolizer.") +COMMON_FLAG( + bool, allow_addr2line, false, + "If set, allows online symbolizer to run addr2line binary to symbolize " + "stack traces (addr2line will only be used if llvm-symbolizer binary is " + "unavailable.") +COMMON_FLAG(const char *, strip_path_prefix, "", + "Strips this prefix from file paths in error reports.") +COMMON_FLAG(bool, fast_unwind_on_check, false, + "If available, use the fast frame-pointer-based unwinder on " + "internal CHECK failures.") +COMMON_FLAG(bool, fast_unwind_on_fatal, false, + "If available, use the fast frame-pointer-based unwinder on fatal " + "errors.") +COMMON_FLAG(bool, fast_unwind_on_malloc, true, + "If available, use the fast frame-pointer-based unwinder on " + "malloc/free.") +COMMON_FLAG(bool, handle_ioctl, false, "Intercept and handle ioctl requests.") +COMMON_FLAG(int, malloc_context_size, 1, + "Max number of stack frames kept for each allocation/deallocation.") +COMMON_FLAG( + const char *, log_path, "stderr", + "Write logs to \"log_path.pid\". The special values are \"stdout\" and " + "\"stderr\". The default is \"stderr\".") +COMMON_FLAG( + int, verbosity, 0, + "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).") +COMMON_FLAG(bool, detect_leaks, true, "Enable memory leak detection.") +COMMON_FLAG( + bool, leak_check_at_exit, true, + "Invoke leak checking in an atexit handler. Has no effect if " + "detect_leaks=false, or if __lsan_do_leak_check() is called before the " + "handler has a chance to run.") +COMMON_FLAG(bool, allocator_may_return_null, false, + "If false, the allocator will crash instead of returning 0 on " + "out-of-memory.") +COMMON_FLAG(bool, print_summary, true, + "If false, disable printing error summaries in addition to error " + "reports.") +COMMON_FLAG(bool, check_printf, true, "Check printf arguments.") +COMMON_FLAG(bool, handle_segv, SANITIZER_NEEDS_SEGV, + "If set, registers the tool's custom SEGV handler (both SIGBUS and " + "SIGSEGV on OSX).") +COMMON_FLAG(bool, allow_user_segv_handler, false, + "If set, allows user to register a SEGV handler even if the tool " + "registers one.") +COMMON_FLAG(bool, use_sigaltstack, true, + "If set, uses alternate stack for signal handling.") +COMMON_FLAG(bool, detect_deadlocks, false, + "If set, deadlock detection is enabled.") +COMMON_FLAG( + uptr, clear_shadow_mmap_threshold, 64 * 1024, + "Large shadow regions are zero-filled using mmap(NORESERVE) instead of " + "memset(). This is the threshold size in bytes.") +COMMON_FLAG(const char *, color, "auto", + "Colorize reports: (always|never|auto).") +COMMON_FLAG( + bool, legacy_pthread_cond, false, + "Enables support for dynamic libraries linked with libpthread 2.2.5.") +COMMON_FLAG(bool, intercept_tls_get_addr, false, "Intercept __tls_get_addr.") +COMMON_FLAG(bool, help, false, "Print the flag descriptions.") +COMMON_FLAG(uptr, mmap_limit_mb, 0, + "Limit the amount of mmap-ed memory (excluding shadow) in Mb; " + "not a user-facing flag, used mosly for testing the tools") +COMMON_FLAG(uptr, hard_rss_limit_mb, 0, + "Hard RSS limit in Mb." + " If non-zero, a background thread is spawned at startup" + " which periodically reads RSS and aborts the process if the" + " limit is reached") +COMMON_FLAG(uptr, soft_rss_limit_mb, 0, + "Soft RSS limit in Mb." + " If non-zero, a background thread is spawned at startup" + " which periodically reads RSS. If the limit is reached" + " all subsequent malloc/new calls will fail or return NULL" + " (depending on the value of allocator_may_return_null)" + " until the RSS goes below the soft limit." + " This limit does not affect memory allocations other than" + " malloc/new.") +COMMON_FLAG(bool, can_use_proc_maps_statm, true, + "If false, do not attempt to read /proc/maps/statm." + " Mostly useful for testing sanitizers.") +COMMON_FLAG( + bool, coverage, false, + "If set, coverage information will be dumped at program shutdown (if the " + "coverage instrumentation was enabled at compile time).") +// On by default, but works only if coverage == true. +COMMON_FLAG(bool, coverage_pcs, true, + "If set (and if 'coverage' is set too), the coverage information " + "will be dumped as a set of PC offsets for every module.") +COMMON_FLAG(bool, coverage_bitset, false, + "If set (and if 'coverage' is set too), the coverage information " + "will also be dumped as a bitset to a separate file.") +COMMON_FLAG(bool, coverage_direct, SANITIZER_ANDROID, + "If set, coverage information will be dumped directly to a memory " + "mapped file. This way data is not lost even if the process is " + "suddenly killed.") +COMMON_FLAG(const char *, coverage_dir, ".", + "Target directory for coverage dumps. Defaults to the current " + "directory.") +COMMON_FLAG(bool, full_address_space, false, + "Sanitize complete address space; " + "by default kernel area on 32-bit platforms will not be sanitized") +COMMON_FLAG(const char *, suppressions, "", "Suppressions file name.") +COMMON_FLAG(bool, print_suppressions, true, + "Print matched suppressions at exit.") +COMMON_FLAG( + bool, disable_coredump, (SANITIZER_WORDSIZE == 64), + "Disable core dumping. By default, disable_core=1 on 64-bit to avoid " + "dumping a 16T+ core file. Ignored on OSes that don't dump core by" + "default and for sanitizers that don't reserve lots of virtual memory.") +COMMON_FLAG(bool, use_madv_dontdump, true, + "If set, instructs kernel to not store the (huge) shadow " + "in core file.") +COMMON_FLAG(bool, symbolize_inline_frames, true, + "Print inlined frames in stacktraces. Defaults to true.") +COMMON_FLAG(const char *, stack_trace_format, "DEFAULT", + "Format string used to render stack frames. " + "See sanitizer_stacktrace_printer.h for the format description. " + "Use DEFAULT to get default format.") +COMMON_FLAG(bool, no_huge_pages_for_shadow, true, + "If true, the shadow is not allowed to use huge pages. ") diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h index 97eef744bd14..2a0b41f0bb99 100644 --- a/lib/sanitizer_common/sanitizer_internal_defs.h +++ b/lib/sanitizer_common/sanitizer_internal_defs.h @@ -15,6 +15,10 @@ #include "sanitizer_platform.h" +#ifndef SANITIZER_DEBUG +# define SANITIZER_DEBUG 0 +#endif + // Only use SANITIZER_*ATTRIBUTE* before the function return type! #if SANITIZER_WINDOWS # define SANITIZER_INTERFACE_ATTRIBUTE __declspec(dllexport) @@ -81,8 +85,9 @@ typedef int fd_t; // WARNING: OFF_T may be different from OS type off_t, depending on the value of // _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls // like pread and mmap, as opposed to pread64 and mmap64. -// Mac and Linux/x86-64 are special. -#if SANITIZER_MAC || (SANITIZER_LINUX && defined(__x86_64__)) +// FreeBSD, Mac and Linux/x86-64 are special. +#if SANITIZER_FREEBSD || SANITIZER_MAC || \ + (SANITIZER_LINUX && defined(__x86_64__)) typedef u64 OFF_T; #else typedef uptr OFF_T; @@ -120,7 +125,7 @@ extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init(); - SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u8 *guard); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard); SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_annotate_contiguous_container(const void *beg, const void *end, @@ -240,7 +245,7 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond, #define CHECK_GT(a, b) CHECK_IMPL((a), >, (b)) #define CHECK_GE(a, b) CHECK_IMPL((a), >=, (b)) -#if TSAN_DEBUG +#if SANITIZER_DEBUG #define DCHECK(a) CHECK(a) #define DCHECK_EQ(a, b) CHECK_EQ(a, b) #define DCHECK_NE(a, b) CHECK_NE(a, b) @@ -320,4 +325,11 @@ extern "C" void* _ReturnAddress(void); } while (internal_iserror(res, &rverrno) && rverrno == EINTR); \ } +// Forces the compiler to generate a frame pointer in the function. +#define ENABLE_FRAME_POINTER \ + do { \ + volatile uptr enable_fp; \ + enable_fp = GET_CURRENT_FRAME(); \ + } while (0) + #endif // SANITIZER_DEFS_H diff --git a/lib/sanitizer_common/sanitizer_libc.cc b/lib/sanitizer_common/sanitizer_libc.cc index d8bd1cf7a7e0..cb162a4c4984 100644 --- a/lib/sanitizer_common/sanitizer_libc.cc +++ b/lib/sanitizer_common/sanitizer_libc.cc @@ -28,6 +28,15 @@ void *internal_memchr(const void *s, int c, uptr n) { return 0; } +void *internal_memrchr(const void *s, int c, uptr n) { + const char *t = (const char *)s; + void *res = nullptr; + for (uptr i = 0; i < n; ++i, ++t) { + if (*t == c) res = reinterpret_cast(const_cast(t)); + } + return res; +} + int internal_memcmp(const void* s1, const void* s2, uptr n) { const char *t1 = (const char *)s1; const char *t2 = (const char *)s2; @@ -101,6 +110,14 @@ char* internal_strdup(const char *s) { return s2; } +char* internal_strndup(const char *s, uptr n) { + uptr len = internal_strnlen(s, n); + char *s2 = (char*)InternalAlloc(len + 1); + internal_memcpy(s2, s, len); + s2[len] = 0; + return s2; +} + int internal_strcmp(const char *s1, const char *s2) { while (true) { unsigned c1 = *s1; diff --git a/lib/sanitizer_common/sanitizer_libc.h b/lib/sanitizer_common/sanitizer_libc.h index 6995626821ab..c086b8a9139e 100644 --- a/lib/sanitizer_common/sanitizer_libc.h +++ b/lib/sanitizer_common/sanitizer_libc.h @@ -26,6 +26,7 @@ namespace __sanitizer { // String functions s64 internal_atoll(const char *nptr); void *internal_memchr(const void *s, int c, uptr n); +void *internal_memrchr(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); @@ -38,6 +39,7 @@ char *internal_strchrnul(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); +char *internal_strndup(const char *s, uptr n); uptr internal_strlen(const char *s); char *internal_strncat(char *dst, const char *src, uptr n); int internal_strncmp(const char *s1, const char *s2, uptr n); @@ -98,6 +100,25 @@ int internal_fork(); // Threading uptr internal_sched_yield(); +// These functions call appropriate pthread_ functions directly, bypassing +// the interceptor. They are weak and may not be present in some tools. +SANITIZER_WEAK_ATTRIBUTE +int real_pthread_create(void *th, void *attr, void *(*callback)(void *), + void *param); +SANITIZER_WEAK_ATTRIBUTE +int real_pthread_join(void *th, void **ret); + +#define DEFINE_REAL_PTHREAD_FUNCTIONS \ + namespace __sanitizer { \ + int real_pthread_create(void *th, void *attr, void *(*callback)(void *), \ + void *param) { \ + return REAL(pthread_create)(th, attr, callback, param); \ + } \ + int real_pthread_join(void *th, void **ret) { \ + return REAL(pthread_join(th, ret)); \ + } \ + } // namespace __sanitizer + // Error handling bool internal_iserror(uptr retval, int *rverrno = 0); diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc index 36de1ec70e97..26138ba29d35 100644 --- a/lib/sanitizer_common/sanitizer_linux.cc +++ b/lib/sanitizer_common/sanitizer_linux.cc @@ -31,6 +31,17 @@ #include #endif +// For mips64, syscall(__NR_stat) fills the buffer in the 'struct kernel_stat' +// format. Struct kernel_stat is defined as 'struct stat' in asm/stat.h. To +// access stat from asm/stat.h, without conflicting with definition in +// sys/stat.h, we use this trick. +#if defined(__mips64) +#include +#define stat kernel_stat +#include +#undef stat +#endif + #include #include #include @@ -98,14 +109,16 @@ namespace __sanitizer { #endif // --------------- sanitizer_libc.h -uptr internal_mmap(void *addr, uptr length, int prot, int flags, - int fd, u64 offset) { +uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, + u64 offset) { #if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd, offset); #else + // mmap2 specifies file offset in 4096-byte units. + CHECK(IsAligned(offset, 4096)); return internal_syscall(SYSCALL(mmap2), addr, length, prot, flags, fd, - offset); + offset / 4096); #endif } @@ -179,6 +192,26 @@ static void stat64_to_stat(struct stat64 *in, struct stat *out) { } #endif +#if defined(__mips64) +static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) { + internal_memset(out, 0, sizeof(*out)); + out->st_dev = in->st_dev; + out->st_ino = in->st_ino; + out->st_mode = in->st_mode; + out->st_nlink = in->st_nlink; + out->st_uid = in->st_uid; + out->st_gid = in->st_gid; + out->st_rdev = in->st_rdev; + out->st_size = in->st_size; + out->st_blksize = in->st_blksize; + out->st_blocks = in->st_blocks; + out->st_atime = in->st_atime_nsec; + out->st_mtime = in->st_mtime_nsec; + out->st_ctime = in->st_ctime_nsec; + out->st_ino = in->st_ino; +} +#endif + uptr internal_stat(const char *path, void *buf) { #if SANITIZER_FREEBSD return internal_syscall(SYSCALL(stat), path, buf); @@ -186,7 +219,15 @@ uptr internal_stat(const char *path, void *buf) { return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0); #elif SANITIZER_LINUX_USES_64BIT_SYSCALLS +# if defined(__mips64) + // For mips64, stat syscall fills buffer in the format of kernel_stat + struct kernel_stat kbuf; + int res = internal_syscall(SYSCALL(stat), path, &kbuf); + kernel_stat_to_stat(&kbuf, (struct stat *)buf); + return res; +# else return internal_syscall(SYSCALL(stat), (uptr)path, (uptr)buf); +# endif #else struct stat64 buf64; int res = internal_syscall(SYSCALL(stat64), path, &buf64); @@ -381,33 +422,6 @@ static void ReadNullSepFileToArray(const char *path, char ***arr, } #endif -uptr GetRSS() { - uptr fd = OpenFile("/proc/self/statm", false); - if ((sptr)fd < 0) - return 0; - char buf[64]; - uptr len = internal_read(fd, buf, sizeof(buf) - 1); - internal_close(fd); - if ((sptr)len <= 0) - return 0; - buf[len] = 0; - // The format of the file is: - // 1084 89 69 11 0 79 0 - // We need the second number which is RSS in 4K units. - char *pos = buf; - // Skip the first number. - while (*pos >= '0' && *pos <= '9') - pos++; - // Skip whitespaces. - while (!(*pos >= '0' && *pos <= '9') && *pos != 0) - pos++; - // Read the number. - uptr rss = 0; - while (*pos >= '0' && *pos <= '9') - rss = rss * 10 + *pos++ - '0'; - return rss * 4096; -} - static void GetArgsAndEnv(char*** argv, char*** envp) { #if !SANITIZER_GO if (&__libc_stack_end) { @@ -435,32 +449,18 @@ void ReExec() { Die(); } -// Stub implementation of GetThreadStackAndTls for Go. -#if SANITIZER_GO -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; -} -#endif // SANITIZER_GO - enum MutexState { MtxUnlocked = 0, MtxLocked = 1, MtxSleeping = 2 }; -BlockingMutex::BlockingMutex(LinkerInitialized) { - CHECK_EQ(owner_, 0); -} - BlockingMutex::BlockingMutex() { internal_memset(this, 0, sizeof(*this)); } void BlockingMutex::Lock() { + CHECK_EQ(owner_, 0); atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked) return; @@ -760,6 +760,7 @@ bool LibraryNameIs(const char *full_name, const char *base_name) { #if !SANITIZER_ANDROID // Call cb for each region mapped by map. void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) { + CHECK_NE(map, nullptr); #if !SANITIZER_FREEBSD typedef ElfW(Phdr) Elf_Phdr; typedef ElfW(Ehdr) Elf_Ehdr; @@ -896,9 +897,30 @@ void GetExtraActivationFlags(char *buf, uptr size) { #endif bool IsDeadlySignal(int signum) { - return (signum == SIGSEGV) && common_flags()->handle_segv; + return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv; } +#ifndef SANITIZER_GO +void *internal_start_thread(void(*func)(void *arg), void *arg) { + // Start the thread with signals blocked, otherwise it can steal user signals. + __sanitizer_sigset_t set, old; + internal_sigfillset(&set); + internal_sigprocmask(SIG_SETMASK, &set, &old); + void *th; + real_pthread_create(&th, 0, (void*(*)(void *arg))func, arg); + internal_sigprocmask(SIG_SETMASK, &old, 0); + return th; +} + +void internal_join_thread(void *th) { + real_pthread_join(th, 0); +} +#else +void *internal_start_thread(void (*func)(void *), void *arg) { return 0; } + +void internal_join_thread(void *th) {} +#endif + } // namespace __sanitizer #endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc index 4e09081e7f54..df42c3604ae9 100644 --- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc @@ -58,8 +58,10 @@ real_pthread_attr_getstack(void *attr, void **addr, size_t *size); } // extern "C" static int my_pthread_attr_getstack(void *attr, void **addr, size_t *size) { - if (real_pthread_attr_getstack) +#if !SANITIZER_GO + if (&real_pthread_attr_getstack) return real_pthread_attr_getstack((pthread_attr_t *)attr, addr, size); +#endif return pthread_attr_getstack((pthread_attr_t *)attr, addr, size); } @@ -67,8 +69,10 @@ SANITIZER_WEAK_ATTRIBUTE int real_sigaction(int signum, const void *act, void *oldact); int internal_sigaction(int signum, const void *act, void *oldact) { - if (real_sigaction) +#if !SANITIZER_GO + if (&real_sigaction) return real_sigaction(signum, act, oldact); +#endif return sigaction(signum, (const struct sigaction *)act, (struct sigaction *)oldact); } @@ -120,6 +124,7 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, *stack_bottom = (uptr)stackaddr; } +#if !SANITIZER_GO bool SetEnv(const char *name, const char *value) { void *f = dlsym(RTLD_NEXT, "setenv"); if (f == 0) @@ -130,6 +135,7 @@ bool SetEnv(const char *name, const char *value) { internal_memcpy(&setenv_f, &f, sizeof(f)); return setenv_f(name, value, 1) == 0; } +#endif bool SanitizerSetThreadName(const char *name) { #ifdef PR_SET_NAME @@ -163,7 +169,7 @@ static uptr g_tls_size; #endif void InitTlsSize() { -#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID +#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION; get_tls_func get_tls; void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info"); @@ -208,6 +214,8 @@ uptr ThreadDescriptorSize() { val = FIRST_32_SECOND_64(1168, 1776); else if (minor <= 12) val = FIRST_32_SECOND_64(1168, 2288); + else if (minor == 13) + val = FIRST_32_SECOND_64(1168, 2304); else val = FIRST_32_SECOND_64(1216, 2304); } @@ -259,6 +267,7 @@ uptr ThreadSelf() { } #endif // SANITIZER_FREEBSD +#if !SANITIZER_GO static void GetTls(uptr *addr, uptr *size) { #if SANITIZER_LINUX # if defined(__x86_64__) || defined(__i386__) @@ -287,6 +296,7 @@ static void GetTls(uptr *addr, uptr *size) { # error "Unknown OS" #endif } +#endif uptr GetTlsSize() { #if SANITIZER_FREEBSD @@ -300,6 +310,10 @@ uptr GetTlsSize() { void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size) { +#if SANITIZER_GO + // Stub implementation for Go. + *stk_addr = *stk_size = *tls_addr = *tls_size = 0; +#else GetTls(tls_addr, tls_size); uptr stack_top, stack_bottom; @@ -316,6 +330,7 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, *tls_addr = *stk_addr + *stk_size; } } +#endif } void AdjustStackSize(void *attr_) { @@ -420,6 +435,45 @@ void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { #endif } +// getrusage does not give us the current RSS, only the max RSS. +// Still, this is better than nothing if /proc/self/statm is not available +// for some reason, e.g. due to a sandbox. +static uptr GetRSSFromGetrusage() { + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage)) // Failed, probably due to a sandbox. + return 0; + return usage.ru_maxrss << 10; // ru_maxrss is in Kb. +} + +uptr GetRSS() { + if (!common_flags()->can_use_proc_maps_statm) + return GetRSSFromGetrusage(); + uptr fd = OpenFile("/proc/self/statm", false); + if ((sptr)fd < 0) + return GetRSSFromGetrusage(); + char buf[64]; + uptr len = internal_read(fd, buf, sizeof(buf) - 1); + internal_close(fd); + if ((sptr)len <= 0) + return 0; + buf[len] = 0; + // The format of the file is: + // 1084 89 69 11 0 79 0 + // We need the second number which is RSS in pages. + char *pos = buf; + // Skip the first number. + while (*pos >= '0' && *pos <= '9') + pos++; + // Skip whitespaces. + while (!(*pos >= '0' && *pos <= '9') && *pos != 0) + pos++; + // Read the number. + uptr rss = 0; + while (*pos >= '0' && *pos <= '9') + rss = rss * 10 + *pos++ - '0'; + return rss * GetPageSizeCached(); +} + } // namespace __sanitizer #endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/lib/sanitizer_common/sanitizer_list.h b/lib/sanitizer_common/sanitizer_list.h index a47bc7d45e3e..6dd9c8f7bca1 100644 --- a/lib/sanitizer_common/sanitizer_list.h +++ b/lib/sanitizer_common/sanitizer_list.h @@ -115,21 +115,25 @@ struct IntrusiveList { } } - class Iterator { + template + class IteratorBase { public: - explicit Iterator(IntrusiveList *list) + explicit IteratorBase(ListTy *list) : list_(list), current_(list->first_) { } - Item *next() { - Item *ret = current_; + ItemTy *next() { + ItemTy *ret = current_; if (current_) current_ = current_->next; return ret; } bool hasNext() const { return current_ != 0; } private: - IntrusiveList *list_; - Item *current_; + ListTy *list_; + ItemTy *current_; }; + typedef IteratorBase, Item> Iterator; + typedef IteratorBase, const Item> ConstIterator; + // private, don't use directly. uptr size_; Item *first_; diff --git a/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc index 98c5b94112cf..39a5c7e8d24f 100644 --- a/lib/sanitizer_common/sanitizer_mac.cc +++ b/lib/sanitizer_common/sanitizer_mac.cc @@ -109,6 +109,10 @@ uptr internal_readlink(const char *path, char *buf, uptr bufsize) { return readlink(path, buf, bufsize); } +uptr internal_unlink(const char *path) { + return unlink(path); +} + uptr internal_sched_yield() { return sched_yield(); } @@ -213,10 +217,6 @@ uptr GetPageSize() { return sysconf(_SC_PAGESIZE); } -BlockingMutex::BlockingMutex(LinkerInitialized) { - // We assume that OS_SPINLOCK_INIT is zero -} - BlockingMutex::BlockingMutex() { internal_memset(this, 0, sizeof(*this)); } @@ -298,7 +298,11 @@ MacosVersion GetMacosVersionInternal() { case '2': return MACOS_VERSION_MOUNTAIN_LION; case '3': return MACOS_VERSION_MAVERICKS; case '4': return MACOS_VERSION_YOSEMITE; - default: return MACOS_VERSION_UNKNOWN; + default: + if (IsDigit(version[1])) + return MACOS_VERSION_UNKNOWN_NEWER; + else + return MACOS_VERSION_UNKNOWN; } } default: return MACOS_VERSION_UNKNOWN; @@ -321,6 +325,9 @@ uptr GetRSS() { return 0; } +void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; } +void internal_join_thread(void *th) { } + } // namespace __sanitizer #endif // SANITIZER_MAC diff --git a/lib/sanitizer_common/sanitizer_mac.h b/lib/sanitizer_common/sanitizer_mac.h index 3ed0ed3b00c2..9eed905187ec 100644 --- a/lib/sanitizer_common/sanitizer_mac.h +++ b/lib/sanitizer_common/sanitizer_mac.h @@ -27,6 +27,7 @@ enum MacosVersion { MACOS_VERSION_MOUNTAIN_LION, MACOS_VERSION_MAVERICKS, MACOS_VERSION_YOSEMITE, + MACOS_VERSION_UNKNOWN_NEWER }; MacosVersion GetMacosVersion(); diff --git a/lib/sanitizer_common/sanitizer_mutex.h b/lib/sanitizer_common/sanitizer_mutex.h index c7589f76ed3b..d06fc45ff931 100644 --- a/lib/sanitizer_common/sanitizer_mutex.h +++ b/lib/sanitizer_common/sanitizer_mutex.h @@ -73,7 +73,13 @@ class SpinMutex : public StaticSpinMutex { class BlockingMutex { public: +#if SANITIZER_WINDOWS + // Windows does not currently support LinkerInitialized explicit BlockingMutex(LinkerInitialized); +#else + explicit constexpr BlockingMutex(LinkerInitialized) + : opaque_storage_ {0, }, owner_(0) {} +#endif BlockingMutex(); void Lock(); void Unlock(); diff --git a/lib/sanitizer_common/sanitizer_platform_interceptors.h b/lib/sanitizer_common/sanitizer_platform_interceptors.h index 7ca88fa972f3..54a1cfd8582b 100644 --- a/lib/sanitizer_common/sanitizer_platform_interceptors.h +++ b/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -57,7 +57,7 @@ #define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MEMCHR 1 -#define SANITIZER_INTERCEPT_MEMRCHR SI_LINUX +#define SANITIZER_INTERCEPT_MEMRCHR SI_FREEBSD || SI_LINUX #define SANITIZER_INTERCEPT_READ SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PREAD SI_NOT_WINDOWS @@ -70,7 +70,7 @@ #define SANITIZER_INTERCEPT_READV SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_WRITEV SI_NOT_WINDOWS -#define SANITIZER_INTERCEPT_PREADV SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PREADV SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PREADV64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PWRITEV64 SI_LINUX_NOT_ANDROID @@ -85,6 +85,7 @@ #ifndef SANITIZER_INTERCEPT_PRINTF # define SANITIZER_INTERCEPT_PRINTF SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PRINTF_L SI_FREEBSD # define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX_NOT_ANDROID #endif @@ -93,12 +94,13 @@ #define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \ - SI_MAC || SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_GETPWENT SI_MAC || SI_LINUX_NOT_ANDROID + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_GETPWENT \ + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_GETPWENT_R SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_GETPWENT_R SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SETPWENT SI_MAC || SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_LINUX +#define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_FREEBSD || SI_LINUX #define SANITIZER_INTERCEPT_GETITIMER SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_TIME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GLOB SI_LINUX_NOT_ANDROID @@ -109,10 +111,10 @@ #define SANITIZER_INTERCEPT_GETNAMEINFO SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS -#define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_LINUX -#define SANITIZER_INTERCEPT_GETHOSTBYNAME2_R SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_GETHOSTBYADDR_R SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_GETHOSTENT_R SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_FREEBSD || SI_LINUX +#define SANITIZER_INTERCEPT_GETHOSTBYNAME2_R SI_FREEBSD || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_GETHOSTBYADDR_R SI_FREEBSD || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_GETHOSTENT_R SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETSOCKOPT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ACCEPT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ACCEPT4 SI_LINUX_NOT_ANDROID @@ -133,12 +135,15 @@ #define SANITIZER_INTERCEPT_MBSTOWCS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS -#define SANITIZER_INTERCEPT_WCSNRTOMBS SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_WCSNRTOMBS \ + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_CONFSTR SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_CONFSTR \ + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_SCHED_GETPARAM SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STRERROR SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_STRERROR_R SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_XPG_STRERROR_R SI_LINUX_NOT_ANDROID @@ -147,7 +152,8 @@ #define SANITIZER_INTERCEPT_GETGROUPS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_POLL SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_WORDEXP (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_WORDEXP \ + SI_FREEBSD || (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGWAIT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID @@ -158,21 +164,22 @@ #define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX #define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_STATFS SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_STATFS SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STATFS64 \ (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_STATVFS SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_STATVFS SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_INITGROUPS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ETHER_NTOA_ATON SI_NOT_WINDOWS -#define SANITIZER_INTERCEPT_ETHER_HOST SI_MAC || SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_ETHER_R SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_ETHER_HOST \ + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_ETHER_R SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SHMCTL \ ((SI_FREEBSD || SI_LINUX_NOT_ANDROID) && SANITIZER_WORDSIZE == 64) #define SANITIZER_INTERCEPT_RANDOM_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \ - SI_MAC || SI_LINUX_NOT_ANDROID + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE SI_NOT_WINDOWS @@ -193,7 +200,7 @@ #define SANITIZER_INTERCEPT_SINCOS SI_LINUX #define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS -#define SANITIZER_INTERCEPT_LGAMMA_R SI_LINUX +#define SANITIZER_INTERCEPT_LGAMMA_R SI_FREEBSD || SI_LINUX #define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_RAND_R SI_MAC || SI_LINUX_NOT_ANDROID diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc index 6ffc1433cddd..4eabcd7a3d22 100644 --- a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc +++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc @@ -1061,7 +1061,13 @@ CHECK_SIZE_AND_OFFSET(ipc_perm, uid); CHECK_SIZE_AND_OFFSET(ipc_perm, gid); CHECK_SIZE_AND_OFFSET(ipc_perm, cuid); CHECK_SIZE_AND_OFFSET(ipc_perm, cgid); +#ifndef __GLIBC_PREREQ +#define __GLIBC_PREREQ(x, y) 0 +#endif +#if !defined(__aarch64__) || !SANITIZER_LINUX || __GLIBC_PREREQ (2, 21) +/* On aarch64 glibc 2.20 and earlier provided incorrect mode field. */ CHECK_SIZE_AND_OFFSET(ipc_perm, mode); +#endif CHECK_TYPE_SIZE(shmid_ds); CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm); diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h index 80a3ddb36670..d375e01a20e4 100644 --- a/lib/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.h @@ -18,6 +18,15 @@ #include "sanitizer_internal_defs.h" #include "sanitizer_platform.h" +#if SANITIZER_FREEBSD +// FreeBSD's dlopen() returns a pointer to an Obj_Entry structure that +// incroporates the map structure. +# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \ + ((link_map*)((handle) == nullptr ? nullptr : ((char*)(handle) + 544))) +#else +# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) ((link_map*)(handle)) +#endif // !SANITIZER_FREEBSD + namespace __sanitizer { extern unsigned struct_utsname_sz; extern unsigned struct_stat_sz; @@ -169,7 +178,7 @@ namespace __sanitizer { unsigned __seq; u64 __unused1; u64 __unused2; -#elif defined(__mips__) +#elif defined(__mips__) || defined(__aarch64__) unsigned int mode; unsigned short __seq; unsigned short __pad1; diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc index 4205c2b116ba..6cb51efce2d1 100644 --- a/lib/sanitizer_common/sanitizer_posix.cc +++ b/lib/sanitizer_common/sanitizer_posix.cc @@ -78,16 +78,15 @@ static uptr GetKernelAreaSize() { uptr GetMaxVirtualAddress() { #if SANITIZER_WORDSIZE == 64 -# if defined(__powerpc64__) +# if defined(__powerpc64__) || defined(__aarch64__) // On PowerPC64 we have two different address space layouts: 44- and 46-bit. // We somehow need to figure out which one we are using now and choose // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL. // Note that with 'ulimit -s unlimited' the stack is moved away from the top // of the address space, so simply checking the stack address is not enough. // This should (does) work for both PowerPC64 Endian modes. + // Similarly, aarch64 has multiple address space layouts: 39, 42 and 47-bit. return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1; -# elif defined(__aarch64__) - return (1ULL << 39) - 1; # elif defined(__mips64) return (1ULL << 40) - 1; // 0x000000ffffffffffUL; # else @@ -238,7 +237,8 @@ bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { while (proc_maps.Next(&start, &end, /*offset*/0, /*filename*/0, /*filename_size*/0, /*protection*/0)) { - if (!IntervalsAreSeparate(start, end, range_start, range_end)) + CHECK_NE(0, end); + if (!IntervalsAreSeparate(start, end - 1, range_start, range_end)) return false; } return true; diff --git a/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_posix_libcdep.cc index ed1e3729a00e..11828e6cdf51 100644 --- a/lib/sanitizer_common/sanitizer_posix_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_posix_libcdep.cc @@ -44,6 +44,18 @@ void FlushUnneededShadowMemory(uptr addr, uptr size) { madvise((void*)addr, size, MADV_DONTNEED); } +void NoHugePagesInRegion(uptr addr, uptr size) { +#ifdef MADV_NOHUGEPAGE // May not be defined on old systems. + madvise((void *)addr, size, MADV_NOHUGEPAGE); +#endif // MADV_NOHUGEPAGE +} + +void DontDumpShadowMemory(uptr addr, uptr length) { +#ifdef MADV_DONTDUMP + madvise((void *)addr, length, MADV_DONTDUMP); +#endif +} + static rlim_t getlim(int res) { rlimit rlim; CHECK_EQ(0, getrlimit(res, &rlim)); diff --git a/lib/sanitizer_common/sanitizer_quarantine.h b/lib/sanitizer_common/sanitizer_quarantine.h index db4eb74505f9..404d3753f7e9 100644 --- a/lib/sanitizer_common/sanitizer_quarantine.h +++ b/lib/sanitizer_common/sanitizer_quarantine.h @@ -49,11 +49,14 @@ class Quarantine { } void Init(uptr size, uptr cache_size) { - max_size_ = size; - min_size_ = size / 10 * 9; // 90% of max size. + atomic_store(&max_size_, size, memory_order_release); + atomic_store(&min_size_, size / 10 * 9, + memory_order_release); // 90% of max size. max_cache_size_ = cache_size; } + uptr GetSize() const { return atomic_load(&max_size_, memory_order_acquire); } + void Put(Cache *c, Callback cb, Node *ptr, uptr size) { c->Enqueue(cb, ptr, size); if (c->Size() > max_cache_size_) @@ -65,15 +68,15 @@ class Quarantine { SpinMutexLock l(&cache_mutex_); cache_.Transfer(c); } - if (cache_.Size() > max_size_ && recycle_mutex_.TryLock()) + if (cache_.Size() > GetSize() && recycle_mutex_.TryLock()) Recycle(cb); } private: // Read-only data. char pad0_[kCacheLineSize]; - uptr max_size_; - uptr min_size_; + atomic_uintptr_t max_size_; + atomic_uintptr_t min_size_; uptr max_cache_size_; char pad1_[kCacheLineSize]; SpinMutex cache_mutex_; @@ -83,9 +86,10 @@ class Quarantine { void NOINLINE Recycle(Callback cb) { Cache tmp; + uptr min_size = atomic_load(&min_size_, memory_order_acquire); { SpinMutexLock l(&cache_mutex_); - while (cache_.Size() > min_size_) { + while (cache_.Size() > min_size) { QuarantineBatch *b = cache_.DequeueBatch(); tmp.EnqueueBatch(b); } @@ -130,6 +134,7 @@ class QuarantineCache { size += sizeof(QuarantineBatch); // Count the batch in Quarantine size. } QuarantineBatch *b = list_.back(); + CHECK(b); b->batch[b->count++] = ptr; b->size += size; SizeAdd(size); @@ -168,6 +173,7 @@ class QuarantineCache { NOINLINE QuarantineBatch* AllocBatch(Callback cb) { QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b)); + CHECK(b); b->count = 0; b->size = 0; list_.push_back(b); diff --git a/lib/sanitizer_common/sanitizer_stackdepot.cc b/lib/sanitizer_common/sanitizer_stackdepot.cc index f10f1f973fd0..59b53f4dcd84 100644 --- a/lib/sanitizer_common/sanitizer_stackdepot.cc +++ b/lib/sanitizer_common/sanitizer_stackdepot.cc @@ -22,7 +22,8 @@ struct StackDepotNode { StackDepotNode *link; u32 id; atomic_uint32_t hash_and_use_count; // hash_bits : 12; use_count : 20; - uptr size; + u32 size; + u32 tag; uptr stack[1]; // [size] static const u32 kTabSizeLog = 20; @@ -37,7 +38,8 @@ struct StackDepotNode { bool eq(u32 hash, const args_type &args) const { u32 hash_bits = atomic_load(&hash_and_use_count, memory_order_relaxed) & kHashMask; - if ((hash & kHashMask) != hash_bits || args.size != size) return false; + if ((hash & kHashMask) != hash_bits || args.size != size || args.tag != tag) + return false; uptr i = 0; for (; i < size; i++) { if (stack[i] != args.trace[i]) return false; @@ -72,10 +74,11 @@ struct StackDepotNode { void store(const args_type &args, u32 hash) { atomic_store(&hash_and_use_count, hash & kHashMask, memory_order_relaxed); size = args.size; + tag = args.tag; internal_memcpy(stack, args.trace, size * sizeof(uptr)); } args_type load() const { - return args_type(&stack[0], size); + return args_type(&stack[0], size, tag); } StackDepotHandle get_handle() { return StackDepotHandle(this); } diff --git a/lib/sanitizer_common/sanitizer_stacktrace.cc b/lib/sanitizer_common/sanitizer_stacktrace.cc index cf061fb8c73f..2deadb6e3560 100644 --- a/lib/sanitizer_common/sanitizer_stacktrace.cc +++ b/lib/sanitizer_common/sanitizer_stacktrace.cc @@ -17,21 +17,6 @@ namespace __sanitizer { -uptr StackTrace::GetPreviousInstructionPc(uptr pc) { -#if defined(__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__) || defined(__mips__) - return pc - 8; -#else - return pc - 1; -#endif -} - uptr StackTrace::GetNextInstructionPc(uptr pc) { #if defined(__mips__) return pc + 8; @@ -83,7 +68,7 @@ static inline uhwptr *GetCanonicFrame(uptr bp, } void BufferedStackTrace::FastUnwindStack(uptr pc, uptr bp, uptr stack_top, - uptr stack_bottom, uptr max_depth) { + uptr stack_bottom, u32 max_depth) { CHECK_GE(max_depth, 2); trace_buffer[0] = pc; size = 1; @@ -120,7 +105,7 @@ void BufferedStackTrace::PopStackFrames(uptr count) { uptr BufferedStackTrace::LocatePcInTrace(uptr pc) { // Use threshold to find PC in stack trace, as PC we want to unwind from may // slightly differ from return address in the actual unwinded stack trace. - const int kPcThreshold = 288; + const int kPcThreshold = 304; for (uptr i = 0; i < size; ++i) { if (MatchPc(pc, trace[i], kPcThreshold)) return i; diff --git a/lib/sanitizer_common/sanitizer_stacktrace.h b/lib/sanitizer_common/sanitizer_stacktrace.h index e755c052cb77..6c3a1511f337 100644 --- a/lib/sanitizer_common/sanitizer_stacktrace.h +++ b/lib/sanitizer_common/sanitizer_stacktrace.h @@ -17,7 +17,7 @@ namespace __sanitizer { -static const uptr kStackTraceMax = 256; +static const u32 kStackTraceMax = 256; #if SANITIZER_LINUX && (defined(__aarch64__) || defined(__powerpc__) || \ defined(__powerpc64__) || defined(__sparc__) || \ @@ -40,10 +40,18 @@ static const uptr kStackTraceMax = 256; struct StackTrace { const uptr *trace; - uptr size; + u32 size; + u32 tag; - StackTrace() : trace(nullptr), size(0) {} - StackTrace(const uptr *trace, uptr size) : trace(trace), size(size) {} + static const int TAG_UNKNOWN = 0; + static const int TAG_ALLOC = 1; + static const int TAG_DEALLOC = 2; + static const int TAG_CUSTOM = 100; // Tool specific tags start here. + + StackTrace() : trace(nullptr), size(0), tag(0) {} + StackTrace(const uptr *trace, u32 size) : trace(trace), size(size), tag(0) {} + StackTrace(const uptr *trace, u32 size, u32 tag) + : trace(trace), size(size), tag(tag) {} // Prints a symbolized stacktrace, followed by an empty line. void Print() const; @@ -57,12 +65,29 @@ struct StackTrace { } static uptr GetCurrentPc(); - static uptr GetPreviousInstructionPc(uptr pc); + static inline uptr GetPreviousInstructionPc(uptr pc); static uptr GetNextInstructionPc(uptr pc); typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer, int out_size); }; +// Performance-critical, must be in the header. +ALWAYS_INLINE +uptr StackTrace::GetPreviousInstructionPc(uptr pc) { +#if defined(__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__) || defined(__mips__) + return pc - 8; +#else + return pc - 1; +#endif +} + // StackTrace that owns the buffer used to store the addresses. struct BufferedStackTrace : public StackTrace { uptr trace_buffer[kStackTraceMax]; @@ -71,15 +96,15 @@ struct BufferedStackTrace : public StackTrace { BufferedStackTrace() : StackTrace(trace_buffer, 0), top_frame_bp(0) {} void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0); - void Unwind(uptr max_depth, uptr pc, uptr bp, void *context, uptr stack_top, + void Unwind(u32 max_depth, uptr pc, uptr bp, void *context, uptr stack_top, uptr stack_bottom, bool request_fast_unwind); private: void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom, - uptr max_depth); - void SlowUnwindStack(uptr pc, uptr max_depth); + u32 max_depth); + void SlowUnwindStack(uptr pc, u32 max_depth); void SlowUnwindStackWithContext(uptr pc, void *context, - uptr max_depth); + u32 max_depth); void PopStackFrames(uptr count); uptr LocatePcInTrace(uptr pc); diff --git a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc index 0d90980e6a68..0f98c7d5af4c 100644 --- a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc @@ -44,7 +44,7 @@ void StackTrace::Print() const { Printf("\n"); } -void BufferedStackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, void *context, +void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context, uptr stack_top, uptr stack_bottom, bool request_fast_unwind) { top_frame_bp = (max_depth > 0) ? bp : 0; diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc index bb8ba6b81674..9317a78eef13 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc +++ b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc @@ -173,9 +173,9 @@ SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr, return data.first; } -bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) { - backtrace_syminfo((backtrace_state *)state_, info->address, - SymbolizeDataCallback, ErrorCallback, info); +bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { + backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeDataCallback, + ErrorCallback, info); return true; } @@ -192,7 +192,7 @@ SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr, return nullptr; } -bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) { +bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; } diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h index a335cb23788c..1ff005042a11 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h +++ b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h @@ -35,7 +35,7 @@ class LibbacktraceSymbolizer { SymbolizedStack *SymbolizeCode(uptr addr, const char *module_name, uptr module_offset); - bool SymbolizeData(DataInfo *info); + bool SymbolizeData(uptr addr, DataInfo *info); // May return NULL if demangling failed. static char *Demangle(const char *name, bool always_alloc = false); diff --git a/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc index d46c249e6cab..69ac18e8426f 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -596,7 +596,7 @@ class POSIXSymbolizer : public Symbolizer { // First, try to use libbacktrace symbolizer (if it's available). if (libbacktrace_symbolizer_ != 0) { mu_.CheckLocked(); - if (libbacktrace_symbolizer_->SymbolizeData(info)) + if (libbacktrace_symbolizer_->SymbolizeData(addr, info)) return true; } const char *str = SendCommand(true, module_name, module_offset); diff --git a/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc index a98e61771c02..7ab2efbd75bd 100644 --- a/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc @@ -96,7 +96,7 @@ uptr Unwind_GetIP(struct _Unwind_Context *ctx) { struct UnwindTraceArg { BufferedStackTrace *stack; - uptr max_depth; + u32 max_depth; }; _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { @@ -108,7 +108,7 @@ _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { return UNWIND_CONTINUE; } -void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { +void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) { CHECK_GE(max_depth, 2); size = 0; UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)}; @@ -128,7 +128,7 @@ void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { } void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, - uptr max_depth) { + u32 max_depth) { CHECK_GE(max_depth, 2); if (!unwind_backtrace_signal_arch) { SlowUnwindStack(pc, max_depth); diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc index 3e9014199651..edd4bd11b369 100644 --- a/lib/sanitizer_common/sanitizer_win.cc +++ b/lib/sanitizer_common/sanitizer_win.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "sanitizer_common.h" @@ -122,18 +123,34 @@ void *MmapNoReserveOrDie(uptr size, const char *mem_type) { } void *Mprotect(uptr fixed_addr, uptr size) { - return VirtualAlloc((LPVOID)fixed_addr, size, - MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); + void *res = VirtualAlloc((LPVOID)fixed_addr, size, + MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); + if (res == 0) + Report("WARNING: %s failed to " + "mprotect %p (%zd) bytes at %p (error code: %d)\n", + SanitizerToolName, size, size, fixed_addr, GetLastError()); + return res; } void FlushUnneededShadowMemory(uptr addr, uptr size) { // This is almost useless on 32-bits. - // FIXME: add madvice-analog when we move to 64-bits. + // FIXME: add madvise-analog when we move to 64-bits. +} + +void NoHugePagesInRegion(uptr addr, uptr size) { + // FIXME: probably similar to FlushUnneededShadowMemory. +} + +void DontDumpShadowMemory(uptr addr, uptr length) { + // This is almost useless on 32-bits. + // FIXME: add madvise-analog when we move to 64-bits. } bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { - // FIXME: shall we do anything here on Windows? - return true; + MEMORY_BASIC_INFORMATION mbi; + CHECK(VirtualQuery((void *)range_start, &mbi, sizeof(mbi))); + return mbi.Protect == PAGE_NOACCESS && + (uptr)mbi.BaseAddress + mbi.RegionSize >= range_end; } void *MapFileToMemory(const char *file_name, uptr *buff_size) { @@ -187,8 +204,77 @@ u32 GetUid() { UNIMPLEMENTED(); } +namespace { +struct ModuleInfo { + HMODULE handle; + uptr base_address; + uptr end_address; +}; + +int CompareModulesBase(const void *pl, const void *pr) { + const ModuleInfo &l = *(ModuleInfo *)pl, &r = *(ModuleInfo *)pr; + if (l.base_address < r.base_address) + return -1; + return l.base_address > r.base_address; +} +} // namespace + void DumpProcessMap() { - UNIMPLEMENTED(); + Report("Dumping process modules:\n"); + HANDLE cur_process = GetCurrentProcess(); + + // Query the list of modules. Start by assuming there are no more than 256 + // modules and retry if that's not sufficient. + ModuleInfo *modules; + size_t num_modules; + { + HMODULE *hmodules = 0; + uptr modules_buffer_size = sizeof(HMODULE) * 256; + DWORD bytes_required; + while (!hmodules) { + hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__); + CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size, + &bytes_required)); + if (bytes_required > modules_buffer_size) { + // Either there turned out to be more than 256 hmodules, or new hmodules + // could have loaded since the last try. Retry. + UnmapOrDie(hmodules, modules_buffer_size); + hmodules = 0; + modules_buffer_size = bytes_required; + } + } + + num_modules = bytes_required / sizeof(HMODULE); + modules = + (ModuleInfo *)MmapOrDie(num_modules * sizeof(ModuleInfo), __FUNCTION__); + for (size_t i = 0; i < num_modules; ++i) { + modules[i].handle = hmodules[i]; + MODULEINFO mi; + if (!GetModuleInformation(cur_process, hmodules[i], &mi, sizeof(mi))) + continue; + modules[i].base_address = (uptr)mi.lpBaseOfDll; + modules[i].end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage; + } + UnmapOrDie(hmodules, modules_buffer_size); + } + + qsort(modules, num_modules, sizeof(ModuleInfo), CompareModulesBase); + + for (size_t i = 0; i < num_modules; ++i) { + const ModuleInfo &mi = modules[i]; + char module_name[MAX_PATH]; + bool got_module_name = GetModuleFileNameEx( + cur_process, mi.handle, module_name, sizeof(module_name)); + if (mi.end_address != 0) { + Printf("\t%p-%p %s\n", mi.base_address, mi.end_address, + got_module_name ? module_name : "[no name]"); + } else if (got_module_name) { + Printf("\t??\?-??? %s\n", module_name); + } else { + Printf("\t???\n"); + } + } + UnmapOrDie(modules, num_modules * sizeof(ModuleInfo)); } void DisableCoreDumperIfNecessary() { @@ -238,8 +324,9 @@ u64 NanoTime() { } void Abort() { - abort(); - internal__exit(-1); // abort is not NORETURN on Windows. + if (::IsDebuggerPresent()) + __debugbreak(); + internal__exit(3); } uptr GetListOfModules(LoadedModule *modules, uptr max_modules, @@ -379,6 +466,9 @@ uptr GetRSS() { return 0; } +void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; } +void internal_join_thread(void *th) { } + // ---------------------- BlockingMutex ---------------- {{{1 const uptr LOCK_UNINITIALIZED = 0; const uptr LOCK_READY = (uptr)-1; @@ -448,7 +538,7 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, } #if !SANITIZER_GO -void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { +void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) { CHECK_GE(max_depth, 2); // FIXME: CaptureStackBackTrace might be too slow for us. // FIXME: Compare with StackWalk64. @@ -464,7 +554,7 @@ void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { } void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, - uptr max_depth) { + u32 max_depth) { CONTEXT ctx = *(CONTEXT *)context; STACKFRAME64 stack_frame; memset(&stack_frame, 0, sizeof(stack_frame)); diff --git a/lib/sanitizer_common/scripts/check_lint.sh b/lib/sanitizer_common/scripts/check_lint.sh index 267273d97794..7ed05d73563a 100755 --- a/lib/sanitizer_common/scripts/check_lint.sh +++ b/lib/sanitizer_common/scripts/check_lint.sh @@ -32,7 +32,14 @@ LSAN_LIT_TEST_LINT_FILTER=${LSAN_RTL_LINT_FILTER},-whitespace/line_length DFSAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int,-runtime/printf,-runtime/references,-readability/function COMMON_RTL_INC_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int,-runtime/sizeof,-runtime/printf,-readability/fn_size SANITIZER_INCLUDES_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int -MKTEMP="mktemp -q /tmp/tmp.XXXXXXXXXX" + +MKTEMP_DIR=$(mktemp -qd /tmp/check_lint.XXXXXXXXXX) +MKTEMP="mktemp -q ${MKTEMP_DIR}/tmp.XXXXXXXXXX" +cleanup() { + rm -rf $MKTEMP_DIR +} +trap cleanup EXIT + cd ${LLVM_CHECKOUT} EXITSTATUS=0 diff --git a/lib/sanitizer_common/scripts/sancov.py b/lib/sanitizer_common/scripts/sancov.py index 476953015280..566116eb2334 100755 --- a/lib/sanitizer_common/scripts/sancov.py +++ b/lib/sanitizer_common/scripts/sancov.py @@ -118,7 +118,7 @@ def UnpackOneRawFile(path, map_path): if len(pc_list) == 0: continue assert path.endswith('.sancov.raw') dst_path = module_path + '.' + os.path.basename(path)[:-4] - print "writing %d PCs to %s" % (len(pc_list), dst_path) + print >> sys.stderr, "%s: writing %d PCs to %s" % (prog_name, len(pc_list), dst_path) arr = array.array('I') arr.fromlist(sorted(pc_list)) with open(dst_path, 'ab') as f2: diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt index bb7a399b0ec6..75008db3b6c9 100644 --- a/lib/sanitizer_common/tests/CMakeLists.txt +++ b/lib/sanitizer_common/tests/CMakeLists.txt @@ -2,6 +2,9 @@ include(CompilerRTCompile) clang_compiler_add_cxx_check() +# FIXME: use SANITIZER_COMMON_SUPPORTED_ARCH here +filter_available_targets(SANITIZER_UNITTEST_SUPPORTED_ARCH x86_64 i386 mips64 mips64el) + set(SANITIZER_UNITTESTS sanitizer_allocator_test.cc sanitizer_atomic_test.cc @@ -157,24 +160,18 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS AND NOT ANDROID) $) else() if(CAN_TARGET_x86_64) - add_sanitizer_common_lib("RTSanitizerCommon.test.x86_64" - $ - $) add_sanitizer_common_lib("RTSanitizerCommon.test.nolibc.x86_64" $) endif() - if(CAN_TARGET_i386) - add_sanitizer_common_lib("RTSanitizerCommon.test.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) + foreach(arch ${SANITIZER_UNITTEST_SUPPORTED_ARCH}) + add_sanitizer_common_lib("RTSanitizerCommon.test.${arch}" + $ + $) + endforeach() endif() + foreach(arch ${SANITIZER_UNITTEST_SUPPORTED_ARCH}) + add_sanitizer_tests_for_arch(${arch}) + endforeach() endif() if(ANDROID) diff --git a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc index f61d58dea7d9..be8fc91aa861 100644 --- a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc @@ -14,7 +14,6 @@ #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_test_utils.h" #include "sanitizer_pthread_wrappers.h" @@ -27,9 +26,9 @@ #include // Too slow for debug build -#if TSAN_DEBUG == 0 +#if !SANITIZER_DEBUG -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 static const uptr kAllocatorSpace = 0x700000000000ULL; static const uptr kAllocatorSize = 0x010000000000ULL; // 1T. static const u64 kAddressSpaceSize = 1ULL << 47; @@ -39,6 +38,8 @@ typedef SizeClassAllocator64< typedef SizeClassAllocator64< kAllocatorSpace, kAllocatorSize, 16, CompactSizeClassMap> Allocator64Compact; +#elif defined(__mips64) +static const u64 kAddressSpaceSize = 1ULL << 40; #else static const u64 kAddressSpaceSize = 1ULL << 32; #endif @@ -140,7 +141,7 @@ void TestSizeClassAllocator() { delete a; } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64) { TestSizeClassAllocator(); } @@ -184,7 +185,7 @@ void SizeClassAllocatorMetadataStress() { delete a; } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) { SizeClassAllocatorMetadataStress(); } @@ -192,7 +193,7 @@ TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) { TEST(SanitizerCommon, SizeClassAllocator64CompactMetadataStress) { SizeClassAllocatorMetadataStress(); } -#endif // SANITIZER_WORDSIZE == 64 +#endif // SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator32CompactMetadataStress) { SizeClassAllocatorMetadataStress(); } @@ -221,7 +222,7 @@ void SizeClassAllocatorGetBlockBeginStress() { delete a; } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64GetBlockBegin) { SizeClassAllocatorGetBlockBeginStress(); } @@ -231,7 +232,7 @@ TEST(SanitizerCommon, SizeClassAllocator64CompactGetBlockBegin) { TEST(SanitizerCommon, SizeClassAllocator32CompactGetBlockBegin) { SizeClassAllocatorGetBlockBeginStress(); } -#endif // SANITIZER_WORDSIZE == 64 +#endif // SANITIZER_CAN_USE_ALLOCATOR64 struct TestMapUnmapCallback { static int map_count, unmap_count; @@ -241,7 +242,7 @@ struct TestMapUnmapCallback { int TestMapUnmapCallback::map_count; int TestMapUnmapCallback::unmap_count; -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64MapUnmapCallback) { TestMapUnmapCallback::map_count = 0; TestMapUnmapCallback::unmap_count = 0; @@ -297,7 +298,7 @@ TEST(SanitizerCommon, LargeMmapAllocatorMapUnmapCallback) { TestMapUnmapCallback::map_count = 0; TestMapUnmapCallback::unmap_count = 0; LargeMmapAllocator a; - a.Init(); + a.Init(/* may_return_null */ false); AllocatorStats stats; stats.Init(); void *x = a.Allocate(&stats, 1 << 20, 1); @@ -322,7 +323,7 @@ void FailInAssertionOnOOM() { a.TestOnlyUnmap(); } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64Overflow) { EXPECT_DEATH(FailInAssertionOnOOM(), "Out of memory"); } @@ -331,7 +332,7 @@ TEST(SanitizerCommon, SizeClassAllocator64Overflow) { #if !defined(_WIN32) // FIXME: This currently fails on Windows. TEST(SanitizerCommon, LargeMmapAllocator) { LargeMmapAllocator<> a; - a.Init(); + a.Init(/* may_return_null */ false); AllocatorStats stats; stats.Init(); @@ -413,25 +414,22 @@ void TestCombinedAllocator() { CombinedAllocator Allocator; Allocator *a = new Allocator; - a->Init(); + a->Init(/* may_return_null */ true); AllocatorCache cache; memset(&cache, 0, sizeof(cache)); a->InitCache(&cache); - bool allocator_may_return_null = common_flags()->allocator_may_return_null; - common_flags()->allocator_may_return_null = true; 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); - common_flags()->allocator_may_return_null = false; + // Set to false + a->SetMayReturnNull(false); EXPECT_DEATH(a->Allocate(&cache, -1, 1), "allocator is terminating the process"); - // Restore the original value. - common_flags()->allocator_may_return_null = allocator_may_return_null; const uptr kNumAllocs = 100000; const uptr kNumIter = 10; @@ -465,7 +463,7 @@ void TestCombinedAllocator() { a->TestOnlyUnmap(); } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, CombinedAllocator64) { TestCombinedAllocator, @@ -521,7 +519,7 @@ void TestSizeClassAllocatorLocalCache() { delete a; } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64LocalCache) { TestSizeClassAllocatorLocalCache< SizeClassAllocatorLocalCache >(); @@ -538,7 +536,7 @@ TEST(SanitizerCommon, SizeClassAllocator32CompactLocalCache) { SizeClassAllocatorLocalCache >(); } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 typedef SizeClassAllocatorLocalCache AllocatorCache; static AllocatorCache static_allocator_cache; @@ -694,7 +692,7 @@ void TestSizeClassAllocatorIteration() { delete a; } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator64Iteration) { TestSizeClassAllocatorIteration(); } @@ -706,7 +704,7 @@ TEST(SanitizerCommon, SizeClassAllocator32Iteration) { TEST(SanitizerCommon, LargeMmapAllocatorIteration) { LargeMmapAllocator<> a; - a.Init(); + a.Init(/* may_return_null */ false); AllocatorStats stats; stats.Init(); @@ -733,7 +731,7 @@ TEST(SanitizerCommon, LargeMmapAllocatorIteration) { TEST(SanitizerCommon, LargeMmapAllocatorBlockBegin) { LargeMmapAllocator<> a; - a.Init(); + a.Init(/* may_return_null */ false); AllocatorStats stats; stats.Init(); @@ -769,7 +767,7 @@ TEST(SanitizerCommon, LargeMmapAllocatorBlockBegin) { } -#if SANITIZER_WORDSIZE == 64 +#if SANITIZER_CAN_USE_ALLOCATOR64 // Regression test for out-of-memory condition in PopulateFreeList(). TEST(SanitizerCommon, SizeClassAllocator64PopulateFreeListOOM) { // In a world where regions are small and chunks are huge... @@ -859,4 +857,4 @@ TEST(SanitizerCommon, ThreadedTwoLevelByteMap) { EXPECT_EQ((uptr)TestMapUnmapCallback::unmap_count, m.size1()); } -#endif // #if TSAN_DEBUG==0 +#endif // #if !SANITIZER_DEBUG diff --git a/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc b/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc index 8c8363353507..7835eef76d06 100644 --- a/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_deadlock_detector_test.cc @@ -268,7 +268,7 @@ void RunMultipleEpochsTest() { } EXPECT_EQ(d.testOnlyGetEpoch(), 4 * d.size()); -#if TSAN_DEBUG == 0 +#if !SANITIZER_DEBUG // EXPECT_DEATH clones a thread with 4K stack, // which is overflown by tsan memory accesses functions in debug mode. diff --git a/lib/sanitizer_common/tests/sanitizer_flags_test.cc b/lib/sanitizer_common/tests/sanitizer_flags_test.cc index 1055f5d24d6b..3e5d8381ed3a 100644 --- a/lib/sanitizer_common/tests/sanitizer_flags_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_flags_test.cc @@ -12,7 +12,9 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" #include "gtest/gtest.h" #include @@ -20,58 +22,79 @@ namespace __sanitizer { static const char kFlagName[] = "flag_name"; +static const char kFlagDesc[] = "flag description"; template static void TestFlag(T start_value, const char *env, T final_value) { T flag = start_value; - ParseFlag(env, &flag, kFlagName, "flag description"); + + FlagParser parser; + RegisterFlag(&parser, kFlagName, kFlagDesc, &flag); + + parser.ParseString(env); + EXPECT_EQ(final_value, flag); } -static void TestStrFlag(const char *start_value, const char *env, - const char *final_value) { +template <> +void TestFlag(const char *start_value, const char *env, + const char *final_value) { const char *flag = start_value; - ParseFlag(env, &flag, kFlagName, "flag description"); + + FlagParser parser; + RegisterFlag(&parser, kFlagName, kFlagDesc, &flag); + + parser.ParseString(env); + EXPECT_EQ(0, internal_strcmp(final_value, flag)); } TEST(SanitizerCommon, BooleanFlags) { - TestFlag(true, "--flag_name", true); - TestFlag(false, "flag_name", false); - TestFlag(false, "--flag_name=1", true); - TestFlag(true, "asdas flag_name=0 asdas", false); - TestFlag(true, " --flag_name=0 ", false); + TestFlag(false, "flag_name=1", true); TestFlag(false, "flag_name=yes", true); TestFlag(false, "flag_name=true", true); + TestFlag(true, "flag_name=0", false); TestFlag(true, "flag_name=no", false); TestFlag(true, "flag_name=false", false); } TEST(SanitizerCommon, IntFlags) { TestFlag(-11, 0, -11); - TestFlag(-11, "flag_name", -11); - TestFlag(-11, "--flag_name=", 0); - TestFlag(-11, "--flag_name=42", 42); - TestFlag(-11, "--flag_name=-42", -42); + TestFlag(-11, "flag_name=0", 0); + TestFlag(-11, "flag_name=42", 42); + TestFlag(-11, "flag_name=-42", -42); + + // Unrecognized flags are ignored. + TestFlag(-11, "--flag_name=42", -11); + TestFlag(-11, "zzzzzzz=42", -11); + + EXPECT_DEATH(TestFlag(-11, "flag_name", 0), "expected '='"); + EXPECT_DEATH(TestFlag(-11, "flag_name=42U", 0), + "Invalid value for int option"); } TEST(SanitizerCommon, StrFlags) { - TestStrFlag("zzz", 0, "zzz"); - TestStrFlag("zzz", "flag_name", "zzz"); - 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"); - TestStrFlag("", "other_flag_name=zzz", ""); + TestFlag("zzz", 0, "zzz"); + TestFlag("zzz", "flag_name=", ""); + TestFlag("zzz", "flag_name=abc", "abc"); + TestFlag("", "flag_name=abc", "abc"); + TestFlag("", "flag_name='abc zxc'", "abc zxc"); + // TestStrFlag("", "flag_name=\"abc qwe\" asd", "abc qwe"); } static void TestTwoFlags(const char *env, bool expected_flag1, - const char *expected_flag2) { + const char *expected_flag2, + const char *name1 = "flag1", + const char *name2 = "flag2") { bool flag1 = !expected_flag1; const char *flag2 = ""; - ParseFlag(env, &flag1, "flag1", "flag1 description"); - ParseFlag(env, &flag2, "flag2", "flag2 description"); + + FlagParser parser; + RegisterFlag(&parser, name1, kFlagDesc, &flag1); + RegisterFlag(&parser, name2, kFlagDesc, &flag2); + + parser.ParseString(env); + EXPECT_EQ(expected_flag1, flag1); EXPECT_EQ(0, internal_strcmp(flag2, expected_flag2)); } @@ -81,6 +104,39 @@ TEST(SanitizerCommon, MultipleFlags) { TestTwoFlags("flag2='qxx' flag1=0", false, "qxx"); TestTwoFlags("flag1=false:flag2='zzz'", false, "zzz"); TestTwoFlags("flag2=qxx:flag1=yes", true, "qxx"); + TestTwoFlags("flag2=qxx\nflag1=yes", true, "qxx"); + TestTwoFlags("flag2=qxx\r\nflag1=yes", true, "qxx"); + TestTwoFlags("flag2=qxx\tflag1=yes", true, "qxx"); +} + +TEST(SanitizerCommon, CommonSuffixFlags) { + TestTwoFlags("flag=1 other_flag='zzz'", true, "zzz", "flag", "other_flag"); + TestTwoFlags("other_flag='zzz' flag=1", true, "zzz", "flag", "other_flag"); + TestTwoFlags("other_flag=' flag=0 ' flag=1", true, " flag=0 ", "flag", + "other_flag"); + TestTwoFlags("flag=1 other_flag=' flag=0 '", true, " flag=0 ", "flag", + "other_flag"); +} + +TEST(SanitizerCommon, CommonFlags) { + CommonFlags cf; + FlagParser parser; + RegisterCommonFlags(&parser, &cf); + + cf.SetDefaults(); + EXPECT_TRUE(cf.symbolize); + EXPECT_STREQ(".", cf.coverage_dir); + + cf.symbolize = false; + cf.coverage = true; + cf.coverage_direct = true; + cf.log_path = "path/one"; + + parser.ParseString("symbolize=1:coverage_direct=false log_path='path/two'"); + EXPECT_TRUE(cf.symbolize); + EXPECT_TRUE(cf.coverage); + EXPECT_FALSE(cf.coverage_direct); + EXPECT_STREQ("path/two", cf.log_path); } } // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_libc_test.cc b/lib/sanitizer_common/tests/sanitizer_libc_test.cc index 660710d5bb7e..8712d2c1b2a5 100644 --- a/lib/sanitizer_common/tests/sanitizer_libc_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_libc_test.cc @@ -55,6 +55,19 @@ struct stat_and_more { unsigned char z; }; +static void temp_file_name(char *buf, size_t bufsize, const char *prefix) { + const char *tmpdir = "/tmp"; +#if SANITIZER_ANDROID + // I don't know a way to query temp directory location on Android without + // going through Java interfaces. The code below is not ideal, but should + // work. May require "adb root", but it is needed for almost any use of ASan + // on Android already. + tmpdir = GetEnv("EXTERNAL_STORAGE"); +#endif + u32 uid = GetUid(); + internal_snprintf(buf, bufsize, "%s/%s%d", tmpdir, prefix, uid); +} + // FIXME: File manipulations are not yet supported on Windows #if !defined(_WIN32) TEST(SanitizerCommon, FileOps) { @@ -63,28 +76,16 @@ TEST(SanitizerCommon, FileOps) { const char *str2 = "zxcv"; uptr len2 = internal_strlen(str2); - u32 uid = GetUid(); - char temp_filename[128]; -#if SANITIZER_ANDROID - // I don't know a way to query temp directory location on Android without - // going through Java interfaces. The code below is not ideal, but should - // work. May require "adb root", but it is needed for almost any use of ASan - // on Android already. - internal_snprintf(temp_filename, sizeof(temp_filename), - "%s/sanitizer_common.tmp.%d", - GetEnv("EXTERNAL_STORAGE"), uid); -#else - internal_snprintf(temp_filename, sizeof(temp_filename), - "/tmp/sanitizer_common.tmp.%d", uid); -#endif - uptr openrv = OpenFile(temp_filename, true); + char tmpfile[128]; + temp_file_name(tmpfile, sizeof(tmpfile), "sanitizer_common.fileops.tmp."); + uptr openrv = OpenFile(tmpfile, true); EXPECT_FALSE(internal_iserror(openrv)); fd_t fd = openrv; EXPECT_EQ(len1, internal_write(fd, str1, len1)); EXPECT_EQ(len2, internal_write(fd, str2, len2)); internal_close(fd); - openrv = OpenFile(temp_filename, false); + openrv = OpenFile(tmpfile, false); EXPECT_FALSE(internal_iserror(openrv)); fd = openrv; uptr fsize = internal_filesize(fd); @@ -92,8 +93,8 @@ TEST(SanitizerCommon, FileOps) { #if SANITIZER_TEST_HAS_STAT_H struct stat st1, st2, st3; - EXPECT_EQ(0u, internal_stat(temp_filename, &st1)); - EXPECT_EQ(0u, internal_lstat(temp_filename, &st2)); + EXPECT_EQ(0u, internal_stat(tmpfile, &st1)); + EXPECT_EQ(0u, internal_lstat(tmpfile, &st2)); EXPECT_EQ(0u, internal_fstat(fd, &st3)); EXPECT_EQ(fsize, (uptr)st3.st_size); @@ -115,6 +116,7 @@ TEST(SanitizerCommon, FileOps) { EXPECT_EQ(len2, internal_read(fd, buf, len2)); EXPECT_EQ(0, internal_memcmp(buf, str2, len2)); internal_close(fd); + internal_unlink(tmpfile); } #endif @@ -125,3 +127,35 @@ TEST(SanitizerCommon, InternalStrFunctions) { EXPECT_EQ(0, internal_strchr(haystack, 'z')); EXPECT_EQ(haystack + 8, internal_strchrnul(haystack, 'z')); } + +// FIXME: File manipulations are not yet supported on Windows +#if !defined(_WIN32) && !SANITIZER_MAC +TEST(SanitizerCommon, InternalMmapWithOffset) { + char tmpfile[128]; + temp_file_name(tmpfile, sizeof(tmpfile), + "sanitizer_common.internalmmapwithoffset.tmp."); + uptr res = OpenFile(tmpfile, true); + ASSERT_FALSE(internal_iserror(res)); + fd_t fd = res; + + uptr page_size = GetPageSizeCached(); + res = internal_ftruncate(fd, page_size * 2); + ASSERT_FALSE(internal_iserror(res)); + + res = internal_lseek(fd, page_size, SEEK_SET); + ASSERT_FALSE(internal_iserror(res)); + + res = internal_write(fd, "AB", 2); + ASSERT_FALSE(internal_iserror(res)); + + char *p = (char *)MapWritableFileToMemory(nullptr, page_size, fd, page_size); + ASSERT_NE(nullptr, p); + + ASSERT_EQ('A', p[0]); + ASSERT_EQ('B', p[1]); + + internal_close(fd); + internal_munmap(p, page_size); + internal_unlink(tmpfile); +} +#endif diff --git a/lib/sanitizer_common/tests/sanitizer_printf_test.cc b/lib/sanitizer_common/tests/sanitizer_printf_test.cc index d0b46ac94ff2..5e39e0a591d6 100644 --- a/lib/sanitizer_common/tests/sanitizer_printf_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_printf_test.cc @@ -28,14 +28,11 @@ TEST(Printf, Basic) { (unsigned)10, (unsigned long)11, // NOLINT (void*)0x123, "_string_"); EXPECT_EQ(len, strlen(buf)); - 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); - } + + std::string expectedString = "a-1b-2c4294967292e5fahbq0x"; + expectedString += std::string(SANITIZER_POINTER_FORMAT_LENGTH - 3, '0'); + expectedString += "123e_string_r"; + EXPECT_STREQ(expectedString.c_str(), buf); } TEST(Printf, OverflowStr) { diff --git a/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc b/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc index 495b726dcc45..abe4ef43093f 100644 --- a/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc @@ -48,6 +48,7 @@ TEST(MemoryMappingLayout, DumpListOfModules) { if (strstr(modules[i].full_name(), binary_name) != 0) found = true; } + modules[i].clear(); } EXPECT_TRUE(found); free(modules); diff --git a/lib/sanitizer_common/tests/sanitizer_test_utils.h b/lib/sanitizer_common/tests/sanitizer_test_utils.h index 64db37f341d3..9c162a66f547 100644 --- a/lib/sanitizer_common/tests/sanitizer_test_utils.h +++ b/lib/sanitizer_common/tests/sanitizer_test_utils.h @@ -118,4 +118,10 @@ static inline uint32_t my_rand() { # define SANITIZER_TEST_HAS_STRNLEN 0 #endif +#if defined(__FreeBSD__) +# define SANITIZER_TEST_HAS_PRINTF_L 1 +#else +# define SANITIZER_TEST_HAS_PRINTF_L 0 +#endif + #endif // SANITIZER_TEST_UTILS_H diff --git a/lib/tsan/CMakeLists.txt b/lib/tsan/CMakeLists.txt index deaabc2380fd..baf07a034e3b 100644 --- a/lib/tsan/CMakeLists.txt +++ b/lib/tsan/CMakeLists.txt @@ -9,8 +9,11 @@ append_list_if(COMPILER_RT_HAS_FPIE_FLAG -fPIE TSAN_CFLAGS) append_no_rtti_flag(TSAN_CFLAGS) set(TSAN_RTL_CFLAGS ${TSAN_CFLAGS}) -append_list_if(COMPILER_RT_HAS_WFRAME_LARGER_THAN_FLAG -Wframe-larger-than=512 TSAN_RTL_CFLAGS) -append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors TSAN_RTL_CFLAGS) +append_list_if(COMPILER_RT_HAS_MSSE3_FLAG -msse3 TSAN_RTL_CFLAGS) +append_list_if(SANITIZER_LIMIT_FRAME_SIZE -Wframe-larger-than=512 + TSAN_RTL_CFLAGS) +append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors + TSAN_RTL_CFLAGS) # FIXME: Add support for --sysroot=. compile flag: set(TSAN_SOURCES @@ -52,6 +55,7 @@ set(TSAN_HEADERS rtl/tsan_dense_alloc.h rtl/tsan_fd.h rtl/tsan_flags.h + rtl/tsan_flags.inc rtl/tsan_ignoreset.h rtl/tsan_interface_ann.h rtl/tsan_interface.h @@ -96,6 +100,15 @@ endif() add_dependencies(compiler-rt tsan) +# Sanity check for Go runtime. +set(BUILDGO_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/go/buildgo.sh) +add_custom_target(GotsanRuntimeCheck + COMMAND CC=${CMAKE_C_COMPILER} IN_TMPDIR=1 SILENT=1 ${BUILDGO_SCRIPT} + DEPENDS clang_rt.tsan-${arch} ${BUILDGO_SCRIPT} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/go + COMMENT "Checking TSan Go runtime..." + VERBATIM) + # Build libcxx instrumented with TSan. if(TSAN_SUPPORTED_ARCH AND COMPILER_RT_HAS_LIBCXX_SOURCES AND diff --git a/lib/tsan/Makefile.old b/lib/tsan/Makefile.old index b982e663a0cf..9e0693fa04c4 100644 --- a/lib/tsan/Makefile.old +++ b/lib/tsan/Makefile.old @@ -1,7 +1,7 @@ DEBUG=0 LDFLAGS=-ldl -lrt -lpthread -pie CXXFLAGS = -std=c++11 -fPIE -fno-rtti -g -Wall -Werror \ - -DGTEST_HAS_RTTI=0 -DTSAN_DEBUG=$(DEBUG) -DSANITIZER_DEBUG=$(DEBUG) + -DGTEST_HAS_RTTI=0 -DSANITIZER_DEBUG=$(DEBUG) CLANG=clang FILECHECK=FileCheck # Silence warnings that Clang produces for gtest code. diff --git a/lib/tsan/check_analyze.sh b/lib/tsan/check_analyze.sh index 08bfc7a76313..4b33393ef648 100755 --- a/lib/tsan/check_analyze.sh +++ b/lib/tsan/check_analyze.sh @@ -8,17 +8,6 @@ PrintRes() { PrintRes -wmops="write1 \ - write2 \ - write4 \ - write8" -rmops="read1 \ - read2 \ - read4 \ - read8" -func="func_entry \ - func_exit" - check() { res=$(PrintRes | egrep "$1 .* $2 $3; ") if [ "$res" == "" ]; then @@ -27,19 +16,25 @@ check() { fi } -for f in $wmops; do - check $f rsp 3 - check $f push 1 +for f in write1; do + check $f rsp 1 + check $f push 2 + check $f pop 2 +done + +for f in write2 write4 write8; do + check $f rsp 1 + check $f push 3 + check $f pop 3 +done + +for f in read1 read2 read4 read8; do + check $f rsp 1 + check $f push 5 check $f pop 5 done -for f in $rmops; do - check $f rsp 3 - check $f push 1 - check $f pop 4 -done - -for f in $func; do +for f in func_entry func_exit; do check $f rsp 0 check $f push 0 check $f pop 0 diff --git a/lib/tsan/dd/dd_rtl.cc b/lib/tsan/dd/dd_rtl.cc index 41b75bf755b8..2ba1ee324111 100644 --- a/lib/tsan/dd/dd_rtl.cc +++ b/lib/tsan/dd/dd_rtl.cc @@ -11,6 +11,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_stackdepot.h" @@ -70,14 +71,21 @@ void InitializeFlags(Flags *f, const char *env) { // Default values. f->second_deadlock_stack = false; - CommonFlags *cf = common_flags(); - SetCommonFlagsDefaults(cf); - // Override some common flags defaults. - cf->allow_addr2line = true; + SetCommonFlagsDefaults(); + { + // Override some common flags defaults. + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.allow_addr2line = true; + OverrideCommonFlags(cf); + } // Override from command line. - ParseFlag(env, &f->second_deadlock_stack, "second_deadlock_stack", ""); - ParseCommonFlagsFromString(cf, env); + FlagParser parser; + RegisterFlag(&parser, "second_deadlock_stack", "", &f->second_deadlock_stack); + RegisterCommonFlags(&parser); + parser.ParseString(env); + SetVerbosity(common_flags()->verbosity); } void Initialize() { diff --git a/lib/tsan/go/build.bat b/lib/tsan/go/build.bat index 8f8087fc6352..9b350a2d633e 100644 --- a/lib/tsan/go/build.bat +++ b/lib/tsan/go/build.bat @@ -1,4 +1,4 @@ type tsan_go.cc ..\rtl\tsan_interface_atomic.cc ..\rtl\tsan_clock.cc ..\rtl\tsan_flags.cc ..\rtl\tsan_md5.cc ..\rtl\tsan_mutex.cc ..\rtl\tsan_report.cc ..\rtl\tsan_rtl.cc ..\rtl\tsan_rtl_mutex.cc ..\rtl\tsan_rtl_report.cc ..\rtl\tsan_rtl_thread.cc ..\rtl\tsan_stat.cc ..\rtl\tsan_suppressions.cc ..\rtl\tsan_sync.cc ..\rtl\tsan_stack_trace.cc ..\..\sanitizer_common\sanitizer_allocator.cc ..\..\sanitizer_common\sanitizer_common.cc ..\..\sanitizer_common\sanitizer_flags.cc ..\..\sanitizer_common\sanitizer_stacktrace.cc ..\..\sanitizer_common\sanitizer_libc.cc ..\..\sanitizer_common\sanitizer_printf.cc ..\..\sanitizer_common\sanitizer_suppressions.cc ..\..\sanitizer_common\sanitizer_thread_registry.cc ..\rtl\tsan_platform_windows.cc ..\..\sanitizer_common\sanitizer_win.cc ..\..\sanitizer_common\sanitizer_deadlock_detector1.cc ..\..\sanitizer_common\sanitizer_stackdepot.cc ..\..\sanitizer_common\sanitizer_persistent_allocator.cc > gotsan.cc -gcc -c -o race_windows_amd64.syso gotsan.cc -I..\rtl -I..\.. -I..\..\sanitizer_common -I..\..\..\include -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4 -Wno-error=attributes -Wno-attributes -Wno-format -DTSAN_DEBUG=0 -O3 -fomit-frame-pointer +gcc -c -o race_windows_amd64.syso gotsan.cc -I..\rtl -I..\.. -I..\..\sanitizer_common -I..\..\..\include -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO -Wno-error=attributes -Wno-attributes -Wno-format -DSANITIZER_DEBUG=0 -O3 -fomit-frame-pointer diff --git a/lib/tsan/go/buildgo.sh b/lib/tsan/go/buildgo.sh index 4df2e96c5a8c..e00408cb10f0 100755 --- a/lib/tsan/go/buildgo.sh +++ b/lib/tsan/go/buildgo.sh @@ -1,3 +1,5 @@ +#!/bin/sh + set -e SRCS=" @@ -19,6 +21,7 @@ SRCS=" ../../sanitizer_common/sanitizer_allocator.cc ../../sanitizer_common/sanitizer_common.cc ../../sanitizer_common/sanitizer_deadlock_detector2.cc + ../../sanitizer_common/sanitizer_flag_parser.cc ../../sanitizer_common/sanitizer_flags.cc ../../sanitizer_common/sanitizer_libc.cc ../../sanitizer_common/sanitizer_persistent_allocator.cc @@ -34,33 +37,38 @@ if [ "`uname -a | grep Linux`" != "" ]; then SUFFIX="linux_amd64" OSCFLAGS="-fPIC -ffreestanding -Wno-maybe-uninitialized -Wno-unused-const-variable -Werror -Wno-unknown-warning-option" OSLDFLAGS="-lpthread -fPIC -fpie" - SRCS+=" + SRCS=" + $SRCS ../rtl/tsan_platform_linux.cc ../../sanitizer_common/sanitizer_posix.cc ../../sanitizer_common/sanitizer_posix_libcdep.cc ../../sanitizer_common/sanitizer_procmaps_common.cc ../../sanitizer_common/sanitizer_procmaps_linux.cc ../../sanitizer_common/sanitizer_linux.cc + ../../sanitizer_common/sanitizer_linux_libcdep.cc ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc " elif [ "`uname -a | grep FreeBSD`" != "" ]; then SUFFIX="freebsd_amd64" OSCFLAGS="-fno-strict-aliasing -fPIC -Werror" OSLDFLAGS="-lpthread -fPIC -fpie" - SRCS+=" + SRCS=" + $SRCS ../rtl/tsan_platform_linux.cc ../../sanitizer_common/sanitizer_posix.cc ../../sanitizer_common/sanitizer_posix_libcdep.cc ../../sanitizer_common/sanitizer_procmaps_common.cc ../../sanitizer_common/sanitizer_procmaps_freebsd.cc ../../sanitizer_common/sanitizer_linux.cc + ../../sanitizer_common/sanitizer_linux_libcdep.cc ../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc " elif [ "`uname -a | grep Darwin`" != "" ]; then SUFFIX="darwin_amd64" OSCFLAGS="-fPIC -Wno-unused-const-variable -Wno-unknown-warning-option" OSLDFLAGS="-lpthread -fPIC -fpie" - SRCS+=" + SRCS=" + $SRCS ../rtl/tsan_platform_mac.cc ../../sanitizer_common/sanitizer_mac.cc ../../sanitizer_common/sanitizer_posix.cc @@ -71,7 +79,8 @@ elif [ "`uname -a | grep MINGW`" != "" ]; then SUFFIX="windows_amd64" OSCFLAGS="-Wno-error=attributes -Wno-attributes -Wno-unused-const-variable -Wno-unknown-warning-option" OSLDFLAGS="" - SRCS+=" + SRCS=" + $SRCS ../rtl/tsan_platform_windows.cc ../../sanitizer_common/sanitizer_win.cc " @@ -80,24 +89,44 @@ else exit 1 fi -SRCS+=$ADD_SRCS +CC=${CC:-gcc} +IN_TMPDIR=${IN_TMPDIR:-0} +SILENT=${SILENT:-0} -rm -f gotsan.cc -for F in $SRCS; do - cat $F >> gotsan.cc -done - -FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -std=c++11 -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO -DTSAN_SHADOW_COUNT=4 -DSANITIZER_DEADLOCK_DETECTOR_VERSION=2 $OSCFLAGS" -if [ "$DEBUG" == "" ]; then - FLAGS+=" -DTSAN_DEBUG=0 -O3 -msse3 -fomit-frame-pointer" +if [ $IN_TMPDIR != "0" ]; then + DIR=$(mktemp -qd /tmp/gotsan.XXXXXXXXXX) + cleanup() { + rm -rf $DIR + } + trap cleanup EXIT else - FLAGS+=" -DTSAN_DEBUG=1 -g" + DIR=. fi -CC=${CC:-gcc} +SRCS="$SRCS $ADD_SRCS" -echo $CC gotsan.cc -c -o race_$SUFFIX.syso $FLAGS $CFLAGS -$CC gotsan.cc -c -o race_$SUFFIX.syso $FLAGS $CFLAGS +rm -f $DIR/gotsan.cc +for F in $SRCS; do + cat $F >> $DIR/gotsan.cc +done -$CC test.c race_$SUFFIX.syso -m64 -o test $OSLDFLAGS -GORACE="exitcode=0 atexit_sleep_ms=0" ./test +FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -std=c++11 -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO -DSANITIZER_DEADLOCK_DETECTOR_VERSION=2 $OSCFLAGS" +if [ "$DEBUG" = "" ]; then + FLAGS="$FLAGS -DSANITIZER_DEBUG=0 -O3 -msse3 -fomit-frame-pointer" +else + FLAGS="$FLAGS -DSANITIZER_DEBUG=1 -g" +fi + +if [ "$SILENT" != "1" ]; then + echo $CC gotsan.cc -c -o $DIR/race_$SUFFIX.syso $FLAGS $CFLAGS +fi +$CC $DIR/gotsan.cc -c -o $DIR/race_$SUFFIX.syso $FLAGS $CFLAGS + +$CC test.c $DIR/race_$SUFFIX.syso -m64 -o $DIR/test $OSLDFLAGS + +export GORACE="exitcode=0 atexit_sleep_ms=0" +if [ "$SILENT" != "1" ]; then + $DIR/test +else + $DIR/test 2>/dev/null +fi diff --git a/lib/tsan/go/tsan_go.cc b/lib/tsan/go/tsan_go.cc index cccf72cedd27..ea0beb74215b 100644 --- a/lib/tsan/go/tsan_go.cc +++ b/lib/tsan/go/tsan_go.cc @@ -28,13 +28,6 @@ bool IsExpectedReport(uptr addr, uptr size) { return false; } -void *internal_start_thread(void(*func)(void*), void *arg) { - return 0; -} - -void internal_join_thread(void *th) { -} - ReportLocation *SymbolizeData(uptr addr) { return 0; } diff --git a/lib/tsan/rtl/Makefile.old b/lib/tsan/rtl/Makefile.old index 79c761ce3f4e..150b376343be 100644 --- a/lib/tsan/rtl/Makefile.old +++ b/lib/tsan/rtl/Makefile.old @@ -1,4 +1,4 @@ -CXXFLAGS = -std=c++11 -fPIE -g -Wall -Werror -fno-builtin -msse3 -DTSAN_DEBUG=$(DEBUG) -DSANITIZER_DEBUG=$(DEBUG) +CXXFLAGS = -std=c++11 -fPIE -g -Wall -Werror -fno-builtin -msse3 -DSANITIZER_DEBUG=$(DEBUG) CLANG=clang ifeq ($(DEBUG), 0) CXXFLAGS += -O3 diff --git a/lib/tsan/rtl/tsan_clock.cc b/lib/tsan/rtl/tsan_clock.cc index f2b39a182b39..59e3de435f1b 100644 --- a/lib/tsan/rtl/tsan_clock.cc +++ b/lib/tsan/rtl/tsan_clock.cc @@ -104,8 +104,8 @@ ThreadClock::ThreadClock(unsigned tid, unsigned reused) } void ThreadClock::acquire(ClockCache *c, const SyncClock *src) { - DCHECK(nclk_ <= kMaxTid); - DCHECK(src->size_ <= kMaxTid); + DCHECK_LE(nclk_, kMaxTid); + DCHECK_LE(src->size_, kMaxTid); CPP_STAT_INC(StatClockAcquire); // Check if it's empty -> no need to do anything. @@ -215,8 +215,8 @@ void ThreadClock::release(ClockCache *c, SyncClock *dst) const { } void ThreadClock::ReleaseStore(ClockCache *c, SyncClock *dst) const { - DCHECK(nclk_ <= kMaxTid); - DCHECK(dst->size_ <= kMaxTid); + DCHECK_LE(nclk_, kMaxTid); + DCHECK_LE(dst->size_, kMaxTid); CPP_STAT_INC(StatClockStore); // Check if we need to resize dst. diff --git a/lib/tsan/rtl/tsan_defs.h b/lib/tsan/rtl/tsan_defs.h index 7ed3796b5012..f19aee999332 100644 --- a/lib/tsan/rtl/tsan_defs.h +++ b/lib/tsan/rtl/tsan_defs.h @@ -18,10 +18,6 @@ #include "sanitizer_common/sanitizer_libc.h" #include "tsan_stat.h" -#ifndef TSAN_DEBUG -#define TSAN_DEBUG 0 -#endif // TSAN_DEBUG - namespace __tsan { #ifdef SANITIZER_GO @@ -44,18 +40,8 @@ const int kClkBits = 42; const unsigned kMaxTidReuse = (1 << (64 - kClkBits)) - 1; const uptr kShadowStackSize = 64 * 1024; -#ifdef TSAN_SHADOW_COUNT -# if TSAN_SHADOW_COUNT == 2 \ - || TSAN_SHADOW_COUNT == 4 || TSAN_SHADOW_COUNT == 8 -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. -#define TSAN_SHADOW_COUNT 4 const uptr kShadowCnt = 4; -#endif // That many user bytes are mapped onto a single shadow cell. const uptr kShadowCell = 8; @@ -88,7 +74,7 @@ const bool kCollectStats = false; // The following "build consistency" machinery ensures that all source files // are built in the same configuration. Inconsistent builds lead to // hard to debug crashes. -#if TSAN_DEBUG +#if SANITIZER_DEBUG void build_consistency_debug(); #else void build_consistency_release(); @@ -100,18 +86,8 @@ void build_consistency_stats(); void build_consistency_nostats(); #endif -#if TSAN_SHADOW_COUNT == 1 -void build_consistency_shadow1(); -#elif TSAN_SHADOW_COUNT == 2 -void build_consistency_shadow2(); -#elif TSAN_SHADOW_COUNT == 4 -void build_consistency_shadow4(); -#else -void build_consistency_shadow8(); -#endif - static inline void USED build_consistency() { -#if TSAN_DEBUG +#if SANITIZER_DEBUG build_consistency_debug(); #else build_consistency_release(); @@ -121,15 +97,6 @@ static inline void USED build_consistency() { #else build_consistency_nostats(); #endif -#if TSAN_SHADOW_COUNT == 1 - build_consistency_shadow1(); -#elif TSAN_SHADOW_COUNT == 2 - build_consistency_shadow2(); -#elif TSAN_SHADOW_COUNT == 4 - build_consistency_shadow4(); -#else - build_consistency_shadow8(); -#endif } template diff --git a/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc index 5dc331f59469..fed3de8db2ed 100644 --- a/lib/tsan/rtl/tsan_flags.cc +++ b/lib/tsan/rtl/tsan_flags.cc @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" #include "sanitizer_common/sanitizer_libc.h" #include "tsan_flags.h" #include "tsan_rtl.h" @@ -33,80 +34,44 @@ const char *WEAK __tsan_default_options() { } #endif -static void ParseFlags(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->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_mutex_bugs, "report_mutex_bugs", ""); - ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe", ""); - ParseFlag(env, &f->report_atomic_races, "report_atomic_races", ""); - ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics", ""); - ParseFlag(env, &f->print_benign, "print_benign", ""); - ParseFlag(env, &f->exitcode, "exitcode", ""); - ParseFlag(env, &f->halt_on_error, "halt_on_error", ""); - ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms", ""); - ParseFlag(env, &f->profile_memory, "profile_memory", ""); - ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms", ""); - ParseFlag(env, &f->flush_symbolizer_ms, "flush_symbolizer_ms", ""); - ParseFlag(env, &f->memory_limit_mb, "memory_limit_mb", ""); - ParseFlag(env, &f->stop_on_start, "stop_on_start", ""); - ParseFlag(env, &f->running_on_valgrind, "running_on_valgrind", ""); - ParseFlag(env, &f->history_size, "history_size", ""); - ParseFlag(env, &f->io_sync, "io_sync", ""); - ParseFlag(env, &f->die_after_fork, "die_after_fork", ""); - +void Flags::SetDefaults() { +#define TSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "tsan_flags.inc" +#undef TSAN_FLAG // DDFlags - ParseFlag(env, &f->second_deadlock_stack, "second_deadlock_stack", ""); + second_deadlock_stack = false; +} + +void RegisterTsanFlags(FlagParser *parser, Flags *f) { +#define TSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "tsan_flags.inc" +#undef TSAN_FLAG } void InitializeFlags(Flags *f, const char *env) { - internal_memset(f, 0, sizeof(*f)); + FlagParser parser; + RegisterTsanFlags(&parser, f); + RegisterCommonFlags(&parser); - // Default values. - f->enable_annotations = true; - f->suppress_equal_stacks = true; - f->suppress_equal_addresses = true; - f->report_bugs = true; - f->report_thread_leaks = true; - f->report_destroy_locked = true; - f->report_mutex_bugs = true; - f->report_signal_unsafe = true; - f->report_atomic_races = true; - f->force_seq_cst_atomics = false; - f->print_benign = false; - f->exitcode = 66; - f->halt_on_error = false; - f->atexit_sleep_ms = 1000; - f->profile_memory = ""; - f->flush_memory_ms = 0; - f->flush_symbolizer_ms = 5000; - f->memory_limit_mb = 0; - f->stop_on_start = false; - f->running_on_valgrind = false; - f->history_size = kGoMode ? 1 : 2; // There are a lot of goroutines in Go. - f->io_sync = 1; - f->die_after_fork = true; + f->SetDefaults(); - // DDFlags - f->second_deadlock_stack = false; - - CommonFlags *cf = common_flags(); - SetCommonFlagsDefaults(cf); - // Override some common flags defaults. - cf->allow_addr2line = true; - cf->detect_deadlocks = true; - cf->print_suppressions = false; - cf->stack_trace_format = " #%n %f %S %M"; + SetCommonFlagsDefaults(); + { + // Override some common flags defaults. + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.allow_addr2line = true; + cf.detect_deadlocks = true; + cf.print_suppressions = false; + cf.stack_trace_format = " #%n %f %S %M"; + OverrideCommonFlags(cf); + } // Let a frontend override. - ParseFlags(f, __tsan_default_options()); - ParseCommonFlagsFromString(cf, __tsan_default_options()); + parser.ParseString(__tsan_default_options()); // Override from command line. - ParseFlags(f, env); - ParseCommonFlagsFromString(cf, env); + parser.ParseString(env); // Sanity check. if (!f->report_bugs) { @@ -115,7 +80,11 @@ void InitializeFlags(Flags *f, const char *env) { f->report_signal_unsafe = false; } - if (cf->help) PrintFlagDescriptions(); + SetVerbosity(common_flags()->verbosity); + + if (Verbosity()) ReportUnrecognizedFlags(); + + if (common_flags()->help) parser.PrintFlagDescriptions(); if (f->history_size < 0 || f->history_size > 7) { Printf("ThreadSanitizer: incorrect value for history_size" diff --git a/lib/tsan/rtl/tsan_flags.h b/lib/tsan/rtl/tsan_flags.h index 621ca139236f..e2f6b3c9f021 100644 --- a/lib/tsan/rtl/tsan_flags.h +++ b/lib/tsan/rtl/tsan_flags.h @@ -20,65 +20,12 @@ namespace __tsan { struct Flags : DDFlags { - // Enable dynamic annotations, otherwise they are no-ops. - bool enable_annotations; - // Suppress a race report if we've already output another race report - // with the same stack. - bool suppress_equal_stacks; - // Suppress a race report if we've already output another race report - // on the same address. - bool suppress_equal_addresses; - // 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 incorrect usages of mutexes and mutex annotations? - bool report_mutex_bugs; - // Report violations of async signal-safety - // (e.g. malloc() call from a signal handler). - bool report_signal_unsafe; - // Report races between atomic and plain memory accesses. - bool report_atomic_races; - // If set, all atomics are effectively sequentially consistent (seq_cst), - // regardless of what user actually specified. - bool force_seq_cst_atomics; - // Print matched "benign" races at exit. - bool print_benign; - // Override exit status if something was reported. - int exitcode; - // Exit after first reported error. - bool halt_on_error; - // Sleep in main thread before exiting for that many ms - // (useful to catch "at exit" races). - int atexit_sleep_ms; - // If set, periodically write memory profile to that file. - const char *profile_memory; - // Flush shadow memory every X ms. - int flush_memory_ms; - // Flush symbolizer caches every X ms. - int flush_symbolizer_ms; - // Resident memory limit in MB to aim at. - // If the process consumes more memory, then TSan will flush shadow memory. - int memory_limit_mb; - // Stops on start until __tsan_resume() is called (for debugging). - bool stop_on_start; - // Controls whether RunningOnValgrind() returns true or false. - bool running_on_valgrind; - // 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; - // Die after multi-threaded fork if the child creates new threads. - bool die_after_fork; +#define TSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "tsan_flags.inc" +#undef TSAN_FLAG + + void SetDefaults(); + void ParseFromString(const char *str); }; Flags *flags(); diff --git a/lib/tsan/rtl/tsan_flags.inc b/lib/tsan/rtl/tsan_flags.inc new file mode 100644 index 000000000000..2925f38482a2 --- /dev/null +++ b/lib/tsan/rtl/tsan_flags.inc @@ -0,0 +1,78 @@ +//===-- tsan_flags.inc ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// TSan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef TSAN_FLAG +# error "Define TSAN_FLAG prior to including this file!" +#endif + +// TSAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +TSAN_FLAG(bool, enable_annotations, true, + "Enable dynamic annotations, otherwise they are no-ops.") +// Suppress a race report if we've already output another race report +// with the same stack. +TSAN_FLAG(bool, suppress_equal_stacks, true, + "Suppress a race report if we've already output another race report " + "with the same stack.") +TSAN_FLAG(bool, suppress_equal_addresses, true, + "Suppress a race report if we've already output another race report " + "on the same address.") + +TSAN_FLAG(bool, report_bugs, true, + "Turns off bug reporting entirely (useful for benchmarking).") +TSAN_FLAG(bool, report_thread_leaks, true, "Report thread leaks at exit?") +TSAN_FLAG(bool, report_destroy_locked, true, + "Report destruction of a locked mutex?") +TSAN_FLAG(bool, report_mutex_bugs, true, + "Report incorrect usages of mutexes and mutex annotations?") +TSAN_FLAG(bool, report_signal_unsafe, true, + "Report violations of async signal-safety " + "(e.g. malloc() call from a signal handler).") +TSAN_FLAG(bool, report_atomic_races, true, + "Report races between atomic and plain memory accesses.") +TSAN_FLAG( + bool, force_seq_cst_atomics, false, + "If set, all atomics are effectively sequentially consistent (seq_cst), " + "regardless of what user actually specified.") +TSAN_FLAG(bool, print_benign, false, "Print matched \"benign\" races at exit.") +TSAN_FLAG(int, exitcode, 66, "Override exit status if something was reported.") +TSAN_FLAG(bool, halt_on_error, false, "Exit after first reported error.") +TSAN_FLAG(int, atexit_sleep_ms, 1000, + "Sleep in main thread before exiting for that many ms " + "(useful to catch \"at exit\" races).") +TSAN_FLAG(const char *, profile_memory, "", + "If set, periodically write memory profile to that file.") +TSAN_FLAG(int, flush_memory_ms, 0, "Flush shadow memory every X ms.") +TSAN_FLAG(int, flush_symbolizer_ms, 5000, "Flush symbolizer caches every X ms.") +TSAN_FLAG( + int, memory_limit_mb, 0, + "Resident memory limit in MB to aim at." + "If the process consumes more memory, then TSan will flush shadow memory.") +TSAN_FLAG(bool, stop_on_start, false, + "Stops on start until __tsan_resume() is called (for debugging).") +TSAN_FLAG(bool, running_on_valgrind, false, + "Controls whether RunningOnValgrind() returns true or false.") +TSAN_FLAG( + int, history_size, kGoMode ? 1 : 2, // There are a lot of goroutines in Go. + "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).") +TSAN_FLAG(int, io_sync, 1, + "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.") +TSAN_FLAG(bool, die_after_fork, true, + "Die after multi-threaded fork if the child creates new threads.") diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc index 5bede0ec7d0c..9a6401167bc1 100644 --- a/lib/tsan/rtl/tsan_interceptors.cc +++ b/lib/tsan/rtl/tsan_interceptors.cc @@ -72,6 +72,7 @@ extern "C" void *__libc_malloc(uptr size); extern "C" void *__libc_calloc(uptr size, uptr n); extern "C" void *__libc_realloc(void *ptr, uptr size); extern "C" void __libc_free(void *ptr); +extern "C" int dirfd(void *dirp); #if !SANITIZER_FREEBSD extern "C" int mallopt(int param, int value); #endif @@ -101,14 +102,15 @@ typedef long long_t; // NOLINT # 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()) +typedef void (*sighandler_t)(int sig); +typedef void (*sigactionhandler_t)(int sig, my_siginfo_t *siginfo, void *uctx); + struct sigaction_t { union { sighandler_t sa_handler; - void (*sa_sigaction)(int sig, my_siginfo_t *siginfo, void *uctx); + sigactionhandler_t sa_sigaction; }; #if SANITIZER_FREEBSD int sa_flags; @@ -505,14 +507,10 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { if (cur_thread()->in_symbolizer) return __libc_calloc(size, n); - if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) - return AllocatorReturnNull(); void *p = 0; { SCOPED_INTERCEPTOR_RAW(calloc, size, n); - p = user_alloc(thr, pc, n * size); - if (p) - internal_memset(p, 0, n * size); + p = user_calloc(thr, pc, size, n); } invoke_malloc_hook(p, n * size); return p; @@ -952,6 +950,8 @@ TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) { return res; } +DEFINE_REAL_PTHREAD_FUNCTIONS + TSAN_INTERCEPTOR(int, pthread_detach, void *th) { SCOPED_TSAN_INTERCEPTOR(pthread_detach, th); int tid = ThreadTid(thr, pc, (uptr)th); @@ -1826,12 +1826,11 @@ TSAN_INTERCEPTOR(int, rmdir, char *path) { return res; } -TSAN_INTERCEPTOR(void*, opendir, char *path) { - SCOPED_TSAN_INTERCEPTOR(opendir, path); - void *res = REAL(opendir)(path); - if (res != 0) - Acquire(thr, pc, Dir2addr(path)); - return res; +TSAN_INTERCEPTOR(int, closedir, void *dirp) { + SCOPED_TSAN_INTERCEPTOR(closedir, dirp); + int fd = dirfd(dirp); + FdClose(thr, pc, fd); + return REAL(closedir)(dirp); } #if !SANITIZER_FREEBSD @@ -1875,15 +1874,18 @@ static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire, // Ensure that the handler does not spoil errno. const int saved_errno = errno; errno = 99; - // Need to remember pc before the call, because the handler can reset it. - uptr pc = sigact ? + // This code races with sigaction. Be careful to not read sa_sigaction twice. + // Also need to remember pc for reporting before the call, + // because the handler can reset it. + volatile uptr pc = sigact ? (uptr)sigactions[sig].sa_sigaction : (uptr)sigactions[sig].sa_handler; - pc += 1; // return address is expected, OutputReport() will undo this - if (sigact) - sigactions[sig].sa_sigaction(sig, info, uctx); - else - sigactions[sig].sa_handler(sig); + if (pc != (uptr)SIG_DFL && pc != (uptr)SIG_IGN) { + if (sigact) + ((sigactionhandler_t)pc)(sig, info, uctx); + else + ((sighandler_t)pc)(sig); + } // We do not detect errno spoiling for SIGTERM, // because some SIGTERM handlers do spoil errno but reraise SIGTERM, // tsan reports false positive in such case. @@ -1893,7 +1895,9 @@ static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire, // signal; and it looks too fragile to intercept all ways to reraise a signal. if (flags()->report_bugs && !sync && sig != SIGTERM && errno != 99) { VarSizeStackTrace stack; - ObtainCurrentStack(thr, pc, &stack); + // Add 1 to pc because return address is expected, + // OutputReport() will undo this. + ObtainCurrentStack(thr, pc + 1, &stack); ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeErrnoInSignal); if (!IsFiredSuppression(ctx, rep, stack)) { @@ -1919,11 +1923,8 @@ void ProcessPendingSignals(ThreadState *thr) { SignalDesc *signal = &sctx->pending_signals[sig]; if (signal->armed) { signal->armed = false; - if (sigactions[sig].sa_handler != SIG_DFL - && sigactions[sig].sa_handler != SIG_IGN) { - CallUserSignalHandler(thr, false, true, signal->sigaction, - sig, &signal->siginfo, &signal->ctx); - } + CallUserSignalHandler(thr, false, true, signal->sigaction, sig, + &signal->siginfo, &signal->ctx); } } pthread_sigmask(SIG_SETMASK, &oldset, 0); @@ -2005,7 +2006,19 @@ TSAN_INTERCEPTOR(int, sigaction, int sig, sigaction_t *act, sigaction_t *old) { internal_memcpy(old, &sigactions[sig], sizeof(*old)); if (act == 0) return 0; - internal_memcpy(&sigactions[sig], act, sizeof(*act)); + // Copy act into sigactions[sig]. + // Can't use struct copy, because compiler can emit call to memcpy. + // Can't use internal_memcpy, because it copies byte-by-byte, + // and signal handler reads the sa_handler concurrently. It it can read + // some bytes from old value and some bytes from new value. + // Use volatile to prevent insertion of memcpy. + sigactions[sig].sa_handler = *(volatile sighandler_t*)&act->sa_handler; + sigactions[sig].sa_flags = *(volatile int*)&act->sa_flags; + internal_memcpy(&sigactions[sig].sa_mask, &act->sa_mask, + sizeof(sigactions[sig].sa_mask)); +#if !SANITIZER_FREEBSD + sigactions[sig].sa_restorer = act->sa_restorer; +#endif sigaction_t newact; internal_memcpy(&newact, act, sizeof(newact)); REAL(sigfillset)(&newact.sa_mask); @@ -2171,6 +2184,16 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc, #undef SANITIZER_INTERCEPT_FGETPWENT #undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS #undef SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS +// __tls_get_addr can be called with mis-aligned stack due to: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066 +// There are two potential issues: +// 1. Sanitizer code contains a MOVDQA spill (it does not seem to be the case +// right now). or 2. ProcessPendingSignal calls user handler which contains +// MOVDQA spill (this happens right now). +// Since the interceptor only initializes memory for msan, the simplest solution +// is to disable the interceptor in tsan (other sanitizers do not call +// signal handlers from COMMON_INTERCEPTOR_ENTER). +#undef SANITIZER_INTERCEPT_TLS_GET_ADDR #define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name) @@ -2209,12 +2232,15 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc, if (fd >= 0) FdClose(thr, pc, fd); \ } -#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res) \ +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \ libignore()->OnLibraryLoaded(filename) #define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() \ libignore()->OnLibraryUnloaded() +#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \ + Acquire(((TsanInterceptorContext *) ctx)->thr, pc, Dir2addr(path)) + #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ FdAcquire(((TsanInterceptorContext *) ctx)->thr, pc, fd) @@ -2530,7 +2556,7 @@ void InitializeInterceptors() { TSAN_INTERCEPT(abort); TSAN_INTERCEPT(puts); TSAN_INTERCEPT(rmdir); - TSAN_INTERCEPT(opendir); + TSAN_INTERCEPT(closedir); TSAN_MAYBE_INTERCEPT_EPOLL_CTL; TSAN_MAYBE_INTERCEPT_EPOLL_WAIT; @@ -2569,19 +2595,4 @@ void InitializeInterceptors() { FdInit(); } -void *internal_start_thread(void(*func)(void *arg), void *arg) { - // Start the thread with signals blocked, otherwise it can steal user signals. - __sanitizer_sigset_t set, old; - internal_sigfillset(&set); - internal_sigprocmask(SIG_SETMASK, &set, &old); - void *th; - REAL(pthread_create)(&th, 0, (void*(*)(void *arg))func, arg); - internal_sigprocmask(SIG_SETMASK, &old, 0); - return th; -} - -void internal_join_thread(void *th) { - REAL(pthread_join)(th, 0); -} - } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_interface.cc b/lib/tsan/rtl/tsan_interface.cc index 9de3808e79ff..9bc9a696363d 100644 --- a/lib/tsan/rtl/tsan_interface.cc +++ b/lib/tsan/rtl/tsan_interface.cc @@ -38,57 +38,80 @@ void __tsan_write16(void *addr) { MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); } -u16 __tsan_unaligned_read2(const uu16 *addr) { +// __tsan_unaligned_read/write calls are emitted by compiler. + +void __tsan_unaligned_read2(const void *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, false, false); - return *addr; } -u32 __tsan_unaligned_read4(const uu32 *addr) { +void __tsan_unaligned_read4(const void *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, false, false); - return *addr; } -u64 __tsan_unaligned_read8(const uu64 *addr) { +void __tsan_unaligned_read8(const void *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, false, false); - return *addr; } -void __tsan_unaligned_write2(uu16 *addr, u16 v) { +void __tsan_unaligned_read16(const void *addr) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 16, false, false); +} + +void __tsan_unaligned_write2(void *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, true, false); - *addr = v; } -void __tsan_unaligned_write4(uu32 *addr, u32 v) { +void __tsan_unaligned_write4(void *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, true, false); - *addr = v; } -void __tsan_unaligned_write8(uu64 *addr, u64 v) { +void __tsan_unaligned_write8(void *addr) { UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, true, false); - *addr = v; } +void __tsan_unaligned_write16(void *addr) { + UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 16, true, false); +} + +// __sanitizer_unaligned_load/store are for user instrumentation. + extern "C" { SANITIZER_INTERFACE_ATTRIBUTE -uint16_t __sanitizer_unaligned_load16(void *addr) - ALIAS("__tsan_unaligned_read2"); -SANITIZER_INTERFACE_ATTRIBUTE -uint32_t __sanitizer_unaligned_load32(void *addr) - ALIAS("__tsan_unaligned_read4"); -SANITIZER_INTERFACE_ATTRIBUTE -uint64_t __sanitizer_unaligned_load64(void *addr) - ALIAS("__tsan_unaligned_read8"); -SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_unaligned_store16(void *addr, uint16_t v) - ALIAS("__tsan_unaligned_write2"); -SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_unaligned_store32(void *addr, uint32_t v) - ALIAS("__tsan_unaligned_write4"); -SANITIZER_INTERFACE_ATTRIBUTE -void __sanitizer_unaligned_store64(void *addr, uint64_t v) - ALIAS("__tsan_unaligned_write8"); +u16 __sanitizer_unaligned_load16(const uu16 *addr) { + __tsan_unaligned_read2(addr); + return *addr; } +SANITIZER_INTERFACE_ATTRIBUTE +u32 __sanitizer_unaligned_load32(const uu32 *addr) { + __tsan_unaligned_read4(addr); + return *addr; +} + +SANITIZER_INTERFACE_ATTRIBUTE +u64 __sanitizer_unaligned_load64(const uu64 *addr) { + __tsan_unaligned_read8(addr); + return *addr; +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store16(uu16 *addr, u16 v) { + __tsan_unaligned_write2(addr); + *addr = v; +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store32(uu32 *addr, u32 v) { + __tsan_unaligned_write4(addr); + *addr = v; +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store64(uu64 *addr, u64 v) { + __tsan_unaligned_write8(addr); + *addr = v; +} +} // extern "C" + void __tsan_acquire(void *addr) { Acquire(cur_thread(), CALLERPC, (uptr)addr); } diff --git a/lib/tsan/rtl/tsan_interface.h b/lib/tsan/rtl/tsan_interface.h index 70450697d480..a05e6f0f6d09 100644 --- a/lib/tsan/rtl/tsan_interface.h +++ b/lib/tsan/rtl/tsan_interface.h @@ -41,12 +41,15 @@ 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 u16 __tsan_unaligned_read2(const uu16 *addr); -SANITIZER_INTERFACE_ATTRIBUTE u32 __tsan_unaligned_read4(const uu32 *addr); -SANITIZER_INTERFACE_ATTRIBUTE u64 __tsan_unaligned_read8(const uu64 *addr); -SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write2(uu16 *addr, u16 v); -SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write4(uu32 *addr, u32 v); -SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write8(uu64 *addr, u64 v); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read2(const void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read4(const void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read8(const void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_read16(const void *addr); + +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write2(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write4(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write8(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write16(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_read(void **vptr_p); SANITIZER_INTERFACE_ATTRIBUTE diff --git a/lib/tsan/rtl/tsan_interface_java.cc b/lib/tsan/rtl/tsan_interface_java.cc index 8615349f657f..0aea63d11671 100644 --- a/lib/tsan/rtl/tsan_interface_java.cc +++ b/lib/tsan/rtl/tsan_interface_java.cc @@ -219,3 +219,33 @@ int __tsan_java_mutex_unlock_rec(jptr addr) { return MutexUnlock(thr, pc, addr, true); } + +void __tsan_java_acquire(jptr addr) { + SCOPED_JAVA_FUNC(__tsan_java_acquire); + DPrintf("#%d: java_acquire(%p)\n", thr->tid, addr); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + + Acquire(thr, caller_pc, addr); +} + +void __tsan_java_release(jptr addr) { + SCOPED_JAVA_FUNC(__tsan_java_release); + DPrintf("#%d: java_release(%p)\n", thr->tid, addr); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + + Release(thr, caller_pc, addr); +} + +void __tsan_java_release_store(jptr addr) { + SCOPED_JAVA_FUNC(__tsan_java_release); + DPrintf("#%d: java_release_store(%p)\n", thr->tid, addr); + CHECK_NE(jctx, 0); + CHECK_GE(addr, jctx->heap_begin); + CHECK_LT(addr, jctx->heap_begin + jctx->heap_size); + + ReleaseStore(thr, caller_pc, addr); +} diff --git a/lib/tsan/rtl/tsan_interface_java.h b/lib/tsan/rtl/tsan_interface_java.h index 1f793df712de..30153a1d8505 100644 --- a/lib/tsan/rtl/tsan_interface_java.h +++ b/lib/tsan/rtl/tsan_interface_java.h @@ -79,6 +79,14 @@ void __tsan_java_mutex_lock_rec(jptr addr, int rec) INTERFACE_ATTRIBUTE; // the same recursion level. int __tsan_java_mutex_unlock_rec(jptr addr) INTERFACE_ATTRIBUTE; +// Raw acquire/release primitives. +// Can be used to establish happens-before edges on volatile/final fields, +// in atomic operations, etc. release_store is the same as release, but it +// breaks release sequence on addr (see C++ standard 1.10/7 for details). +void __tsan_java_acquire(jptr addr) INTERFACE_ATTRIBUTE; +void __tsan_java_release(jptr addr) INTERFACE_ATTRIBUTE; +void __tsan_java_release_store(jptr addr) INTERFACE_ATTRIBUTE; + #ifdef __cplusplus } // extern "C" #endif diff --git a/lib/tsan/rtl/tsan_mman.cc b/lib/tsan/rtl/tsan_mman.cc index 285bdb34d91d..ebb3f77fb992 100644 --- a/lib/tsan/rtl/tsan_mman.cc +++ b/lib/tsan/rtl/tsan_mman.cc @@ -45,7 +45,7 @@ Allocator *allocator() { } void InitializeAllocator() { - allocator()->Init(); + allocator()->Init(common_flags()->allocator_may_return_null); } void AllocatorThreadStart(ThreadState *thr) { @@ -78,7 +78,7 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) { void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align, bool signal) { if ((sz >= (1ull << 40)) || (align >= (1ull << 40))) - return AllocatorReturnNull(); + return allocator()->ReturnNullOrDie(); void *p = allocator()->Allocate(&thr->alloc_cache, sz, align); if (p == 0) return 0; @@ -89,6 +89,15 @@ void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align, bool signal) { return p; } +void *user_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) { + if (CallocShouldReturnNullDueToOverflow(size, n)) + return allocator()->ReturnNullOrDie(); + void *p = user_alloc(thr, pc, n * size); + if (p) + internal_memset(p, 0, n * size); + return p; +} + void user_free(ThreadState *thr, uptr pc, void *p, bool signal) { if (ctx && ctx->initialized) OnUserFree(thr, pc, (uptr)p, true); diff --git a/lib/tsan/rtl/tsan_mman.h b/lib/tsan/rtl/tsan_mman.h index 7d41fa864a26..5ff956d827f6 100644 --- a/lib/tsan/rtl/tsan_mman.h +++ b/lib/tsan/rtl/tsan_mman.h @@ -27,6 +27,7 @@ void AllocatorPrintStats(); // For user allocations. void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align = kDefaultAlignment, bool signal = true); +void *user_calloc(ThreadState *thr, uptr pc, uptr sz, uptr n); // Does not accept NULL. void user_free(ThreadState *thr, uptr pc, void *p, bool signal = true); void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz); diff --git a/lib/tsan/rtl/tsan_mutex.cc b/lib/tsan/rtl/tsan_mutex.cc index 9ea9bae21b50..dc5a462a8081 100644 --- a/lib/tsan/rtl/tsan_mutex.cc +++ b/lib/tsan/rtl/tsan_mutex.cc @@ -25,7 +25,7 @@ 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 && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO const MutexType MutexTypeLeaf = (MutexType)-1; static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = { /*0 MutexTypeInvalid*/ {}, @@ -47,7 +47,7 @@ static bool CanLockAdj[MutexTypeCount][MutexTypeCount]; #endif void InitializeMutex() { -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_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; @@ -128,7 +128,7 @@ InternalDeadlockDetector::InternalDeadlockDetector() { // Rely on zero initialization because some mutexes can be locked before ctor. } -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO void InternalDeadlockDetector::Lock(MutexType t) { // Printf("LOCK %d @%zu\n", t, seq_ + 1); CHECK_GT(t, MutexTypeInvalid); @@ -170,7 +170,7 @@ void InternalDeadlockDetector::CheckNoLocks() { #endif void CheckNoLocks(ThreadState *thr) { -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO thr->internal_deadlock_detector.CheckNoLocks(); #endif } @@ -208,7 +208,7 @@ class Backoff { Mutex::Mutex(MutexType type, StatType stat_type) { CHECK_GT(type, MutexTypeInvalid); CHECK_LT(type, MutexTypeCount); -#if TSAN_DEBUG +#if SANITIZER_DEBUG type_ = type; #endif #if TSAN_COLLECT_STATS @@ -222,7 +222,7 @@ Mutex::~Mutex() { } void Mutex::Lock() { -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO cur_thread()->internal_deadlock_detector.Lock(type_); #endif uptr cmp = kUnlocked; @@ -247,13 +247,13 @@ void Mutex::Unlock() { uptr prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release); (void)prev; DCHECK_NE(prev & kWriteLock, 0); -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO cur_thread()->internal_deadlock_detector.Unlock(type_); #endif } void Mutex::ReadLock() { -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO cur_thread()->internal_deadlock_detector.Lock(type_); #endif uptr prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire); @@ -275,7 +275,7 @@ void Mutex::ReadUnlock() { (void)prev; DCHECK_EQ(prev & kWriteLock, 0); DCHECK_GT(prev & ~kWriteLock, 0); -#if TSAN_DEBUG && !SANITIZER_GO +#if SANITIZER_DEBUG && !SANITIZER_GO cur_thread()->internal_deadlock_detector.Unlock(type_); #endif } diff --git a/lib/tsan/rtl/tsan_mutex.h b/lib/tsan/rtl/tsan_mutex.h index 7bb1c48fcac8..88fad57c78a0 100644 --- a/lib/tsan/rtl/tsan_mutex.h +++ b/lib/tsan/rtl/tsan_mutex.h @@ -52,7 +52,7 @@ class Mutex { private: atomic_uintptr_t state_; -#if TSAN_DEBUG +#if SANITIZER_DEBUG MutexType type_; #endif #if TSAN_COLLECT_STATS diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h index 270a7519dd0a..03f95694d26c 100644 --- a/lib/tsan/rtl/tsan_platform.h +++ b/lib/tsan/rtl/tsan_platform.h @@ -252,9 +252,6 @@ void InitializePlatform(); void FlushShadowMemory(); void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive); -void *internal_start_thread(void(*func)(void*), void *arg); -void internal_join_thread(void *th); - // Says whether the addr relates to a global var. // Guesses with high probability, may yield both false positives and negatives. bool IsGlobalVar(uptr addr); diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc index 4dcfa558529c..7bc28db296ec 100644 --- a/lib/tsan/rtl/tsan_platform_linux.cc +++ b/lib/tsan/rtl/tsan_platform_linux.cc @@ -215,10 +215,11 @@ void InitializeShadowMemory() { // Frequently a thread uses only a small part of stack and similarly // a program uses a small part of large mmap. On some programs // we see 20% memory usage reduction without huge pages for this range. -#ifdef MADV_NOHUGEPAGE - madvise((void*)MemToShadow(0x7f0000000000ULL), - 0x10000000000ULL * kShadowMultiplier, MADV_NOHUGEPAGE); -#endif + // FIXME: don't use constants here. + NoHugePagesInRegion(MemToShadow(0x7f0000000000ULL), + 0x10000000000ULL * kShadowMultiplier); + if (common_flags()->use_madv_dontdump) + DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg); DPrintf("memory shadow: %zx-%zx (%zuGB)\n", kShadowBeg, kShadowEnd, (kShadowEnd - kShadowBeg) >> 30); @@ -232,6 +233,8 @@ void InitializeShadowMemory() { "to link with -pie (%p, %p).\n", meta, kMetaShadowBeg); Die(); } + if (common_flags()->use_madv_dontdump) + DontDumpShadowMemory(meta, meta_size); DPrintf("meta shadow: %zx-%zx (%zuGB)\n", meta, meta + meta_size, meta_size >> 30); diff --git a/lib/tsan/rtl/tsan_platform_mac.cc b/lib/tsan/rtl/tsan_platform_mac.cc index 15b9f9d2cb19..63f1748e13ce 100644 --- a/lib/tsan/rtl/tsan_platform_mac.cc +++ b/lib/tsan/rtl/tsan_platform_mac.cc @@ -60,6 +60,8 @@ void InitializeShadowMemory() { "to link with -pie.\n"); Die(); } + if (common_flags()->use_madv_dontdump) + DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg); DPrintf("kShadow %zx-%zx (%zuGB)\n", kShadowBeg, kShadowEnd, (kShadowEnd - kShadowBeg) >> 30); diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc index 7cb7008e2980..b3320aad8038 100644 --- a/lib/tsan/rtl/tsan_rtl.cc +++ b/lib/tsan/rtl/tsan_rtl.cc @@ -365,8 +365,7 @@ int Finalize(ThreadState *thr) { ctx->report_mtx.Unlock(); #ifndef SANITIZER_GO - if (common_flags()->verbosity) - AllocatorPrintStats(); + if (Verbosity()) AllocatorPrintStats(); #endif ThreadFinalize(thr); @@ -565,43 +564,26 @@ void MemoryAccessImpl1(ThreadState *thr, uptr addr, // it's just not worth it (performance- and complexity-wise). Shadow old(0); - if (kShadowCnt == 1) { - int idx = 0; + + // It release mode we manually unroll the loop, + // because empirically gcc generates better code this way. + // However, we can't afford unrolling in debug mode, because the function + // consumes almost 4K of stack. Gtest gives only 4K of stack to death test + // threads, which is not enough for the unrolled loop. +#if SANITIZER_DEBUG + for (int idx = 0; idx < 4; idx++) { #include "tsan_update_shadow_word_inl.h" - } else if (kShadowCnt == 2) { - int idx = 0; -#include "tsan_update_shadow_word_inl.h" - idx = 1; -#include "tsan_update_shadow_word_inl.h" - } else if (kShadowCnt == 4) { - int idx = 0; -#include "tsan_update_shadow_word_inl.h" - idx = 1; -#include "tsan_update_shadow_word_inl.h" - idx = 2; -#include "tsan_update_shadow_word_inl.h" - idx = 3; -#include "tsan_update_shadow_word_inl.h" - } else if (kShadowCnt == 8) { - int idx = 0; -#include "tsan_update_shadow_word_inl.h" - idx = 1; -#include "tsan_update_shadow_word_inl.h" - idx = 2; -#include "tsan_update_shadow_word_inl.h" - idx = 3; -#include "tsan_update_shadow_word_inl.h" - idx = 4; -#include "tsan_update_shadow_word_inl.h" - idx = 5; -#include "tsan_update_shadow_word_inl.h" - idx = 6; -#include "tsan_update_shadow_word_inl.h" - idx = 7; -#include "tsan_update_shadow_word_inl.h" - } else { - CHECK(false); } +#else + int idx = 0; +#include "tsan_update_shadow_word_inl.h" + idx = 1; +#include "tsan_update_shadow_word_inl.h" + idx = 2; +#include "tsan_update_shadow_word_inl.h" + idx = 3; +#include "tsan_update_shadow_word_inl.h" +#endif // we did not find any races and had already stored // the current access info, so we are done @@ -652,7 +634,7 @@ bool ContainsSameAccessSlow(u64 *s, u64 a, u64 sync_epoch, bool is_write) { return false; } -#if defined(__SSE3__) && TSAN_SHADOW_COUNT == 4 +#if defined(__SSE3__) #define SHUF(v0, v1, i0, i1, i2, i3) _mm_castps_si128(_mm_shuffle_ps( \ _mm_castsi128_ps(v0), _mm_castsi128_ps(v1), \ (i0)*1 + (i1)*4 + (i2)*16 + (i3)*64)) @@ -712,11 +694,12 @@ bool ContainsSameAccessFast(u64 *s, u64 a, u64 sync_epoch, bool is_write) { ALWAYS_INLINE bool ContainsSameAccess(u64 *s, u64 a, u64 sync_epoch, bool is_write) { -#if defined(__SSE3__) && TSAN_SHADOW_COUNT == 4 +#if defined(__SSE3__) bool res = ContainsSameAccessFast(s, a, sync_epoch, is_write); // NOTE: this check can fail if the shadow is concurrently mutated - // by other threads. - DCHECK_EQ(res, ContainsSameAccessSlow(s, a, sync_epoch, is_write)); + // by other threads. But it still can be useful if you modify + // ContainsSameAccessFast and want to ensure that it's not completely broken. + // DCHECK_EQ(res, ContainsSameAccessSlow(s, a, sync_epoch, is_write)); return res; #else return ContainsSameAccessSlow(s, a, sync_epoch, is_write); @@ -733,7 +716,7 @@ void MemoryAccess(ThreadState *thr, uptr pc, uptr addr, (int)(1 << kAccessSizeLog), kAccessIsWrite, shadow_mem, (uptr)shadow_mem[0], (uptr)shadow_mem[1], (uptr)shadow_mem[2], (uptr)shadow_mem[3]); -#if TSAN_DEBUG +#if SANITIZER_DEBUG if (!IsAppMem(addr)) { Printf("Access to non app mem %zx\n", addr); DCHECK(IsAppMem(addr)); @@ -990,7 +973,7 @@ bool MD5Hash::operator==(const MD5Hash &other) const { return hash[0] == other.hash[0] && hash[1] == other.hash[1]; } -#if TSAN_DEBUG +#if SANITIZER_DEBUG void build_consistency_debug() {} #else void build_consistency_release() {} @@ -1002,16 +985,6 @@ void build_consistency_stats() {} void build_consistency_nostats() {} #endif -#if TSAN_SHADOW_COUNT == 1 -void build_consistency_shadow1() {} -#elif TSAN_SHADOW_COUNT == 2 -void build_consistency_shadow2() {} -#elif TSAN_SHADOW_COUNT == 4 -void build_consistency_shadow4() {} -#else -void build_consistency_shadow8() {} -#endif - } // namespace __tsan #ifndef SANITIZER_GO diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h index 8d886875159b..768e8307fcc8 100644 --- a/lib/tsan/rtl/tsan_rtl.h +++ b/lib/tsan/rtl/tsan_rtl.h @@ -685,7 +685,7 @@ void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); // The trick is that the call preserves all registers and the compiler // does not treat it as a call. // If it does not work for you, use normal call. -#if TSAN_DEBUG == 0 +#if !SANITIZER_DEBUG && defined(__x86_64__) // 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) \ diff --git a/lib/tsan/rtl/tsan_rtl_report.cc b/lib/tsan/rtl/tsan_rtl_report.cc index 0481b23b7be0..d1621454242c 100644 --- a/lib/tsan/rtl/tsan_rtl_report.cc +++ b/lib/tsan/rtl/tsan_rtl_report.cc @@ -87,7 +87,7 @@ static void StackStripMain(SymbolizedStack *frames) { // can actually happen if we do not instrument some code, // so it's only a debug print. However we must try hard to not miss it // due to our fault. - DPrintf("Bottom stack frame of stack %zx is missed\n", stack->info.address); + DPrintf("Bottom stack frame is missed\n"); } #else // The last frame always point into runtime (gosched0, goexit0, runtime.main). @@ -251,7 +251,8 @@ ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) { void ScopedReport::AddThread(int unique_tid, bool suppressable) { #ifndef SANITIZER_GO - AddThread(FindThreadByUidLocked(unique_tid), suppressable); + if (const ThreadContext *tctx = FindThreadByUidLocked(unique_tid)) + AddThread(tctx, suppressable); #endif } @@ -397,7 +398,7 @@ void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, InternalScopedBuffer stack(kShadowStackSize); for (uptr i = 0; i < hdr->stack0.size; i++) { stack[i] = hdr->stack0.trace[i]; - DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]); + DPrintf2(" #%02zu: pc=%zx\n", i, stack[i]); } if (mset) *mset = hdr->mset0; diff --git a/lib/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc index 7b7b27c024f6..e026217ed171 100644 --- a/lib/tsan/rtl/tsan_rtl_thread.cc +++ b/lib/tsan/rtl/tsan_rtl_thread.cc @@ -111,12 +111,13 @@ void ThreadContext::OnStarted(void *arg) { thr->dd_pt = ctx->dd->CreatePhysicalThread(); thr->dd_lt = ctx->dd->CreateLogicalThread(unique_id); } + thr->fast_state.SetHistorySize(flags()->history_size); + // Commit switch to the new part of the trace. + // TraceAddEvent will reset stack0/mset0 in the new part for us. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + thr->fast_synch_epoch = epoch0; AcquireImpl(thr, 0, &sync); - thr->fast_state.SetHistorySize(flags()->history_size); - const uptr trace = (epoch0 / kTracePartSize) % TraceParts(); - Trace *thr_trace = ThreadTrace(thr->tid); - thr_trace->headers[trace].epoch0 = epoch0; StatInc(thr, StatSyncAcquire); sync.Reset(&thr->clock_cache); DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx " @@ -329,7 +330,7 @@ void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr, thr->tid, (void*)pc, (void*)addr, (int)size, is_write); -#if TSAN_DEBUG +#if SANITIZER_DEBUG if (!IsAppMem(addr)) { Printf("Access to non app mem %zx\n", addr); DCHECK(IsAppMem(addr)); diff --git a/lib/tsan/tests/CMakeLists.txt b/lib/tsan/tests/CMakeLists.txt index 2e830c3be714..cb7e5374ec8a 100644 --- a/lib/tsan/tests/CMakeLists.txt +++ b/lib/tsan/tests/CMakeLists.txt @@ -6,6 +6,7 @@ set_target_properties(TsanUnitTests PROPERTIES set(TSAN_UNITTEST_CFLAGS ${TSAN_CFLAGS} + ${COMPILER_RT_TEST_CFLAGS} ${COMPILER_RT_GTEST_CFLAGS} -I${COMPILER_RT_SOURCE_DIR}/lib -I${COMPILER_RT_SOURCE_DIR}/lib/tsan/rtl diff --git a/lib/tsan/tests/rtl/tsan_string.cc b/lib/tsan/tests/rtl/tsan_string.cc index c402f7cbd679..75adc6c85ee9 100644 --- a/lib/tsan/tests/rtl/tsan_string.cc +++ b/lib/tsan/tests/rtl/tsan_string.cc @@ -46,9 +46,6 @@ 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]; @@ -57,7 +54,6 @@ 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/tests/unit/tsan_clock_test.cc b/lib/tsan/tests/unit/tsan_clock_test.cc index a1fd2b7f6e99..92071827d3d8 100644 --- a/lib/tsan/tests/unit/tsan_clock_test.cc +++ b/lib/tsan/tests/unit/tsan_clock_test.cc @@ -211,8 +211,8 @@ TEST(Clock, Growth) { } } -const int kThreads = 4; -const int kClocks = 4; +const uptr kThreads = 4; +const uptr kClocks = 4; // SimpleSyncClock and SimpleThreadClock implement the same thing as // SyncClock and ThreadClock, but in a very simple way. diff --git a/lib/tsan/tests/unit/tsan_mman_test.cc b/lib/tsan/tests/unit/tsan_mman_test.cc index d969989df768..bfaefe648705 100644 --- a/lib/tsan/tests/unit/tsan_mman_test.cc +++ b/lib/tsan/tests/unit/tsan_mman_test.cc @@ -136,7 +136,7 @@ TEST(Mman, Stats) { } TEST(Mman, CallocOverflow) { -#if TSAN_DEBUG +#if SANITIZER_DEBUG // EXPECT_DEATH clones a thread with 4K stack, // which is overflown by tsan memory accesses functions in debug mode. return; diff --git a/lib/tsan/tests/unit/tsan_mutex_test.cc b/lib/tsan/tests/unit/tsan_mutex_test.cc index c39841ddcbb1..cce7f073b92f 100644 --- a/lib/tsan/tests/unit/tsan_mutex_test.cc +++ b/lib/tsan/tests/unit/tsan_mutex_test.cc @@ -64,7 +64,7 @@ class TestData { const int kThreads = 8; const int kWriteRate = 1024; -#if TSAN_DEBUG +#if SANITIZER_DEBUG const int kIters = 16*1024; #else const int kIters = 64*1024; diff --git a/lib/ubsan/CMakeLists.txt b/lib/ubsan/CMakeLists.txt index 09c7a851e075..541138044654 100644 --- a/lib/ubsan/CMakeLists.txt +++ b/lib/ubsan/CMakeLists.txt @@ -17,7 +17,12 @@ include_directories(..) set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS}) append_no_rtti_flag(UBSAN_CFLAGS) +append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors + UBSAN_CFLAGS) + set(UBSAN_CXXFLAGS ${SANITIZER_COMMON_CFLAGS}) +append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors + UBSAN_CXXFLAGS) add_custom_target(ubsan) diff --git a/lib/ubsan/ubsan_flags.cc b/lib/ubsan/ubsan_flags.cc index eda11f1b265c..0dbffc9b102a 100644 --- a/lib/ubsan/ubsan_flags.cc +++ b/lib/ubsan/ubsan_flags.cc @@ -14,6 +14,7 @@ #include "ubsan_flags.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" +#include "sanitizer_common/sanitizer_flag_parser.h" namespace __ubsan { @@ -21,36 +22,51 @@ static const char *MaybeCallUbsanDefaultOptions() { return (&__ubsan_default_options) ? __ubsan_default_options() : ""; } -void InitializeCommonFlags() { - CommonFlags *cf = common_flags(); - SetCommonFlagsDefaults(cf); - cf->print_summary = false; - // Override from user-specified string. - ParseCommonFlagsFromString(cf, MaybeCallUbsanDefaultOptions()); - // Override from environment variable. - ParseCommonFlagsFromString(cf, GetEnv("UBSAN_OPTIONS")); -} - Flags ubsan_flags; -static void ParseFlagsFromString(Flags *f, const char *str) { - if (!str) - return; - ParseFlag(str, &f->halt_on_error, "halt_on_error", - "Crash the program after printing the first error report"); - ParseFlag(str, &f->print_stacktrace, "print_stacktrace", - "Include full stacktrace into an error report"); +void Flags::SetDefaults() { +#define UBSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; +#include "ubsan_flags.inc" +#undef UBSAN_FLAG } -void InitializeFlags() { +void RegisterUbsanFlags(FlagParser *parser, Flags *f) { +#define UBSAN_FLAG(Type, Name, DefaultValue, Description) \ + RegisterFlag(parser, #Name, Description, &f->Name); +#include "ubsan_flags.inc" +#undef UBSAN_FLAG +} + +void InitializeFlags(bool standalone) { Flags *f = flags(); - // Default values. - f->halt_on_error = false; - f->print_stacktrace = false; + FlagParser parser; + RegisterUbsanFlags(&parser, f); + + if (standalone) { + RegisterCommonFlags(&parser); + + SetCommonFlagsDefaults(); + CommonFlags cf; + cf.CopyFrom(*common_flags()); + cf.print_summary = false; + OverrideCommonFlags(cf); + } else { + // Ignore common flags if not standalone. + // This is inconsistent with LSan, which allows common flags in LSAN_FLAGS. + // This is caused by undefined initialization order between ASan and UBsan, + // which makes it impossible to make sure that common flags from ASAN_OPTIONS + // have not been used (in __asan_init) before they are overwritten with flags + // from UBSAN_OPTIONS. + CommonFlags cf_ignored; + RegisterCommonFlags(&parser, &cf_ignored); + } + + f->SetDefaults(); // Override from user-specified string. - ParseFlagsFromString(f, MaybeCallUbsanDefaultOptions()); + parser.ParseString(MaybeCallUbsanDefaultOptions()); // Override from environment variable. - ParseFlagsFromString(f, GetEnv("UBSAN_OPTIONS")); + parser.ParseString(GetEnv("UBSAN_OPTIONS")); + SetVerbosity(common_flags()->verbosity); } } // namespace __ubsan diff --git a/lib/ubsan/ubsan_flags.h b/lib/ubsan/ubsan_flags.h index c496469f5f41..b47f14e1e2fd 100644 --- a/lib/ubsan/ubsan_flags.h +++ b/lib/ubsan/ubsan_flags.h @@ -18,15 +18,17 @@ namespace __ubsan { struct Flags { - bool halt_on_error; - bool print_stacktrace; +#define UBSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; +#include "ubsan_flags.inc" +#undef UBSAN_FLAG + + void SetDefaults(); }; extern Flags ubsan_flags; inline Flags *flags() { return &ubsan_flags; } -void InitializeCommonFlags(); -void InitializeFlags(); +void InitializeFlags(bool standalone); } // namespace __ubsan diff --git a/lib/ubsan/ubsan_flags.inc b/lib/ubsan/ubsan_flags.inc new file mode 100644 index 000000000000..3260e8e13df8 --- /dev/null +++ b/lib/ubsan/ubsan_flags.inc @@ -0,0 +1,24 @@ +//===-- ubsan_flags.inc -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// UBSan runtime flags. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_FLAG +# error "Define UBSAN_FLAG prior to including this file!" +#endif + +// UBSAN_FLAG(Type, Name, DefaultValue, Description) +// See COMMON_FLAG in sanitizer_flags.inc for more details. + +UBSAN_FLAG(bool, halt_on_error, false, + "Crash the program after printing the first error report") +UBSAN_FLAG(bool, print_stacktrace, false, + "Include full stacktrace into an error report") + diff --git a/lib/ubsan/ubsan_init.cc b/lib/ubsan/ubsan_init.cc index 6080e304c122..48fa492486dd 100644 --- a/lib/ubsan/ubsan_init.cc +++ b/lib/ubsan/ubsan_init.cc @@ -31,6 +31,7 @@ void __ubsan::InitIfNecessary() { #endif if (LIKELY(ubsan_inited)) return; + bool standalone = false; if (0 == internal_strcmp(SanitizerToolName, "SanitizerTool")) { // WARNING: If this condition holds, then either UBSan runs in a standalone // mode, or initializer for another sanitizer hasn't run yet. In a latter @@ -38,11 +39,12 @@ void __ubsan::InitIfNecessary() { // common flags. It means, that we are not allowed to *use* common flags // in this function. SanitizerToolName = "UndefinedBehaviorSanitizer"; - InitializeCommonFlags(); + standalone = true; } // Initialize UBSan-specific flags. - InitializeFlags(); + InitializeFlags(standalone); SuppressionContext::InitIfNecessary(); + InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); ubsan_inited = true; } diff --git a/lib/ubsan/ubsan_type_hash.cc b/lib/ubsan/ubsan_type_hash.cc index 808a4332d01f..a388bcc6d72e 100644 --- a/lib/ubsan/ubsan_type_hash.cc +++ b/lib/ubsan/ubsan_type_hash.cc @@ -115,8 +115,7 @@ __ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize]; /// \brief Determine whether \p Derived has a \p Base base class subobject at /// offset \p Offset. -static bool isDerivedFromAtOffset(sptr Object, - const abi::__class_type_info *Derived, +static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived, const abi::__class_type_info *Base, sptr Offset) { if (Derived->__type_name == Base->__type_name) @@ -124,7 +123,7 @@ static bool isDerivedFromAtOffset(sptr Object, if (const abi::__si_class_type_info *SI = dynamic_cast(Derived)) - return isDerivedFromAtOffset(Object, SI->__base_type, Base, Offset); + return isDerivedFromAtOffset(SI->__base_type, Base, Offset); const abi::__vmi_class_type_info *VTI = dynamic_cast(Derived); @@ -139,13 +138,13 @@ static bool isDerivedFromAtOffset(sptr Object, 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) { - sptr VTable = *reinterpret_cast(Object); - OffsetHere = *reinterpret_cast(VTable + OffsetHere); - } - if (isDerivedFromAtOffset(Object + OffsetHere, - VTI->base_info[base].__base_type, Base, - Offset - OffsetHere)) + 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; } @@ -154,15 +153,14 @@ static bool isDerivedFromAtOffset(sptr Object, /// \brief Find the derived-most dynamic base class of \p Derived at offset /// \p Offset. -static const abi::__class_type_info * -findBaseAtOffset(sptr Object, const abi::__class_type_info *Derived, - sptr 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(Derived)) - return findBaseAtOffset(Object, SI->__base_type, Offset); + return findBaseAtOffset(SI->__base_type, Offset); const abi::__vmi_class_type_info *VTI = dynamic_cast(Derived); @@ -174,13 +172,12 @@ findBaseAtOffset(sptr Object, const abi::__class_type_info *Derived, 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) { - sptr VTable = *reinterpret_cast(Object); - OffsetHere = *reinterpret_cast(VTable + OffsetHere); - } - if (const abi::__class_type_info *Base = findBaseAtOffset( - Object + OffsetHere, VTI->base_info[base].__base_type, - Offset - OffsetHere)) + 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; } @@ -232,8 +229,7 @@ bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) { return false; abi::__class_type_info *Base = (abi::__class_type_info*)Type; - if (!isDerivedFromAtOffset(reinterpret_cast(Object), Derived, Base, - -Vtable->Offset)) + if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset)) return false; // Success. Cache this result. @@ -247,9 +243,8 @@ __ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfo(void *Object) { if (!Vtable) return DynamicTypeInfo(0, 0, 0); const abi::__class_type_info *ObjectType = findBaseAtOffset( - reinterpret_cast(Object), - static_cast(Vtable->TypeInfo), - -Vtable->Offset); + static_cast(Vtable->TypeInfo), + -Vtable->Offset); return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset, ObjectType ? ObjectType->__type_name : ""); } diff --git a/make/platform/clang_darwin.mk b/make/platform/clang_darwin.mk index 6ed3230a0edd..4f71c0b46052 100644 --- a/make/platform/clang_darwin.mk +++ b/make/platform/clang_darwin.mk @@ -44,10 +44,14 @@ XCRun = \ result=`xcrun -find $(1) 2> /dev/null`; \ if [ "$$?" != "0" ]; then result=$(1); fi; \ echo $$result) +# Prefer building with the internal SDKs. XCRunSdkPath = \ $(shell \ - result=`xcrun --sdk $(1) --show-sdk-path 2> /dev/null`; \ - if [ "$$?" != "0" ]; then result=""; fi; \ + result=`xcrun --sdk $(1).internal --show-sdk-path 2> /dev/null`; \ + if [ "$$?" != "0" ]; then \ + result=`xcrun --sdk $(1) --show-sdk-path 2> /dev/null`; \ + if [ "$$?" != "0" ]; then result=""; fi; \ + fi; \ echo $$result) ### @@ -170,17 +174,20 @@ CFLAGS.10.4 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) CFLAGS.asan_osx_dynamic := \ $(CFLAGS) -mmacosx-version-min=10.7 \ + -stdlib=libc++ \ -isysroot $(OSX_SDK) \ -fno-builtin \ -gline-tables-only \ - -DMAC_INTERPOSE_FUNCTIONS=1 + -DMAC_INTERPOSE_FUNCTIONS=1 \ + -DASAN_DYNAMIC=1 CFLAGS.asan_iossim_dynamic := \ $(CFLAGS) -mios-simulator-version-min=7.0 \ -isysroot $(IOSSIM_SDK) \ -fno-builtin \ -gline-tables-only \ - -DMAC_INTERPOSE_FUNCTIONS=1 + -DMAC_INTERPOSE_FUNCTIONS=1 \ + -DASAN_DYNAMIC=1 CFLAGS.ubsan_osx := $(CFLAGS) -mmacosx-version-min=10.6 \ -isysroot $(OSX_SDK) \ @@ -217,7 +224,9 @@ CFLAGS.profile_ios.arm64 := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS) # Configure the asan_osx_dynamic library to be built shared. SHARED_LIBRARY.asan_osx_dynamic := 1 -LDFLAGS.asan_osx_dynamic := -lstdc++ -undefined dynamic_lookup -install_name @rpath/libclang_rt.asan_osx_dynamic.dylib +LDFLAGS.asan_osx_dynamic := -lc++ -undefined dynamic_lookup -install_name @rpath/libclang_rt.asan_osx_dynamic.dylib \ + -mmacosx-version-min=10.7 \ + -isysroot $(OSX_SDK) # Configure the asan_iossim_dynamic library to be built shared. SHARED_LIBRARY.asan_iossim_dynamic := 1 @@ -257,7 +266,13 @@ FUNCTIONS.asan_iossim_dynamic := $(AsanFunctions) $(AsanCXXFunctions) \ FUNCTIONS.ubsan_osx := $(UbsanFunctions) $(UbsanCXXFunctions) \ $(SanitizerCommonFunctions) +CCKEXT_PROFILE_FUNCTIONS := \ + InstrProfiling \ + InstrProfilingBuffer \ + InstrProfilingPlatformDarwin + CCKEXT_COMMON_FUNCTIONS := \ + $(CCKEXT_PROFILE_FUNCTIONS) \ absvdi2 \ absvsi2 \ addvdi3 \ @@ -402,10 +417,17 @@ CCKEXT_ARMVFP_FUNCTIONS := $(CCKEXT_ARM_FUNCTIONS) \ unorddf2vfp \ unordsf2vfp +CCKEXT_ARM64_FUNCTIONS := \ + $(CCKEXT_PROFILE_FUNCTIONS) \ + divdc3 \ + divsc3 \ + muldc3 \ + mulsc3 + FUNCTIONS.cc_kext.armv7 := $(CCKEXT_ARMVFP_FUNCTIONS) FUNCTIONS.cc_kext.armv7k := $(CCKEXT_ARMVFP_FUNCTIONS) FUNCTIONS.cc_kext.armv7s := $(CCKEXT_ARMVFP_FUNCTIONS) -FUNCTIONS.cc_kext.arm64 := mulsc3 muldc3 divsc3 divdc3 +FUNCTIONS.cc_kext.arm64 := $(CCKEXT_ARM64_FUNCTIONS) FUNCTIONS.cc_kext_ios5.armv7 := $(CCKEXT_ARMVFP_FUNCTIONS) FUNCTIONS.cc_kext_ios5.armv7k := $(CCKEXT_ARMVFP_FUNCTIONS) FUNCTIONS.cc_kext_ios5.armv7s := $(CCKEXT_ARMVFP_FUNCTIONS) diff --git a/make/platform/darwin_bni.mk b/make/platform/darwin_bni.mk index 229609a185df..2fd5089baa92 100644 --- a/make/platform/darwin_bni.mk +++ b/make/platform/darwin_bni.mk @@ -118,6 +118,7 @@ FUNCTIONS.armv7s := $(FUNCTIONS.armv7) FUNCTIONS.arm64 := divti3 modti3 \ udivmodti4 \ udivti3 umodti3 \ + mulsc3 muldc3 \ powisf2 powidf2 \ clzti2 \ fixdfti fixsfti \ diff --git a/make/platform/darwin_fat.mk b/make/platform/darwin_fat.mk deleted file mode 100644 index 65ecd75c9adf..000000000000 --- a/make/platform/darwin_fat.mk +++ /dev/null @@ -1,56 +0,0 @@ -# Configurations to build -# -# This section must define: -# Description - A description of this target. -# Configs - The names of each configuration to build; this is used to build -# multiple libraries inside a single configuration file (for -# example, Debug and Release builds, or builds with and without -# software floating point). -# -# This section must define one of: -# UniversalArchs - A list of architectures to build for, when using universal build -# support (e.g., on Darwin). This should only be used to build fat -# libraries, simply building multiple libraries for different -# architectures should do so using distinct configs, with the -# appropriate choices for CC and CFLAGS. -# -# Arch - The target architecture; this must match the compiler-rt name for the -# architecture and is used to find the appropriate function -# implementations. -# -# When not universal builds, this section may define: -# Arch. - Set the target architecture on a per-config basis. - -Description := Target for building universal libraries for Darwin. - -Configs := Debug Release Profile -UniversalArchs := i386 x86_64 - -# Platform Options -# -# This section may override any of the variables in make/options.mk, using: -#