aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYi Kong <yikong@google.com>2018-10-16 01:12:10 -0700
committerYi Kong <yikong@google.com>2018-10-16 01:12:10 -0700
commitd771bdcf828b26e5fd4638ccc484c09fa00f0f7c (patch)
tree45d24b875aea29a5aabe9fc8326ed7515d6213e1
parent33c3974304a9078af778736e1e5f65fc456e5bbe (diff)
parentfd6579e355cd493aa59e055083e16f6b25fc6f98 (diff)
downloadcompiler-rt-llvm-svn.344140.tar.gz
Merge fd6579e35 for LLVM update to 344140llvm-svn.344140
Change-Id: I8ee9da9db85c45170dc51297c6268fe81af6ad1d
-rw-r--r--CMakeLists.txt72
-rw-r--r--cmake/Modules/AddCompilerRT.cmake10
-rw-r--r--cmake/Modules/CompilerRTUtils.cmake38
-rw-r--r--cmake/Modules/SanitizerUtils.cmake10
-rw-r--r--cmake/base-config-ix.cmake8
-rw-r--r--cmake/config-ix.cmake36
-rw-r--r--include/sanitizer/allocator_interface.h1
-rw-r--r--include/sanitizer/common_interface_defs.h6
-rw-r--r--include/sanitizer/hwasan_interface.h51
-rw-r--r--include/xray/xray_log_interface.h25
-rw-r--r--lib/asan/CMakeLists.txt12
-rw-r--r--lib/asan/asan_globals_win.cc2
-rw-r--r--lib/asan/asan_malloc_win.cc15
-rw-r--r--lib/asan/asan_new_delete.cc2
-rw-r--r--lib/asan/asan_win.cc8
-rw-r--r--lib/asan/tests/asan_interface_test.cc2
-rw-r--r--lib/asan/tests/asan_noinst_test.cc2
-rw-r--r--lib/builtins/arm/addsf3.S2
-rw-r--r--lib/builtins/arm/aeabi_cdcmp.S6
-rw-r--r--lib/builtins/arm/aeabi_cfcmp.S6
-rw-r--r--lib/builtins/clzdi2.c9
-rw-r--r--lib/builtins/ctzdi2.c9
-rw-r--r--lib/builtins/divdc3.c4
-rw-r--r--lib/builtins/divsc3.c5
-rw-r--r--lib/builtins/divtc3.c5
-rw-r--r--lib/builtins/emutls.c13
-rw-r--r--lib/builtins/fp_lib.h57
-rw-r--r--lib/builtins/int_math.h4
-rw-r--r--lib/builtins/int_util.c8
-rw-r--r--lib/builtins/int_util.h6
-rw-r--r--lib/builtins/ppc/divtc3.c12
-rw-r--r--lib/cfi/CMakeLists.txt2
-rw-r--r--lib/cfi/cfi.cc40
-rw-r--r--lib/cfi/cfi_blacklist.txt6
-rw-r--r--lib/esan/CMakeLists.txt1
-rw-r--r--lib/esan/esan_interceptors.cpp10
-rw-r--r--lib/esan/esan_shadow.h2
-rw-r--r--lib/esan/esan_sideline_bsd.cpp35
-rw-r--r--lib/fuzzer/CMakeLists.txt2
-rw-r--r--lib/fuzzer/FuzzerCommand.h4
-rw-r--r--lib/fuzzer/FuzzerDefs.h7
-rw-r--r--lib/fuzzer/FuzzerDriver.cpp2
-rw-r--r--lib/fuzzer/FuzzerExtFunctionsDlsymWin.cpp62
-rw-r--r--lib/fuzzer/FuzzerExtFunctionsWeak.cpp2
-rw-r--r--lib/fuzzer/FuzzerFlags.def3
-rw-r--r--lib/fuzzer/FuzzerIO.cpp8
-rw-r--r--lib/fuzzer/FuzzerIO.h2
-rw-r--r--lib/fuzzer/FuzzerIOPosix.cpp7
-rw-r--r--lib/fuzzer/FuzzerIOWindows.cpp22
-rw-r--r--lib/fuzzer/FuzzerLoop.cpp5
-rw-r--r--lib/fuzzer/FuzzerMutate.cpp82
-rw-r--r--lib/fuzzer/FuzzerMutate.h25
-rw-r--r--lib/fuzzer/FuzzerOptions.h2
-rw-r--r--lib/fuzzer/FuzzerTracePC.cpp49
-rw-r--r--lib/fuzzer/FuzzerUtilFuchsia.cpp32
-rw-r--r--lib/fuzzer/FuzzerUtilWindows.cpp6
-rw-r--r--lib/fuzzer/tests/CMakeLists.txt2
-rw-r--r--lib/fuzzer/tests/FuzzerUnittest.cpp7
-rw-r--r--lib/hwasan/CMakeLists.txt28
-rw-r--r--lib/hwasan/hwasan.cc172
-rw-r--r--lib/hwasan/hwasan.h27
-rw-r--r--lib/hwasan/hwasan_allocator.cc213
-rw-r--r--lib/hwasan/hwasan_allocator.h73
-rw-r--r--lib/hwasan/hwasan_dynamic_shadow.cc30
-rw-r--r--lib/hwasan/hwasan_flags.h2
-rw-r--r--lib/hwasan/hwasan_flags.inc26
-rw-r--r--lib/hwasan/hwasan_interceptors.cc289
-rw-r--r--lib/hwasan/hwasan_interface_internal.h65
-rw-r--r--lib/hwasan/hwasan_linux.cc203
-rw-r--r--lib/hwasan/hwasan_mapping.h63
-rw-r--r--lib/hwasan/hwasan_poisoning.cc6
-rw-r--r--lib/hwasan/hwasan_poisoning.h2
-rw-r--r--lib/hwasan/hwasan_report.cc275
-rw-r--r--lib/hwasan/hwasan_report.h1
-rw-r--r--lib/hwasan/hwasan_thread.cc120
-rw-r--r--lib/hwasan/hwasan_thread.h62
-rw-r--r--lib/hwasan/hwasan_thread_list.cc15
-rw-r--r--lib/hwasan/hwasan_thread_list.h200
-rw-r--r--lib/interception/interception.h1
-rw-r--r--lib/interception/interception_win.cc8
-rw-r--r--lib/lsan/lsan_common.cc9
-rw-r--r--lib/lsan/lsan_common_mac.cc8
-rw-r--r--lib/msan/msan.cc6
-rw-r--r--lib/msan/msan_interceptors.cc4
-rw-r--r--lib/msan/tests/msan_test.cc4
-rw-r--r--lib/profile/InstrProfilingValue.c5
-rw-r--r--lib/profile/WindowsMMap.h2
-rw-r--r--lib/safestack/CMakeLists.txt27
-rw-r--r--lib/safestack/safestack.cc83
-rw-r--r--lib/sanitizer_common/CMakeLists.txt2
-rw-r--r--lib/sanitizer_common/sanitizer_allocator_size_class_map.h5
-rw-r--r--lib/sanitizer_common/sanitizer_atomic_clang_x86.h6
-rw-r--r--lib/sanitizer_common/sanitizer_common.cc5
-rw-r--r--lib/sanitizer_common/sanitizer_common.h1
-rw-r--r--lib/sanitizer_common/sanitizer_common_interceptors.inc4
-rw-r--r--lib/sanitizer_common/sanitizer_coverage_fuchsia.cc6
-rw-r--r--lib/sanitizer_common/sanitizer_coverage_win_sections.cc53
-rw-r--r--lib/sanitizer_common/sanitizer_fuchsia.cc55
-rw-r--r--lib/sanitizer_common/sanitizer_internal_defs.h7
-rw-r--r--lib/sanitizer_common/sanitizer_libc.cc12
-rw-r--r--lib/sanitizer_common/sanitizer_linux.cc205
-rw-r--r--lib/sanitizer_common/sanitizer_linux.h2
-rw-r--r--lib/sanitizer_common/sanitizer_linux_libcdep.cc4
-rw-r--r--lib/sanitizer_common/sanitizer_mac.cc34
-rw-r--r--lib/sanitizer_common/sanitizer_mac.h4
-rw-r--r--lib/sanitizer_common/sanitizer_netbsd.cc330
-rw-r--r--lib/sanitizer_common/sanitizer_openbsd.cc16
-rw-r--r--lib/sanitizer_common/sanitizer_platform.h7
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_posix.cc11
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_posix.h4
-rw-r--r--lib/sanitizer_common/sanitizer_posix.h5
-rw-r--r--lib/sanitizer_common/sanitizer_procmaps_bsd.cc8
-rw-r--r--lib/sanitizer_common/sanitizer_procmaps_mac.cc8
-rw-r--r--lib/sanitizer_common/sanitizer_ring_buffer.h162
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc24
-rw-r--r--lib/sanitizer_common/sanitizer_syscall_generic.inc39
-rw-r--r--lib/sanitizer_common/sanitizer_syscall_linux_aarch64.inc3
-rw-r--r--lib/sanitizer_common/sanitizer_syscall_linux_arm.inc3
-rw-r--r--lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc3
-rw-r--r--lib/sanitizer_common/sanitizer_termination.cc9
-rw-r--r--lib/sanitizer_common/sanitizer_win.cc4
-rw-r--r--lib/sanitizer_common/sanitizer_win_defs.h20
-rw-r--r--lib/sanitizer_common/tests/CMakeLists.txt1
-rw-r--r--lib/sanitizer_common/tests/sanitizer_allocator_test.cc18
-rw-r--r--lib/sanitizer_common/tests/sanitizer_common_test.cc3
-rw-r--r--lib/sanitizer_common/tests/sanitizer_ring_buffer_test.cc99
-rw-r--r--lib/scudo/CMakeLists.txt8
-rw-r--r--lib/scudo/scudo_allocator.cpp27
-rw-r--r--lib/tsan/CMakeLists.txt9
-rwxr-xr-xlib/tsan/check_analyze.sh12
-rwxr-xr-xlib/tsan/go/buildgo.sh14
-rw-r--r--lib/tsan/rtl/tsan_interceptors.cc3
-rw-r--r--lib/tsan/rtl/tsan_libdispatch_mac.cc5
-rw-r--r--lib/tsan/rtl/tsan_platform.h44
-rw-r--r--lib/tsan/rtl/tsan_platform_linux.cc8
-rw-r--r--lib/tsan/rtl/tsan_platform_mac.cc8
-rw-r--r--lib/tsan/rtl/tsan_rtl_aarch64.S12
-rw-r--r--lib/tsan/rtl/tsan_rtl_amd64.S3
-rw-r--r--lib/xray/CMakeLists.txt2
-rw-r--r--lib/xray/tests/CMakeLists.txt42
-rw-r--r--lib/xray/tests/unit/CMakeLists.txt20
-rw-r--r--lib/xray/tests/unit/fdr_log_writer_test.cc92
-rw-r--r--lib/xray/tests/unit/fdr_logging_test.cc202
-rw-r--r--lib/xray/xray_allocator.h93
-rw-r--r--lib/xray/xray_basic_logging.cc75
-rw-r--r--lib/xray/xray_buffer_queue.cc121
-rw-r--r--lib/xray/xray_buffer_queue.h26
-rw-r--r--lib/xray/xray_fdr_log_records.h5
-rw-r--r--lib/xray/xray_fdr_log_writer.h119
-rw-r--r--lib/xray/xray_fdr_logging.cc369
-rw-r--r--lib/xray/xray_function_call_trie.h61
-rw-r--r--lib/xray/xray_init.cc9
-rw-r--r--lib/xray/xray_profile_collector.cc228
-rw-r--r--lib/xray/xray_profiling.cc68
-rw-r--r--lib/xray/xray_segmented_array.h87
-rw-r--r--lib/xray/xray_trampoline_x86_64.S2
-rw-r--r--lib/xray/xray_utils.cc5
-rw-r--r--lib/xray/xray_x86_64.cc14
-rw-r--r--test/asan/TestCases/Darwin/segv_read_write.c5
-rw-r--r--test/asan/TestCases/Linux/coverage-missing.cc10
-rw-r--r--test/asan/TestCases/Posix/stack-use-after-return.cc2
-rw-r--r--test/asan/TestCases/intercept-rethrow-exception.cc4
-rw-r--r--test/asan/TestCases/throw_catch.cc2
-rw-r--r--test/builtins/Unit/compiler_rt_logb_test.c63
-rw-r--r--test/builtins/Unit/compiler_rt_logbf_test.c63
-rw-r--r--test/builtins/Unit/compiler_rt_logbl_test.c79
-rw-r--r--test/cfi/cross-dso/lit.local.cfg2
-rw-r--r--test/esan/TestCases/large-stack-linux.c2
-rw-r--r--test/esan/TestCases/workingset-early-fault.c2
-rw-r--r--test/esan/TestCases/workingset-memset.cpp2
-rw-r--r--test/esan/TestCases/workingset-midreport.cpp2
-rw-r--r--test/esan/TestCases/workingset-samples.cpp2
-rw-r--r--test/esan/TestCases/workingset-signal-posix.cpp2
-rw-r--r--test/esan/TestCases/workingset-simple.cpp2
-rw-r--r--test/esan/lit.cfg3
-rw-r--r--test/fuzzer/InitializeTest.cpp5
-rw-r--r--test/fuzzer/SymbolizeDeadlock.cpp1
-rw-r--r--test/fuzzer/afl-driver-extra-stats.test2
-rw-r--r--test/fuzzer/afl-driver-stderr.test3
-rw-r--r--test/fuzzer/coverage.test3
-rw-r--r--test/fuzzer/dso.test2
-rw-r--r--test/fuzzer/dump_coverage.test5
-rw-r--r--test/fuzzer/exit_on_src_pos.test10
-rw-r--r--test/fuzzer/fuzzer-implicit-integer-truncation.test2
-rw-r--r--test/fuzzer/fuzzer-mutationstats.test10
-rw-r--r--test/fuzzer/fuzzer-oom.test20
-rw-r--r--test/fuzzer/gc-sections.test7
-rw-r--r--test/fuzzer/handle-unstable.test3
-rw-r--r--test/fuzzer/lit.cfg21
-rw-r--r--test/fuzzer/merge-control-file.test18
-rw-r--r--test/fuzzer/merge-posix.test1
-rw-r--r--test/fuzzer/merge-sigusr.test4
-rw-r--r--test/fuzzer/minimize_crash.test1
-rw-r--r--test/fuzzer/minimize_two_crashes.test4
-rw-r--r--test/fuzzer/null-deref-on-empty.test1
-rw-r--r--test/fuzzer/null-deref.test1
-rw-r--r--test/fuzzer/print_unstable_stats.test5
-rw-r--r--test/fuzzer/sigusr.test4
-rw-r--r--test/fuzzer/trace-malloc-threaded.test3
-rw-r--r--test/fuzzer/trace-malloc-unbalanced.test9
-rw-r--r--test/fuzzer/ulimit.test2
-rw-r--r--test/fuzzer/value-profile-cmp.test2
-rw-r--r--test/fuzzer/value-profile-cmp2.test1
-rw-r--r--test/fuzzer/value-profile-cmp3.test1
-rw-r--r--test/fuzzer/value-profile-cmp4.test2
-rw-r--r--test/fuzzer/value-profile-div.test2
-rw-r--r--test/fuzzer/value-profile-load.test2
-rw-r--r--test/fuzzer/value-profile-mem.test1
-rw-r--r--test/fuzzer/value-profile-set.test1
-rw-r--r--test/fuzzer/value-profile-strcmp.test1
-rw-r--r--test/fuzzer/value-profile-strncmp.test2
-rw-r--r--test/fuzzer/value-profile-switch.test2
-rw-r--r--test/hwasan/TestCases/deep-recursion.c73
-rw-r--r--test/hwasan/TestCases/double-free.c23
-rw-r--r--test/hwasan/TestCases/heap-buffer-overflow.c26
-rw-r--r--test/hwasan/TestCases/hwasan-print-shadow.cc29
-rw-r--r--test/hwasan/TestCases/longjmp.c26
-rw-r--r--test/hwasan/TestCases/malloc-test.c16
-rw-r--r--test/hwasan/TestCases/malloc_fill.cc22
-rw-r--r--test/hwasan/TestCases/many-threads-uaf.c45
-rw-r--r--test/hwasan/TestCases/new-test.cc18
-rw-r--r--test/hwasan/TestCases/print-memory-usage-android.c21
-rw-r--r--test/hwasan/TestCases/print-memory-usage.c72
-rw-r--r--test/hwasan/TestCases/pthread_exit.c5
-rw-r--r--test/hwasan/TestCases/realloc-after-free.c28
-rw-r--r--test/hwasan/TestCases/realloc-test.cc37
-rw-r--r--test/hwasan/TestCases/rich-stack.c66
-rw-r--r--test/hwasan/TestCases/sanitizer_malloc.cc29
-rw-r--r--test/hwasan/TestCases/sizes.cpp80
-rw-r--r--test/hwasan/TestCases/stack-history-length.c36
-rw-r--r--test/hwasan/TestCases/stack-oob.c25
-rw-r--r--test/hwasan/TestCases/stack-oob.cc25
-rw-r--r--test/hwasan/TestCases/stack-uar.c (renamed from test/hwasan/TestCases/stack-uar.cc)6
-rw-r--r--test/hwasan/TestCases/thread-uaf.c56
-rw-r--r--test/hwasan/TestCases/uaf_with_rb_distance.c27
-rw-r--r--test/hwasan/TestCases/use-after-free.c40
-rw-r--r--test/lit.common.cfg28
-rw-r--r--test/lsan/TestCases/Linux/fork_and_leak.cc23
-rw-r--r--test/lsan/TestCases/Linux/fork_with_threads.cc35
-rw-r--r--test/msan/chained_origin_with_signals.cc3
-rw-r--r--test/msan/dtls_test.c3
-rw-r--r--test/msan/fork.cc3
-rw-r--r--test/msan/ioctl_custom.cc3
-rw-r--r--test/msan/lit.cfg8
-rw-r--r--test/msan/signal_stress_test.cc3
-rw-r--r--test/msan/tls_reuse.cc1
-rw-r--r--test/msan/vararg.cc60
-rw-r--r--test/profile/Inputs/instrprof-gcov-exceptions.cpp.gcov2
-rw-r--r--test/profile/Inputs/instrprof-gcov-switch1.c.gcov4
-rw-r--r--test/profile/Inputs/instrprof-gcov-switch2.c.gcov4
-rw-r--r--test/profile/Inputs/instrprof-shared-main.c.gcov4
-rw-r--r--test/profile/instrprof-dlopen-dlclose-gcov.test4
-rw-r--r--test/profile/instrprof-gcov-multiple-bbs-single-line.test2
-rw-r--r--test/profile/instrprof-gcov-two-objects.test2
-rw-r--r--test/profile/instrprof-shared-gcov-flush.test8
-rw-r--r--test/safestack/lit.cfg2
-rw-r--r--test/safestack/pthread-cleanup.c41
-rw-r--r--test/safestack/pthread-stack-size.c53
-rw-r--r--test/safestack/pthread.c2
-rw-r--r--test/sanitizer_common/TestCases/Posix/devname_r.cc5
-rw-r--r--test/sanitizer_common/TestCases/get_module_and_offset_for_pc.cc2
-rw-r--r--test/sanitizer_common/TestCases/pthread_mutexattr_get.cc3
-rw-r--r--test/sanitizer_common/TestCases/sanitizer_coverage_symbolize.cc2
-rw-r--r--test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc2
-rw-r--r--test/sanitizer_common/TestCases/symbolize_pc.cc23
-rw-r--r--test/sanitizer_common/TestCases/symbolize_pc_inline.cc32
-rwxr-xr-xtest/sanitizer_common/ios_commands/iossim_run.py24
-rw-r--r--test/sanitizer_common/lit.common.cfg6
-rw-r--r--test/scudo/dealloc-race.c69
-rw-r--r--test/tsan/Darwin/gcd-sync-block-copy.mm34
-rw-r--r--test/tsan/large_malloc_meta.cc3
-rw-r--r--test/tsan/mmap_large.cc3
-rw-r--r--test/ubsan/CMakeLists.txt63
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/integer-conversion.c324
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/integer-truncation-blacklist.c16
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/integer-truncation.c363
-rw-r--r--test/ubsan/TestCases/Misc/log-path_test.cc2
-rw-r--r--test/ubsan/TestCases/Pointer/index-overflow.cpp6
-rw-r--r--test/ubsan/TestCases/Pointer/unsigned-index-expression.cpp2
-rw-r--r--test/ubsan/lit.common.cfg5
-rw-r--r--test/ubsan/lit.site.cfg.in1
-rw-r--r--test/ubsan_minimal/TestCases/implicit-integer-truncation.c3
-rw-r--r--test/xray/TestCases/Posix/argv0-log-file-name.cc2
-rw-r--r--test/xray/TestCases/Posix/clang-no-xray-instrument.cc11
-rw-r--r--test/xray/TestCases/Posix/fdr-mode.cc1
-rw-r--r--test/xray/TestCases/Posix/fdr-single-thread.cc8
-rw-r--r--test/xray/TestCases/Posix/fdr-thread-order.cc27
-rw-r--r--test/xray/TestCases/Posix/profiling-multi-threaded.cc2
-rw-r--r--test/xray/Unit/lit.site.cfg.in8
-rw-r--r--unittests/lit.common.unit.configured.in1
290 files changed, 6083 insertions, 2387 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 86ca2b3ef..12bb52a4b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -59,6 +59,9 @@ if (NOT COMPILER_RT_ASAN_SHADOW_SCALE STREQUAL "")
-D${COMPILER_RT_ASAN_SHADOW_SCALE_DEFINITION})
endif()
+set(COMPILER_RT_HWASAN_WITH_INTERCEPTORS ON CACHE BOOLEAN
+ "Enable libc interceptors in HWASan (testing mode)")
+
set(COMPILER_RT_BAREMETAL_BUILD OFF CACHE BOOLEAN
"Build for a bare-metal target.")
@@ -79,6 +82,61 @@ if (COMPILER_RT_STANDALONE_BUILD)
or specify the PYTHON_EXECUTABLE CMake variable.")
endif()
+ # Ensure that fat libraries are built correctly on Darwin
+ if(CMAKE_SYSTEM_NAME STREQUAL Darwin)
+ if(NOT CMAKE_LIBTOOL)
+ find_program(CMAKE_XCRUN
+ NAMES
+ xcrun)
+ if(CMAKE_XCRUN)
+ execute_process(COMMAND
+ ${CMAKE_XCRUN} -find libtool
+ OUTPUT_VARIABLE
+ CMAKE_LIBTOOL
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ endif()
+
+ if(NOT CMAKE_LIBTOOL OR NOT EXISTS CMAKE_LIBTOOL)
+ find_program(CMAKE_LIBTOOL
+ NAMES
+ libtool)
+ endif()
+ endif()
+
+ get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES)
+
+ if(CMAKE_LIBTOOL)
+ set(CMAKE_LIBTOOL ${CMAKE_LIBTOOL} CACHE PATH "libtool executable")
+ message(STATUS "Found libtool - ${CMAKE_LIBTOOL}")
+
+ execute_process(COMMAND
+ ${CMAKE_LIBTOOL} -V
+ OUTPUT_VARIABLE
+ LIBTOOL_V_OUTPUT
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ if("${LIBTOOL_V_OUTPUT}" MATCHES ".*cctools-([0-9]+).*")
+ string(REGEX REPLACE ".*cctools-([0-9]+).*" "\\1" LIBTOOL_VERSION ${LIBTOOL_V_OUTPUT})
+ if(NOT LIBTOOL_VERSION VERSION_LESS "862")
+ set(LIBTOOL_NO_WARNING_FLAG "-no_warning_for_no_symbols")
+ endif()
+ endif()
+
+ foreach(lang ${languages})
+ set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "\"${CMAKE_LIBTOOL}\" -static ${LIBTOOL_NO_WARNING_FLAG} -o <TARGET> <LINK_FLAGS> <OBJECTS>")
+ endforeach()
+ endif()
+
+ # Workaround SIP :-(
+ if(DYLD_LIBRARY_PATH)
+ set(dyld_envar "DYLD_LIBRARY_PATH=${DYLD_LIBRARY_PATH}")
+ foreach(lang ${languages})
+ foreach(cmd ${CMAKE_${lang}_CREATE_STATIC_LIBRARY})
+ list(APPEND CMAKE_${lang}_CREATE_STATIC_LIBRARY_NEW "${dyld_envar} ${cmd}")
+ endforeach()
+ endforeach()
+ endif()
+ endif()
+
# Define default arguments to lit.
set(LIT_ARGS_DEFAULT "-sv")
if (MSVC OR XCODE)
@@ -89,12 +147,12 @@ if (COMPILER_RT_STANDALONE_BUILD)
endif()
construct_compiler_rt_default_triple()
-if ("${COMPILER_RT_DEFAULT_TARGET_ABI}" MATCHES "hf$")
+if ("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" MATCHES ".*hf$")
if (${COMPILER_RT_DEFAULT_TARGET_ARCH} MATCHES "^arm")
set(COMPILER_RT_DEFAULT_TARGET_ARCH "armhf")
endif()
endif()
-if ("${COMPILER_RT_DEFAULT_TARGET_ABI}" MATCHES "^android")
+if ("${COMPILER_RT_DEFAULT_TARGET_TRIPLE}" MATCHES ".*android.*")
set(ANDROID 1)
endif()
pythonize_bool(ANDROID)
@@ -310,6 +368,8 @@ 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)
+append_list_if(MINGW -fms-extensions SANITIZER_COMMON_CFLAGS)
+
# Set common link flags.
append_list_if(COMPILER_RT_HAS_NODEFAULTLIBS_FLAG -nodefaultlibs SANITIZER_COMMON_LINK_FLAGS)
@@ -325,14 +385,6 @@ endif()
append_list_if(COMPILER_RT_HAS_LIBC c SANITIZER_COMMON_LINK_LIBS)
-if(ANDROID)
-# Put the Sanitizer shared libraries in the global group. For more details, see
-# android-changes-for-ndk-developers.md#changes-to-library-search-order
- if (COMPILER_RT_HAS_Z_GLOBAL)
- list(APPEND SANITIZER_COMMON_LINK_FLAGS -Wl,-z,global)
- endif()
-endif()
-
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia")
list(APPEND SANITIZER_COMMON_LINK_FLAGS -Wl,-z,defs,-z,now,-z,relro)
list(APPEND SANITIZER_COMMON_LINK_LIBS zircon)
diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake
index cd4c704fc..4efbd3a4a 100644
--- a/cmake/Modules/AddCompilerRT.cmake
+++ b/cmake/Modules/AddCompilerRT.cmake
@@ -357,6 +357,16 @@ set(COMPILER_RT_GTEST_CFLAGS
-I${COMPILER_RT_GTEST_PATH}
)
+# Mocking support.
+set(COMPILER_RT_GMOCK_PATH ${LLVM_MAIN_SRC_DIR}/utils/unittest/googlemock)
+set(COMPILER_RT_GMOCK_SOURCE ${COMPILER_RT_GMOCK_PATH}/src/gmock-all.cc)
+set(COMPILER_RT_GMOCK_CFLAGS
+ -DGTEST_NO_LLVM_RAW_OSTREAM=1
+ -DGTEST_HAS_RTTI=0
+ -I${COMPILER_RT_GMOCK_PATH}/include
+ -I${COMPILER_RT_GMOCK_PATH}
+)
+
append_list_if(COMPILER_RT_DEBUG -DSANITIZER_DEBUG=1 COMPILER_RT_UNITTEST_CFLAGS)
append_list_if(COMPILER_RT_HAS_WCOVERED_SWITCH_DEFAULT_FLAG -Wno-covered-switch-default COMPILER_RT_UNITTEST_CFLAGS)
diff --git a/cmake/Modules/CompilerRTUtils.cmake b/cmake/Modules/CompilerRTUtils.cmake
index c3f2237d8..d2cb48677 100644
--- a/cmake/Modules/CompilerRTUtils.cmake
+++ b/cmake/Modules/CompilerRTUtils.cmake
@@ -217,7 +217,7 @@ macro(load_llvm_config)
endif()
if (LLVM_CONFIG_PATH)
execute_process(
- COMMAND ${LLVM_CONFIG_PATH} "--obj-root" "--bindir" "--libdir" "--src-root"
+ COMMAND ${LLVM_CONFIG_PATH} "--obj-root" "--bindir" "--libdir" "--src-root" "--includedir"
RESULT_VARIABLE HAD_ERROR
OUTPUT_VARIABLE CONFIG_OUTPUT)
if (HAD_ERROR)
@@ -228,11 +228,31 @@ macro(load_llvm_config)
list(GET CONFIG_OUTPUT 1 TOOLS_BINARY_DIR)
list(GET CONFIG_OUTPUT 2 LIBRARY_DIR)
list(GET CONFIG_OUTPUT 3 MAIN_SRC_DIR)
+ list(GET CONFIG_OUTPUT 4 INCLUDE_DIR)
set(LLVM_BINARY_DIR ${BINARY_DIR} CACHE PATH "Path to LLVM build tree")
- set(LLVM_TOOLS_BINARY_DIR ${TOOLS_BINARY_DIR} CACHE PATH "Path to llvm/bin")
set(LLVM_LIBRARY_DIR ${LIBRARY_DIR} CACHE PATH "Path to llvm/lib")
set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree")
+ set(LLVM_TOOLS_BINARY_DIR ${TOOLS_BINARY_DIR} CACHE PATH "Path to llvm/bin")
+ set(LLVM_INCLUDE_DIR ${INCLUDE_DIR} CACHE PATH "Paths to LLVM headers")
+
+ # Detect if we have the LLVMXRay and TestingSupport library installed and
+ # available from llvm-config.
+ execute_process(
+ COMMAND ${LLVM_CONFIG_PATH} "--ldflags" "--libs" "xray" "testingsupport"
+ RESULT_VARIABLE HAD_ERROR
+ OUTPUT_VARIABLE CONFIG_OUTPUT)
+ if (HAD_ERROR)
+ message(WARNING "llvm-config finding xray failed with status ${HAD_ERROR}")
+ set(COMPILER_RT_HAS_LLVMXRAY FALSE)
+ else()
+ string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" CONFIG_OUTPUT ${CONFIG_OUTPUT})
+ list(GET CONFIG_OUTPUT 0 LDFLAGS)
+ list(GET CONFIG_OUTPUT 1 LIBLIST)
+ set(LLVM_XRAY_LDFLAGS ${LDFLAGS} CACHE STRING "Linker flags for LLVMXRay library")
+ set(LLVM_XRAY_LIBLIST ${LIBLIST} CACHE STRING "Library list for LLVMXRay")
+ set(COMPILER_RT_HAS_LLVMXRAY TRUE)
+ endif()
# Make use of LLVM CMake modules.
# --cmakedir is supported since llvm r291218 (4.0 release)
@@ -276,11 +296,6 @@ macro(construct_compiler_rt_default_triple)
string(REPLACE "-" ";" TARGET_TRIPLE_LIST ${COMPILER_RT_DEFAULT_TARGET_TRIPLE})
list(GET TARGET_TRIPLE_LIST 0 COMPILER_RT_DEFAULT_TARGET_ARCH)
- list(GET TARGET_TRIPLE_LIST 1 COMPILER_RT_DEFAULT_TARGET_OS)
- list(LENGTH TARGET_TRIPLE_LIST TARGET_TRIPLE_LIST_LENGTH)
- if(TARGET_TRIPLE_LIST_LENGTH GREATER 2)
- list(GET TARGET_TRIPLE_LIST 2 COMPILER_RT_DEFAULT_TARGET_ABI)
- endif()
# Determine if test target triple is specified explicitly, and doesn't match the
# default.
if(NOT COMPILER_RT_DEFAULT_TARGET_TRIPLE STREQUAL TARGET_TRIPLE)
@@ -320,13 +335,12 @@ function(filter_builtin_sources output_var exclude_or_include excluded_list)
endfunction()
function(get_compiler_rt_target arch variable)
+ string(FIND ${COMPILER_RT_DEFAULT_TARGET_TRIPLE} "-" dash_index)
+ string(SUBSTRING ${COMPILER_RT_DEFAULT_TARGET_TRIPLE} ${dash_index} -1 triple_suffix)
if(ANDROID AND ${arch} STREQUAL "i386")
- set(target "i686${COMPILER_RT_OS_SUFFIX}-${COMPILER_RT_DEFAULT_TARGET_OS}")
+ set(target "i686${COMPILER_RT_OS_SUFFIX}${triple_suffix}")
else()
- set(target "${arch}-${COMPILER_RT_DEFAULT_TARGET_OS}")
- endif()
- if(COMPILER_RT_DEFAULT_TARGET_ABI)
- set(target "${target}-${COMPILER_RT_DEFAULT_TARGET_ABI}")
+ set(target "${arch}${triple_suffix}")
endif()
set(${variable} ${target} PARENT_SCOPE)
endfunction()
diff --git a/cmake/Modules/SanitizerUtils.cmake b/cmake/Modules/SanitizerUtils.cmake
index b6312426c..8fe4baae4 100644
--- a/cmake/Modules/SanitizerUtils.cmake
+++ b/cmake/Modules/SanitizerUtils.cmake
@@ -51,7 +51,15 @@ endmacro()
# This function is only used on Darwin, where undefined symbols must be specified
# in the linker invocation.
function(add_weak_symbols libname link_flags)
- file(STRINGS "${COMPILER_RT_SOURCE_DIR}/lib/${libname}/weak_symbols.txt" WEAK_SYMBOLS)
+ set(weak_symbols_file "${COMPILER_RT_SOURCE_DIR}/lib/${libname}/weak_symbols.txt")
+ file(STRINGS "${weak_symbols_file}" WEAK_SYMBOLS)
+ # Add this file as a configure-time dependency so that changes to this
+ # file trigger a re-configure. This is necessary so that `${link_flags}`
+ # is changed when appropriate.
+ set_property(
+ DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ APPEND
+ PROPERTY CMAKE_CONFIGURE_DEPENDS "${weak_symbols_file}")
set(local_link_flags ${${link_flags}})
foreach(SYMBOL ${WEAK_SYMBOLS})
set(local_link_flags ${local_link_flags} -Wl,-U,${SYMBOL})
diff --git a/cmake/base-config-ix.cmake b/cmake/base-config-ix.cmake
index 91fe2494b..b92e29a2e 100644
--- a/cmake/base-config-ix.cmake
+++ b/cmake/base-config-ix.cmake
@@ -191,11 +191,11 @@ macro(test_targets)
# clang's default CPU's. In the 64-bit case, we must also specify the ABI
# since the default ABI differs between gcc and clang.
# FIXME: Ideally, we would build the N32 library too.
- test_target_arch(mipsel "" "-mips32r2" "--target=mipsel-linux-gnu")
- test_target_arch(mips64el "" "-mips64r2" "--target=mips64el-linux-gnu" "-mabi=64")
+ test_target_arch(mipsel "" "-mips32r2" "-mabi=32")
+ test_target_arch(mips64el "" "-mips64r2" "-mabi=64")
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "mips")
- test_target_arch(mips "" "-mips32r2" "--target=mips-linux-gnu")
- test_target_arch(mips64 "" "-mips64r2" "--target=mips64-linux-gnu" "-mabi=64")
+ test_target_arch(mips "" "-mips32r2" "-mabi=32")
+ test_target_arch(mips64 "" "-mips64r2" "-mabi=64")
elseif("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "arm")
if(WIN32)
test_target_arch(arm "" "" "")
diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake
index f3935ffd6..27097b7e9 100644
--- a/cmake/config-ix.cmake
+++ b/cmake/config-ix.cmake
@@ -37,6 +37,19 @@ if (COMPILER_RT_HAS_NODEFAULTLIBS_FLAG)
elseif (COMPILER_RT_HAS_GCC_LIB)
list(APPEND CMAKE_REQUIRED_LIBRARIES gcc)
endif ()
+ if (MINGW)
+ # Mingw64 requires quite a few "C" runtime libraries in order for basic
+ # programs to link successfully with -nodefaultlibs.
+ if (COMPILER_RT_USE_BUILTINS_LIBRARY)
+ set(MINGW_RUNTIME ${COMPILER_RT_BUILTINS_LIBRARY})
+ else ()
+ set(MINGW_RUNTIME gcc_s gcc)
+ endif()
+ set(MINGW_LIBRARIES mingw32 ${MINGW_RUNTIME} moldname mingwex msvcrt advapi32
+ shell32 user32 kernel32 mingw32 ${MINGW_RUNTIME}
+ moldname mingwex msvcrt)
+ list(APPEND CMAKE_REQUIRED_LIBRARIES ${MINGW_LIBRARIES})
+ endif()
endif ()
# CodeGen options.
@@ -105,6 +118,19 @@ check_library_exists(dl dlopen "" COMPILER_RT_HAS_LIBDL)
check_library_exists(rt shm_open "" COMPILER_RT_HAS_LIBRT)
check_library_exists(m pow "" COMPILER_RT_HAS_LIBM)
check_library_exists(pthread pthread_create "" COMPILER_RT_HAS_LIBPTHREAD)
+
+# Look for terminfo library, used in unittests that depend on LLVMSupport.
+if(LLVM_ENABLE_TERMINFO)
+ foreach(library tinfo terminfo curses ncurses ncursesw)
+ check_library_exists(
+ ${library} setupterm "" COMPILER_RT_HAS_TERMINFO)
+ if(COMPILER_RT_HAS_TERMINFO)
+ set(COMPILER_RT_TERMINFO_LIB "${library}")
+ break()
+ endif()
+ endforeach()
+endif()
+
if (ANDROID AND COMPILER_RT_HAS_LIBDL)
# Android's libstdc++ has a dependency on libdl.
list(APPEND CMAKE_REQUIRED_LIBRARIES dl)
@@ -501,7 +527,8 @@ list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}")
if (SANITIZER_COMMON_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND
(OS_NAME MATCHES "Android|Darwin|Linux|FreeBSD|NetBSD|OpenBSD|Fuchsia|SunOS" OR
- (OS_NAME MATCHES "Windows" AND (NOT MINGW AND NOT CYGWIN))))
+ (OS_NAME MATCHES "Windows" AND NOT CYGWIN AND
+ (NOT MINGW OR CMAKE_CXX_COMPILER_ID MATCHES "Clang"))))
set(COMPILER_RT_HAS_SANITIZER_COMMON TRUE)
else()
set(COMPILER_RT_HAS_SANITIZER_COMMON FALSE)
@@ -598,7 +625,7 @@ else()
endif()
if (COMPILER_RT_HAS_SANITIZER_COMMON AND ESAN_SUPPORTED_ARCH AND
- OS_NAME MATCHES "Linux")
+ OS_NAME MATCHES "Linux|FreeBSD")
set(COMPILER_RT_HAS_ESAN TRUE)
else()
set(COMPILER_RT_HAS_ESAN FALSE)
@@ -619,7 +646,10 @@ else()
endif()
if (COMPILER_RT_HAS_SANITIZER_COMMON AND FUZZER_SUPPORTED_ARCH AND
- OS_NAME MATCHES "Android|Darwin|Linux|NetBSD|FreeBSD|OpenBSD|Fuchsia")
+ OS_NAME MATCHES "Android|Darwin|Linux|NetBSD|FreeBSD|OpenBSD|Fuchsia|Windows" AND
+ # TODO: Support builds with MSVC.
+ NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" AND
+ NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
set(COMPILER_RT_HAS_FUZZER TRUE)
else()
set(COMPILER_RT_HAS_FUZZER FALSE)
diff --git a/include/sanitizer/allocator_interface.h b/include/sanitizer/allocator_interface.h
index 89f328301..e44c4a13e 100644
--- a/include/sanitizer/allocator_interface.h
+++ b/include/sanitizer/allocator_interface.h
@@ -82,7 +82,6 @@ extern "C" {
Currently available with ASan only.
*/
void __sanitizer_purge_allocator(void);
-
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/include/sanitizer/common_interface_defs.h b/include/sanitizer/common_interface_defs.h
index d11cb1add..bf015eb23 100644
--- a/include/sanitizer/common_interface_defs.h
+++ b/include/sanitizer/common_interface_defs.h
@@ -124,6 +124,12 @@ extern "C" {
// Symbolizes the supplied 'pc' using the format string 'fmt'.
// Outputs at most 'out_buf_size' bytes into 'out_buf'.
+ // If 'out_buf' is not empty then output is zero or more non empty C strings
+ // followed by single empty C string. Multiple strings can be returned if PC
+ // corresponds to inlined function. Inlined frames are printed in the order
+ // from "most-inlined" to the "least-inlined", so the last frame should be the
+ // not inlined function.
+ // Inlined frames can be removed with 'symbolize_inline_frames=0'.
// The format syntax is described in
// lib/sanitizer_common/sanitizer_stacktrace_printer.h.
void __sanitizer_symbolize_pc(void *pc, const char *fmt, char *out_buf,
diff --git a/include/sanitizer/hwasan_interface.h b/include/sanitizer/hwasan_interface.h
index 0c306cf27..1affd875a 100644
--- a/include/sanitizer/hwasan_interface.h
+++ b/include/sanitizer/hwasan_interface.h
@@ -19,6 +19,12 @@
#ifdef __cplusplus
extern "C" {
#endif
+ // Initialize shadow but not the rest of the runtime.
+ // Does not call libc unless there is an error.
+ // Can be called multiple times, or not at all (in which case shadow will
+ // be initialized in compiler-inserted __hwasan_init() call).
+ void __hwasan_shadow_init(void);
+
// This function may be optionally provided by user and should return
// a string containing HWASan runtime options. See asan_flags.h for details.
const char* __hwasan_default_options(void);
@@ -26,6 +32,51 @@ extern "C" {
void __hwasan_enable_allocator_tagging(void);
void __hwasan_disable_allocator_tagging(void);
+ // Mark region of memory with the given tag. Both address and size need to be
+ // 16-byte aligned.
+ void __hwasan_tag_memory(const volatile void *p, unsigned char tag,
+ size_t size);
+
+ /// Set pointer tag. Previous tag is lost.
+ void *__hwasan_tag_pointer(const volatile void *p, unsigned char tag);
+
+ // Set memory tag from the current SP address to the given address to zero.
+ // This is meant to annotate longjmp and other non-local jumps.
+ // This function needs to know the (almost) exact destination frame address;
+ // clearing shadow for the entire thread stack like __asan_handle_no_return
+ // does would cause false reports.
+ void __hwasan_handle_longjmp(const void *sp_dst);
+
+ // Libc hook for thread creation. Should be called in the child thread before
+ // any instrumented code.
+ void __hwasan_thread_enter();
+
+ // Libc hook for thread destruction. No instrumented code should run after
+ // this call.
+ void __hwasan_thread_exit();
+
+ // Print shadow and origin for the memory range to stderr in a human-readable
+ // format.
+ void __hwasan_print_shadow(const volatile void *x, size_t size);
+
+ // Print one-line report about the memory usage of the current process.
+ void __hwasan_print_memory_usage();
+
+ int __sanitizer_posix_memalign(void **memptr, size_t alignment, size_t size);
+ void * __sanitizer_memalign(size_t alignment, size_t size);
+ void * __sanitizer_aligned_alloc(size_t alignment, size_t size);
+ void * __sanitizer___libc_memalign(size_t alignment, size_t size);
+ void * __sanitizer_valloc(size_t size);
+ void * __sanitizer_pvalloc(size_t size);
+ void __sanitizer_free(void *ptr);
+ void __sanitizer_cfree(void *ptr);
+ size_t __sanitizer_malloc_usable_size(const void *ptr);
+ struct mallinfo __sanitizer_mallinfo();
+ int __sanitizer_mallopt(int cmd, int value);
+ void __sanitizer_malloc_stats(void);
+ void * __sanitizer_calloc(size_t nmemb, size_t size);
+ void * __sanitizer_realloc(void *ptr, size_t size);
+ void * __sanitizer_malloc(size_t size);
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/include/xray/xray_log_interface.h b/include/xray/xray_log_interface.h
index 5f8b3a4a0..399467860 100644
--- a/include/xray/xray_log_interface.h
+++ b/include/xray/xray_log_interface.h
@@ -158,8 +158,8 @@ struct XRayLogImpl {
/// The log initialization routine provided by the implementation, always
/// provided with the following parameters:
///
- /// - buffer size
- /// - maximum number of buffers
+ /// - buffer size (unused)
+ /// - maximum number of buffers (unused)
/// - a pointer to an argument struct that the implementation MUST handle
/// - the size of the argument struct
///
@@ -355,25 +355,4 @@ XRayLogFlushStatus __xray_log_process_buffers(void (*Processor)(const char *,
} // extern "C"
-namespace __xray {
-
-/// DEPRECATED: Use __xray_log_init_mode(...) instead, and provide flag
-/// configuration strings to set the options instead.
-/// Options used by the LLVM XRay FDR logging implementation.
-struct FDRLoggingOptions {
- bool ReportErrors = false;
- int Fd = -1;
-};
-
-/// DEPRECATED: Use __xray_log_init_mode(...) instead, and provide flag
-/// configuration strings to set the options instead.
-/// Options used by the LLVM XRay Basic (Naive) logging implementation.
-struct BasicLoggingOptions {
- int DurationFilterMicros = 0;
- size_t MaxStackDepth = 0;
- size_t ThreadBufferSize = 0;
-};
-
-} // namespace __xray
-
#endif // XRAY_XRAY_LOG_INTERFACE_H
diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt
index 2ae5c85ec..4afc8c944 100644
--- a/lib/asan/CMakeLists.txt
+++ b/lib/asan/CMakeLists.txt
@@ -75,6 +75,14 @@ append_rtti_flag(OFF ASAN_CFLAGS)
set(ASAN_DYNAMIC_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS})
+if(ANDROID)
+# Put most Sanitizer shared libraries in the global group. For more details, see
+# android-changes-for-ndk-developers.md#changes-to-library-search-order
+ if (COMPILER_RT_HAS_Z_GLOBAL)
+ list(APPEND ASAN_DYNAMIC_LINK_FLAGS -Wl,-z,global)
+ endif()
+endif()
+
set(ASAN_DYNAMIC_DEFINITIONS
${ASAN_COMMON_DEFINITIONS} ASAN_DYNAMIC=1)
append_list_if(WIN32 INTERCEPTION_DYNAMIC_CRT ASAN_DYNAMIC_DEFINITIONS)
@@ -91,6 +99,7 @@ append_list_if(COMPILER_RT_HAS_LIBRT rt ASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBM m ASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread ASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBLOG log ASAN_DYNAMIC_LIBS)
+append_list_if(MINGW "${MINGW_LIBRARIES}" ASAN_DYNAMIC_LIBS)
# Compile ASan sources into an object library.
@@ -138,6 +147,7 @@ if(APPLE)
add_weak_symbols("lsan" WEAK_SYMBOL_LINK_FLAGS)
add_weak_symbols("ubsan" WEAK_SYMBOL_LINK_FLAGS)
add_weak_symbols("sanitizer_common" WEAK_SYMBOL_LINK_FLAGS)
+ add_weak_symbols("xray" WEAK_SYMBOL_LINK_FLAGS)
add_compiler_rt_runtime(clang_rt.asan
SHARED
@@ -215,7 +225,7 @@ else()
endif()
set(ASAN_DYNAMIC_WEAK_INTERCEPTION)
- if (MSVC)
+ if (WIN32)
add_compiler_rt_object_libraries(AsanWeakInterception
${SANITIZER_COMMON_SUPPORTED_OS}
ARCHS ${arch}
diff --git a/lib/asan/asan_globals_win.cc b/lib/asan/asan_globals_win.cc
index 29ab5ebf1..0e75992bf 100644
--- a/lib/asan/asan_globals_win.cc
+++ b/lib/asan/asan_globals_win.cc
@@ -29,7 +29,7 @@ static void call_on_globals(void (*hook)(__asan_global *, uptr)) {
__asan_global *end = &__asan_globals_end;
uptr bytediff = (uptr)end - (uptr)start;
if (bytediff % sizeof(__asan_global) != 0) {
-#ifdef SANITIZER_DLL_THUNK
+#if defined(SANITIZER_DLL_THUNK) || defined(SANITIZER_DYNAMIC_RUNTIME_THUNK)
__debugbreak();
#else
CHECK("corrupt asan global array");
diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc
index efa058243..a094e051c 100644
--- a/lib/asan/asan_malloc_win.cc
+++ b/lib/asan/asan_malloc_win.cc
@@ -14,8 +14,17 @@
#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_WINDOWS
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
+// Intentionally not including windows.h here, to avoid the risk of
+// pulling in conflicting declarations of these functions. (With mingw-w64,
+// there's a risk of windows.h pulling in stdint.h.)
+typedef int BOOL;
+typedef void *HANDLE;
+typedef const void *LPCVOID;
+typedef void *LPVOID;
+
+#define HEAP_ZERO_MEMORY 0x00000008
+#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010
+
#include "asan_allocator.h"
#include "asan_interceptors.h"
@@ -125,7 +134,7 @@ void *_recalloc_base(void *p, size_t n, size_t elem_size) {
}
ALLOCATION_FUNCTION_ATTRIBUTE
-size_t _msize(const void *ptr) {
+size_t _msize(void *ptr) {
GET_CURRENT_PC_BP_SP;
(void)sp;
return asan_malloc_usable_size(ptr, pc, bp);
diff --git a/lib/asan/asan_new_delete.cc b/lib/asan/asan_new_delete.cc
index 30efd61a9..e6053c1fe 100644
--- a/lib/asan/asan_new_delete.cc
+++ b/lib/asan/asan_new_delete.cc
@@ -26,7 +26,7 @@
// anyway by passing extra -export flags to the linker, which is exactly that
// dllexport would normally do. We need to export them in order to make the
// VS2015 dynamic CRT (MD) work.
-#if SANITIZER_WINDOWS
+#if SANITIZER_WINDOWS && defined(_MSC_VER)
#define CXX_OPERATOR_ATTRIBUTE
#define COMMENT_EXPORT(sym) __pragma(comment(linker, "/export:" sym))
#ifdef _WIN64
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index 67125d38a..5661d911c 100644
--- a/lib/asan/asan_win.cc
+++ b/lib/asan/asan_win.cc
@@ -159,6 +159,14 @@ INTERCEPTOR_WINAPI(DWORD, CreateThread,
namespace __asan {
void InitializePlatformInterceptors() {
+ // The interceptors were not designed to be removable, so we have to keep this
+ // module alive for the life of the process.
+ HMODULE pinned;
+ CHECK(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_PIN,
+ (LPCWSTR)&InitializePlatformInterceptors,
+ &pinned));
+
ASAN_INTERCEPT_FUNC(CreateThread);
ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter);
diff --git a/lib/asan/tests/asan_interface_test.cc b/lib/asan/tests/asan_interface_test.cc
index 69c8fe6f4..b5c8303cb 100644
--- a/lib/asan/tests/asan_interface_test.cc
+++ b/lib/asan/tests/asan_interface_test.cc
@@ -102,6 +102,7 @@ TEST(AddressSanitizerInterface, GetHeapSizeTest) {
}
}
+#if !defined(__NetBSD__)
static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357};
static const size_t kManyThreadsIterations = 250;
static const size_t kManyThreadsNumThreads =
@@ -135,6 +136,7 @@ TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) {
// so we can't check for equality here.
EXPECT_LT(after_test, before_test + (1UL<<20));
}
+#endif
static void DoDoubleFree() {
int *x = Ident(new int);
diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc
index 65acb2839..3e366842c 100644
--- a/lib/asan/tests/asan_noinst_test.cc
+++ b/lib/asan/tests/asan_noinst_test.cc
@@ -153,6 +153,7 @@ TEST(AddressSanitizer, QuarantineTest) {
EXPECT_LT(i, max_i);
}
+#if !defined(__NetBSD__)
void *ThreadedQuarantineTestWorker(void *unused) {
(void)unused;
u32 seed = my_rand();
@@ -187,6 +188,7 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) {
EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20));
}
}
+#endif
void *ThreadedOneSizeMallocStress(void *unused) {
(void)unused;
diff --git a/lib/builtins/arm/addsf3.S b/lib/builtins/arm/addsf3.S
index 362b5c147..74723cbef 100644
--- a/lib/builtins/arm/addsf3.S
+++ b/lib/builtins/arm/addsf3.S
@@ -178,7 +178,7 @@ LOCAL_LABEL(do_substraction):
push {r0, r1, r2, r3}
movs r0, r4
- bl __clzsi2
+ bl SYMBOL_NAME(__clzsi2)
movs r5, r0
pop {r0, r1, r2, r3}
// shift = rep_clz(aSignificand) - rep_clz(implicitBit << 3);
diff --git a/lib/builtins/arm/aeabi_cdcmp.S b/lib/builtins/arm/aeabi_cdcmp.S
index 87dd03dce..adc2d55d9 100644
--- a/lib/builtins/arm/aeabi_cdcmp.S
+++ b/lib/builtins/arm/aeabi_cdcmp.S
@@ -55,7 +55,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmpeq)
mov ip, #APSR_C
msr APSR_nzcvq, ip
#else
- msr CPSR_f, #APSR_C
+ msr APSR_nzcvq, #APSR_C
#endif
JMP(lr)
#endif
@@ -115,11 +115,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmple)
movne ip, #(APSR_C)
1:
-#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
msr APSR_nzcvq, ip
-#else
- msr CPSR_f, ip
-#endif
pop {r0-r3}
POP_PC()
#endif
diff --git a/lib/builtins/arm/aeabi_cfcmp.S b/lib/builtins/arm/aeabi_cfcmp.S
index c5fee6b6a..4b1de9976 100644
--- a/lib/builtins/arm/aeabi_cfcmp.S
+++ b/lib/builtins/arm/aeabi_cfcmp.S
@@ -55,7 +55,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmpeq)
mov ip, #APSR_C
msr APSR_nzcvq, ip
#else
- msr CPSR_f, #APSR_C
+ msr APSR_nzcvq, #APSR_C
#endif
JMP(lr)
#endif
@@ -115,11 +115,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmple)
movne ip, #(APSR_C)
1:
-#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
msr APSR_nzcvq, ip
-#else
- msr CPSR_f, ip
-#endif
pop {r0-r3}
POP_PC()
#endif
diff --git a/lib/builtins/clzdi2.c b/lib/builtins/clzdi2.c
index b56d98f5c..1819e6be4 100644
--- a/lib/builtins/clzdi2.c
+++ b/lib/builtins/clzdi2.c
@@ -16,8 +16,13 @@
/* Returns: the number of leading 0-bits */
-#if !defined(__clang__) && (defined(__sparc64__) || defined(__mips64) || defined(__riscv__))
-/* gcc resolves __builtin_clz -> __clzdi2 leading to infinite recursion */
+#if !defined(__clang__) && \
+ ((defined(__sparc__) && defined(__arch64__)) || \
+ defined(__mips64) || \
+ (defined(__riscv) && __SIZEOF_POINTER__ >= 8))
+/* On 64-bit architectures with neither a native clz instruction nor a native
+ * ctz instruction, gcc resolves __builtin_clz to __clzdi2 rather than
+ * __clzsi2, leading to infinite recursion. */
#define __builtin_clz(a) __clzsi2(a)
extern si_int __clzsi2(si_int);
#endif
diff --git a/lib/builtins/ctzdi2.c b/lib/builtins/ctzdi2.c
index eecde2971..ef6d7fea1 100644
--- a/lib/builtins/ctzdi2.c
+++ b/lib/builtins/ctzdi2.c
@@ -16,8 +16,13 @@
/* Returns: the number of trailing 0-bits */
-#if !defined(__clang__) && (defined(__sparc64__) || defined(__mips64) || defined(__riscv__))
-/* gcc resolves __builtin_ctz -> __ctzdi2 leading to infinite recursion */
+#if !defined(__clang__) && \
+ ((defined(__sparc__) && defined(__arch64__)) || \
+ defined(__mips64) || \
+ (defined(__riscv) && __SIZEOF_POINTER__ >= 8))
+/* On 64-bit architectures with neither a native clz instruction nor a native
+ * ctz instruction, gcc resolves __builtin_ctz to __ctzdi2 rather than
+ * __ctzsi2, leading to infinite recursion. */
#define __builtin_ctz(a) __ctzsi2(a)
extern si_int __ctzsi2(si_int);
#endif
diff --git a/lib/builtins/divdc3.c b/lib/builtins/divdc3.c
index 3c88390b5..392d6ecac 100644
--- a/lib/builtins/divdc3.c
+++ b/lib/builtins/divdc3.c
@@ -12,6 +12,8 @@
* ===----------------------------------------------------------------------===
*/
+#define DOUBLE_PRECISION
+#include "fp_lib.h"
#include "int_lib.h"
#include "int_math.h"
@@ -21,7 +23,7 @@ COMPILER_RT_ABI Dcomplex
__divdc3(double __a, double __b, double __c, double __d)
{
int __ilogbw = 0;
- double __logbw = crt_logb(crt_fmax(crt_fabs(__c), crt_fabs(__d)));
+ double __logbw = __compiler_rt_logb(crt_fmax(crt_fabs(__c), crt_fabs(__d)));
if (crt_isfinite(__logbw))
{
__ilogbw = (int)__logbw;
diff --git a/lib/builtins/divsc3.c b/lib/builtins/divsc3.c
index 42a48315e..0d18a256c 100644
--- a/lib/builtins/divsc3.c
+++ b/lib/builtins/divsc3.c
@@ -12,6 +12,8 @@
*===----------------------------------------------------------------------===
*/
+#define SINGLE_PRECISION
+#include "fp_lib.h"
#include "int_lib.h"
#include "int_math.h"
@@ -21,7 +23,8 @@ COMPILER_RT_ABI Fcomplex
__divsc3(float __a, float __b, float __c, float __d)
{
int __ilogbw = 0;
- float __logbw = crt_logbf(crt_fmaxf(crt_fabsf(__c), crt_fabsf(__d)));
+ float __logbw =
+ __compiler_rt_logbf(crt_fmaxf(crt_fabsf(__c), crt_fabsf(__d)));
if (crt_isfinite(__logbw))
{
__ilogbw = (int)__logbw;
diff --git a/lib/builtins/divtc3.c b/lib/builtins/divtc3.c
index 16e538ba4..e5ea00d84 100644
--- a/lib/builtins/divtc3.c
+++ b/lib/builtins/divtc3.c
@@ -12,6 +12,8 @@
*===----------------------------------------------------------------------===
*/
+#define QUAD_PRECISION
+#include "fp_lib.h"
#include "int_lib.h"
#include "int_math.h"
@@ -21,7 +23,8 @@ COMPILER_RT_ABI Lcomplex
__divtc3(long double __a, long double __b, long double __c, long double __d)
{
int __ilogbw = 0;
- long double __logbw = crt_logbl(crt_fmaxl(crt_fabsl(__c), crt_fabsl(__d)));
+ long double __logbw =
+ __compiler_rt_logbl(crt_fmaxl(crt_fabsl(__c), crt_fabsl(__d)));
if (crt_isfinite(__logbw))
{
__ilogbw = (int)__logbw;
diff --git a/lib/builtins/emutls.c b/lib/builtins/emutls.c
index 07d436e26..ef95a1c26 100644
--- a/lib/builtins/emutls.c
+++ b/lib/builtins/emutls.c
@@ -42,6 +42,7 @@ static void emutls_shutdown(emutls_address_array *array);
static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_key_t emutls_pthread_key;
+static bool emutls_key_created = false;
typedef unsigned int gcc_word __attribute__((mode(word)));
typedef unsigned int gcc_pointer __attribute__((mode(pointer)));
@@ -109,6 +110,7 @@ static void emutls_key_destructor(void* ptr) {
static __inline void emutls_init(void) {
if (pthread_key_create(&emutls_pthread_key, emutls_key_destructor) != 0)
abort();
+ emutls_key_created = true;
}
static __inline void emutls_init_once(void) {
@@ -390,3 +392,14 @@ void* __emutls_get_address(__emutls_control* control) {
array->data[index] = emutls_allocate_object(control);
return array->data[index];
}
+
+#ifdef __BIONIC__
+/* Called by Bionic on dlclose to delete the emutls pthread key. */
+__attribute__((visibility("hidden")))
+void __emutls_unregister_key(void) {
+ if (emutls_key_created) {
+ pthread_key_delete(emutls_pthread_key);
+ emutls_key_created = false;
+ }
+}
+#endif
diff --git a/lib/builtins/fp_lib.h b/lib/builtins/fp_lib.h
index 223fb980a..a0e19ab6a 100644
--- a/lib/builtins/fp_lib.h
+++ b/lib/builtins/fp_lib.h
@@ -25,6 +25,7 @@
#include <stdbool.h>
#include <limits.h>
#include "int_lib.h"
+#include "int_math.h"
// x86_64 FreeBSD prior v9.3 define fixed-width types incorrectly in
// 32-bit mode.
@@ -265,6 +266,62 @@ static __inline void wideRightShiftWithSticky(rep_t *hi, rep_t *lo, unsigned int
*hi = 0;
}
}
+
+// Implements logb methods (logb, logbf, logbl) for IEEE-754. This avoids
+// pulling in a libm dependency from compiler-rt, but is not meant to replace
+// it (i.e. code calling logb() should get the one from libm, not this), hence
+// the __compiler_rt prefix.
+static __inline fp_t __compiler_rt_logbX(fp_t x) {
+ rep_t rep = toRep(x);
+ int exp = (rep & exponentMask) >> significandBits;
+
+ // Abnormal cases:
+ // 1) +/- inf returns +inf; NaN returns NaN
+ // 2) 0.0 returns -inf
+ if (exp == maxExponent) {
+ if (((rep & signBit) == 0) || (x != x)) {
+ return x; // NaN or +inf: return x
+ } else {
+ return -x; // -inf: return -x
+ }
+ } else if (x == 0.0) {
+ // 0.0: return -inf
+ return fromRep(infRep | signBit);
+ }
+
+ if (exp != 0) {
+ // Normal number
+ return exp - exponentBias; // Unbias exponent
+ } else {
+ // Subnormal number; normalize and repeat
+ rep &= absMask;
+ const int shift = 1 - normalize(&rep);
+ exp = (rep & exponentMask) >> significandBits;
+ return exp - exponentBias - shift; // Unbias exponent
+ }
+}
+#endif
+
+#if defined(SINGLE_PRECISION)
+static __inline fp_t __compiler_rt_logbf(fp_t x) {
+ return __compiler_rt_logbX(x);
+}
+#elif defined(DOUBLE_PRECISION)
+static __inline fp_t __compiler_rt_logb(fp_t x) {
+ return __compiler_rt_logbX(x);
+}
+#elif defined(QUAD_PRECISION)
+ #if defined(CRT_LDBL_128BIT)
+static __inline fp_t __compiler_rt_logbl(fp_t x) {
+ return __compiler_rt_logbX(x);
+}
+ #else
+// The generic implementation only works for ieee754 floating point. For other
+// floating point types, continue to rely on the libm implementation for now.
+static __inline long double __compiler_rt_logbl(long double x) {
+ return crt_logbl(x);
+}
+ #endif
#endif
#endif // FP_LIB_HEADER
diff --git a/lib/builtins/int_math.h b/lib/builtins/int_math.h
index fc81fb7f0..aa3d0721a 100644
--- a/lib/builtins/int_math.h
+++ b/lib/builtins/int_math.h
@@ -92,12 +92,8 @@
#endif
#if defined(_MSC_VER) && !defined(__clang__)
-#define crt_logb(x) logb((x))
-#define crt_logbf(x) logbf((x))
#define crt_logbl(x) logbl((x))
#else
-#define crt_logb(x) __builtin_logb((x))
-#define crt_logbf(x) __builtin_logbf((x))
#define crt_logbl(x) __builtin_logbl((x))
#endif
diff --git a/lib/builtins/int_util.c b/lib/builtins/int_util.c
index de87410db..752f20155 100644
--- a/lib/builtins/int_util.c
+++ b/lib/builtins/int_util.c
@@ -27,7 +27,7 @@ NORETURN extern void panic(const char *, ...);
#ifndef _WIN32
__attribute__((visibility("hidden")))
#endif
-void compilerrt_abort_impl(const char *file, int line, const char *function) {
+void __compilerrt_abort_impl(const char *file, int line, const char *function) {
panic("%s:%d: abort in %s", file, line, function);
}
@@ -41,7 +41,7 @@ NORETURN extern void __assert_rtn(const char *func, const char *file, int line,
__attribute__((weak))
__attribute__((visibility("hidden")))
#endif
-void compilerrt_abort_impl(const char *file, int line, const char *function) {
+void __compilerrt_abort_impl(const char *file, int line, const char *function) {
__assert_rtn(function, file, line, "libcompiler_rt abort");
}
@@ -51,7 +51,7 @@ void compilerrt_abort_impl(const char *file, int line, const char *function) {
__attribute__((weak))
__attribute__((visibility("hidden")))
#endif
-void compilerrt_abort_impl(const char *file, int line, const char *function) {
+void __compilerrt_abort_impl(const char *file, int line, const char *function) {
__builtin_trap();
}
@@ -64,7 +64,7 @@ void compilerrt_abort_impl(const char *file, int line, const char *function) {
__attribute__((weak))
__attribute__((visibility("hidden")))
#endif
-void compilerrt_abort_impl(const char *file, int line, const char *function) {
+void __compilerrt_abort_impl(const char *file, int line, const char *function) {
abort();
}
diff --git a/lib/builtins/int_util.h b/lib/builtins/int_util.h
index a7b20ed66..c3c87381a 100644
--- a/lib/builtins/int_util.h
+++ b/lib/builtins/int_util.h
@@ -20,10 +20,10 @@
#define INT_UTIL_H
/** \brief Trigger a program abort (or panic for kernel code). */
-#define compilerrt_abort() compilerrt_abort_impl(__FILE__, __LINE__, __func__)
+#define compilerrt_abort() __compilerrt_abort_impl(__FILE__, __LINE__, __func__)
-NORETURN void compilerrt_abort_impl(const char *file, int line,
- const char *function);
+NORETURN void __compilerrt_abort_impl(const char *file, int line,
+ const char *function);
#define COMPILE_TIME_ASSERT(expr) COMPILE_TIME_ASSERT1(expr, __COUNTER__)
#define COMPILE_TIME_ASSERT1(expr, cnt) COMPILE_TIME_ASSERT2(expr, cnt)
diff --git a/lib/builtins/ppc/divtc3.c b/lib/builtins/ppc/divtc3.c
index 8ec41c528..ef532b841 100644
--- a/lib/builtins/ppc/divtc3.c
+++ b/lib/builtins/ppc/divtc3.c
@@ -4,6 +4,11 @@
#include "DD.h"
#include "../int_math.h"
+// Use DOUBLE_PRECISION because the soft-fp method we use is logb (on the upper
+// half of the long doubles), even though this file defines complex division for
+// 128-bit floats.
+#define DOUBLE_PRECISION
+#include "../fp_lib.h"
#if !defined(CRT_INFINITY) && defined(HUGE_VAL)
#define CRT_INFINITY HUGE_VAL
@@ -21,9 +26,10 @@ __divtc3(long double a, long double b, long double c, long double d)
DD dDD = { .ld = d };
int ilogbw = 0;
- const double logbw = crt_logb(crt_fmax(crt_fabs(cDD.s.hi), crt_fabs(dDD.s.hi) ));
-
- if (crt_isfinite(logbw))
+ const double logbw = __compiler_rt_logb(
+ crt_fmax(crt_fabs(cDD.s.hi), crt_fabs(dDD.s.hi)));
+
+ if (crt_isfinite(logbw))
{
ilogbw = (int)logbw;
diff --git a/lib/cfi/CMakeLists.txt b/lib/cfi/CMakeLists.txt
index 7ed72bca5..463a1fd59 100644
--- a/lib/cfi/CMakeLists.txt
+++ b/lib/cfi/CMakeLists.txt
@@ -1,6 +1,6 @@
add_compiler_rt_component(cfi)
-if(OS_NAME MATCHES "Linux")
+if(OS_NAME MATCHES "Linux" OR OS_NAME MATCHES "FreeBSD" OR OS_NAME MATCHES "NetBSD")
set(CFI_SOURCES cfi.cc)
include_directories(..)
diff --git a/lib/cfi/cfi.cc b/lib/cfi/cfi.cc
index a2f127f93..b0a943759 100644
--- a/lib/cfi/cfi.cc
+++ b/lib/cfi/cfi.cc
@@ -13,15 +13,33 @@
#include <assert.h>
#include <elf.h>
+
+#include "sanitizer_common/sanitizer_common.h"
+#if SANITIZER_FREEBSD
+#include <sys/link_elf.h>
+#endif
#include <link.h>
#include <string.h>
+#include <stdlib.h>
#include <sys/mman.h>
+#if SANITIZER_LINUX
typedef ElfW(Phdr) Elf_Phdr;
typedef ElfW(Ehdr) Elf_Ehdr;
+typedef ElfW(Addr) Elf_Addr;
+typedef ElfW(Sym) Elf_Sym;
+typedef ElfW(Dyn) Elf_Dyn;
+#elif SANITIZER_FREEBSD
+#if SANITIZER_WORDSIZE == 64
+#define ElfW64_Dyn Elf_Dyn
+#define ElfW64_Sym Elf_Sym
+#else
+#define ElfW32_Dyn Elf_Dyn
+#define ElfW32_Sym Elf_Sym
+#endif
+#endif
#include "interception/interception.h"
-#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "ubsan/ubsan_init.h"
#include "ubsan/ubsan_flags.h"
@@ -154,15 +172,25 @@ void ShadowBuilder::Add(uptr begin, uptr end, uptr cfi_check) {
*s = sv;
}
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD
void ShadowBuilder::Install() {
MprotectReadOnly(shadow_, GetShadowSize());
uptr main_shadow = GetShadow();
if (main_shadow) {
// Update.
+#if SANITIZER_LINUX
void *res = mremap((void *)shadow_, GetShadowSize(), GetShadowSize(),
MREMAP_MAYMOVE | MREMAP_FIXED, (void *)main_shadow);
CHECK(res != MAP_FAILED);
+#elif SANITIZER_NETBSD
+ void *res = mremap((void *)shadow_, GetShadowSize(), (void *)main_shadow,
+ GetShadowSize(), MAP_FIXED);
+ CHECK(res != MAP_FAILED);
+#else
+ void *res = MmapFixedOrDie(shadow_, GetShadowSize());
+ CHECK(res != MAP_FAILED);
+ ::memcpy(&shadow_, &main_shadow, GetShadowSize());
+#endif
} else {
// Initial setup.
CHECK_EQ(kCfiShadowLimitsStorageSize, GetPageSizeCached());
@@ -183,17 +211,17 @@ void ShadowBuilder::Install() {
// dlopen(RTLD_NOLOAD | RTLD_LAZY)
// dlsym("__cfi_check").
uptr find_cfi_check_in_dso(dl_phdr_info *info) {
- const ElfW(Dyn) *dynamic = nullptr;
+ const Elf_Dyn *dynamic = nullptr;
for (int i = 0; i < info->dlpi_phnum; ++i) {
if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
dynamic =
- (const ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
+ (const Elf_Dyn *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
break;
}
}
if (!dynamic) return 0;
uptr strtab = 0, symtab = 0, strsz = 0;
- for (const ElfW(Dyn) *p = dynamic; p->d_tag != PT_NULL; ++p) {
+ for (const Elf_Dyn *p = dynamic; p->d_tag != PT_NULL; ++p) {
if (p->d_tag == DT_SYMTAB)
symtab = p->d_un.d_ptr;
else if (p->d_tag == DT_STRTAB)
@@ -227,7 +255,7 @@ uptr find_cfi_check_in_dso(dl_phdr_info *info) {
return 0;
}
- for (const ElfW(Sym) *p = (const ElfW(Sym) *)symtab; (ElfW(Addr))p < strtab;
+ for (const Elf_Sym *p = (const Elf_Sym *)symtab; (Elf_Addr)p < strtab;
++p) {
// There is no reliable way to find the end of the symbol table. In
// lld-produces files, there are other sections between symtab and strtab.
diff --git a/lib/cfi/cfi_blacklist.txt b/lib/cfi/cfi_blacklist.txt
index 3d73508f5..4a0f03949 100644
--- a/lib/cfi/cfi_blacklist.txt
+++ b/lib/cfi/cfi_blacklist.txt
@@ -1,13 +1,11 @@
[cfi-unrelated-cast]
# The specification of std::get_temporary_buffer mandates a cast to
-# uninitialized T* (libstdc++, libc++, MSVC stdlib).
+# uninitialized T* (libstdc++, MSVC stdlib).
fun:_ZSt20get_temporary_buffer*
-fun:_ZNSt3__120get_temporary_buffer*
fun:*get_temporary_buffer@.*@std@@*
-# STL address-of magic (libstdc++, libc++).
+# STL address-of magic (libstdc++).
fun:*__addressof*
-fun:_ZNSt3__19addressof*
# Windows C++ stdlib headers that contain bad unrelated casts.
src:*xmemory0
diff --git a/lib/esan/CMakeLists.txt b/lib/esan/CMakeLists.txt
index 4de5c0205..c880971e3 100644
--- a/lib/esan/CMakeLists.txt
+++ b/lib/esan/CMakeLists.txt
@@ -14,6 +14,7 @@ set(ESAN_SOURCES
esan_interceptors.cpp
esan_linux.cpp
esan_sideline_linux.cpp
+ esan_sideline_bsd.cpp
cache_frag.cpp
working_set.cpp
working_set_posix.cpp)
diff --git a/lib/esan/esan_interceptors.cpp b/lib/esan/esan_interceptors.cpp
index 0c596f1cf..833faa2cd 100644
--- a/lib/esan/esan_interceptors.cpp
+++ b/lib/esan/esan_interceptors.cpp
@@ -327,7 +327,7 @@ INTERCEPTOR(int, rmdir, char *path) {
// Signal-related interceptors
//===----------------------------------------------------------------------===//
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
typedef void (*signal_handler_t)(int);
INTERCEPTOR(signal_handler_t, signal, int signum, signal_handler_t handler) {
void *ctx;
@@ -344,7 +344,7 @@ INTERCEPTOR(signal_handler_t, signal, int signum, signal_handler_t handler) {
#define ESAN_MAYBE_INTERCEPT_SIGNAL
#endif
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
struct sigaction *oldact)
INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act,
@@ -363,7 +363,11 @@ int real_sigaction(int signum, const void *act, void *oldact) {
if (REAL(sigaction) == nullptr) {
// With an instrumented allocator, this is called during interceptor init
// and we need a raw syscall solution.
+#if SANITIZER_LINUX
return internal_sigaction_syscall(signum, act, oldact);
+#else
+ return internal_sigaction(signum, act, oldact);
+#endif
}
return REAL(sigaction)(signum, (const struct sigaction *)act,
(struct sigaction *)oldact);
@@ -376,7 +380,7 @@ int real_sigaction(int signum, const void *act, void *oldact) {
#define ESAN_MAYBE_INTERCEPT_SIGACTION
#endif
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set,
__sanitizer_sigset_t *oldset) {
void *ctx;
diff --git a/lib/esan/esan_shadow.h b/lib/esan/esan_shadow.h
index 72a919a19..b76a9ccbd 100644
--- a/lib/esan/esan_shadow.h
+++ b/lib/esan/esan_shadow.h
@@ -30,7 +30,7 @@ struct ApplicationRegion {
bool ShadowMergedWithPrev;
};
-#if SANITIZER_LINUX && defined(__x86_64__)
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && defined(__x86_64__)
// Linux x86_64
//
// Application memory falls into these 5 regions (ignoring the corner case
diff --git a/lib/esan/esan_sideline_bsd.cpp b/lib/esan/esan_sideline_bsd.cpp
new file mode 100644
index 000000000..3134d3776
--- /dev/null
+++ b/lib/esan/esan_sideline_bsd.cpp
@@ -0,0 +1,35 @@
+//===-- esan_sideline_bsd.cpp -----------------------------------*- 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 EfficiencySanitizer, a family of performance tuners.
+//
+// Support for a separate or "sideline" tool thread on FreeBSD.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_FREEBSD
+
+#include "esan_sideline.h"
+
+namespace __esan {
+
+static SidelineThread *TheThread;
+
+bool SidelineThread::launchThread(SidelineFunc takeSample, void *Arg,
+ u32 FreqMilliSec) {
+ return true;
+}
+
+bool SidelineThread::joinThread() {
+ return true;
+}
+
+} // namespace __esan
+
+#endif // SANITIZER_FREEBSD
diff --git a/lib/fuzzer/CMakeLists.txt b/lib/fuzzer/CMakeLists.txt
index 679318e46..aae3df8db 100644
--- a/lib/fuzzer/CMakeLists.txt
+++ b/lib/fuzzer/CMakeLists.txt
@@ -3,7 +3,7 @@ set(LIBFUZZER_SOURCES
FuzzerDataFlowTrace.cpp
FuzzerDriver.cpp
FuzzerExtFunctionsDlsym.cpp
- FuzzerExtFunctionsDlsymWin.cpp
+ FuzzerExtFunctionsWeakAlias.cpp
FuzzerExtFunctionsWeak.cpp
FuzzerExtraCounters.cpp
FuzzerIO.cpp
diff --git a/lib/fuzzer/FuzzerCommand.h b/lib/fuzzer/FuzzerCommand.h
index 255f571ec..9d258a228 100644
--- a/lib/fuzzer/FuzzerCommand.h
+++ b/lib/fuzzer/FuzzerCommand.h
@@ -81,7 +81,7 @@ public:
}
// Like hasArgument, but checks for "-[Flag]=...".
- bool hasFlag(const std::string &Flag) {
+ bool hasFlag(const std::string &Flag) const {
std::string Arg("-" + Flag + "=");
auto IsMatch = [&](const std::string &Other) {
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
@@ -92,7 +92,7 @@ public:
// Returns the value of the first instance of a given flag, or an empty string
// if the flag isn't present. Ignores any occurrences after
// "-ignore_remaining_args=1", if present.
- std::string getFlagValue(const std::string &Flag) {
+ std::string getFlagValue(const std::string &Flag) const {
std::string Arg("-" + Flag + "=");
auto IsMatch = [&](const std::string &Other) {
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
diff --git a/lib/fuzzer/FuzzerDefs.h b/lib/fuzzer/FuzzerDefs.h
index a35c7a181..31655d562 100644
--- a/lib/fuzzer/FuzzerDefs.h
+++ b/lib/fuzzer/FuzzerDefs.h
@@ -129,8 +129,15 @@
#if LIBFUZZER_WINDOWS
#define ATTRIBUTE_INTERFACE __declspec(dllexport)
+// This is used for __sancov_lowest_stack which is needed for
+// -fsanitize-coverage=stack-depth. That feature is not yet available on
+// Windows, so make the symbol static to avoid linking errors.
+#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \
+ __attribute__((tls_model("initial-exec"))) thread_local static
#else
#define ATTRIBUTE_INTERFACE __attribute__((visibility("default")))
+#define ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC \
+ ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec"))) thread_local
#endif
namespace fuzzer {
diff --git a/lib/fuzzer/FuzzerDriver.cpp b/lib/fuzzer/FuzzerDriver.cpp
index d11f9a606..918a972ff 100644
--- a/lib/fuzzer/FuzzerDriver.cpp
+++ b/lib/fuzzer/FuzzerDriver.cpp
@@ -615,8 +615,6 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
Options.PrintNewCovPcs = Flags.print_pcs;
Options.PrintNewCovFuncs = Flags.print_funcs;
Options.PrintFinalStats = Flags.print_final_stats;
- Options.PrintMutationStats = Flags.print_mutation_stats;
- Options.UseWeightedMutations = Flags.use_weighted_mutations;
Options.PrintCorpusStats = Flags.print_corpus_stats;
Options.PrintCoverage = Flags.print_coverage;
Options.PrintUnstableStats = Flags.print_unstable_stats;
diff --git a/lib/fuzzer/FuzzerExtFunctionsDlsymWin.cpp b/lib/fuzzer/FuzzerExtFunctionsDlsymWin.cpp
deleted file mode 100644
index 321b3ec5d..000000000
--- a/lib/fuzzer/FuzzerExtFunctionsDlsymWin.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-//===- FuzzerExtFunctionsDlsymWin.cpp - Interface to external functions ---===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// Implementation using dynamic loading for Windows.
-//===----------------------------------------------------------------------===//
-#include "FuzzerDefs.h"
-#if LIBFUZZER_WINDOWS
-
-#include "FuzzerExtFunctions.h"
-#include "FuzzerIO.h"
-#include "Windows.h"
-
-// This must be included after Windows.h.
-#include "Psapi.h"
-
-namespace fuzzer {
-
-ExternalFunctions::ExternalFunctions() {
- HMODULE Modules[1024];
- DWORD BytesNeeded;
- HANDLE CurrentProcess = GetCurrentProcess();
-
- if (!EnumProcessModules(CurrentProcess, Modules, sizeof(Modules),
- &BytesNeeded)) {
- Printf("EnumProcessModules failed (error: %d).\n", GetLastError());
- exit(1);
- }
-
- if (sizeof(Modules) < BytesNeeded) {
- Printf("Error: the array is not big enough to hold all loaded modules.\n");
- exit(1);
- }
-
- for (size_t i = 0; i < (BytesNeeded / sizeof(HMODULE)); i++)
- {
- FARPROC Fn;
-#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
- if (this->NAME == nullptr) { \
- Fn = GetProcAddress(Modules[i], #NAME); \
- if (Fn == nullptr) \
- Fn = GetProcAddress(Modules[i], #NAME "__dll"); \
- this->NAME = (decltype(ExternalFunctions::NAME)) Fn; \
- }
-#include "FuzzerExtFunctions.def"
-#undef EXT_FUNC
- }
-
-#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
- if (this->NAME == nullptr && WARN) \
- Printf("WARNING: Failed to find function \"%s\".\n", #NAME);
-#include "FuzzerExtFunctions.def"
-#undef EXT_FUNC
-}
-
-} // namespace fuzzer
-
-#endif // LIBFUZZER_WINDOWS
diff --git a/lib/fuzzer/FuzzerExtFunctionsWeak.cpp b/lib/fuzzer/FuzzerExtFunctionsWeak.cpp
index a4e56fc27..6a6ef4932 100644
--- a/lib/fuzzer/FuzzerExtFunctionsWeak.cpp
+++ b/lib/fuzzer/FuzzerExtFunctionsWeak.cpp
@@ -22,7 +22,7 @@
extern "C" {
// Declare these symbols as weak to allow them to be optionally defined.
#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
- __attribute__((weak)) RETURN_TYPE NAME FUNC_SIG
+ __attribute__((weak, visibility("default"))) RETURN_TYPE NAME FUNC_SIG
#include "FuzzerExtFunctions.def"
diff --git a/lib/fuzzer/FuzzerFlags.def b/lib/fuzzer/FuzzerFlags.def
index 258427c3f..0417dda5b 100644
--- a/lib/fuzzer/FuzzerFlags.def
+++ b/lib/fuzzer/FuzzerFlags.def
@@ -162,6 +162,3 @@ FUZZER_DEPRECATED_FLAG(use_equivalence_server)
FUZZER_FLAG_INT(analyze_dict, 0, "Experimental")
FUZZER_DEPRECATED_FLAG(use_clang_coverage)
FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace")
-FUZZER_FLAG_INT(print_mutation_stats, 0, "Experimental")
-FUZZER_FLAG_INT(use_weighted_mutations, 0, "Experimental: If 1, fuzzing will "
- "favor mutations that perform better during runtime.")
diff --git a/lib/fuzzer/FuzzerIO.cpp b/lib/fuzzer/FuzzerIO.cpp
index f3ead0ec5..dac5ec658 100644
--- a/lib/fuzzer/FuzzerIO.cpp
+++ b/lib/fuzzer/FuzzerIO.cpp
@@ -100,14 +100,6 @@ std::string DirPlusFile(const std::string &DirPath,
return DirPath + GetSeparator() + FileName;
}
-std::string Basename(const std::string &Path, char Separator) {
- size_t Pos = Path.rfind(Separator);
- if (Pos == std::string::npos)
- return Path;
- assert(Pos < Path.size());
- return Path.substr(Pos + 1);
-}
-
void DupAndCloseStderr() {
int OutputFd = DuplicateFile(2);
if (OutputFd > 0) {
diff --git a/lib/fuzzer/FuzzerIO.h b/lib/fuzzer/FuzzerIO.h
index 6d7757435..b4a68190e 100644
--- a/lib/fuzzer/FuzzerIO.h
+++ b/lib/fuzzer/FuzzerIO.h
@@ -68,7 +68,7 @@ void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V);
char GetSeparator();
// Similar to the basename utility: returns the file name w/o the dir prefix.
-std::string Basename(const std::string &Path, char Separator = GetSeparator());
+std::string Basename(const std::string &Path);
FILE* OpenFile(int Fd, const char *Mode);
diff --git a/lib/fuzzer/FuzzerIOPosix.cpp b/lib/fuzzer/FuzzerIOPosix.cpp
index 17e884d3c..401b4cbbf 100644
--- a/lib/fuzzer/FuzzerIOPosix.cpp
+++ b/lib/fuzzer/FuzzerIOPosix.cpp
@@ -46,6 +46,13 @@ size_t FileSize(const std::string &Path) {
return St.st_size;
}
+std::string Basename(const std::string &Path) {
+ size_t Pos = Path.rfind(GetSeparator());
+ if (Pos == std::string::npos) return Path;
+ assert(Pos < Path.size());
+ return Path.substr(Pos + 1);
+}
+
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
Vector<std::string> *V, bool TopDir) {
auto E = GetEpoch(Dir);
diff --git a/lib/fuzzer/FuzzerIOWindows.cpp b/lib/fuzzer/FuzzerIOWindows.cpp
index 74853646b..75dcaf72a 100644
--- a/lib/fuzzer/FuzzerIOWindows.cpp
+++ b/lib/fuzzer/FuzzerIOWindows.cpp
@@ -72,6 +72,26 @@ bool IsFile(const std::string &Path) {
return IsFile(Path, Att);
}
+std::string Basename(const std::string &Path) {
+ size_t Pos = Path.find_last_of("/\\");
+ if (Pos == std::string::npos) return Path;
+ assert(Pos < Path.size());
+ return Path.substr(Pos + 1);
+}
+
+size_t FileSize(const std::string &Path) {
+ WIN32_FILE_ATTRIBUTE_DATA attr;
+ if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) {
+ Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n",
+ Path.c_str(), GetLastError());
+ return 0;
+ }
+ ULARGE_INTEGER size;
+ size.HighPart = attr.nFileSizeHigh;
+ size.LowPart = attr.nFileSizeLow;
+ return size.QuadPart;
+}
+
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
Vector<std::string> *V, bool TopDir) {
auto E = GetEpoch(Dir);
@@ -91,7 +111,7 @@ void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
{
if (GetLastError() == ERROR_FILE_NOT_FOUND)
return;
- Printf("No such directory: %s; exiting\n", Dir.c_str());
+ Printf("No such file or directory: %s; exiting\n", Dir.c_str());
exit(1);
}
diff --git a/lib/fuzzer/FuzzerLoop.cpp b/lib/fuzzer/FuzzerLoop.cpp
index c7b13d1e5..7b98f55ae 100644
--- a/lib/fuzzer/FuzzerLoop.cpp
+++ b/lib/fuzzer/FuzzerLoop.cpp
@@ -38,7 +38,6 @@
namespace fuzzer {
static const size_t kMaxUnitSizeToPrint = 256;
-static const size_t kUpdateMutationWeightRuns = 10000;
thread_local bool Fuzzer::IsMyThread;
@@ -361,7 +360,6 @@ void Fuzzer::PrintFinalStats() {
TPC.DumpCoverage();
if (Options.PrintCorpusStats)
Corpus.PrintStats();
- if (Options.PrintMutationStats) MD.PrintMutationStats();
if (!Options.PrintFinalStats)
return;
size_t ExecPerSec = execPerSec();
@@ -550,9 +548,6 @@ static bool LooseMemeq(const uint8_t *A, const uint8_t *B, size_t Size) {
void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
TPC.RecordInitialStack();
- if (Options.UseWeightedMutations &&
- TotalNumberOfRuns % kUpdateMutationWeightRuns == 0)
- MD.UpdateDistribution();
TotalNumberOfRuns++;
assert(InFuzzingThread());
if (SMR.IsClient())
diff --git a/lib/fuzzer/FuzzerMutate.cpp b/lib/fuzzer/FuzzerMutate.cpp
index fac3c7afb..142b2b0b0 100644
--- a/lib/fuzzer/FuzzerMutate.cpp
+++ b/lib/fuzzer/FuzzerMutate.cpp
@@ -30,41 +30,34 @@ MutationDispatcher::MutationDispatcher(Random &Rand,
DefaultMutators.insert(
DefaultMutators.begin(),
{
- // Initialize useful and total mutation counts as 1 in order to
- // have mutation stats (i.e. weights) with equal non-zero values.
- {&MutationDispatcher::Mutate_EraseBytes, "EraseBytes", 1, 1},
- {&MutationDispatcher::Mutate_InsertByte, "InsertByte", 1, 1},
+ {&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"},
+ {&MutationDispatcher::Mutate_InsertByte, "InsertByte"},
{&MutationDispatcher::Mutate_InsertRepeatedBytes,
- "InsertRepeatedBytes", 1, 1},
- {&MutationDispatcher::Mutate_ChangeByte, "ChangeByte", 1, 1},
- {&MutationDispatcher::Mutate_ChangeBit, "ChangeBit", 1, 1},
- {&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes", 1, 1},
- {&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt", 1,
- 1},
- {&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt", 1,
- 1},
- {&MutationDispatcher::Mutate_CopyPart, "CopyPart", 1, 1},
- {&MutationDispatcher::Mutate_CrossOver, "CrossOver", 1, 1},
+ "InsertRepeatedBytes"},
+ {&MutationDispatcher::Mutate_ChangeByte, "ChangeByte"},
+ {&MutationDispatcher::Mutate_ChangeBit, "ChangeBit"},
+ {&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes"},
+ {&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt"},
+ {&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt"},
+ {&MutationDispatcher::Mutate_CopyPart, "CopyPart"},
+ {&MutationDispatcher::Mutate_CrossOver, "CrossOver"},
{&MutationDispatcher::Mutate_AddWordFromManualDictionary,
- "ManualDict", 1, 1},
+ "ManualDict"},
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
- "PersAutoDict", 1, 1},
+ "PersAutoDict"},
});
if(Options.UseCmp)
DefaultMutators.push_back(
- {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP", 1, 1});
+ {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
if (EF->LLVMFuzzerCustomMutator)
- Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom", 1, 1});
+ Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
else
Mutators = DefaultMutators;
if (EF->LLVMFuzzerCustomCrossOver)
Mutators.push_back(
- {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver", 1, 1});
-
- // For weighted mutation selection, init with uniform weights distribution.
- Stats.resize(Mutators.size());
+ {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"});
}
static char RandCh(Random &Rand) {
@@ -471,7 +464,6 @@ void MutationDispatcher::RecordSuccessfulMutationSequence() {
if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
PersistentAutoDictionary.push_back({DE->GetW(), 1});
}
- RecordUsefulMutations();
}
void MutationDispatcher::PrintRecommendedDictionary() {
@@ -492,7 +484,8 @@ void MutationDispatcher::PrintRecommendedDictionary() {
void MutationDispatcher::PrintMutationSequence() {
Printf("MS: %zd ", CurrentMutatorSequence.size());
- for (auto M : CurrentMutatorSequence) Printf("%s-", M->Name);
+ for (auto M : CurrentMutatorSequence)
+ Printf("%s-", M.Name);
if (!CurrentDictionaryEntrySequence.empty()) {
Printf(" DE: ");
for (auto DE : CurrentDictionaryEntrySequence) {
@@ -519,20 +512,13 @@ size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
// Some mutations may fail (e.g. can't insert more bytes if Size == MaxSize),
// in which case they will return 0.
// Try several times before returning un-mutated data.
- Mutator *M = nullptr;
for (int Iter = 0; Iter < 100; Iter++) {
- // Even when using weighted mutations, fallback to the default selection in
- // 20% of cases.
- if (Options.UseWeightedMutations && Rand(5))
- M = &Mutators[WeightedIndex()];
- else
- M = &Mutators[Rand(Mutators.size())];
- size_t NewSize = (this->*(M->Fn))(Data, Size, MaxSize);
+ auto M = Mutators[Rand(Mutators.size())];
+ size_t NewSize = (this->*(M.Fn))(Data, Size, MaxSize);
if (NewSize && NewSize <= MaxSize) {
if (Options.OnlyASCII)
ToASCII(Data, NewSize);
CurrentMutatorSequence.push_back(M);
- M->TotalCount++;
return NewSize;
}
}
@@ -573,34 +559,4 @@ void MutationDispatcher::AddWordToManualDictionary(const Word &W) {
{W, std::numeric_limits<size_t>::max()});
}
-void MutationDispatcher::RecordUsefulMutations() {
- for (auto M : CurrentMutatorSequence) M->UsefulCount++;
-}
-
-void MutationDispatcher::PrintMutationStats() {
- Printf("\nstat::mutation_usefulness: ");
- UpdateMutationStats();
- for (size_t i = 0; i < Stats.size(); i++) {
- Printf("%.3f", 100 * Stats[i]);
- if (i < Stats.size() - 1)
- Printf(",");
- else
- Printf("\n");
- }
-}
-
-void MutationDispatcher::UpdateMutationStats() {
- // Calculate usefulness statistic for each mutation
- for (size_t i = 0; i < Stats.size(); i++)
- Stats[i] =
- static_cast<double>(Mutators[i].UsefulCount) / Mutators[i].TotalCount;
-}
-
-void MutationDispatcher::UpdateDistribution() {
- UpdateMutationStats();
- Distribution = std::discrete_distribution<size_t>(Stats.begin(), Stats.end());
-}
-
-size_t MutationDispatcher::WeightedIndex() { return Distribution(GetRand()); }
-
} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerMutate.h b/lib/fuzzer/FuzzerMutate.h
index d89667cc4..a51c7fb44 100644
--- a/lib/fuzzer/FuzzerMutate.h
+++ b/lib/fuzzer/FuzzerMutate.h
@@ -93,29 +93,10 @@ public:
Random &GetRand() { return Rand; }
- /// Records tally of mutations resulting in new coverage, for usefulness
- /// metric.
- void RecordUsefulMutations();
-
- /// Outputs usefulness stats on command line if option is enabled.
- void PrintMutationStats();
-
- /// Recalculates mutation stats based on latest run data.
- void UpdateMutationStats();
-
- /// Sets weights based on mutation performance during fuzzer run.
- void UpdateDistribution();
-
- /// Returns the index of a mutation based on how useful it has been.
- /// Favors mutations with higher usefulness ratios but can return any index.
- size_t WeightedIndex();
-
private:
struct Mutator {
size_t (MutationDispatcher::*Fn)(uint8_t *Data, size_t Size, size_t Max);
const char *Name;
- uint64_t UsefulCount;
- uint64_t TotalCount;
};
size_t AddWordFromDictionary(Dictionary &D, uint8_t *Data, size_t Size,
@@ -154,7 +135,6 @@ public:
Dictionary PersistentAutoDictionary;
Vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
- Vector<Mutator *> CurrentMutatorSequence;
static const size_t kCmpDictionaryEntriesDequeSize = 16;
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
@@ -169,10 +149,7 @@ public:
Vector<Mutator> Mutators;
Vector<Mutator> DefaultMutators;
-
- // Used to weight mutations based on usefulness.
- Vector<double> Stats;
- std::discrete_distribution<size_t> Distribution;
+ Vector<Mutator> CurrentMutatorSequence;
};
} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerOptions.h b/lib/fuzzer/FuzzerOptions.h
index 82855ce0b..bb642f1e2 100644
--- a/lib/fuzzer/FuzzerOptions.h
+++ b/lib/fuzzer/FuzzerOptions.h
@@ -52,8 +52,6 @@ struct FuzzingOptions {
bool PrintNewCovPcs = false;
int PrintNewCovFuncs = 0;
bool PrintFinalStats = false;
- bool PrintMutationStats = false;
- bool UseWeightedMutations = false;
bool PrintCorpusStats = false;
bool PrintCoverage = false;
bool PrintUnstableStats = false;
diff --git a/lib/fuzzer/FuzzerTracePC.cpp b/lib/fuzzer/FuzzerTracePC.cpp
index 1aba816e8..7ba75c7b2 100644
--- a/lib/fuzzer/FuzzerTracePC.cpp
+++ b/lib/fuzzer/FuzzerTracePC.cpp
@@ -32,8 +32,7 @@ ATTRIBUTE_INTERFACE
uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs];
// Used by -fsanitize-coverage=stack-depth to track stack depth
-ATTRIBUTE_INTERFACE __attribute__((tls_model("initial-exec")))
-thread_local uintptr_t __sancov_lowest_stack;
+ATTRIBUTES_INTERFACE_TLS_INITIAL_EXEC uintptr_t __sancov_lowest_stack;
namespace fuzzer {
@@ -195,11 +194,42 @@ void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) {
ValueProfileMap.AddValueModPrime(Idx);
}
+/// \return the address of the previous instruction.
+/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.h`
+inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) {
+#if defined(__arm__)
+ // T32 (Thumb) branch instructions might be 16 or 32 bit long,
+ // so we return (pc-2) in that case in order to be safe.
+ // For A32 mode we return (pc-4) because all instructions are 32 bit long.
+ return (PC - 3) & (~1);
+#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__aarch64__)
+ // PCs are always 4 byte aligned.
+ return PC - 4;
+#elif defined(__sparc__) || defined(__mips__)
+ return PC - 8;
+#else
+ return PC - 1;
+#endif
+}
+
+/// \return the address of the next instruction.
+/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.cc`
+inline ALWAYS_INLINE uintptr_t GetNextInstructionPc(uintptr_t PC) {
+#if defined(__mips__)
+ return PC + 8;
+#elif defined(__powerpc__) || defined(__sparc__) || defined(__arm__) || \
+ defined(__aarch64__)
+ return PC + 4;
+#else
+ return PC + 1;
+#endif
+}
+
void TracePC::UpdateObservedPCs() {
Vector<uintptr_t> CoveredFuncs;
auto ObservePC = [&](uintptr_t PC) {
if (ObservedPCs.insert(PC).second && DoPrintNewPCs) {
- PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", PC + 1);
+ PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", GetNextInstructionPc(PC));
Printf("\n");
}
};
@@ -234,22 +264,11 @@ void TracePC::UpdateObservedPCs() {
for (size_t i = 0, N = Min(CoveredFuncs.size(), NumPrintNewFuncs); i < N;
i++) {
Printf("\tNEW_FUNC[%zd/%zd]: ", i + 1, CoveredFuncs.size());
- PrintPC("%p %F %L", "%p", CoveredFuncs[i] + 1);
+ PrintPC("%p %F %L", "%p", GetNextInstructionPc(CoveredFuncs[i]));
Printf("\n");
}
}
-inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) {
- // TODO: this implementation is x86 only.
- // see sanitizer_common GetPreviousInstructionPc for full implementation.
- return PC - 1;
-}
-
-inline ALWAYS_INLINE uintptr_t GetNextInstructionPc(uintptr_t PC) {
- // TODO: this implementation is x86 only.
- // see sanitizer_common GetPreviousInstructionPc for full implementation.
- return PC + 1;
-}
static std::string GetModuleName(uintptr_t PC) {
char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++?
diff --git a/lib/fuzzer/FuzzerUtilFuchsia.cpp b/lib/fuzzer/FuzzerUtilFuchsia.cpp
index cd2bb7438..cd48fefef 100644
--- a/lib/fuzzer/FuzzerUtilFuchsia.cpp
+++ b/lib/fuzzer/FuzzerUtilFuchsia.cpp
@@ -49,9 +49,6 @@ void CrashTrampolineAsm() __asm__("CrashTrampolineAsm");
namespace {
-// TODO(phosek): remove this and replace it with ZX_TIME_INFINITE
-#define ZX_TIME_INFINITE_OLD INT64_MAX
-
// A magic value for the Zircon exception port, chosen to spell 'FUZZING'
// when interpreted as a byte sequence on little-endian platforms.
const uint64_t kFuzzingCrash = 0x474e495a5a5546;
@@ -237,7 +234,7 @@ void CrashHandler(zx_handle_t *Event) {
"_zx_object_signal");
zx_port_packet_t Packet;
- ExitOnErr(_zx_port_wait(Port.Handle, ZX_TIME_INFINITE_OLD, &Packet),
+ ExitOnErr(_zx_port_wait(Port.Handle, ZX_TIME_INFINITE, &Packet),
"_zx_port_wait");
// At this point, we want to get the state of the crashing thread, but
@@ -315,8 +312,8 @@ void SetSignalHandler(const FuzzingOptions &Options) {
ExitOnErr(_zx_event_create(0, &Event), "_zx_event_create");
std::thread T(CrashHandler, &Event);
- zx_status_t Status = _zx_object_wait_one(Event, ZX_USER_SIGNAL_0,
- ZX_TIME_INFINITE_OLD, nullptr);
+ zx_status_t Status =
+ _zx_object_wait_one(Event, ZX_USER_SIGNAL_0, ZX_TIME_INFINITE, nullptr);
_zx_handle_close(Event);
ExitOnErr(Status, "_zx_object_wait_one");
@@ -378,19 +375,28 @@ int ExecuteCommand(const Command &Cmd) {
Argv[i] = Args[i].c_str();
Argv[Argc] = nullptr;
- // Determine stdout
+ // Determine output. On Fuchsia, the fuzzer is typically run as a component
+ // that lacks a mutable working directory. Fortunately, when this is the case
+ // a mutable output directory must be specified using "-artifact_prefix=...",
+ // so write the log file(s) there.
int FdOut = STDOUT_FILENO;
-
if (Cmd.hasOutputFile()) {
- auto Filename = Cmd.getOutputFile();
- FdOut = open(Filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0);
+ std::string Path;
+ if (Cmd.hasFlag("artifact_prefix"))
+ Path = Cmd.getFlagValue("artifact_prefix") + "/" + Cmd.getOutputFile();
+ else
+ Path = Cmd.getOutputFile();
+ FdOut = open(Path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0);
if (FdOut == -1) {
- Printf("libFuzzer: failed to open %s: %s\n", Filename.c_str(),
+ Printf("libFuzzer: failed to open %s: %s\n", Path.c_str(),
strerror(errno));
return ZX_ERR_IO;
}
}
- auto CloseFdOut = at_scope_exit([&]() { close(FdOut); } );
+ auto CloseFdOut = at_scope_exit([FdOut]() {
+ if (FdOut != STDOUT_FILENO)
+ close(FdOut);
+ });
// Determine stderr
int FdErr = STDERR_FILENO;
@@ -440,7 +446,7 @@ int ExecuteCommand(const Command &Cmd) {
// Now join the process and return the exit status.
if ((rc = _zx_object_wait_one(ProcessHandle, ZX_PROCESS_TERMINATED,
- ZX_TIME_INFINITE_OLD, nullptr)) != ZX_OK) {
+ ZX_TIME_INFINITE, nullptr)) != ZX_OK) {
Printf("libFuzzer: failed to join '%s': %s\n", Argv[0],
_zx_status_get_string(rc));
return rc;
diff --git a/lib/fuzzer/FuzzerUtilWindows.cpp b/lib/fuzzer/FuzzerUtilWindows.cpp
index 8227e778e..393b4768b 100644
--- a/lib/fuzzer/FuzzerUtilWindows.cpp
+++ b/lib/fuzzer/FuzzerUtilWindows.cpp
@@ -24,7 +24,7 @@
#include <windows.h>
// This must be included after windows.h.
-#include <Psapi.h>
+#include <psapi.h>
namespace fuzzer {
@@ -179,7 +179,9 @@ const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
}
std::string DisassembleCmd(const std::string &FileName) {
- if (ExecuteCommand("dumpbin /summary > nul") == 0)
+ Vector<std::string> command_vector;
+ command_vector.push_back("dumpbin /summary > nul");
+ if (ExecuteCommand(Command(command_vector)) == 0)
return "dumpbin /disasm " + FileName;
Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n");
exit(1);
diff --git a/lib/fuzzer/tests/CMakeLists.txt b/lib/fuzzer/tests/CMakeLists.txt
index ed5807168..0b561c170 100644
--- a/lib/fuzzer/tests/CMakeLists.txt
+++ b/lib/fuzzer/tests/CMakeLists.txt
@@ -17,6 +17,8 @@ list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS --driver-mode=g++)
if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lc++ -lpthread)
+elseif(WIN32)
+ list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lstdc++)
else()
list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lstdc++ -lpthread)
endif()
diff --git a/lib/fuzzer/tests/FuzzerUnittest.cpp b/lib/fuzzer/tests/FuzzerUnittest.cpp
index e3b067026..7cdd44582 100644
--- a/lib/fuzzer/tests/FuzzerUnittest.cpp
+++ b/lib/fuzzer/tests/FuzzerUnittest.cpp
@@ -34,6 +34,13 @@ TEST(Fuzzer, Basename) {
EXPECT_EQ(Basename("/bar"), "bar");
EXPECT_EQ(Basename("foo/x"), "x");
EXPECT_EQ(Basename("foo/"), "");
+#if LIBFUZZER_WINDOWS
+ EXPECT_EQ(Basename("foo\\bar"), "bar");
+ EXPECT_EQ(Basename("foo\\bar/baz"), "baz");
+ EXPECT_EQ(Basename("\\bar"), "bar");
+ EXPECT_EQ(Basename("foo\\x"), "x");
+ EXPECT_EQ(Basename("foo\\"), "");
+#endif
}
TEST(Fuzzer, CrossOver) {
diff --git a/lib/hwasan/CMakeLists.txt b/lib/hwasan/CMakeLists.txt
index 42bf4366f..552133e8d 100644
--- a/lib/hwasan/CMakeLists.txt
+++ b/lib/hwasan/CMakeLists.txt
@@ -10,6 +10,7 @@ set(HWASAN_RTL_SOURCES
hwasan_poisoning.cc
hwasan_report.cc
hwasan_thread.cc
+ hwasan_thread_list.cc
)
set(HWASAN_RTL_CXX_SOURCES
@@ -25,8 +26,12 @@ set(HWASAN_RTL_HEADERS
hwasan_mapping.h
hwasan_poisoning.h
hwasan_report.h
- hwasan_thread.h)
+ hwasan_thread.h
+ hwasan_thread_list.h
+ )
+set(HWASAN_DEFINITIONS)
+append_list_if(COMPILER_RT_HWASAN_WITH_INTERCEPTORS HWASAN_WITH_INTERCEPTORS=1 HWASAN_DEFINITIONS)
set(HWASAN_RTL_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_rtti_flag(OFF HWASAN_RTL_CFLAGS)
@@ -36,6 +41,14 @@ append_list_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding HWASAN_RTL_CFLA
set(HWASAN_DYNAMIC_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS})
+if(ANDROID)
+# Put most Sanitizer shared libraries in the global group. For more details, see
+# android-changes-for-ndk-developers.md#changes-to-library-search-order
+ if (COMPILER_RT_HAS_Z_GLOBAL)
+ list(APPEND HWASAN_DYNAMIC_LINK_FLAGS -Wl,-z,global)
+ endif()
+endif()
+
set(HWASAN_DYNAMIC_CFLAGS ${HWASAN_RTL_CFLAGS})
append_list_if(COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC
-ftls-model=initial-exec HWASAN_DYNAMIC_CFLAGS)
@@ -47,7 +60,6 @@ append_list_if(COMPILER_RT_HAS_LIBDL dl HWASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBRT rt HWASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBM m HWASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread HWASAN_DYNAMIC_LIBS)
-append_list_if(COMPILER_RT_HAS_LIBLOG log HWASAN_DYNAMIC_LIBS)
# Static runtime library.
add_compiler_rt_component(hwasan)
@@ -56,23 +68,27 @@ add_compiler_rt_object_libraries(RTHwasan
ARCHS ${HWASAN_SUPPORTED_ARCH}
SOURCES ${HWASAN_RTL_SOURCES}
ADDITIONAL_HEADERS ${HWASAN_RTL_HEADERS}
- CFLAGS ${HWASAN_RTL_CFLAGS})
+ CFLAGS ${HWASAN_RTL_CFLAGS}
+ DEFS ${HWASAN_DEFINITIONS})
add_compiler_rt_object_libraries(RTHwasan_cxx
ARCHS ${HWASAN_SUPPORTED_ARCH}
SOURCES ${HWASAN_RTL_CXX_SOURCES}
ADDITIONAL_HEADERS ${HWASAN_RTL_HEADERS}
- CFLAGS ${HWASAN_RTL_CFLAGS})
+ CFLAGS ${HWASAN_RTL_CFLAGS}
+ DEFS ${HWASAN_DEFINITIONS})
add_compiler_rt_object_libraries(RTHwasan_dynamic
ARCHS ${HWASAN_SUPPORTED_ARCH}
SOURCES ${HWASAN_RTL_SOURCES} ${HWASAN_RTL_CXX_SOURCES}
ADDITIONAL_HEADERS ${HWASAN_RTL_HEADERS}
- CFLAGS ${HWASAN_DYNAMIC_CFLAGS})
+ CFLAGS ${HWASAN_DYNAMIC_CFLAGS}
+ DEFS ${HWASAN_DEFINITIONS})
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/dummy.cc "")
add_compiler_rt_object_libraries(RTHwasan_dynamic_version_script_dummy
ARCHS ${HWASAN_SUPPORTED_ARCH}
SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dummy.cc
- CFLAGS ${HWASAN_DYNAMIC_CFLAGS})
+ CFLAGS ${HWASAN_DYNAMIC_CFLAGS}
+ DEFS ${HWASAN_DEFINITIONS})
foreach(arch ${HWASAN_SUPPORTED_ARCH})
add_compiler_rt_runtime(clang_rt.hwasan
diff --git a/lib/hwasan/hwasan.cc b/lib/hwasan/hwasan.cc
index 7dab8249e..02aee4d61 100644
--- a/lib/hwasan/hwasan.cc
+++ b/lib/hwasan/hwasan.cc
@@ -17,6 +17,7 @@
#include "hwasan_poisoning.h"
#include "hwasan_report.h"
#include "hwasan_thread.h"
+#include "hwasan_thread_list.h"
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
@@ -36,17 +37,17 @@ using namespace __sanitizer;
namespace __hwasan {
void EnterSymbolizer() {
- HwasanThread *t = GetCurrentThread();
+ Thread *t = GetCurrentThread();
CHECK(t);
t->EnterSymbolizer();
}
void ExitSymbolizer() {
- HwasanThread *t = GetCurrentThread();
+ Thread *t = GetCurrentThread();
CHECK(t);
t->LeaveSymbolizer();
}
bool IsInSymbolizer() {
- HwasanThread *t = GetCurrentThread();
+ Thread *t = GetCurrentThread();
return t && t->InSymbolizer();
}
@@ -57,6 +58,7 @@ Flags *flags() {
}
int hwasan_inited = 0;
+int hwasan_shadow_inited = 0;
bool hwasan_init_is_running;
int hwasan_report_count = 0;
@@ -86,7 +88,18 @@ static void InitializeFlags() {
cf.check_printf = false;
cf.intercept_tls_get_addr = true;
cf.exitcode = 99;
+ // Sigtrap is used in error reporting.
cf.handle_sigtrap = kHandleSignalExclusive;
+
+#if SANITIZER_ANDROID
+ // Let platform handle other signals. It is better at reporting them then we
+ // are.
+ cf.handle_segv = kHandleSignalNo;
+ cf.handle_sigbus = kHandleSignalNo;
+ cf.handle_abort = kHandleSignalNo;
+ cf.handle_sigill = kHandleSignalNo;
+ cf.handle_sigfpe = kHandleSignalNo;
+#endif
OverrideCommonFlags(cf);
}
@@ -119,7 +132,8 @@ static void InitializeFlags() {
#if HWASAN_CONTAINS_UBSAN
ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
#endif
- VPrintf(1, "HWASAN_OPTIONS: %s\n", hwasan_options ? hwasan_options : "<empty>");
+ VPrintf(1, "HWASAN_OPTIONS: %s\n",
+ hwasan_options ? hwasan_options : "<empty>");
InitializeCommonFlags();
@@ -130,8 +144,13 @@ static void InitializeFlags() {
void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp,
void *context, bool request_fast_unwind) {
- HwasanThread *t = GetCurrentThread();
- if (!t || !StackTrace::WillUseFastUnwind(request_fast_unwind)) {
+ Thread *t = GetCurrentThread();
+ if (!t) {
+ // the thread is still being created.
+ stack->size = 0;
+ return;
+ }
+ if (!StackTrace::WillUseFastUnwind(request_fast_unwind)) {
// Block reports from our interceptors during _Unwind_Backtrace.
SymbolizerScope sym_scope;
return stack->Unwind(max_s, pc, bp, context, 0, 0, request_fast_unwind);
@@ -153,6 +172,54 @@ static void HWAsanCheckFailed(const char *file, int line, const char *cond,
Die();
}
+static constexpr uptr kMemoryUsageBufferSize = 4096;
+
+static void HwasanFormatMemoryUsage(InternalScopedString &s) {
+ HwasanThreadList &thread_list = hwasanThreadList();
+ auto thread_stats = thread_list.GetThreadStats();
+ auto *sds = StackDepotGetStats();
+ AllocatorStatCounters asc;
+ GetAllocatorStats(asc);
+ s.append(
+ "HWASAN pid: %d rss: %zd threads: %zd stacks: %zd"
+ " thr_aux: %zd stack_depot: %zd uniq_stacks: %zd"
+ " heap: %zd",
+ internal_getpid(), GetRSS(), thread_stats.n_live_threads,
+ thread_stats.total_stack_size,
+ thread_stats.n_live_threads * thread_list.MemoryUsedPerThread(),
+ sds->allocated, sds->n_uniq_ids, asc[AllocatorStatMapped]);
+}
+
+#if SANITIZER_ANDROID
+static char *memory_usage_buffer = nullptr;
+
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
+
+static void InitMemoryUsage() {
+ memory_usage_buffer =
+ (char *)MmapOrDie(kMemoryUsageBufferSize, "memory usage string");
+ CHECK(memory_usage_buffer);
+ memory_usage_buffer[0] = '\0';
+ CHECK(internal_prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
+ (uptr)memory_usage_buffer, kMemoryUsageBufferSize,
+ (uptr)memory_usage_buffer) == 0);
+}
+
+void UpdateMemoryUsage() {
+ if (!flags()->export_memory_stats)
+ return;
+ if (!memory_usage_buffer)
+ InitMemoryUsage();
+ InternalScopedString s(kMemoryUsageBufferSize);
+ HwasanFormatMemoryUsage(s);
+ internal_strncpy(memory_usage_buffer, s.data(), kMemoryUsageBufferSize - 1);
+ memory_usage_buffer[kMemoryUsageBufferSize - 1] = '\0';
+}
+#else
+void UpdateMemoryUsage() {}
+#endif
+
} // namespace __hwasan
// Interface.
@@ -161,6 +228,16 @@ using namespace __hwasan;
uptr __hwasan_shadow_memory_dynamic_address; // Global interface symbol.
+void __hwasan_shadow_init() {
+ if (hwasan_shadow_inited) return;
+ if (!InitShadow()) {
+ Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
+ DumpProcessMap();
+ Die();
+ }
+ hwasan_shadow_inited = 1;
+}
+
void __hwasan_init() {
CHECK(!hwasan_init_is_running);
if (hwasan_inited) return;
@@ -178,17 +255,16 @@ void __hwasan_init() {
__sanitizer_set_report_path(common_flags()->log_path);
DisableCoreDumperIfNecessary();
- if (!InitShadow()) {
- Printf("FATAL: HWAddressSanitizer cannot mmap the shadow memory.\n");
- if (HWASAN_FIXED_MAPPING) {
- Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
- Printf("FATAL: Disabling ASLR is known to cause this error.\n");
- Printf("FATAL: If running under GDB, try "
- "'set disable-randomization off'.\n");
- }
- DumpProcessMap();
- Die();
- }
+
+ __hwasan_shadow_init();
+
+ InitThreads();
+ hwasanThreadList().CreateCurrentThread();
+
+ MadviseShadow();
+
+ // This may call libc -> needs initialized shadow.
+ AndroidLogInit();
InitializeInterceptors();
InstallDeadlySignalHandlers(HwasanOnDeadlySignal);
@@ -198,14 +274,11 @@ void __hwasan_init() {
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
- HwasanTSDInit(HwasanTSDDtor);
+ HwasanTSDInit();
+ HwasanTSDThreadInit();
HwasanAllocatorInit();
- HwasanThread *main_thread = HwasanThread::Create(nullptr, nullptr);
- SetCurrentThread(main_thread);
- main_thread->ThreadStart();
-
#if HWASAN_CONTAINS_UBSAN
__ubsan::InitAsPlugin();
#endif
@@ -216,9 +289,14 @@ void __hwasan_init() {
hwasan_inited = 1;
}
-void __hwasan_print_shadow(const void *x, uptr size) {
- // FIXME:
- Printf("FIXME: __hwasan_print_shadow unimplemented\n");
+void __hwasan_print_shadow(const void *p, uptr sz) {
+ uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p));
+ uptr shadow_first = MemToShadow(ptr_raw);
+ uptr shadow_last = MemToShadow(ptr_raw + sz - 1);
+ Printf("HWASan shadow map for %zx .. %zx (pointer tag %x)\n", ptr_raw,
+ ptr_raw + sz, GetTagFromPointer((uptr)p));
+ for (uptr s = shadow_first; s <= shadow_last; ++s)
+ Printf(" %zx: %x\n", ShadowToMem(s), *(tag_t *)s);
}
sptr __hwasan_test_shadow(const void *p, uptr sz) {
@@ -227,12 +305,12 @@ sptr __hwasan_test_shadow(const void *p, uptr sz) {
tag_t ptr_tag = GetTagFromPointer((uptr)p);
if (ptr_tag == 0)
return -1;
- uptr ptr_raw = GetAddressFromPointer((uptr)p);
- uptr shadow_first = MEM_TO_SHADOW(ptr_raw);
- uptr shadow_last = MEM_TO_SHADOW(ptr_raw + sz - 1);
+ uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p));
+ uptr shadow_first = MemToShadow(ptr_raw);
+ uptr shadow_last = MemToShadow(ptr_raw + sz - 1);
for (uptr s = shadow_first; s <= shadow_last; ++s)
if (*(tag_t*)s != ptr_tag)
- return SHADOW_TO_MEM(s) - ptr_raw;
+ return ShadowToMem(s) - ptr_raw;
return -1;
}
@@ -288,7 +366,7 @@ template <ErrorAction EA, AccessType AT, unsigned LogSize>
__attribute__((always_inline, nodebug)) static void CheckAddress(uptr p) {
tag_t ptr_tag = GetTagFromPointer(p);
uptr ptr_raw = p & ~kAddressTagMask;
- tag_t mem_tag = *(tag_t *)MEM_TO_SHADOW(ptr_raw);
+ tag_t mem_tag = *(tag_t *)MemToShadow(ptr_raw);
if (UNLIKELY(ptr_tag != mem_tag)) {
SigTrap<0x20 * (EA == ErrorAction::Recover) +
0x10 * (AT == AccessType::Store) + LogSize>(p);
@@ -302,8 +380,8 @@ __attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p,
CHECK_NE(0, sz);
tag_t ptr_tag = GetTagFromPointer(p);
uptr ptr_raw = p & ~kAddressTagMask;
- tag_t *shadow_first = (tag_t *)MEM_TO_SHADOW(ptr_raw);
- tag_t *shadow_last = (tag_t *)MEM_TO_SHADOW(ptr_raw + sz - 1);
+ tag_t *shadow_first = (tag_t *)MemToShadow(ptr_raw);
+ tag_t *shadow_last = (tag_t *)MemToShadow(ptr_raw + sz - 1);
for (tag_t *t = shadow_first; t <= shadow_last; ++t)
if (UNLIKELY(ptr_tag != *t)) {
SigTrap<0x20 * (EA == ErrorAction::Recover) +
@@ -392,10 +470,38 @@ void __hwasan_tag_memory(uptr p, u8 tag, uptr sz) {
TagMemoryAligned(p, sz, tag);
}
+uptr __hwasan_tag_pointer(uptr p, u8 tag) {
+ return AddTagToPointer(p, tag);
+}
+
+void __hwasan_handle_longjmp(const void *sp_dst) {
+ uptr dst = (uptr)sp_dst;
+ // HWASan does not support tagged SP.
+ CHECK(GetTagFromPointer(dst) == 0);
+
+ uptr sp = (uptr)__builtin_frame_address(0);
+ static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M
+ if (dst < sp || dst - sp > kMaxExpectedCleanupSize) {
+ Report(
+ "WARNING: HWASan is ignoring requested __hwasan_handle_longjmp: "
+ "stack top: %p; target %p; distance: %p (%zd)\n"
+ "False positive error reports may follow\n",
+ (void *)sp, (void *)dst, dst - sp);
+ return;
+ }
+ TagMemory(sp, dst - sp, 0);
+}
+
+void __hwasan_print_memory_usage() {
+ InternalScopedString s(kMemoryUsageBufferSize);
+ HwasanFormatMemoryUsage(s);
+ Printf("%s\n", s.data());
+}
+
static const u8 kFallbackTag = 0xBB;
u8 __hwasan_generate_tag() {
- HwasanThread *t = GetCurrentThread();
+ Thread *t = GetCurrentThread();
if (!t) return kFallbackTag;
return t->GenerateRandomTag();
}
diff --git a/lib/hwasan/hwasan.h b/lib/hwasan/hwasan.h
index 47d1d057a..a3da09e88 100644
--- a/lib/hwasan/hwasan.h
+++ b/lib/hwasan/hwasan.h
@@ -30,6 +30,10 @@
# define HWASAN_CONTAINS_UBSAN CAN_SANITIZE_UB
#endif
+#ifndef HWASAN_WITH_INTERCEPTORS
+#define HWASAN_WITH_INTERCEPTORS 0
+#endif
+
typedef u8 tag_t;
// TBI (Top Byte Ignore) feature of AArch64: bits [63:56] are ignored in address
@@ -37,16 +41,21 @@ typedef u8 tag_t;
const unsigned kAddressTagShift = 56;
const uptr kAddressTagMask = 0xFFUL << kAddressTagShift;
+// Minimal alignment of the shadow base address. Determines the space available
+// for threads and stack histories. This is an ABI constant.
+const unsigned kShadowBaseAlignment = 32;
+
static inline tag_t GetTagFromPointer(uptr p) {
return p >> kAddressTagShift;
}
-static inline uptr GetAddressFromPointer(uptr p) {
- return p & ~kAddressTagMask;
+static inline uptr UntagAddr(uptr tagged_addr) {
+ return tagged_addr & ~kAddressTagMask;
}
-static inline void * GetAddressFromPointer(const void *p) {
- return (void *)((uptr)p & ~kAddressTagMask);
+static inline void *UntagPtr(const void *tagged_ptr) {
+ return reinterpret_cast<void *>(
+ UntagAddr(reinterpret_cast<uptr>(tagged_ptr)));
}
static inline uptr AddTagToPointer(uptr p, tag_t tag) {
@@ -61,6 +70,8 @@ extern int hwasan_report_count;
bool ProtectRange(uptr beg, uptr end);
bool InitShadow();
+void InitThreads();
+void MadviseShadow();
char *GetProcSelfMaps();
void InitializeInterceptors();
@@ -135,13 +146,13 @@ class ScopedThreadLocalStateBackup {
u64 va_arg_overflow_size_tls;
};
-void HwasanTSDInit(void (*destructor)(void *tsd));
-void *HwasanTSDGet();
-void HwasanTSDSet(void *tsd);
-void HwasanTSDDtor(void *tsd);
+void HwasanTSDInit();
+void HwasanTSDThreadInit();
void HwasanOnDeadlySignal(int signo, void *info, void *context);
+void UpdateMemoryUsage();
+
} // namespace __hwasan
#define HWASAN_MALLOC_HOOK(ptr, size) \
diff --git a/lib/hwasan/hwasan_allocator.cc b/lib/hwasan/hwasan_allocator.cc
index c2b9b0b69..b9c379ea4 100644
--- a/lib/hwasan/hwasan_allocator.cc
+++ b/lib/hwasan/hwasan_allocator.cc
@@ -12,10 +12,6 @@
// HWAddressSanitizer allocator.
//===----------------------------------------------------------------------===//
-#include "sanitizer_common/sanitizer_allocator.h"
-#include "sanitizer_common/sanitizer_allocator_checks.h"
-#include "sanitizer_common/sanitizer_allocator_interface.h"
-#include "sanitizer_common/sanitizer_allocator_report.h"
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_errno.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
@@ -23,29 +19,14 @@
#include "hwasan_allocator.h"
#include "hwasan_mapping.h"
#include "hwasan_thread.h"
-#include "hwasan_poisoning.h"
+#include "hwasan_report.h"
namespace __hwasan {
-enum {
- CHUNK_INVALID = 0,
- CHUNK_FREE = 1,
- CHUNK_ALLOCATED = 2
-};
-
-struct Metadata {
- u64 state : 2;
- u64 requested_size : 62;
- u32 alloc_context_id;
- u32 free_context_id;
-};
-
-bool HwasanChunkView::IsValid() const {
- return metadata_ && metadata_->state != CHUNK_INVALID;
-}
bool HwasanChunkView::IsAllocated() const {
- return metadata_ && metadata_->state == CHUNK_ALLOCATED;
+ return metadata_ && metadata_->alloc_context_id && metadata_->requested_size;
}
+
uptr HwasanChunkView::Beg() const {
return block_;
}
@@ -58,44 +39,6 @@ uptr HwasanChunkView::UsedSize() const {
u32 HwasanChunkView::GetAllocStackId() const {
return metadata_->alloc_context_id;
}
-u32 HwasanChunkView::GetFreeStackId() const {
- return metadata_->free_context_id;
-}
-
-struct HwasanMapUnmapCallback {
- void OnMap(uptr p, uptr size) const {}
- void OnUnmap(uptr p, uptr size) const {
- // We are about to unmap a chunk of user memory.
- // It can return as user-requested mmap() or another thread stack.
- // Make it accessible with zero-tagged pointer.
- TagMemory(p, size, 0);
- }
-};
-
-#if !defined(__aarch64__) && !defined(__x86_64__)
-#error Unsupported platform
-#endif
-
-static const uptr kMaxAllowedMallocSize = 2UL << 30; // 2G
-static const uptr kRegionSizeLog = 20;
-static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
-typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
-
-struct AP32 {
- static const uptr kSpaceBeg = 0;
- static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
- static const uptr kMetadataSize = sizeof(Metadata);
- typedef __sanitizer::CompactSizeClassMap SizeClassMap;
- static const uptr kRegionSizeLog = __hwasan::kRegionSizeLog;
- typedef __hwasan::ByteMap ByteMap;
- typedef HwasanMapUnmapCallback MapUnmapCallback;
- static const uptr kFlags = 0;
-};
-typedef SizeClassAllocator32<AP32> PrimaryAllocator;
-typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
-typedef LargeMmapAllocator<HwasanMapUnmapCallback> SecondaryAllocator;
-typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
- SecondaryAllocator> Allocator;
static Allocator allocator;
static AllocatorCache fallback_allocator_cache;
@@ -105,6 +48,10 @@ static atomic_uint8_t hwasan_allocator_tagging_enabled;
static const tag_t kFallbackAllocTag = 0xBB;
static const tag_t kFallbackFreeTag = 0xBC;
+void GetAllocatorStats(AllocatorStatCounters s) {
+ allocator.GetStats(s);
+}
+
void HwasanAllocatorInit() {
atomic_store_relaxed(&hwasan_allocator_tagging_enabled,
!flags()->disable_allocator_tagging);
@@ -112,34 +59,34 @@ void HwasanAllocatorInit() {
allocator.Init(common_flags()->allocator_release_to_os_interval_ms);
}
-AllocatorCache *GetAllocatorCache(HwasanThreadLocalMallocStorage *ms) {
- CHECK(ms);
- CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator_cache));
- return reinterpret_cast<AllocatorCache *>(ms->allocator_cache);
+void AllocatorSwallowThreadLocalCache(AllocatorCache *cache) {
+ allocator.SwallowCache(cache);
}
-void HwasanThreadLocalMallocStorage::CommitBack() {
- allocator.SwallowCache(GetAllocatorCache(this));
+static uptr TaggedSize(uptr size) {
+ if (!size) size = 1;
+ uptr new_size = RoundUpTo(size, kShadowAlignment);
+ CHECK_GE(new_size, size);
+ return new_size;
}
-static void *HwasanAllocate(StackTrace *stack, uptr size, uptr alignment,
- bool zeroise) {
- alignment = Max(alignment, kShadowAlignment);
- size = RoundUpTo(size, kShadowAlignment);
-
- if (size > kMaxAllowedMallocSize) {
+static void *HwasanAllocate(StackTrace *stack, uptr orig_size, uptr alignment,
+ bool zeroise) {
+ if (orig_size > kMaxAllowedMallocSize) {
if (AllocatorMayReturnNull()) {
Report("WARNING: HWAddressSanitizer failed to allocate 0x%zx bytes\n",
- size);
+ orig_size);
return nullptr;
}
- ReportAllocationSizeTooBig(size, kMaxAllowedMallocSize, stack);
+ ReportAllocationSizeTooBig(orig_size, kMaxAllowedMallocSize, stack);
}
- HwasanThread *t = GetCurrentThread();
+
+ alignment = Max(alignment, kShadowAlignment);
+ uptr size = TaggedSize(orig_size);
+ Thread *t = GetCurrentThread();
void *allocated;
if (t) {
- AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
- allocated = allocator.Allocate(cache, size, alignment);
+ allocated = allocator.Allocate(t->allocator_cache(), size, alignment);
} else {
SpinMutexLock l(&fallback_mutex);
AllocatorCache *cache = &fallback_allocator_cache;
@@ -153,11 +100,14 @@ static void *HwasanAllocate(StackTrace *stack, uptr size, uptr alignment,
}
Metadata *meta =
reinterpret_cast<Metadata *>(allocator.GetMetaData(allocated));
- meta->state = CHUNK_ALLOCATED;
- meta->requested_size = size;
+ meta->requested_size = static_cast<u32>(orig_size);
meta->alloc_context_id = StackDepotPut(*stack);
- if (zeroise)
+ if (zeroise) {
internal_memset(allocated, 0, size);
+ } else if (flags()->max_malloc_fill_size > 0) {
+ uptr fill_size = Min(size, (uptr)flags()->max_malloc_fill_size);
+ internal_memset(allocated, flags()->malloc_fill_byte, fill_size);
+ }
void *user_ptr = allocated;
if (flags()->tag_in_malloc &&
@@ -169,67 +119,68 @@ static void *HwasanAllocate(StackTrace *stack, uptr size, uptr alignment,
return user_ptr;
}
-void HwasanDeallocate(StackTrace *stack, void *user_ptr) {
- CHECK(user_ptr);
- HWASAN_FREE_HOOK(user_ptr);
+static bool PointerAndMemoryTagsMatch(void *tagged_ptr) {
+ CHECK(tagged_ptr);
+ tag_t ptr_tag = GetTagFromPointer(reinterpret_cast<uptr>(tagged_ptr));
+ tag_t mem_tag = *reinterpret_cast<tag_t *>(
+ MemToShadow(reinterpret_cast<uptr>(UntagPtr(tagged_ptr))));
+ return ptr_tag == mem_tag;
+}
+
+void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) {
+ CHECK(tagged_ptr);
+ HWASAN_FREE_HOOK(tagged_ptr);
+
+ if (!PointerAndMemoryTagsMatch(tagged_ptr))
+ ReportInvalidFree(stack, reinterpret_cast<uptr>(tagged_ptr));
- void *p = GetAddressFromPointer(user_ptr);
- Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(p));
- uptr size = meta->requested_size;
- meta->state = CHUNK_FREE;
+ void *untagged_ptr = UntagPtr(tagged_ptr);
+ Metadata *meta =
+ reinterpret_cast<Metadata *>(allocator.GetMetaData(untagged_ptr));
+ uptr orig_size = meta->requested_size;
+ u32 free_context_id = StackDepotPut(*stack);
+ u32 alloc_context_id = meta->alloc_context_id;
meta->requested_size = 0;
- meta->free_context_id = StackDepotPut(*stack);
+ meta->alloc_context_id = 0;
// This memory will not be reused by anyone else, so we are free to keep it
// poisoned.
- HwasanThread *t = GetCurrentThread();
+ Thread *t = GetCurrentThread();
+ if (flags()->max_free_fill_size > 0) {
+ uptr fill_size = Min(orig_size, (uptr)flags()->max_free_fill_size);
+ internal_memset(untagged_ptr, flags()->free_fill_byte, fill_size);
+ }
if (flags()->tag_in_free &&
atomic_load_relaxed(&hwasan_allocator_tagging_enabled))
- TagMemoryAligned((uptr)p, size,
+ TagMemoryAligned((uptr)untagged_ptr, TaggedSize(orig_size),
t ? t->GenerateRandomTag() : kFallbackFreeTag);
if (t) {
- AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
- allocator.Deallocate(cache, p);
+ allocator.Deallocate(t->allocator_cache(), untagged_ptr);
+ if (auto *ha = t->heap_allocations())
+ ha->push({reinterpret_cast<uptr>(tagged_ptr), alloc_context_id,
+ free_context_id, static_cast<u32>(orig_size)});
} else {
SpinMutexLock l(&fallback_mutex);
AllocatorCache *cache = &fallback_allocator_cache;
- allocator.Deallocate(cache, p);
+ allocator.Deallocate(cache, untagged_ptr);
}
}
-void *HwasanReallocate(StackTrace *stack, void *user_old_p, uptr new_size,
+void *HwasanReallocate(StackTrace *stack, void *tagged_ptr_old, uptr new_size,
uptr alignment) {
- alignment = Max(alignment, kShadowAlignment);
- new_size = RoundUpTo(new_size, kShadowAlignment);
-
- void *old_p = GetAddressFromPointer(user_old_p);
- Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(old_p));
- uptr old_size = meta->requested_size;
- uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(old_p);
- if (new_size <= actually_allocated_size) {
- // We are not reallocating here.
- // FIXME: update stack trace for the allocation?
- meta->requested_size = new_size;
- if (!atomic_load_relaxed(&hwasan_allocator_tagging_enabled))
- return user_old_p;
- if (flags()->retag_in_realloc) {
- HwasanThread *t = GetCurrentThread();
- return (void *)TagMemoryAligned(
- (uptr)old_p, new_size,
- t ? t->GenerateRandomTag() : kFallbackAllocTag);
- }
- if (new_size > old_size) {
- tag_t tag = GetTagFromPointer((uptr)user_old_p);
- TagMemoryAligned((uptr)old_p + old_size, new_size - old_size, tag);
- }
- return user_old_p;
- }
- uptr memcpy_size = Min(new_size, old_size);
- void *new_p = HwasanAllocate(stack, new_size, alignment, false /*zeroise*/);
- if (new_p) {
- internal_memcpy(new_p, old_p, memcpy_size);
- HwasanDeallocate(stack, old_p);
+ if (!PointerAndMemoryTagsMatch(tagged_ptr_old))
+ ReportInvalidFree(stack, reinterpret_cast<uptr>(tagged_ptr_old));
+
+ void *tagged_ptr_new =
+ HwasanAllocate(stack, new_size, alignment, false /*zeroise*/);
+ if (tagged_ptr_old && tagged_ptr_new) {
+ void *untagged_ptr_old = UntagPtr(tagged_ptr_old);
+ Metadata *meta =
+ reinterpret_cast<Metadata *>(allocator.GetMetaData(untagged_ptr_old));
+ internal_memcpy(UntagPtr(tagged_ptr_new), untagged_ptr_old,
+ Min(new_size, static_cast<uptr>(meta->requested_size)));
+ HwasanDeallocate(stack, tagged_ptr_old);
}
- return new_p;
+ return tagged_ptr_new;
}
void *HwasanCalloc(StackTrace *stack, uptr nmemb, uptr size) {
@@ -250,12 +201,12 @@ HwasanChunkView FindHeapChunkByAddress(uptr address) {
return HwasanChunkView(reinterpret_cast<uptr>(block), metadata);
}
-static uptr AllocationSize(const void *user_ptr) {
- const void *p = GetAddressFromPointer(user_ptr);
- if (!p) return 0;
- const void *beg = allocator.GetBlockBegin(p);
- if (beg != p) return 0;
- Metadata *b = (Metadata *)allocator.GetMetaData(p);
+static uptr AllocationSize(const void *tagged_ptr) {
+ const void *untagged_ptr = UntagPtr(tagged_ptr);
+ if (!untagged_ptr) return 0;
+ const void *beg = allocator.GetBlockBegin(untagged_ptr);
+ if (beg != untagged_ptr) return 0;
+ Metadata *b = (Metadata *)allocator.GetMetaData(untagged_ptr);
return b->requested_size;
}
diff --git a/lib/hwasan/hwasan_allocator.h b/lib/hwasan/hwasan_allocator.h
index d025112e9..3589212d8 100644
--- a/lib/hwasan/hwasan_allocator.h
+++ b/lib/hwasan/hwasan_allocator.h
@@ -1,4 +1,4 @@
-//===-- hwasan_allocator.h ----------------------------------------*- C++ -*-===//
+//===-- hwasan_allocator.h --------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -14,35 +14,69 @@
#ifndef HWASAN_ALLOCATOR_H
#define HWASAN_ALLOCATOR_H
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_checks.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_allocator_report.h"
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_ring_buffer.h"
+#include "hwasan_poisoning.h"
+
+#if !defined(__aarch64__) && !defined(__x86_64__)
+#error Unsupported platform
+#endif
namespace __hwasan {
-struct HwasanThreadLocalMallocStorage {
- uptr quarantine_cache[16];
- // Allocator cache contains atomic_uint64_t which must be 8-byte aligned.
- ALIGNED(8) uptr allocator_cache[96 * (512 * 8 + 16)]; // Opaque.
- void CommitBack();
+struct Metadata {
+ u32 requested_size; // sizes are < 4G.
+ u32 alloc_context_id;
+};
- private:
- // These objects are allocated via mmap() and are zero-initialized.
- HwasanThreadLocalMallocStorage() {}
+struct HwasanMapUnmapCallback {
+ void OnMap(uptr p, uptr size) const { UpdateMemoryUsage(); }
+ void OnUnmap(uptr p, uptr size) const {
+ // We are about to unmap a chunk of user memory.
+ // It can return as user-requested mmap() or another thread stack.
+ // Make it accessible with zero-tagged pointer.
+ TagMemory(p, size, 0);
+ }
+};
+
+static const uptr kMaxAllowedMallocSize = 2UL << 30; // 2G
+static const uptr kRegionSizeLog = 20;
+static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
+typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
+
+struct AP32 {
+ static const uptr kSpaceBeg = 0;
+ static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
+ static const uptr kMetadataSize = sizeof(Metadata);
+ typedef __sanitizer::CompactSizeClassMap SizeClassMap;
+ static const uptr kRegionSizeLog = __hwasan::kRegionSizeLog;
+ typedef __hwasan::ByteMap ByteMap;
+ typedef HwasanMapUnmapCallback MapUnmapCallback;
+ static const uptr kFlags = 0;
};
+typedef SizeClassAllocator32<AP32> PrimaryAllocator;
+typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
+typedef LargeMmapAllocator<HwasanMapUnmapCallback> SecondaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
+ SecondaryAllocator> Allocator;
-struct Metadata;
+
+void AllocatorSwallowThreadLocalCache(AllocatorCache *cache);
class HwasanChunkView {
public:
HwasanChunkView() : block_(0), metadata_(nullptr) {}
HwasanChunkView(uptr block, Metadata *metadata)
: block_(block), metadata_(metadata) {}
- bool IsValid() const; // Checks if it points to a valid allocated chunk
bool IsAllocated() const; // Checks if the memory is currently allocated
uptr Beg() const; // First byte of user memory
uptr End() const; // Last byte of user memory
uptr UsedSize() const; // Size requested by the user
u32 GetAllocStackId() const;
- u32 GetFreeStackId() const;
private:
uptr block_;
Metadata *const metadata_;
@@ -50,6 +84,21 @@ class HwasanChunkView {
HwasanChunkView FindHeapChunkByAddress(uptr address);
+// Information about one (de)allocation that happened in the past.
+// These are recorded in a thread-local ring buffer.
+// TODO: this is currently 24 bytes (20 bytes + alignment).
+// Compress it to 16 bytes or extend it to be more useful.
+struct HeapAllocationRecord {
+ uptr tagged_addr;
+ u32 alloc_context_id;
+ u32 free_context_id;
+ u32 requested_size;
+};
+
+typedef RingBuffer<HeapAllocationRecord> HeapAllocationsRingBuffer;
+
+void GetAllocatorStats(AllocatorStatCounters s);
+
} // namespace __hwasan
#endif // HWASAN_ALLOCATOR_H
diff --git a/lib/hwasan/hwasan_dynamic_shadow.cc b/lib/hwasan/hwasan_dynamic_shadow.cc
index 17338003a..87670f508 100644
--- a/lib/hwasan/hwasan_dynamic_shadow.cc
+++ b/lib/hwasan/hwasan_dynamic_shadow.cc
@@ -13,6 +13,7 @@
///
//===----------------------------------------------------------------------===//
+#include "hwasan.h"
#include "hwasan_dynamic_shadow.h"
#include "hwasan_mapping.h"
#include "sanitizer_common/sanitizer_common.h"
@@ -35,12 +36,16 @@ static void UnmapFromTo(uptr from, uptr to) {
}
}
-// Returns an address aligned to 8 pages, such that one page on the left and
-// shadow_size_bytes bytes on the right of it are mapped r/o.
+// Returns an address aligned to kShadowBaseAlignment, such that
+// 2**kShadowBaseAlingment on the left and shadow_size_bytes bytes on the right
+// of it are mapped no access.
static uptr MapDynamicShadow(uptr shadow_size_bytes) {
const uptr granularity = GetMmapGranularity();
- const uptr alignment = granularity * SHADOW_GRANULARITY;
- const uptr left_padding = granularity;
+ const uptr min_alignment = granularity << kShadowScale;
+ const uptr alignment = 1ULL << kShadowBaseAlignment;
+ CHECK_GE(alignment, min_alignment);
+
+ const uptr left_padding = 1ULL << kShadowBaseAlignment;
const uptr shadow_size =
RoundUpTo(shadow_size_bytes, granularity);
const uptr map_size = shadow_size + left_padding + alignment;
@@ -58,8 +63,7 @@ static uptr MapDynamicShadow(uptr shadow_size_bytes) {
} // namespace __hwasan
-#if HWASAN_PREMAP_SHADOW
-
+#if SANITIZER_ANDROID
extern "C" {
INTERFACE_ATTRIBUTE void __hwasan_shadow();
@@ -117,16 +121,22 @@ void __hwasan_shadow();
} // extern "C"
-#endif // HWASAN_PREMAP_SHADOW
-
namespace __hwasan {
uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
-#if HWASAN_PREMAP_SHADOW
if (IsPremapShadowAvailable())
return FindPremappedShadowStart(shadow_size_bytes);
-#endif
return MapDynamicShadow(shadow_size_bytes);
}
} // namespace __hwasan
+#else
+namespace __hwasan {
+
+uptr FindDynamicShadowStart(uptr shadow_size_bytes) {
+ return MapDynamicShadow(shadow_size_bytes);
+}
+
+} // namespace __hwasan
+#
+#endif // SANITIZER_ANDROID
diff --git a/lib/hwasan/hwasan_flags.h b/lib/hwasan/hwasan_flags.h
index 16d60c4d8..492d5bb98 100644
--- a/lib/hwasan/hwasan_flags.h
+++ b/lib/hwasan/hwasan_flags.h
@@ -1,4 +1,4 @@
-//===-- hwasan_flags.h --------------------------------------------*- C++ -*-===//
+//===-- hwasan_flags.h ------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/lib/hwasan/hwasan_flags.inc b/lib/hwasan/hwasan_flags.inc
index c45781168..f1b416d15 100644
--- a/lib/hwasan/hwasan_flags.inc
+++ b/lib/hwasan/hwasan_flags.inc
@@ -17,9 +17,10 @@
// HWASAN_FLAG(Type, Name, DefaultValue, Description)
// See COMMON_FLAG in sanitizer_flags.inc for more details.
+HWASAN_FLAG(bool, verbose_threads, false,
+ "inform on thread creation/destruction")
HWASAN_FLAG(bool, tag_in_malloc, true, "")
HWASAN_FLAG(bool, tag_in_free, true, "")
-HWASAN_FLAG(bool, retag_in_realloc, true, "")
HWASAN_FLAG(bool, print_stats, false, "")
HWASAN_FLAG(bool, halt_on_error, true, "")
HWASAN_FLAG(bool, atexit, false, "")
@@ -31,3 +32,26 @@ HWASAN_FLAG(bool, disable_allocator_tagging, false, "")
// If false, use simple increment of a thread local counter to generate new
// tags.
HWASAN_FLAG(bool, random_tags, true, "")
+
+HWASAN_FLAG(
+ int, max_malloc_fill_size, 0x1000, // By default, fill only the first 4K.
+ "HWASan allocator flag. max_malloc_fill_size is the maximal amount of "
+ "bytes that will be filled with malloc_fill_byte on malloc.")
+HWASAN_FLAG(
+ int, max_free_fill_size, 0,
+ "HWASan allocator flag. max_free_fill_size is the maximal amount of "
+ "bytes that will be filled with free_fill_byte during free.")
+HWASAN_FLAG(int, malloc_fill_byte, 0xbe,
+ "Value used to fill the newly allocated memory.")
+HWASAN_FLAG(int, free_fill_byte, 0x55,
+ "Value used to fill deallocated memory.")
+HWASAN_FLAG(int, heap_history_size, 1023,
+ "The number of heap (de)allocations remembered per thread. "
+ "Affects the quality of heap-related reports, but not the ability "
+ "to find bugs.")
+HWASAN_FLAG(bool, export_memory_stats, true,
+ "Export up-to-date memory stats through /proc")
+HWASAN_FLAG(int, stack_history_size, 1024,
+ "The number of stack frames remembered per thread. "
+ "Affects the quality of stack-related reports, but not the ability "
+ "to find bugs.")
diff --git a/lib/hwasan/hwasan_interceptors.cc b/lib/hwasan/hwasan_interceptors.cc
index 66aab95db..9a0770f56 100644
--- a/lib/hwasan/hwasan_interceptors.cc
+++ b/lib/hwasan/hwasan_interceptors.cc
@@ -1,4 +1,4 @@
-//===-- hwasan_interceptors.cc ----------------------------------------------===//
+//===-- hwasan_interceptors.cc --------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -50,18 +50,18 @@ DECLARE_REAL(void *, memcpy, void *dest, const void *src, uptr n)
DECLARE_REAL(void *, memset, void *dest, int c, uptr n)
bool IsInInterceptorScope() {
- HwasanThread *t = GetCurrentThread();
+ Thread *t = GetCurrentThread();
return t && t->InInterceptorScope();
}
struct InterceptorScope {
InterceptorScope() {
- HwasanThread *t = GetCurrentThread();
+ Thread *t = GetCurrentThread();
if (t)
t->EnterInterceptorScope();
}
~InterceptorScope() {
- HwasanThread *t = GetCurrentThread();
+ Thread *t = GetCurrentThread();
if (t)
t->LeaveInterceptorScope();
}
@@ -128,30 +128,24 @@ static void *AllocateFromLocalPool(uptr size_in_bytes) {
CHECK_UNPOISONED((x), \
common_flags()->strict_string_checks ? (len) + 1 : (n) )
-
-INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) {
+int __sanitizer_posix_memalign(void **memptr, uptr alignment, uptr size) {
GET_MALLOC_STACK_TRACE;
CHECK_NE(memptr, 0);
int res = hwasan_posix_memalign(memptr, alignment, size, &stack);
return res;
}
-#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
-INTERCEPTOR(void *, memalign, SIZE_T alignment, SIZE_T size) {
+void * __sanitizer_memalign(uptr alignment, uptr size) {
GET_MALLOC_STACK_TRACE;
return hwasan_memalign(alignment, size, &stack);
}
-#define HWASAN_MAYBE_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign)
-#else
-#define HWASAN_MAYBE_INTERCEPT_MEMALIGN
-#endif
-INTERCEPTOR(void *, aligned_alloc, SIZE_T alignment, SIZE_T size) {
+void * __sanitizer_aligned_alloc(uptr alignment, uptr size) {
GET_MALLOC_STACK_TRACE;
return hwasan_aligned_alloc(alignment, size, &stack);
}
-INTERCEPTOR(void *, __libc_memalign, SIZE_T alignment, SIZE_T size) {
+void * __sanitizer___libc_memalign(uptr alignment, uptr size) {
GET_MALLOC_STACK_TRACE;
void *ptr = hwasan_memalign(alignment, size, &stack);
if (ptr)
@@ -159,80 +153,47 @@ INTERCEPTOR(void *, __libc_memalign, SIZE_T alignment, SIZE_T size) {
return ptr;
}
-INTERCEPTOR(void *, valloc, SIZE_T size) {
+void * __sanitizer_valloc(uptr size) {
GET_MALLOC_STACK_TRACE;
return hwasan_valloc(size, &stack);
}
-#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
-INTERCEPTOR(void *, pvalloc, SIZE_T size) {
+void * __sanitizer_pvalloc(uptr size) {
GET_MALLOC_STACK_TRACE;
return hwasan_pvalloc(size, &stack);
}
-#define HWASAN_MAYBE_INTERCEPT_PVALLOC INTERCEPT_FUNCTION(pvalloc)
-#else
-#define HWASAN_MAYBE_INTERCEPT_PVALLOC
-#endif
-INTERCEPTOR(void, free, void *ptr) {
+void __sanitizer_free(void *ptr) {
GET_MALLOC_STACK_TRACE;
if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
HwasanDeallocate(&stack, ptr);
}
-#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
-INTERCEPTOR(void, cfree, void *ptr) {
+void __sanitizer_cfree(void *ptr) {
GET_MALLOC_STACK_TRACE;
if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return;
HwasanDeallocate(&stack, ptr);
}
-#define HWASAN_MAYBE_INTERCEPT_CFREE INTERCEPT_FUNCTION(cfree)
-#else
-#define HWASAN_MAYBE_INTERCEPT_CFREE
-#endif
-INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
+uptr __sanitizer_malloc_usable_size(const void *ptr) {
return __sanitizer_get_allocated_size(ptr);
}
-#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
-// This function actually returns a struct by value, but we can't unpoison a
-// temporary! The following is equivalent on all supported platforms but
-// aarch64 (which uses a different register for sret value). We have a test
-// to confirm that.
-INTERCEPTOR(void, mallinfo, __sanitizer_mallinfo *sret) {
-#ifdef __aarch64__
- uptr r8;
- asm volatile("mov %0,x8" : "=r" (r8));
- sret = reinterpret_cast<__sanitizer_mallinfo*>(r8);
-#endif
- REAL(memset)(sret, 0, sizeof(*sret));
+struct __sanitizer_struct_mallinfo __sanitizer_mallinfo() {
+ __sanitizer_struct_mallinfo sret;
+ internal_memset(&sret, 0, sizeof(sret));
+ return sret;
}
-#define HWASAN_MAYBE_INTERCEPT_MALLINFO INTERCEPT_FUNCTION(mallinfo)
-#else
-#define HWASAN_MAYBE_INTERCEPT_MALLINFO
-#endif
-#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
-INTERCEPTOR(int, mallopt, int cmd, int value) {
+int __sanitizer_mallopt(int cmd, int value) {
return -1;
}
-#define HWASAN_MAYBE_INTERCEPT_MALLOPT INTERCEPT_FUNCTION(mallopt)
-#else
-#define HWASAN_MAYBE_INTERCEPT_MALLOPT
-#endif
-#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
-INTERCEPTOR(void, malloc_stats, void) {
+void __sanitizer_malloc_stats(void) {
// FIXME: implement, but don't call REAL(malloc_stats)!
}
-#define HWASAN_MAYBE_INTERCEPT_MALLOC_STATS INTERCEPT_FUNCTION(malloc_stats)
-#else
-#define HWASAN_MAYBE_INTERCEPT_MALLOC_STATS
-#endif
-
-INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) {
+void * __sanitizer_calloc(uptr nmemb, uptr size) {
GET_MALLOC_STACK_TRACE;
if (UNLIKELY(!hwasan_inited))
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
@@ -240,7 +201,7 @@ INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) {
return hwasan_calloc(nmemb, size, &stack);
}
-INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) {
+void * __sanitizer_realloc(void *ptr, uptr size) {
GET_MALLOC_STACK_TRACE;
if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
@@ -258,7 +219,7 @@ INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) {
return hwasan_realloc(ptr, size, &stack);
}
-INTERCEPTOR(void *, malloc, SIZE_T size) {
+void * __sanitizer_malloc(uptr size) {
GET_MALLOC_STACK_TRACE;
if (UNLIKELY(!hwasan_init_is_running))
ENSURE_HWASAN_INITED();
@@ -268,48 +229,62 @@ INTERCEPTOR(void *, malloc, SIZE_T size) {
return hwasan_malloc(size, &stack);
}
-template <class Mmap>
-static void *mmap_interceptor(Mmap real_mmap, void *addr, SIZE_T sz, int prot,
- int flags, int fd, OFF64_T off) {
- if (addr && !MEM_IS_APP(addr)) {
- if (flags & map_fixed) {
- errno = errno_EINVAL;
- return (void *)-1;
- } else {
- addr = nullptr;
- }
- }
- return real_mmap(addr, sz, prot, flags, fd, off);
-}
+#if HWASAN_WITH_INTERCEPTORS
+#define INTERCEPTOR_ALIAS(RET, FN, ARGS...) \
+ extern "C" SANITIZER_INTERFACE_ATTRIBUTE RET WRAP(FN)(ARGS) \
+ ALIAS("__sanitizer_" #FN); \
+ extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE RET FN( \
+ ARGS) ALIAS("__sanitizer_" #FN)
+
+INTERCEPTOR_ALIAS(int, posix_memalign, void **memptr, SIZE_T alignment,
+ SIZE_T size);
+INTERCEPTOR_ALIAS(void *, aligned_alloc, SIZE_T alignment, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, __libc_memalign, SIZE_T alignment, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, valloc, SIZE_T size);
+INTERCEPTOR_ALIAS(void, free, void *ptr);
+INTERCEPTOR_ALIAS(uptr, malloc_usable_size, const void *ptr);
+INTERCEPTOR_ALIAS(void *, calloc, SIZE_T nmemb, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, realloc, void *ptr, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, malloc, SIZE_T size);
+
+#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
+INTERCEPTOR_ALIAS(void *, memalign, SIZE_T alignment, SIZE_T size);
+INTERCEPTOR_ALIAS(void *, pvalloc, SIZE_T size);
+INTERCEPTOR_ALIAS(void, cfree, void *ptr);
+INTERCEPTOR_ALIAS(__sanitizer_struct_mallinfo, mallinfo);
+INTERCEPTOR_ALIAS(int, mallopt, int cmd, int value);
+INTERCEPTOR_ALIAS(void, malloc_stats, void);
+#endif
+#endif // HWASAN_WITH_INTERCEPTORS
+
+#if HWASAN_WITH_INTERCEPTORS
extern "C" int pthread_attr_init(void *attr);
extern "C" int pthread_attr_destroy(void *attr);
+struct ThreadStartArg {
+ thread_callback_t callback;
+ void *param;
+};
+
static void *HwasanThreadStartFunc(void *arg) {
- HwasanThread *t = (HwasanThread *)arg;
- SetCurrentThread(t);
- return t->ThreadStart();
+ __hwasan_thread_enter();
+ ThreadStartArg A = *reinterpret_cast<ThreadStartArg*>(arg);
+ UnmapOrDie(arg, GetPageSizeCached());
+ return A.callback(A.param);
}
INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*),
void * param) {
- ENSURE_HWASAN_INITED(); // for GetTlsSize()
- __sanitizer_pthread_attr_t myattr;
- if (!attr) {
- pthread_attr_init(&myattr);
- attr = &myattr;
- }
-
- AdjustStackSize(attr);
-
- HwasanThread *t = HwasanThread::Create(callback, param);
-
- int res = REAL(pthread_create)(th, attr, HwasanThreadStartFunc, t);
-
- if (attr == &myattr)
- pthread_attr_destroy(&myattr);
+ ScopedTaggingDisabler disabler;
+ ThreadStartArg *A = reinterpret_cast<ThreadStartArg *> (MmapOrDie(
+ GetPageSizeCached(), "pthread_create"));
+ *A = {callback, param};
+ int res = REAL(pthread_create)(UntagPtr(th), UntagPtr(attr),
+ &HwasanThreadStartFunc, A);
return res;
}
+#endif // HWASAN_WITH_INTERCEPTORS
static void BeforeFork() {
StackDepotLockAll();
@@ -341,138 +316,18 @@ int OnExit() {
} // namespace __hwasan
-// A version of CHECK_UNPOISONED using a saved scope value. Used in common
-// interceptors.
-#define CHECK_UNPOISONED_CTX(ctx, x, n) \
- do { \
- if (!((HwasanInterceptorContext *)ctx)->in_interceptor_scope) \
- CHECK_UNPOISONED_0(x, n); \
- } while (0)
-
-#define HWASAN_INTERCEPT_FUNC(name) \
- do { \
- if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \
- VReport(1, "HWAddressSanitizer: failed to intercept '" #name "'\n"); \
- } while (0)
-
-#define HWASAN_INTERCEPT_FUNC_VER(name, ver) \
- do { \
- if ((!INTERCEPT_FUNCTION_VER(name, ver) || !REAL(name))) \
- VReport( \
- 1, "HWAddressSanitizer: failed to intercept '" #name "@@" #ver "'\n"); \
- } while (0)
-
-#define COMMON_INTERCEPT_FUNCTION(name) HWASAN_INTERCEPT_FUNC(name)
-#define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \
- HWASAN_INTERCEPT_FUNC_VER(name, ver)
-#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
- CHECK_UNPOISONED_CTX(ctx, ptr, size)
-#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
- CHECK_UNPOISONED_CTX(ctx, ptr, size)
-#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ptr, size) \
- HWASAN_WRITE_RANGE(ctx, ptr, size)
-#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
- if (hwasan_init_is_running) return REAL(func)(__VA_ARGS__); \
- ENSURE_HWASAN_INITED(); \
- HwasanInterceptorContext hwasan_ctx = {IsInInterceptorScope()}; \
- ctx = (void *)&hwasan_ctx; \
- (void)ctx; \
- InterceptorScope interceptor_scope;
-#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
- do { \
- } while (false)
-#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
- do { \
- } while (false)
-#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
- do { \
- } while (false)
-#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
- do { \
- } while (false)
-#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
- do { \
- } while (false) // FIXME
-#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
- do { \
- } while (false) // FIXME
-#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
-#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
-
-#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
- if (HwasanThread *t = GetCurrentThread()) { \
- *begin = t->tls_begin(); \
- *end = t->tls_end(); \
- } else { \
- *begin = *end = 0; \
- }
-
-#define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size) \
- { \
- COMMON_INTERCEPTOR_ENTER(ctx, memset, dst, v, size); \
- if (common_flags()->intercept_intrin && \
- MEM_IS_APP(GetAddressFromPointer(dst))) \
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size); \
- return REAL(memset)(dst, v, size); \
- }
-
-#define COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, length, prot, flags, fd, \
- offset) \
- do { \
- return mmap_interceptor(REAL(mmap), addr, length, prot, flags, fd, \
- offset); \
- } while (false)
-
-#include "sanitizer_common/sanitizer_platform_interceptors.h"
-#include "sanitizer_common/sanitizer_common_interceptors.inc"
-#include "sanitizer_common/sanitizer_signal_interceptors.inc"
-
-#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s)
-#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \
- do { \
- (void)(p); \
- (void)(s); \
- } while (false)
-#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
- do { \
- (void)(p); \
- (void)(s); \
- } while (false)
-#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
- do { \
- (void)(p); \
- (void)(s); \
- } while (false)
-#include "sanitizer_common/sanitizer_common_syscalls.inc"
-#include "sanitizer_common/sanitizer_syscalls_netbsd.inc"
-
-
-
namespace __hwasan {
void InitializeInterceptors() {
static int inited = 0;
CHECK_EQ(inited, 0);
- InitializeCommonInterceptors();
- InitializeSignalInterceptors();
-
- INTERCEPT_FUNCTION(posix_memalign);
- HWASAN_MAYBE_INTERCEPT_MEMALIGN;
- INTERCEPT_FUNCTION(__libc_memalign);
- INTERCEPT_FUNCTION(valloc);
- HWASAN_MAYBE_INTERCEPT_PVALLOC;
- INTERCEPT_FUNCTION(malloc);
- INTERCEPT_FUNCTION(calloc);
- INTERCEPT_FUNCTION(realloc);
- INTERCEPT_FUNCTION(free);
- HWASAN_MAYBE_INTERCEPT_CFREE;
- INTERCEPT_FUNCTION(malloc_usable_size);
- HWASAN_MAYBE_INTERCEPT_MALLINFO;
- HWASAN_MAYBE_INTERCEPT_MALLOPT;
- HWASAN_MAYBE_INTERCEPT_MALLOC_STATS;
- INTERCEPT_FUNCTION(pthread_create);
+
INTERCEPT_FUNCTION(fork);
+#if HWASAN_WITH_INTERCEPTORS
+ INTERCEPT_FUNCTION(pthread_create);
+#endif
+
inited = 1;
}
} // namespace __hwasan
diff --git a/lib/hwasan/hwasan_interface_internal.h b/lib/hwasan/hwasan_interface_internal.h
index b4e5c8090..448997e5e 100644
--- a/lib/hwasan/hwasan_interface_internal.h
+++ b/lib/hwasan/hwasan_interface_internal.h
@@ -1,4 +1,4 @@
-//===-- hwasan_interface_internal.h -------------------------------*- C++ -*-===//
+//===-- hwasan_interface_internal.h -----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -16,10 +16,14 @@
#define HWASAN_INTERFACE_INTERNAL_H
#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_shadow_init();
+
+SANITIZER_INTERFACE_ATTRIBUTE
void __hwasan_init();
using __sanitizer::uptr;
@@ -91,6 +95,9 @@ SANITIZER_INTERFACE_ATTRIBUTE
void __hwasan_tag_memory(uptr p, u8 tag, uptr sz);
SANITIZER_INTERFACE_ATTRIBUTE
+uptr __hwasan_tag_pointer(uptr p, u8 tag);
+
+SANITIZER_INTERFACE_ATTRIBUTE
u8 __hwasan_generate_tag();
// Returns the offset of the first tag mismatch or -1 if the whole range is
@@ -105,6 +112,9 @@ SANITIZER_INTERFACE_ATTRIBUTE
void __hwasan_print_shadow(const void *x, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_handle_longjmp(const void *sp_dst);
+
+SANITIZER_INTERFACE_ATTRIBUTE
u16 __sanitizer_unaligned_load16(const uu16 *p);
SANITIZER_INTERFACE_ATTRIBUTE
@@ -128,6 +138,59 @@ void __hwasan_enable_allocator_tagging();
SANITIZER_INTERFACE_ATTRIBUTE
void __hwasan_disable_allocator_tagging();
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_thread_enter();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_thread_exit();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_print_memory_usage();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_posix_memalign(void **memptr, uptr alignment, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_memalign(uptr alignment, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_aligned_alloc(uptr alignment, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer___libc_memalign(uptr alignment, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_valloc(uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_pvalloc(uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_free(void *ptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_cfree(void *ptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_malloc_usable_size(const void *ptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+__hwasan::__sanitizer_struct_mallinfo __sanitizer_mallinfo();
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_mallopt(int cmd, int value);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_malloc_stats(void);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_calloc(uptr nmemb, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_realloc(void *ptr, uptr size);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void * __sanitizer_malloc(uptr size);
} // extern "C"
#endif // HWASAN_INTERFACE_INTERNAL_H
diff --git a/lib/hwasan/hwasan_linux.cc b/lib/hwasan/hwasan_linux.cc
index 5ab98dca5..eef332fe9 100644
--- a/lib/hwasan/hwasan_linux.cc
+++ b/lib/hwasan/hwasan_linux.cc
@@ -22,6 +22,7 @@
#include "hwasan_mapping.h"
#include "hwasan_report.h"
#include "hwasan_thread.h"
+#include "hwasan_thread_list.h"
#include <elf.h>
#include <link.h>
@@ -37,6 +38,10 @@
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_procmaps.h"
+#if HWASAN_WITH_INTERCEPTORS && !SANITIZER_ANDROID
+THREADLOCAL uptr __hwasan_tls;
+#endif
+
namespace __hwasan {
static void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
@@ -51,8 +56,6 @@ static void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
size);
Abort();
}
- if (common_flags()->no_huge_pages_for_shadow) NoHugePagesInRegion(beg, size);
- if (common_flags()->use_madv_dontdump) DontDumpShadowMemory(beg, size);
}
static void ProtectGap(uptr addr, uptr size) {
@@ -103,55 +106,31 @@ static void PrintAddressSpaceLayout() {
else
CHECK_EQ(kHighShadowEnd + 1, kHighMemStart);
PrintRange(kHighShadowStart, kHighShadowEnd, "HighShadow");
- if (SHADOW_OFFSET) {
- if (kLowShadowEnd + 1 < kHighShadowStart)
- PrintRange(kLowShadowEnd + 1, kHighShadowStart - 1, "ShadowGap");
- else
- CHECK_EQ(kLowMemEnd + 1, kHighShadowStart);
- PrintRange(kLowShadowStart, kLowShadowEnd, "LowShadow");
- if (kLowMemEnd + 1 < kLowShadowStart)
- PrintRange(kLowMemEnd + 1, kLowShadowStart - 1, "ShadowGap");
- else
- CHECK_EQ(kLowMemEnd + 1, kLowShadowStart);
- PrintRange(kLowMemStart, kLowMemEnd, "LowMem");
- CHECK_EQ(0, kLowMemStart);
- } else {
- if (kLowMemEnd + 1 < kHighShadowStart)
- PrintRange(kLowMemEnd + 1, kHighShadowStart - 1, "ShadowGap");
- else
- CHECK_EQ(kLowMemEnd + 1, kHighShadowStart);
- PrintRange(kLowMemStart, kLowMemEnd, "LowMem");
- CHECK_EQ(kLowShadowEnd + 1, kLowMemStart);
- PrintRange(kLowShadowStart, kLowShadowEnd, "LowShadow");
- PrintRange(0, kLowShadowStart - 1, "ShadowGap");
- }
+ if (kLowShadowEnd + 1 < kHighShadowStart)
+ PrintRange(kLowShadowEnd + 1, kHighShadowStart - 1, "ShadowGap");
+ else
+ CHECK_EQ(kLowMemEnd + 1, kHighShadowStart);
+ PrintRange(kLowShadowStart, kLowShadowEnd, "LowShadow");
+ if (kLowMemEnd + 1 < kLowShadowStart)
+ PrintRange(kLowMemEnd + 1, kLowShadowStart - 1, "ShadowGap");
+ else
+ CHECK_EQ(kLowMemEnd + 1, kLowShadowStart);
+ PrintRange(kLowMemStart, kLowMemEnd, "LowMem");
+ CHECK_EQ(0, kLowMemStart);
}
static uptr GetHighMemEnd() {
// HighMem covers the upper part of the address space.
uptr max_address = GetMaxUserVirtualAddress();
- if (SHADOW_OFFSET)
- // Adjust max address to make sure that kHighMemEnd and kHighMemStart are
- // properly aligned:
- max_address |= SHADOW_GRANULARITY * GetMmapGranularity() - 1;
+ // Adjust max address to make sure that kHighMemEnd and kHighMemStart are
+ // properly aligned:
+ max_address |= (GetMmapGranularity() << kShadowScale) - 1;
return max_address;
}
static void InitializeShadowBaseAddress(uptr shadow_size_bytes) {
- // Set the shadow memory address to uninitialized.
- __hwasan_shadow_memory_dynamic_address = kDefaultShadowSentinel;
- uptr shadow_start = SHADOW_OFFSET;
- // Detect if a dynamic shadow address must be used and find the available
- // location when necessary. When dynamic address is used, the macro
- // kLowShadowBeg expands to __hwasan_shadow_memory_dynamic_address which
- // was just set to kDefaultShadowSentinel.
- if (shadow_start == kDefaultShadowSentinel) {
- __hwasan_shadow_memory_dynamic_address = 0;
- CHECK_EQ(0, SHADOW_OFFSET);
- shadow_start = FindDynamicShadowStart(shadow_size_bytes);
- }
- // Update the shadow memory address (potentially) used by instrumentation.
- __hwasan_shadow_memory_dynamic_address = shadow_start;
+ __hwasan_shadow_memory_dynamic_address =
+ FindDynamicShadowStart(shadow_size_bytes);
}
bool InitShadow() {
@@ -159,29 +138,23 @@ bool InitShadow() {
kHighMemEnd = GetHighMemEnd();
// Determine shadow memory base offset.
- InitializeShadowBaseAddress(MEM_TO_SHADOW_SIZE(kHighMemEnd));
+ InitializeShadowBaseAddress(MemToShadowSize(kHighMemEnd));
// Place the low memory first.
- if (SHADOW_OFFSET) {
- kLowMemEnd = SHADOW_OFFSET - 1;
- kLowMemStart = 0;
- } else {
- // LowMem covers as much of the first 4GB as possible.
- kLowMemEnd = (1UL << 32) - 1;
- kLowMemStart = MEM_TO_SHADOW(kLowMemEnd) + 1;
- }
+ kLowMemEnd = __hwasan_shadow_memory_dynamic_address - 1;
+ kLowMemStart = 0;
// Define the low shadow based on the already placed low memory.
- kLowShadowEnd = MEM_TO_SHADOW(kLowMemEnd);
- kLowShadowStart = SHADOW_OFFSET ? SHADOW_OFFSET : MEM_TO_SHADOW(kLowMemStart);
+ kLowShadowEnd = MemToShadow(kLowMemEnd);
+ kLowShadowStart = __hwasan_shadow_memory_dynamic_address;
// High shadow takes whatever memory is left up there (making sure it is not
// interfering with low memory in the fixed case).
- kHighShadowEnd = MEM_TO_SHADOW(kHighMemEnd);
- kHighShadowStart = Max(kLowMemEnd, MEM_TO_SHADOW(kHighShadowEnd)) + 1;
+ kHighShadowEnd = MemToShadow(kHighMemEnd);
+ kHighShadowStart = Max(kLowMemEnd, MemToShadow(kHighShadowEnd)) + 1;
// High memory starts where allocated shadow allows.
- kHighMemStart = SHADOW_TO_MEM(kHighShadowStart);
+ kHighMemStart = ShadowToMem(kHighShadowStart);
// Check the sanity of the defined memory ranges (there might be gaps).
CHECK_EQ(kHighMemStart % GetMmapGranularity(), 0);
@@ -190,10 +163,7 @@ bool InitShadow() {
CHECK_GT(kHighShadowStart, kLowMemEnd);
CHECK_GT(kLowMemEnd, kLowMemStart);
CHECK_GT(kLowShadowEnd, kLowShadowStart);
- if (SHADOW_OFFSET)
- CHECK_GT(kLowShadowStart, kLowMemEnd);
- else
- CHECK_GT(kLowMemEnd, kLowShadowStart);
+ CHECK_GT(kLowShadowStart, kLowMemEnd);
if (Verbosity())
PrintAddressSpaceLayout();
@@ -204,21 +174,43 @@ bool InitShadow() {
// Protect all the gaps.
ProtectGap(0, Min(kLowMemStart, kLowShadowStart));
- if (SHADOW_OFFSET) {
- if (kLowMemEnd + 1 < kLowShadowStart)
- ProtectGap(kLowMemEnd + 1, kLowShadowStart - kLowMemEnd - 1);
- if (kLowShadowEnd + 1 < kHighShadowStart)
- ProtectGap(kLowShadowEnd + 1, kHighShadowStart - kLowShadowEnd - 1);
- } else {
- if (kLowMemEnd + 1 < kHighShadowStart)
- ProtectGap(kLowMemEnd + 1, kHighShadowStart - kLowMemEnd - 1);
- }
+ if (kLowMemEnd + 1 < kLowShadowStart)
+ ProtectGap(kLowMemEnd + 1, kLowShadowStart - kLowMemEnd - 1);
+ if (kLowShadowEnd + 1 < kHighShadowStart)
+ ProtectGap(kLowShadowEnd + 1, kHighShadowStart - kLowShadowEnd - 1);
if (kHighShadowEnd + 1 < kHighMemStart)
ProtectGap(kHighShadowEnd + 1, kHighMemStart - kHighShadowEnd - 1);
return true;
}
+void InitThreads() {
+ CHECK(__hwasan_shadow_memory_dynamic_address);
+ uptr guard_page_size = GetMmapGranularity();
+ uptr thread_space_start =
+ __hwasan_shadow_memory_dynamic_address - (1ULL << kShadowBaseAlignment);
+ uptr thread_space_end =
+ __hwasan_shadow_memory_dynamic_address - guard_page_size;
+ ReserveShadowMemoryRange(thread_space_start, thread_space_end - 1,
+ "hwasan threads");
+ ProtectGap(thread_space_end,
+ __hwasan_shadow_memory_dynamic_address - thread_space_end);
+ InitThreadList(thread_space_start, thread_space_end - thread_space_start);
+}
+
+static void MadviseShadowRegion(uptr beg, uptr end) {
+ uptr size = end - beg + 1;
+ if (common_flags()->no_huge_pages_for_shadow)
+ NoHugePagesInRegion(beg, size);
+ if (common_flags()->use_madv_dontdump)
+ DontDumpShadowMemory(beg, size);
+}
+
+void MadviseShadow() {
+ MadviseShadowRegion(kLowShadowStart, kLowShadowEnd);
+ MadviseShadowRegion(kHighShadowStart, kHighShadowEnd);
+}
+
bool MemIsApp(uptr p) {
CHECK(GetTagFromPointer(p) == 0);
return p >= kHighMemStart || (p >= kLowMemStart && p <= kLowMemEnd);
@@ -240,37 +232,60 @@ void InstallAtExitHandler() {
// ---------------------- TSD ---------------- {{{1
+extern "C" void __hwasan_thread_enter() {
+ hwasanThreadList().CreateCurrentThread();
+}
+
+extern "C" void __hwasan_thread_exit() {
+ Thread *t = GetCurrentThread();
+ // Make sure that signal handler can not see a stale current thread pointer.
+ atomic_signal_fence(memory_order_seq_cst);
+ if (t)
+ hwasanThreadList().ReleaseThread(t);
+}
+
+#if HWASAN_WITH_INTERCEPTORS
static pthread_key_t tsd_key;
static bool tsd_key_inited = false;
-void HwasanTSDInit(void (*destructor)(void *tsd)) {
+void HwasanTSDThreadInit() {
+ if (tsd_key_inited)
+ CHECK_EQ(0, pthread_setspecific(tsd_key,
+ (void *)GetPthreadDestructorIterations()));
+}
+
+void HwasanTSDDtor(void *tsd) {
+ uptr iterations = (uptr)tsd;
+ if (iterations > 1) {
+ CHECK_EQ(0, pthread_setspecific(tsd_key, (void *)(iterations - 1)));
+ return;
+ }
+ __hwasan_thread_exit();
+}
+
+void HwasanTSDInit() {
CHECK(!tsd_key_inited);
tsd_key_inited = true;
- CHECK_EQ(0, pthread_key_create(&tsd_key, destructor));
+ CHECK_EQ(0, pthread_key_create(&tsd_key, HwasanTSDDtor));
}
+#else
+void HwasanTSDInit() {}
+void HwasanTSDThreadInit() {}
+#endif
-HwasanThread *GetCurrentThread() {
- return (HwasanThread*)pthread_getspecific(tsd_key);
+#if SANITIZER_ANDROID
+uptr *GetCurrentThreadLongPtr() {
+ return (uptr *)get_android_tls_ptr();
}
-
-void SetCurrentThread(HwasanThread *t) {
- // Make sure that HwasanTSDDtor gets called at the end.
- CHECK(tsd_key_inited);
- // Make sure we do not reset the current HwasanThread.
- CHECK_EQ(0, pthread_getspecific(tsd_key));
- pthread_setspecific(tsd_key, (void *)t);
+#else
+uptr *GetCurrentThreadLongPtr() {
+ return &__hwasan_tls;
}
+#endif
-void HwasanTSDDtor(void *tsd) {
- HwasanThread *t = (HwasanThread*)tsd;
- if (t->destructor_iterations_ > 1) {
- t->destructor_iterations_--;
- CHECK_EQ(0, pthread_setspecific(tsd_key, tsd));
- return;
- }
- // Make sure that signal handler can not see a stale current thread pointer.
- atomic_signal_fence(memory_order_seq_cst);
- HwasanThread::TSDDtor(tsd);
+Thread *GetCurrentThread() {
+ auto *R = (StackAllocationsRingBuffer*)GetCurrentThreadLongPtr();
+ return hwasanThreadList().GetThreadByBufferAddress((uptr)(R->Next()));
}
struct AccessInfo {
@@ -340,8 +355,8 @@ static bool HwasanOnSIGTRAP(int signo, siginfo_t *info, ucontext_t *uc) {
BufferedStackTrace *stack = stack_buffer.data();
stack->Reset();
SignalContext sig{info, uc};
- GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, uc,
- common_flags()->fast_unwind_on_fatal);
+ GetStackTrace(stack, kStackTraceMax, StackTrace::GetNextInstructionPc(sig.pc),
+ sig.bp, uc, common_flags()->fast_unwind_on_fatal);
ReportTagMismatch(stack, ai.addr, ai.size, ai.is_store);
@@ -360,8 +375,8 @@ static bool HwasanOnSIGTRAP(int signo, siginfo_t *info, ucontext_t *uc) {
static void OnStackUnwind(const SignalContext &sig, const void *,
BufferedStackTrace *stack) {
- GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, sig.context,
- common_flags()->fast_unwind_on_fatal);
+ GetStackTrace(stack, kStackTraceMax, StackTrace::GetNextInstructionPc(sig.pc),
+ sig.bp, sig.context, common_flags()->fast_unwind_on_fatal);
}
void HwasanOnDeadlySignal(int signo, void *info, void *context) {
diff --git a/lib/hwasan/hwasan_mapping.h b/lib/hwasan/hwasan_mapping.h
index 650a5aefc..e5e23dc60 100644
--- a/lib/hwasan/hwasan_mapping.h
+++ b/lib/hwasan/hwasan_mapping.h
@@ -16,68 +16,41 @@
#define HWASAN_MAPPING_H
#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "hwasan_interface_internal.h"
-// Typical mapping on Linux/x86_64 with fixed shadow mapping:
-// || [0x080000000000, 0x7fffffffffff] || HighMem ||
-// || [0x008000000000, 0x07ffffffffff] || HighShadow ||
-// || [0x000100000000, 0x007fffffffff] || ShadowGap ||
-// || [0x000010000000, 0x0000ffffffff] || LowMem ||
-// || [0x000001000000, 0x00000fffffff] || LowShadow ||
-// || [0x000000000000, 0x000000ffffff] || ShadowGap ||
-//
-// and with dynamic shadow mapped at [0x770d59f40000, 0x7f0d59f40000]:
+// Typical mapping on Linux/x86_64:
+// with dynamic shadow mapped at [0x770d59f40000, 0x7f0d59f40000]:
// || [0x7f0d59f40000, 0x7fffffffffff] || HighMem ||
// || [0x7efe2f934000, 0x7f0d59f3ffff] || HighShadow ||
// || [0x7e7e2f934000, 0x7efe2f933fff] || ShadowGap ||
// || [0x770d59f40000, 0x7e7e2f933fff] || LowShadow ||
// || [0x000000000000, 0x770d59f3ffff] || LowMem ||
-// Typical mapping on Android/AArch64 (39-bit VMA):
-// || [0x001000000000, 0x007fffffffff] || HighMem ||
-// || [0x000800000000, 0x000fffffffff] || ShadowGap ||
-// || [0x000100000000, 0x0007ffffffff] || HighShadow ||
-// || [0x000010000000, 0x0000ffffffff] || LowMem ||
-// || [0x000001000000, 0x00000fffffff] || LowShadow ||
-// || [0x000000000000, 0x000000ffffff] || ShadowGap ||
-//
-// and with dynamic shadow mapped: [0x007477480000, 0x007c77480000]:
+// Typical mapping on Android/AArch64
+// with dynamic shadow mapped: [0x007477480000, 0x007c77480000]:
// || [0x007c77480000, 0x007fffffffff] || HighMem ||
// || [0x007c3ebc8000, 0x007c7747ffff] || HighShadow ||
// || [0x007bbebc8000, 0x007c3ebc7fff] || ShadowGap ||
// || [0x007477480000, 0x007bbebc7fff] || LowShadow ||
// || [0x000000000000, 0x00747747ffff] || LowMem ||
-static constexpr __sanitizer::u64 kDefaultShadowSentinel = ~(__sanitizer::u64)0;
-
// Reasonable values are 4 (for 1/16th shadow) and 6 (for 1/64th).
-constexpr __sanitizer::uptr kShadowScale = 4;
-constexpr __sanitizer::uptr kShadowAlignment = 1ULL << kShadowScale;
-
-#if SANITIZER_ANDROID
-# define HWASAN_FIXED_MAPPING 0
-#else
-# define HWASAN_FIXED_MAPPING 1
-#endif
-
-#if HWASAN_FIXED_MAPPING
-# define SHADOW_OFFSET (0)
-# define HWASAN_PREMAP_SHADOW 0
-#else
-# define SHADOW_OFFSET (__hwasan_shadow_memory_dynamic_address)
-# define HWASAN_PREMAP_SHADOW 1
-#endif
-
-#define SHADOW_GRANULARITY (1ULL << kShadowScale)
-
-#define MEM_TO_SHADOW(mem) (((uptr)(mem) >> kShadowScale) + SHADOW_OFFSET)
-#define SHADOW_TO_MEM(shadow) (((uptr)(shadow) - SHADOW_OFFSET) << kShadowScale)
-
-#define MEM_TO_SHADOW_SIZE(size) ((uptr)(size) >> kShadowScale)
-
-#define MEM_IS_APP(mem) MemIsApp((uptr)(mem))
+constexpr uptr kShadowScale = 4;
+constexpr uptr kShadowAlignment = 1ULL << kShadowScale;
namespace __hwasan {
+inline uptr MemToShadow(uptr untagged_addr) {
+ return (untagged_addr >> kShadowScale) +
+ __hwasan_shadow_memory_dynamic_address;
+}
+inline uptr ShadowToMem(uptr shadow_addr) {
+ return (shadow_addr - __hwasan_shadow_memory_dynamic_address) << kShadowScale;
+}
+inline uptr MemToShadowSize(uptr size) {
+ return size >> kShadowScale;
+}
+
bool MemIsApp(uptr p);
} // namespace __hwasan
diff --git a/lib/hwasan/hwasan_poisoning.cc b/lib/hwasan/hwasan_poisoning.cc
index b99d8ed0b..9c8e16b12 100644
--- a/lib/hwasan/hwasan_poisoning.cc
+++ b/lib/hwasan/hwasan_poisoning.cc
@@ -1,4 +1,4 @@
-//===-- hwasan_poisoning.cc ---------------------------------------*- C++ -*-===//
+//===-- hwasan_poisoning.cc -------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -22,8 +22,8 @@ namespace __hwasan {
uptr TagMemoryAligned(uptr p, uptr size, tag_t tag) {
CHECK(IsAligned(p, kShadowAlignment));
CHECK(IsAligned(size, kShadowAlignment));
- uptr shadow_start = MEM_TO_SHADOW(p);
- uptr shadow_size = MEM_TO_SHADOW_SIZE(size);
+ uptr shadow_start = MemToShadow(p);
+ uptr shadow_size = MemToShadowSize(size);
internal_memset((void *)shadow_start, tag, shadow_size);
return AddTagToPointer(p, tag);
}
diff --git a/lib/hwasan/hwasan_poisoning.h b/lib/hwasan/hwasan_poisoning.h
index b44a91f97..0dbf9d8ed 100644
--- a/lib/hwasan/hwasan_poisoning.h
+++ b/lib/hwasan/hwasan_poisoning.h
@@ -1,4 +1,4 @@
-//===-- hwasan_poisoning.h ----------------------------------------*- C++ -*-===//
+//===-- hwasan_poisoning.h --------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/lib/hwasan/hwasan_report.cc b/lib/hwasan/hwasan_report.cc
index 16e9016ea..cf5f468c4 100644
--- a/lib/hwasan/hwasan_report.cc
+++ b/lib/hwasan/hwasan_report.cc
@@ -15,6 +15,8 @@
#include "hwasan.h"
#include "hwasan_allocator.h"
#include "hwasan_mapping.h"
+#include "hwasan_thread.h"
+#include "hwasan_thread_list.h"
#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
@@ -34,51 +36,169 @@ static StackTrace GetStackTraceFromId(u32 id) {
return res;
}
+// A RAII object that holds a copy of the current thread stack ring buffer.
+// The actual stack buffer may change while we are iterating over it (for
+// example, Printf may call syslog() which can itself be built with hwasan).
+class SavedStackAllocations {
+ public:
+ SavedStackAllocations(StackAllocationsRingBuffer *rb) {
+ uptr size = rb->size() * sizeof(uptr);
+ void *storage =
+ MmapAlignedOrDieOnFatalError(size, size * 2, "saved stack allocations");
+ new (&rb_) StackAllocationsRingBuffer(*rb, storage);
+ }
+
+ ~SavedStackAllocations() {
+ StackAllocationsRingBuffer *rb = get();
+ UnmapOrDie(rb->StartOfStorage(), rb->size() * sizeof(uptr));
+ }
+
+ StackAllocationsRingBuffer *get() {
+ return (StackAllocationsRingBuffer *)&rb_;
+ }
+
+ private:
+ uptr rb_;
+};
+
class Decorator: public __sanitizer::SanitizerCommonDecorator {
public:
Decorator() : SanitizerCommonDecorator() { }
+ const char *Access() { return Blue(); }
const char *Allocation() const { return Magenta(); }
const char *Origin() const { return Magenta(); }
const char *Name() const { return Green(); }
+ const char *Location() { return Green(); }
+ const char *Thread() { return Green(); }
};
-struct HeapAddressDescription {
- uptr addr;
- u32 alloc_stack_id;
- u32 free_stack_id;
-
- void Print() const {
- Decorator d;
- if (free_stack_id) {
- Printf("%sfreed here:%s\n", d.Allocation(), d.Default());
- GetStackTraceFromId(free_stack_id).Print();
- Printf("%spreviously allocated here:%s\n", d.Allocation(), d.Default());
- } else {
- Printf("%sallocated here:%s\n", d.Allocation(), d.Default());
+// Returns the index of the rb element that matches tagged_addr (plus one),
+// or zero if found nothing.
+uptr FindHeapAllocation(HeapAllocationsRingBuffer *rb,
+ uptr tagged_addr,
+ HeapAllocationRecord *har) {
+ if (!rb) return 0;
+ for (uptr i = 0, size = rb->size(); i < size; i++) {
+ auto h = (*rb)[i];
+ if (h.tagged_addr <= tagged_addr &&
+ h.tagged_addr + h.requested_size > tagged_addr) {
+ *har = h;
+ return i + 1;
}
- GetStackTraceFromId(alloc_stack_id).Print();
}
-};
-
-bool GetHeapAddressInformation(uptr addr, uptr access_size,
- HeapAddressDescription *description) {
- HwasanChunkView chunk = FindHeapChunkByAddress(addr);
- if (!chunk.IsValid())
- return false;
- description->addr = addr;
- description->alloc_stack_id = chunk.GetAllocStackId();
- description->free_stack_id = chunk.GetFreeStackId();
- return true;
+ return 0;
}
-void PrintAddressDescription(uptr addr, uptr access_size) {
- HeapAddressDescription heap_description;
- if (GetHeapAddressInformation(addr, access_size, &heap_description)) {
- heap_description.Print();
- return;
+void PrintAddressDescription(
+ uptr tagged_addr, uptr access_size,
+ StackAllocationsRingBuffer *current_stack_allocations) {
+ Decorator d;
+ int num_descriptions_printed = 0;
+ uptr untagged_addr = UntagAddr(tagged_addr);
+ // Check if this looks like a heap buffer overflow by scanning
+ // the shadow left and right and looking for the first adjacent
+ // object with a different memory tag. If that tag matches addr_tag,
+ // check the allocator if it has a live chunk there.
+ tag_t addr_tag = GetTagFromPointer(tagged_addr);
+ tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
+ if (*tag_ptr != addr_tag) { // should be true usually.
+ tag_t *left = tag_ptr, *right = tag_ptr;
+ // scan left.
+ for (int i = 0; i < 1000 && *left == *tag_ptr; i++, left--){}
+ // scan right.
+ for (int i = 0; i < 1000 && *right == *tag_ptr; i++, right++){}
+ // Chose the object that has addr_tag and that is closer to addr.
+ tag_t *candidate = nullptr;
+ if (*right == addr_tag && *left == addr_tag)
+ candidate = right - tag_ptr < tag_ptr - left ? right : left;
+ else if (*right == addr_tag)
+ candidate = right;
+ else if (*left == addr_tag)
+ candidate = left;
+
+ if (candidate) {
+ uptr mem = ShadowToMem(reinterpret_cast<uptr>(candidate));
+ HwasanChunkView chunk = FindHeapChunkByAddress(mem);
+ if (chunk.IsAllocated()) {
+ Printf("%s", d.Location());
+ Printf(
+ "%p is located %zd bytes to the %s of %zd-byte region [%p,%p)\n",
+ untagged_addr,
+ candidate == left ? untagged_addr - chunk.End()
+ : chunk.Beg() - untagged_addr,
+ candidate == right ? "left" : "right", chunk.UsedSize(),
+ chunk.Beg(), chunk.End());
+ Printf("%s", d.Allocation());
+ Printf("allocated here:\n");
+ Printf("%s", d.Default());
+ GetStackTraceFromId(chunk.GetAllocStackId()).Print();
+ num_descriptions_printed++;
+ }
+ }
}
- // We exhausted our possibilities. Bail out.
- Printf("HWAddressSanitizer can not describe address in more detail.\n");
+
+ hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
+ // Scan all threads' ring buffers to find if it's a heap-use-after-free.
+ HeapAllocationRecord har;
+ if (uptr D = FindHeapAllocation(t->heap_allocations(), tagged_addr, &har)) {
+ Printf("%s", d.Location());
+ Printf("%p is located %zd bytes inside of %zd-byte region [%p,%p)\n",
+ untagged_addr, untagged_addr - UntagAddr(har.tagged_addr),
+ har.requested_size, UntagAddr(har.tagged_addr),
+ UntagAddr(har.tagged_addr) + har.requested_size);
+ Printf("%s", d.Allocation());
+ Printf("freed by thread T%zd here:\n", t->unique_id());
+ Printf("%s", d.Default());
+ GetStackTraceFromId(har.free_context_id).Print();
+
+ Printf("%s", d.Allocation());
+ Printf("previously allocated here:\n", t);
+ Printf("%s", d.Default());
+ GetStackTraceFromId(har.alloc_context_id).Print();
+ t->Announce();
+
+ // Print a developer note: the index of this heap object
+ // in the thread's deallocation ring buffer.
+ Printf("hwasan_dev_note_heap_rb_distance: %zd %zd\n", D,
+ flags()->heap_history_size);
+
+ num_descriptions_printed++;
+ }
+
+ // Very basic check for stack memory.
+ if (t->AddrIsInStack(untagged_addr)) {
+ Printf("%s", d.Location());
+ Printf("Address %p is located in stack of thread T%zd\n", untagged_addr,
+ t->unique_id());
+ Printf("%s", d.Default());
+ t->Announce();
+
+ // Temporary report section, needs to be improved.
+ Printf("Previosly allocated frames:\n");
+ auto *sa = (t == GetCurrentThread() && current_stack_allocations)
+ ? current_stack_allocations
+ : t->stack_allocations();
+ uptr frames = Min((uptr)flags()->stack_history_size, sa->size());
+ for (uptr i = 0; i < frames; i++) {
+ uptr record = (*sa)[i];
+ if (!record)
+ break;
+ uptr sp = (record >> 48) << 4;
+ uptr pc_mask = (1ULL << 48) - 1;
+ uptr pc = record & pc_mask;
+ uptr fixed_pc = StackTrace::GetNextInstructionPc(pc);
+ StackTrace stack(&fixed_pc, 1);
+ Printf("record: %p pc: %p sp: %p", record, pc, sp);
+ stack.Print();
+ }
+
+ num_descriptions_printed++;
+ }
+ });
+
+ if (!num_descriptions_printed)
+ // We exhausted our possibilities. Bail out.
+ Printf("HWAddressSanitizer can not describe address in more detail.\n");
}
void ReportInvalidAccess(StackTrace *stack, u32 origin) {
@@ -97,37 +217,106 @@ void ReportStats() {}
void ReportInvalidAccessInsideAddressRange(const char *what, const void *start,
uptr size, uptr offset) {
ScopedErrorReportLock l;
+ SavedStackAllocations current_stack_allocations(
+ GetCurrentThread()->stack_allocations());
Decorator d;
Printf("%s", d.Warning());
Printf("%sTag mismatch in %s%s%s at offset %zu inside [%p, %zu)%s\n",
d.Warning(), d.Name(), what, d.Warning(), offset, start, size,
d.Default());
- PrintAddressDescription((uptr)start + offset, 1);
+ PrintAddressDescription((uptr)start + offset, 1,
+ current_stack_allocations.get());
// if (__sanitizer::Verbosity())
// DescribeMemoryRange(start, size);
}
-void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
+static void PrintTagsAroundAddr(tag_t *tag_ptr) {
+ Printf(
+ "Memory tags around the buggy address (one tag corresponds to %zd "
+ "bytes):\n", kShadowAlignment);
+
+ const uptr row_len = 16; // better be power of two.
+ const uptr num_rows = 11;
+ tag_t *center_row_beg = reinterpret_cast<tag_t *>(
+ RoundDownTo(reinterpret_cast<uptr>(tag_ptr), row_len));
+ tag_t *beg_row = center_row_beg - row_len * (num_rows / 2);
+ tag_t *end_row = center_row_beg + row_len * (num_rows / 2);
+ InternalScopedString s(GetPageSizeCached());
+ for (tag_t *row = beg_row; row < end_row; row += row_len) {
+ s.append("%s", row == center_row_beg ? "=>" : " ");
+ for (uptr i = 0; i < row_len; i++) {
+ s.append("%s", row + i == tag_ptr ? "[" : " ");
+ s.append("%02x", row[i]);
+ s.append("%s", row + i == tag_ptr ? "]" : " ");
+ }
+ s.append("%s\n", row == center_row_beg ? "<=" : " ");
+ Printf("%s", s.data());
+ s.clear();
+ }
+}
+
+void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
+ ScopedErrorReportLock l;
+ uptr untagged_addr = UntagAddr(tagged_addr);
+ tag_t ptr_tag = GetTagFromPointer(tagged_addr);
+ tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
+ tag_t mem_tag = *tag_ptr;
+ Decorator d;
+ Printf("%s", d.Error());
+ uptr pc = stack->size ? stack->trace[0] : 0;
+ const char *bug_type = "invalid-free";
+ Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type,
+ untagged_addr, pc);
+ Printf("%s", d.Access());
+ Printf("tags: %02x/%02x (ptr/mem)\n", ptr_tag, mem_tag);
+ Printf("%s", d.Default());
+
+ stack->Print();
+
+ PrintAddressDescription(tagged_addr, 0, nullptr);
+
+ PrintTagsAroundAddr(tag_ptr);
+
+ ReportErrorSummary(bug_type, stack);
+ Die();
+}
+
+void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
bool is_store) {
ScopedErrorReportLock l;
+ SavedStackAllocations current_stack_allocations(
+ GetCurrentThread()->stack_allocations());
Decorator d;
- Printf("%s", d.Warning());
- uptr address = GetAddressFromPointer(addr);
- Printf("%s of size %zu at %p\n", is_store ? "WRITE" : "READ", access_size,
- address);
+ Printf("%s", d.Error());
+ uptr untagged_addr = UntagAddr(tagged_addr);
+ // TODO: when possible, try to print heap-use-after-free, etc.
+ const char *bug_type = "tag-mismatch";
+ uptr pc = stack->size ? stack->trace[0] : 0;
+ Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type,
+ untagged_addr, pc);
- tag_t ptr_tag = GetTagFromPointer(addr);
- tag_t mem_tag = *(tag_t *)MEM_TO_SHADOW(address);
- Printf("pointer tag 0x%x\nmemory tag 0x%x\n", ptr_tag, mem_tag);
+ Thread *t = GetCurrentThread();
+
+ tag_t ptr_tag = GetTagFromPointer(tagged_addr);
+ tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
+ tag_t mem_tag = *tag_ptr;
+ Printf("%s", d.Access());
+ Printf("%s of size %zu at %p tags: %02x/%02x (ptr/mem) in thread T%zd\n",
+ is_store ? "WRITE" : "READ", access_size, untagged_addr, ptr_tag,
+ mem_tag, t->unique_id());
Printf("%s", d.Default());
stack->Print();
- PrintAddressDescription(address, access_size);
+ PrintAddressDescription(tagged_addr, access_size,
+ current_stack_allocations.get());
+ t->Announce();
+
+ PrintTagsAroundAddr(tag_ptr);
- ReportErrorSummary("tag-mismatch", stack);
+ ReportErrorSummary(bug_type, stack);
}
} // namespace __hwasan
diff --git a/lib/hwasan/hwasan_report.h b/lib/hwasan/hwasan_report.h
index bb33f1a87..7a9eec834 100644
--- a/lib/hwasan/hwasan_report.h
+++ b/lib/hwasan/hwasan_report.h
@@ -27,6 +27,7 @@ void ReportInvalidAccessInsideAddressRange(const char *what, const void *start,
uptr size, uptr offset);
void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
bool is_store);
+void ReportInvalidFree(StackTrace *stack, uptr addr);
void ReportAtExitStatistics();
diff --git a/lib/hwasan/hwasan_thread.cc b/lib/hwasan/hwasan_thread.cc
index b50c0fc76..0d15c7e46 100644
--- a/lib/hwasan/hwasan_thread.cc
+++ b/lib/hwasan/hwasan_thread.cc
@@ -5,8 +5,11 @@
#include "hwasan_poisoning.h"
#include "hwasan_interface_internal.h"
+#include "sanitizer_common/sanitizer_file.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_tls_get_addr.h"
+
namespace __hwasan {
static u32 RandomSeed() {
@@ -22,69 +25,79 @@ static u32 RandomSeed() {
return seed;
}
-HwasanThread *HwasanThread::Create(thread_callback_t start_routine,
- void *arg) {
- uptr PageSize = GetPageSizeCached();
- uptr size = RoundUpTo(sizeof(HwasanThread), PageSize);
- HwasanThread *thread = (HwasanThread*)MmapOrDie(size, __func__);
- thread->start_routine_ = start_routine;
- thread->arg_ = arg;
- thread->destructor_iterations_ = GetPthreadDestructorIterations();
- thread->random_state_ = flags()->random_tags ? RandomSeed() : 0;
-
- return thread;
-}
-
-void HwasanThread::SetThreadStackAndTls() {
- uptr tls_size = 0;
- uptr stack_size = 0;
- GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size,
- &tls_begin_, &tls_size);
- stack_top_ = stack_bottom_ + stack_size;
- tls_end_ = tls_begin_ + tls_size;
-
- int local;
- CHECK(AddrIsInStack((uptr)&local));
-}
-
-void HwasanThread::Init() {
- SetThreadStackAndTls();
- CHECK(MEM_IS_APP(stack_bottom_));
- CHECK(MEM_IS_APP(stack_top_ - 1));
-}
+void Thread::Init(uptr stack_buffer_start, uptr stack_buffer_size) {
+ static u64 unique_id;
+ unique_id_ = unique_id++;
+ random_state_ = flags()->random_tags ? RandomSeed() : unique_id_;
+ if (auto sz = flags()->heap_history_size)
+ heap_allocations_ = HeapAllocationsRingBuffer::New(sz);
+
+ HwasanTSDThreadInit(); // Only needed with interceptors.
+ uptr *ThreadLong = GetCurrentThreadLongPtr();
+ // The following implicitly sets (this) as the current thread.
+ stack_allocations_ = new (ThreadLong)
+ StackAllocationsRingBuffer((void *)stack_buffer_start, stack_buffer_size);
+ // Check that it worked.
+ CHECK_EQ(GetCurrentThread(), this);
+
+ // ScopedTaggingDisable needs GetCurrentThread to be set up.
+ ScopedTaggingDisabler disabler;
+
+ // If this process is "init" (pid 1), /proc may not be mounted yet.
+ if (IsMainThread() && !FileExists("/proc/self/maps")) {
+ stack_top_ = stack_bottom_ = 0;
+ tls_begin_ = tls_end_ = 0;
+ } else {
+ uptr tls_size;
+ uptr stack_size;
+ GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size,
+ &tls_begin_, &tls_size);
+ stack_top_ = stack_bottom_ + stack_size;
+ tls_end_ = tls_begin_ + tls_size;
+
+ int local;
+ CHECK(AddrIsInStack((uptr)&local));
+ CHECK(MemIsApp(stack_bottom_));
+ CHECK(MemIsApp(stack_top_ - 1));
+
+ if (stack_bottom_) {
+ CHECK(MemIsApp(stack_bottom_));
+ CHECK(MemIsApp(stack_top_ - 1));
+ }
+ }
-void HwasanThread::TSDDtor(void *tsd) {
- HwasanThread *t = (HwasanThread*)tsd;
- t->Destroy();
+ if (flags()->verbose_threads) {
+ if (IsMainThread()) {
+ Printf("sizeof(Thread): %zd sizeof(HeapRB): %zd sizeof(StackRB): %zd\n",
+ sizeof(Thread), heap_allocations_->SizeInBytes(),
+ stack_allocations_->size() * sizeof(uptr));
+ }
+ Print("Creating : ");
+ }
}
-void HwasanThread::ClearShadowForThreadStackAndTLS() {
- TagMemory(stack_bottom_, stack_top_ - stack_bottom_, 0);
+void Thread::ClearShadowForThreadStackAndTLS() {
+ if (stack_top_ != stack_bottom_)
+ TagMemory(stack_bottom_, stack_top_ - stack_bottom_, 0);
if (tls_begin_ != tls_end_)
TagMemory(tls_begin_, tls_end_ - tls_begin_, 0);
}
-void HwasanThread::Destroy() {
- malloc_storage().CommitBack();
+void Thread::Destroy() {
+ if (flags()->verbose_threads)
+ Print("Destroying: ");
+ AllocatorSwallowThreadLocalCache(allocator_cache());
ClearShadowForThreadStackAndTLS();
- uptr size = RoundUpTo(sizeof(HwasanThread), GetPageSizeCached());
- UnmapOrDie(this, size);
+ if (heap_allocations_)
+ heap_allocations_->Delete();
DTLS_Destroy();
}
-thread_return_t HwasanThread::ThreadStart() {
- Init();
-
- if (!start_routine_) {
- // start_routine_ == 0 if we're on the main thread or on one of the
- // OS X libdispatch worker threads. But nobody is supposed to call
- // ThreadStart() for the worker threads.
- return 0;
- }
-
- thread_return_t res = start_routine_(arg_);
-
- return res;
+void Thread::Print(const char *Prefix) {
+ Printf("%sT%zd %p stack: [%p,%p) sz: %zd tls: [%p,%p)\n", Prefix,
+ unique_id_, this, stack_bottom(), stack_top(),
+ stack_top() - stack_bottom(),
+ tls_begin(), tls_end());
}
static u32 xorshift(u32 state) {
@@ -95,7 +108,8 @@ static u32 xorshift(u32 state) {
}
// Generate a (pseudo-)random non-zero tag.
-tag_t HwasanThread::GenerateRandomTag() {
+tag_t Thread::GenerateRandomTag() {
+ if (tagging_disabled_) return 0;
tag_t tag;
do {
if (flags()->random_tags) {
diff --git a/lib/hwasan/hwasan_thread.h b/lib/hwasan/hwasan_thread.h
index 1e482adea..4830473f4 100644
--- a/lib/hwasan/hwasan_thread.h
+++ b/lib/hwasan/hwasan_thread.h
@@ -1,4 +1,4 @@
-//===-- hwasan_thread.h -------------------------------------------*- C++ -*-===//
+//===-- hwasan_thread.h -----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
@@ -16,23 +16,23 @@
#include "hwasan_allocator.h"
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_ring_buffer.h"
namespace __hwasan {
-class HwasanThread {
+typedef __sanitizer::CompactRingBuffer<uptr> StackAllocationsRingBuffer;
+
+class Thread {
public:
- static HwasanThread *Create(thread_callback_t start_routine, void *arg);
- static void TSDDtor(void *tsd);
+ void Init(uptr stack_buffer_start, uptr stack_buffer_size); // Must be called from the thread itself.
void Destroy();
- void Init(); // Should be called from the thread itself.
- thread_return_t ThreadStart();
-
uptr stack_top() { return stack_top_; }
uptr stack_bottom() { return stack_bottom_; }
+ uptr stack_size() { return stack_top() - stack_bottom(); }
uptr tls_begin() { return tls_begin_; }
uptr tls_end() { return tls_end_; }
- bool IsMainThread() { return start_routine_ == nullptr; }
+ bool IsMainThread() { return unique_id_ == 0; }
bool AddrIsInStack(uptr addr) {
return addr >= stack_bottom_ && addr < stack_top_;
@@ -50,19 +50,28 @@ class HwasanThread {
void EnterInterceptorScope() { in_interceptor_scope_++; }
void LeaveInterceptorScope() { in_interceptor_scope_--; }
- HwasanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; }
+ AllocatorCache *allocator_cache() { return &allocator_cache_; }
+ HeapAllocationsRingBuffer *heap_allocations() { return heap_allocations_; }
+ StackAllocationsRingBuffer *stack_allocations() { return stack_allocations_; }
tag_t GenerateRandomTag();
- int destructor_iterations_;
+ void DisableTagging() { tagging_disabled_++; }
+ void EnableTagging() { tagging_disabled_--; }
+ bool TaggingIsDisabled() const { return tagging_disabled_; }
+
+ u64 unique_id() const { return unique_id_; }
+ void Announce() {
+ if (announced_) return;
+ announced_ = true;
+ Print("Thread: ");
+ }
private:
- // NOTE: There is no HwasanThread constructor. It is allocated
+ // NOTE: There is no Thread constructor. It is allocated
// via mmap() and *must* be valid in zero-initialized state.
- void SetThreadStackAndTls();
void ClearShadowForThreadStackAndTLS();
- thread_callback_t start_routine_;
- void *arg_;
+ void Print(const char *prefix);
uptr stack_top_;
uptr stack_bottom_;
uptr tls_begin_;
@@ -75,11 +84,30 @@ class HwasanThread {
u32 random_state_;
u32 random_buffer_;
- HwasanThreadLocalMallocStorage malloc_storage_;
+ AllocatorCache allocator_cache_;
+ HeapAllocationsRingBuffer *heap_allocations_;
+ StackAllocationsRingBuffer *stack_allocations_;
+
+ static void InsertIntoThreadList(Thread *t);
+ static void RemoveFromThreadList(Thread *t);
+ Thread *next_; // All live threads form a linked list.
+
+ u64 unique_id_; // counting from zero.
+
+ u32 tagging_disabled_; // if non-zero, malloc uses zero tag in this thread.
+
+ bool announced_;
+
+ friend struct ThreadListHead;
};
-HwasanThread *GetCurrentThread();
-void SetCurrentThread(HwasanThread *t);
+Thread *GetCurrentThread();
+uptr *GetCurrentThreadLongPtr();
+
+struct ScopedTaggingDisabler {
+ ScopedTaggingDisabler() { GetCurrentThread()->DisableTagging(); }
+ ~ScopedTaggingDisabler() { GetCurrentThread()->EnableTagging(); }
+};
} // namespace __hwasan
diff --git a/lib/hwasan/hwasan_thread_list.cc b/lib/hwasan/hwasan_thread_list.cc
new file mode 100644
index 000000000..a31eee84e
--- /dev/null
+++ b/lib/hwasan/hwasan_thread_list.cc
@@ -0,0 +1,15 @@
+#include "hwasan_thread_list.h"
+
+namespace __hwasan {
+static ALIGNED(16) char thread_list_placeholder[sizeof(HwasanThreadList)];
+static HwasanThreadList *hwasan_thread_list;
+
+HwasanThreadList &hwasanThreadList() { return *hwasan_thread_list; }
+
+void InitThreadList(uptr storage, uptr size) {
+ CHECK(hwasan_thread_list == nullptr);
+ hwasan_thread_list =
+ new (thread_list_placeholder) HwasanThreadList(storage, size);
+}
+
+} // namespace
diff --git a/lib/hwasan/hwasan_thread_list.h b/lib/hwasan/hwasan_thread_list.h
new file mode 100644
index 000000000..53747b51f
--- /dev/null
+++ b/lib/hwasan/hwasan_thread_list.h
@@ -0,0 +1,200 @@
+//===-- hwasan_thread_list.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 HWAddressSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+// HwasanThreadList is a registry for live threads, as well as an allocator for
+// HwasanThread objects and their stack history ring buffers. There are
+// constraints on memory layout of the shadow region and CompactRingBuffer that
+// are part of the ABI contract between compiler-rt and llvm.
+//
+// * Start of the shadow memory region is aligned to 2**kShadowBaseAlignment.
+// * All stack ring buffers are located within (2**kShadowBaseAlignment)
+// sized region below and adjacent to the shadow region.
+// * Each ring buffer has a size of (2**N)*4096 where N is in [0, 8), and is
+// aligned to twice its size. The value of N can be different for each buffer.
+//
+// These constrains guarantee that, given an address A of any element of the
+// ring buffer,
+// A_next = (A + sizeof(uptr)) & ~((1 << (N + 13)) - 1)
+// is the address of the next element of that ring buffer (with wrap-around).
+// And, with K = kShadowBaseAlignment,
+// S = (A | ((1 << K) - 1)) + 1
+// (align up to kShadowBaseAlignment) is the start of the shadow region.
+//
+// These calculations are used in compiler instrumentation to update the ring
+// buffer and obtain the base address of shadow using only two inputs: address
+// of the current element of the ring buffer, and N (i.e. size of the ring
+// buffer). Since the value of N is very limited, we pack both inputs into a
+// single thread-local word as
+// (1 << (N + 56)) | A
+// See the implementation of class CompactRingBuffer, which is what is stored in
+// said thread-local word.
+//
+// Note the unusual way of aligning up the address of the shadow:
+// (A | ((1 << K) - 1)) + 1
+// It is only correct if A is not already equal to the shadow base address, but
+// it saves 2 instructions on AArch64.
+
+#include "hwasan.h"
+#include "hwasan_allocator.h"
+#include "hwasan_flags.h"
+#include "hwasan_thread.h"
+
+#include "sanitizer_common/sanitizer_placement_new.h"
+
+namespace __hwasan {
+
+static uptr RingBufferSize() {
+ uptr desired_bytes = flags()->stack_history_size * sizeof(uptr);
+ // FIXME: increase the limit to 8 once this bug is fixed:
+ // https://bugs.llvm.org/show_bug.cgi?id=39030
+ for (int shift = 1; shift < 7; ++shift) {
+ uptr size = 4096 * (1ULL << shift);
+ if (size >= desired_bytes)
+ return size;
+ }
+ Printf("stack history size too large: %d\n", flags()->stack_history_size);
+ CHECK(0);
+ return 0;
+}
+
+struct ThreadListHead {
+ Thread *list_;
+
+ ThreadListHead() : list_(nullptr) {}
+
+ void Push(Thread *t) {
+ t->next_ = list_;
+ list_ = t;
+ }
+
+ Thread *Pop() {
+ Thread *t = list_;
+ if (t)
+ list_ = t->next_;
+ return t;
+ }
+
+ void Remove(Thread *t) {
+ Thread **cur = &list_;
+ while (*cur != t) cur = &(*cur)->next_;
+ CHECK(*cur && "thread not found");
+ *cur = (*cur)->next_;
+ }
+
+ template <class CB>
+ void ForEach(CB cb) {
+ Thread *t = list_;
+ while (t) {
+ cb(t);
+ t = t->next_;
+ }
+ }
+};
+
+struct ThreadStats {
+ uptr n_live_threads;
+ uptr total_stack_size;
+};
+
+class HwasanThreadList {
+ public:
+ HwasanThreadList(uptr storage, uptr size)
+ : free_space_(storage),
+ free_space_end_(storage + size),
+ ring_buffer_size_(RingBufferSize()) {}
+
+ Thread *CreateCurrentThread() {
+ Thread *t;
+ {
+ SpinMutexLock l(&list_mutex_);
+ t = free_list_.Pop();
+ if (t)
+ internal_memset((void *)t, 0, sizeof(Thread) + ring_buffer_size_);
+ else
+ t = AllocThread();
+ live_list_.Push(t);
+ }
+ t->Init((uptr)(t + 1), ring_buffer_size_);
+ AddThreadStats(t);
+ return t;
+ }
+
+ void ReleaseThread(Thread *t) {
+ // FIXME: madvise away the ring buffer?
+ RemoveThreadStats(t);
+ t->Destroy();
+ SpinMutexLock l(&list_mutex_);
+ live_list_.Remove(t);
+ free_list_.Push(t);
+ }
+
+ Thread *GetThreadByBufferAddress(uptr p) {
+ uptr align = ring_buffer_size_ * 2;
+ return (Thread *)(RoundDownTo(p, align) - sizeof(Thread));
+ }
+
+ uptr MemoryUsedPerThread() {
+ uptr res = sizeof(Thread) + ring_buffer_size_;
+ if (auto sz = flags()->heap_history_size)
+ res += HeapAllocationsRingBuffer::SizeInBytes(sz);
+ return res;
+ }
+
+ template <class CB>
+ void VisitAllLiveThreads(CB cb) {
+ SpinMutexLock l(&list_mutex_);
+ live_list_.ForEach(cb);
+ }
+
+ void AddThreadStats(Thread *t) {
+ SpinMutexLock l(&stats_mutex_);
+ stats_.n_live_threads++;
+ stats_.total_stack_size += t->stack_size();
+ }
+
+ void RemoveThreadStats(Thread *t) {
+ SpinMutexLock l(&stats_mutex_);
+ stats_.n_live_threads--;
+ stats_.total_stack_size -= t->stack_size();
+ }
+
+ ThreadStats GetThreadStats() {
+ SpinMutexLock l(&stats_mutex_);
+ return stats_;
+ }
+
+ private:
+ Thread *AllocThread() {
+ uptr align = ring_buffer_size_ * 2;
+ uptr ring_buffer_start = RoundUpTo(free_space_ + sizeof(Thread), align);
+ free_space_ = ring_buffer_start + ring_buffer_size_;
+ CHECK(free_space_ <= free_space_end_ && "out of thread memory");
+ return (Thread *)(ring_buffer_start - sizeof(Thread));
+ }
+
+ uptr free_space_;
+ uptr free_space_end_;
+ uptr ring_buffer_size_;
+
+ ThreadListHead free_list_;
+ ThreadListHead live_list_;
+ SpinMutex list_mutex_;
+
+ ThreadStats stats_;
+ SpinMutex stats_mutex_;
+};
+
+void InitThreadList(uptr storage, uptr size);
+HwasanThreadList &hwasanThreadList();
+
+} // namespace
diff --git a/lib/interception/interception.h b/lib/interception/interception.h
index ddd6ec209..07142f17b 100644
--- a/lib/interception/interception.h
+++ b/lib/interception/interception.h
@@ -29,6 +29,7 @@ typedef __sanitizer::uptr SIZE_T;
typedef __sanitizer::sptr SSIZE_T;
typedef __sanitizer::sptr PTRDIFF_T;
typedef __sanitizer::s64 INTMAX_T;
+typedef __sanitizer::u64 UINTMAX_T;
typedef __sanitizer::OFF_T OFF_T;
typedef __sanitizer::OFF64_T OFF64_T;
diff --git a/lib/interception/interception_win.cc b/lib/interception/interception_win.cc
index bd4ad7274..cd13827e5 100644
--- a/lib/interception/interception_win.cc
+++ b/lib/interception/interception_win.cc
@@ -223,8 +223,8 @@ static bool IsMemoryPadding(uptr address, uptr size) {
return true;
}
-static const u8 kHintNop9Bytes[] = {
- 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00
+static const u8 kHintNop8Bytes[] = {
+ 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00
};
template<class T>
@@ -239,8 +239,8 @@ static bool FunctionHasPrefix(uptr address, const T &pattern) {
static bool FunctionHasPadding(uptr address, uptr size) {
if (IsMemoryPadding(address - size, size))
return true;
- if (size <= sizeof(kHintNop9Bytes) &&
- FunctionHasPrefix(address, kHintNop9Bytes))
+ if (size <= sizeof(kHintNop8Bytes) &&
+ FunctionHasPrefix(address, kHintNop8Bytes))
return true;
return false;
}
diff --git a/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc
index 012a673c3..eaa5cadc8 100644
--- a/lib/lsan/lsan_common.cc
+++ b/lib/lsan/lsan_common.cc
@@ -100,8 +100,6 @@ static SuppressionContext *GetSuppressionContext() {
static InternalMmapVector<RootRegion> *root_regions;
-static uptr initialized_for_pid;
-
InternalMmapVector<RootRegion> const *GetRootRegions() { return root_regions; }
void InitializeRootRegions() {
@@ -115,7 +113,6 @@ const char *MaybeCallLsanDefaultOptions() {
}
void InitCommonLsan() {
- initialized_for_pid = internal_getpid();
InitializeRootRegions();
if (common_flags()->detect_leaks) {
// Initialization which can fail or print warnings should only be done if
@@ -571,12 +568,6 @@ static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads,
static bool CheckForLeaks() {
if (&__lsan_is_turned_off && __lsan_is_turned_off())
return false;
- if (initialized_for_pid != internal_getpid()) {
- // If process was forked and it had threads we fail to detect references
- // from other threads.
- Report("WARNING: LeakSanitizer is disabled in forked process.\n");
- return false;
- }
EnsureMainThreadIDIsCorrect();
CheckForLeaksParam param;
param.success = false;
diff --git a/lib/lsan/lsan_common_mac.cc b/lib/lsan/lsan_common_mac.cc
index 2508c1dbd..8dd247ca5 100644
--- a/lib/lsan/lsan_common_mac.cc
+++ b/lib/lsan/lsan_common_mac.cc
@@ -142,12 +142,6 @@ void ProcessGlobalRegions(Frontier *frontier) {
}
void ProcessPlatformSpecificAllocations(Frontier *frontier) {
- mach_port_name_t port;
- if (task_for_pid(mach_task_self(), internal_getpid(), &port)
- != KERN_SUCCESS) {
- return;
- }
-
unsigned depth = 1;
vm_size_t size = 0;
vm_address_t address = 0;
@@ -158,7 +152,7 @@ void ProcessPlatformSpecificAllocations(Frontier *frontier) {
while (err == KERN_SUCCESS) {
struct vm_region_submap_info_64 info;
- err = vm_region_recurse_64(port, &address, &size, &depth,
+ err = vm_region_recurse_64(mach_task_self(), &address, &size, &depth,
(vm_region_info_t)&info, &count);
uptr end_address = address + size;
diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc
index 06bcbdf88..ba2d5d593 100644
--- a/lib/msan/msan.cc
+++ b/lib/msan/msan.cc
@@ -59,6 +59,10 @@ SANITIZER_INTERFACE_ATTRIBUTE
ALIGNED(16) THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSize / sizeof(u64)];
SANITIZER_INTERFACE_ATTRIBUTE
+ALIGNED(16)
+THREADLOCAL u32 __msan_va_arg_origin_tls[kMsanParamTlsSize / sizeof(u32)];
+
+SANITIZER_INTERFACE_ATTRIBUTE
THREADLOCAL u64 __msan_va_arg_overflow_size_tls;
SANITIZER_INTERFACE_ATTRIBUTE
@@ -277,6 +281,8 @@ void ScopedThreadLocalStateBackup::Restore() {
internal_memset(__msan_param_tls, 0, sizeof(__msan_param_tls));
internal_memset(__msan_retval_tls, 0, sizeof(__msan_retval_tls));
internal_memset(__msan_va_arg_tls, 0, sizeof(__msan_va_arg_tls));
+ internal_memset(__msan_va_arg_origin_tls, 0,
+ sizeof(__msan_va_arg_origin_tls));
if (__msan_get_track_origins()) {
internal_memset(&__msan_retval_origin_tls, 0,
diff --git a/lib/msan/msan_interceptors.cc b/lib/msan/msan_interceptors.cc
index b3429bcf0..f338d422e 100644
--- a/lib/msan/msan_interceptors.cc
+++ b/lib/msan/msan_interceptors.cc
@@ -249,11 +249,11 @@ INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
// temporary! The following is equivalent on all supported platforms but
// aarch64 (which uses a different register for sret value). We have a test
// to confirm that.
-INTERCEPTOR(void, mallinfo, __sanitizer_mallinfo *sret) {
+INTERCEPTOR(void, mallinfo, __sanitizer_struct_mallinfo *sret) {
#ifdef __aarch64__
uptr r8;
asm volatile("mov %0,x8" : "=r" (r8));
- sret = reinterpret_cast<__sanitizer_mallinfo*>(r8);
+ sret = reinterpret_cast<__sanitizer_struct_mallinfo*>(r8);
#endif
REAL(memset)(sret, 0, sizeof(*sret));
__msan_unpoison(sret, sizeof(*sret));
diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc
index 29260f16e..19f46abdc 100644
--- a/lib/msan/tests/msan_test.cc
+++ b/lib/msan/tests/msan_test.cc
@@ -1934,12 +1934,14 @@ TEST(MemorySanitizer, remquof) {
EXPECT_NOT_POISONED(quo);
}
+#if !defined(__NetBSD__)
TEST(MemorySanitizer, remquol) {
int quo;
long double res = remquof(29.0, 3.0, &quo);
ASSERT_NE(0.0, res);
EXPECT_NOT_POISONED(quo);
}
+#endif
TEST(MemorySanitizer, lgamma) {
double res = lgamma(1.1);
@@ -1953,11 +1955,13 @@ TEST(MemorySanitizer, lgammaf) {
EXPECT_NOT_POISONED(signgam);
}
+#if !defined(__NetBSD__)
TEST(MemorySanitizer, lgammal) {
long double res = lgammal(1.1);
ASSERT_NE(0.0, res);
EXPECT_NOT_POISONED(signgam);
}
+#endif
TEST(MemorySanitizer, lgamma_r) {
int sgn;
diff --git a/lib/profile/InstrProfilingValue.c b/lib/profile/InstrProfilingValue.c
index 674a48609..c7b01a570 100644
--- a/lib/profile/InstrProfilingValue.c
+++ b/lib/profile/InstrProfilingValue.c
@@ -105,8 +105,7 @@ static int allocateValueProfileCounters(__llvm_profile_data *Data) {
return 1;
}
-static ValueProfNode *allocateOneNode(__llvm_profile_data *Data, uint32_t Index,
- uint64_t Value) {
+static ValueProfNode *allocateOneNode(void) {
ValueProfNode *Node;
if (!hasStaticCounters)
@@ -205,7 +204,7 @@ instrumentTargetValueImpl(uint64_t TargetValue, void *Data,
return;
}
- CurVNode = allocateOneNode(PData, CounterIndex, TargetValue);
+ CurVNode = allocateOneNode();
if (!CurVNode)
return;
CurVNode->Value = TargetValue;
diff --git a/lib/profile/WindowsMMap.h b/lib/profile/WindowsMMap.h
index 271619aea..ac2c911c8 100644
--- a/lib/profile/WindowsMMap.h
+++ b/lib/profile/WindowsMMap.h
@@ -12,7 +12,7 @@
#if defined(_WIN32)
-#include <BaseTsd.h>
+#include <basetsd.h>
#include <io.h>
#include <sys/types.h>
diff --git a/lib/safestack/CMakeLists.txt b/lib/safestack/CMakeLists.txt
index 5a1bac291..cc874a3fe 100644
--- a/lib/safestack/CMakeLists.txt
+++ b/lib/safestack/CMakeLists.txt
@@ -6,29 +6,14 @@ include_directories(..)
set(SAFESTACK_CFLAGS ${SANITIZER_COMMON_CFLAGS})
-if(APPLE)
- # Build universal binary on APPLE.
+foreach(arch ${SAFESTACK_SUPPORTED_ARCH})
add_compiler_rt_runtime(clang_rt.safestack
STATIC
- OS osx
- ARCHS ${SAFESTACK_SUPPORTED_ARCH}
+ ARCHS ${arch}
SOURCES ${SAFESTACK_SOURCES}
- $<TARGET_OBJECTS:RTInterception.osx>
- $<TARGET_OBJECTS:RTSanitizerCommon.osx>
- $<TARGET_OBJECTS:RTSanitizerCommonNoLibc.osx>
+ $<TARGET_OBJECTS:RTInterception.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonNoLibc.${arch}>
CFLAGS ${SAFESTACK_CFLAGS}
PARENT_TARGET safestack)
-else()
- # Otherwise, build separate libraries for each target.
- foreach(arch ${SAFESTACK_SUPPORTED_ARCH})
- add_compiler_rt_runtime(clang_rt.safestack
- STATIC
- ARCHS ${arch}
- SOURCES ${SAFESTACK_SOURCES}
- $<TARGET_OBJECTS:RTInterception.${arch}>
- $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
- $<TARGET_OBJECTS:RTSanitizerCommonNoLibc.${arch}>
- CFLAGS ${SAFESTACK_CFLAGS}
- PARENT_TARGET safestack)
- endforeach()
-endif()
+endforeach()
diff --git a/lib/safestack/safestack.cc b/lib/safestack/safestack.cc
index 8af93624b..e68208015 100644
--- a/lib/safestack/safestack.cc
+++ b/lib/safestack/safestack.cc
@@ -14,11 +14,13 @@
//
//===----------------------------------------------------------------------===//
+#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <stddef.h>
#include <stdint.h>
#include <unistd.h>
+#include <stdlib.h>
#include <sys/resource.h>
#include <sys/types.h>
#if !defined(__NetBSD__)
@@ -115,14 +117,6 @@ static inline void unsafe_stack_setup(void *start, size_t size, size_t guard) {
unsafe_stack_guard = guard;
}
-static void unsafe_stack_free() {
- if (unsafe_stack_start) {
- UnmapOrDie((char *)unsafe_stack_start - unsafe_stack_guard,
- unsafe_stack_size + unsafe_stack_guard);
- }
- unsafe_stack_start = nullptr;
-}
-
/// Thread data for the cleanup handler
static pthread_key_t thread_cleanup_key;
@@ -149,26 +143,73 @@ static void *thread_start(void *arg) {
tinfo->unsafe_stack_guard);
// Make sure out thread-specific destructor will be called
- // FIXME: we can do this only any other specific key is set by
- // intercepting the pthread_setspecific function itself
pthread_setspecific(thread_cleanup_key, (void *)1);
return start_routine(start_routine_arg);
}
-/// Thread-specific data destructor
+/// Linked list used to store exiting threads stack/thread information.
+struct thread_stack_ll {
+ struct thread_stack_ll *next;
+ void *stack_base;
+ size_t size;
+ pid_t pid;
+ tid_t tid;
+};
+
+/// Linked list of unsafe stacks for threads that are exiting. We delay
+/// unmapping them until the thread exits.
+static thread_stack_ll *thread_stacks = nullptr;
+static pthread_mutex_t thread_stacks_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/// Thread-specific data destructor. We want to free the unsafe stack only after
+/// this thread is terminated. libc can call functions in safestack-instrumented
+/// code (like free) after thread-specific data destructors have run.
static void thread_cleanup_handler(void *_iter) {
- // We want to free the unsafe stack only after all other destructors
- // have already run. We force this function to be called multiple times.
- // User destructors that might run more then PTHREAD_DESTRUCTOR_ITERATIONS-1
- // times might still end up executing after the unsafe stack is deallocated.
- size_t iter = (size_t)_iter;
- if (iter < PTHREAD_DESTRUCTOR_ITERATIONS) {
- pthread_setspecific(thread_cleanup_key, (void *)(iter + 1));
- } else {
- // This is the last iteration
- unsafe_stack_free();
+ CHECK_NE(unsafe_stack_start, nullptr);
+ pthread_setspecific(thread_cleanup_key, NULL);
+
+ pthread_mutex_lock(&thread_stacks_mutex);
+ // Temporary list to hold the previous threads stacks so we don't hold the
+ // thread_stacks_mutex for long.
+ thread_stack_ll *temp_stacks = thread_stacks;
+ thread_stacks = nullptr;
+ pthread_mutex_unlock(&thread_stacks_mutex);
+
+ pid_t pid = getpid();
+ tid_t tid = GetTid();
+
+ // Free stacks for dead threads
+ thread_stack_ll **stackp = &temp_stacks;
+ while (*stackp) {
+ thread_stack_ll *stack = *stackp;
+ int error;
+ if (stack->pid != pid ||
+ (internal_iserror(TgKill(stack->pid, stack->tid, 0), &error) &&
+ error == ESRCH)) {
+ UnmapOrDie(stack->stack_base, stack->size);
+ *stackp = stack->next;
+ free(stack);
+ } else
+ stackp = &stack->next;
}
+
+ thread_stack_ll *cur_stack =
+ (thread_stack_ll *)malloc(sizeof(thread_stack_ll));
+ cur_stack->stack_base = (char *)unsafe_stack_start - unsafe_stack_guard;
+ cur_stack->size = unsafe_stack_size + unsafe_stack_guard;
+ cur_stack->pid = pid;
+ cur_stack->tid = tid;
+
+ pthread_mutex_lock(&thread_stacks_mutex);
+ // Merge thread_stacks with the current thread's stack and any remaining
+ // temp_stacks
+ *stackp = thread_stacks;
+ cur_stack->next = temp_stacks;
+ thread_stacks = cur_stack;
+ pthread_mutex_unlock(&thread_stacks_mutex);
+
+ unsafe_stack_start = nullptr;
}
static void EnsureInterceptorsInitialized();
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index 1be99616e..ad3aba005 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -16,6 +16,7 @@ set(SANITIZER_SOURCES_NOTERMINATION
sanitizer_linux.cc
sanitizer_linux_s390.cc
sanitizer_mac.cc
+ sanitizer_netbsd.cc
sanitizer_openbsd.cc
sanitizer_persistent_allocator.cc
sanitizer_platform_limits_linux.cc
@@ -158,6 +159,7 @@ set(SANITIZER_IMPL_HEADERS
sanitizer_procmaps.h
sanitizer_quarantine.h
sanitizer_report_decorator.h
+ sanitizer_ring_buffer.h
sanitizer_rtems.h
sanitizer_signal_interceptors.inc
sanitizer_stackdepot.h
diff --git a/lib/sanitizer_common/sanitizer_allocator_size_class_map.h b/lib/sanitizer_common/sanitizer_allocator_size_class_map.h
index 77ab4fb54..07958424c 100644
--- a/lib/sanitizer_common/sanitizer_allocator_size_class_map.h
+++ b/lib/sanitizer_common/sanitizer_allocator_size_class_map.h
@@ -232,3 +232,8 @@ class SizeClassMap {
typedef SizeClassMap<3, 4, 8, 17, 128, 16> DefaultSizeClassMap;
typedef SizeClassMap<3, 4, 8, 17, 64, 14> CompactSizeClassMap;
typedef SizeClassMap<2, 5, 9, 16, 64, 14> VeryCompactSizeClassMap;
+
+// The following SizeClassMap only holds a way small number of cached entries,
+// allowing for denser per-class arrays, smaller memory footprint and usually
+// better performances in threaded environments.
+typedef SizeClassMap<3, 4, 8, 17, 8, 10> DenseSizeClassMap;
diff --git a/lib/sanitizer_common/sanitizer_atomic_clang_x86.h b/lib/sanitizer_common/sanitizer_atomic_clang_x86.h
index 38feb2928..195533ea2 100644
--- a/lib/sanitizer_common/sanitizer_atomic_clang_x86.h
+++ b/lib/sanitizer_common/sanitizer_atomic_clang_x86.h
@@ -61,8 +61,7 @@ INLINE typename T::Type atomic_load(
"emms;" // Empty mmx state/Reset FP regs
: "=m" (v)
: "m" (a->val_dont_use)
- : // mark the FP stack and mmx registers as clobbered
- "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)",
+ : // mark the mmx registers as clobbered
#ifdef __MMX__
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
#endif // #ifdef __MMX__
@@ -100,8 +99,7 @@ INLINE void atomic_store(volatile T *a, typename T::Type v, memory_order mo) {
"emms;" // Empty mmx state/Reset FP regs
: "=m" (a->val_dont_use)
: "m" (v)
- : // mark the FP stack and mmx registers as clobbered
- "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)",
+ : // mark the mmx registers as clobbered
#ifdef __MMX__
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
#endif // #ifdef __MMX__
diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc
index 7d72b0cfe..686896190 100644
--- a/lib/sanitizer_common/sanitizer_common.cc
+++ b/lib/sanitizer_common/sanitizer_common.cc
@@ -339,11 +339,6 @@ int __sanitizer_acquire_crash_state() {
}
SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_set_death_callback(void (*callback)(void)) {
- SetUserDieCallback(callback);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
int __sanitizer_install_malloc_and_free_hooks(void (*malloc_hook)(const void *,
uptr),
void (*free_hook)(const void *)) {
diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h
index 3b999edfb..cf060587e 100644
--- a/lib/sanitizer_common/sanitizer_common.h
+++ b/lib/sanitizer_common/sanitizer_common.h
@@ -73,6 +73,7 @@ uptr GetMaxVirtualAddress();
uptr GetMaxUserVirtualAddress();
// Threads
tid_t GetTid();
+int TgKill(pid_t pid, tid_t tid, int sig);
uptr GetThreadSelf();
void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
uptr *stack_bottom);
diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc
index 801d6a0bd..530469997 100644
--- a/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -3345,14 +3345,14 @@ INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
return res;
}
-INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) {
+INTERCEPTOR(UINTMAX_T, strtoumax, const char *nptr, char **endptr, int base) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, strtoumax, nptr, endptr, base);
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
char *real_endptr;
- INTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base);
+ UINTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base);
StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return res;
}
diff --git a/lib/sanitizer_common/sanitizer_coverage_fuchsia.cc b/lib/sanitizer_common/sanitizer_coverage_fuchsia.cc
index b4ffcca5c..f27b95fa9 100644
--- a/lib/sanitizer_common/sanitizer_coverage_fuchsia.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_fuchsia.cc
@@ -146,9 +146,9 @@ class TracePcGuardController final {
// indices, but we'll never move the mapping address so we don't have
// any multi-thread synchronization issues with that.
uintptr_t mapping;
- status = _zx_vmar_map_old(_zx_vmar_root_self(), 0, vmo_, 0, MappingSize,
- ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE,
- &mapping);
+ status =
+ _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
+ 0, vmo_, 0, MappingSize, &mapping);
CHECK_EQ(status, ZX_OK);
// Hereafter other threads are free to start storing into
diff --git a/lib/sanitizer_common/sanitizer_coverage_win_sections.cc b/lib/sanitizer_common/sanitizer_coverage_win_sections.cc
index 4b0bbf1ed..108f76eff 100644
--- a/lib/sanitizer_common/sanitizer_coverage_win_sections.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_win_sections.cc
@@ -7,16 +7,57 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines delimiters for Sanitizer Coverage's section.
+// This file defines delimiters for Sanitizer Coverage's section. It contains
+// Windows specific tricks to coax the linker into giving us the start and stop
+// addresses of a section, as ELF linkers can do, to get the size of certain
+// arrays. According to https://msdn.microsoft.com/en-us/library/7977wcck.aspx
+// sections with the same name before "$" are sorted alphabetically by the
+// string that comes after "$" and merged into one section. We take advantage
+// of this by putting data we want the size of into the middle (M) of a section,
+// by using the letter "M" after "$". We get the start of this data (ie:
+// __start_section_name) by making the start variable come at the start of the
+// section (using the letter A after "$"). We do the same to get the end of the
+// data by using the letter "Z" after "$" to make the end variable come after
+// the data. Note that because of our technique the address of the start
+// variable is actually the address of data that comes before our middle
+// section. We also need to prevent the linker from adding any padding. Each
+// technique we use for this is explained in the comments below.
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
#if SANITIZER_WINDOWS
#include <stdint.h>
-#pragma section(".SCOV$A", read, write) // NOLINT
-#pragma section(".SCOV$Z", read, write) // NOLINT
extern "C" {
-__declspec(allocate(".SCOV$A")) uint32_t __start___sancov_guards = 0;
-__declspec(allocate(".SCOV$Z")) uint32_t __stop___sancov_guards = 0;
+// The Guard array and counter array should both be merged into the .data
+// section to reduce the number of PE sections However, because PCTable is
+// constant it should be merged with the .rdata section.
+#pragma section(".SCOV$GA", read, write) // NOLINT
+// Use align(1) to avoid adding any padding that will mess up clients trying to
+// determine the start and end of the array.
+__declspec(allocate(".SCOV$GA")) __declspec(align(1)) uint64_t
+ __start___sancov_guards = 0;
+#pragma section(".SCOV$GZ", read, write) // NOLINT
+__declspec(allocate(".SCOV$GZ")) __declspec(align(1)) uint64_t
+ __stop___sancov_guards = 0;
+
+#pragma section(".SCOV$CA", read, write) // NOLINT
+__declspec(allocate(".SCOV$CA")) __declspec(align(1)) uint64_t
+ __start___sancov_cntrs = 0;
+#pragma section(".SCOV$CZ", read, write) // NOLINT
+__declspec(allocate(".SCOV$CZ")) __declspec(align(1)) uint64_t
+ __stop___sancov_cntrs = 0;
+
+#pragma comment(linker, "/MERGE:.SCOV=.data")
+
+// Use uint64_t so there won't be any issues if the linker tries to word align
+// the pc array.
+#pragma section(".SCOVP$A", read) // NOLINT
+__declspec(allocate(".SCOVP$A")) __declspec(align(1)) uint64_t
+ __start___sancov_pcs = 0;
+#pragma section(".SCOVP$Z", read) // NOLINT
+__declspec(allocate(".SCOVP$Z")) __declspec(align(1)) uint64_t
+ __stop___sancov_pcs = 0;
+
+#pragma comment(linker, "/MERGE:.SCOVP=.rdata")
}
-#endif // SANITIZER_WINDOWS
+#endif // SANITIZER_WINDOWS
diff --git a/lib/sanitizer_common/sanitizer_fuchsia.cc b/lib/sanitizer_common/sanitizer_fuchsia.cc
index 391620690..9c54e1ed3 100644
--- a/lib/sanitizer_common/sanitizer_fuchsia.cc
+++ b/lib/sanitizer_common/sanitizer_fuchsia.cc
@@ -29,9 +29,6 @@
namespace __sanitizer {
-// TODO(phosek): remove this and replace it with ZX_TIME_INFINITE
-#define ZX_TIME_INFINITE_OLD INT64_MAX
-
void NORETURN internal__exit(int exitcode) { _zx_process_exit(exitcode); }
uptr internal_sched_yield() {
@@ -123,7 +120,7 @@ void BlockingMutex::Lock() {
return;
while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) {
zx_status_t status = _zx_futex_wait(reinterpret_cast<zx_futex_t *>(m),
- MtxSleeping, ZX_TIME_INFINITE_OLD);
+ MtxSleeping, ZX_TIME_INFINITE);
if (status != ZX_ERR_BAD_STATE) // Normal race.
CHECK_EQ(status, ZX_OK);
}
@@ -175,8 +172,8 @@ static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type,
// TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
uintptr_t addr;
status =
- _zx_vmar_map_old(_zx_vmar_root_self(), 0, vmo, 0, size,
- ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE, &addr);
+ _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
+ vmo, 0, size, &addr);
_zx_handle_close(vmo);
if (status != ZX_OK) {
@@ -239,10 +236,9 @@ static uptr DoMmapFixedOrDie(zx_handle_t vmar, uptr fixed_addr, uptr map_size,
DCHECK_GE(base + size_, map_size + offset);
uintptr_t addr;
- status = _zx_vmar_map_old(
- vmar, offset, vmo, 0, map_size,
- ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE | ZX_VM_FLAG_SPECIFIC,
- &addr);
+ status =
+ _zx_vmar_map(vmar, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC,
+ offset, vmo, 0, map_size, &addr);
_zx_handle_close(vmo);
if (status != ZX_OK) {
if (status != ZX_ERR_NO_MEMORY || die_for_nomem) {
@@ -281,14 +277,22 @@ void UnmapOrDieVmar(void *addr, uptr size, zx_handle_t target_vmar) {
void ReservedAddressRange::Unmap(uptr addr, uptr size) {
CHECK_LE(size, size_);
- if (addr == reinterpret_cast<uptr>(base_))
- // If we unmap the whole range, just null out the base.
- base_ = (size == size_) ? nullptr : reinterpret_cast<void*>(addr + size);
- else
+ const zx_handle_t vmar = static_cast<zx_handle_t>(os_handle_);
+ if (addr == reinterpret_cast<uptr>(base_)) {
+ if (size == size_) {
+ // Destroying the vmar effectively unmaps the whole mapping.
+ _zx_vmar_destroy(vmar);
+ _zx_handle_close(vmar);
+ os_handle_ = static_cast<uptr>(ZX_HANDLE_INVALID);
+ DecreaseTotalMmap(size);
+ return;
+ }
+ } else {
CHECK_EQ(addr + size, reinterpret_cast<uptr>(base_) + size_);
- size_ -= size;
- UnmapOrDieVmar(reinterpret_cast<void *>(addr), size,
- static_cast<zx_handle_t>(os_handle_));
+ }
+ // Partial unmapping does not affect the fact that the initial range is still
+ // reserved, and the resulting unmapped memory can't be reused.
+ UnmapOrDieVmar(reinterpret_cast<void *>(addr), size, vmar);
}
// This should never be called.
@@ -321,8 +325,8 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
size_t map_size = size + alignment;
uintptr_t addr;
status =
- _zx_vmar_map_old(_zx_vmar_root_self(), 0, vmo, 0, map_size,
- ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE, &addr);
+ _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
+ vmo, 0, map_size, &addr);
if (status == ZX_OK) {
uintptr_t map_addr = addr;
uintptr_t map_end = map_addr + map_size;
@@ -334,11 +338,10 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
sizeof(info), NULL, NULL);
if (status == ZX_OK) {
uintptr_t new_addr;
- status = _zx_vmar_map_old(_zx_vmar_root_self(), addr - info.base, vmo,
- 0, size,
- ZX_VM_FLAG_PERM_READ | ZX_VM_FLAG_PERM_WRITE |
- ZX_VM_FLAG_SPECIFIC_OVERWRITE,
- &new_addr);
+ status = _zx_vmar_map(
+ _zx_vmar_root_self(),
+ ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC_OVERWRITE,
+ addr - info.base, vmo, 0, size, &new_addr);
if (status == ZX_OK) CHECK_EQ(new_addr, addr);
}
}
@@ -398,8 +401,8 @@ bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
if (vmo_size < max_len) max_len = vmo_size;
size_t map_size = RoundUpTo(max_len, PAGE_SIZE);
uintptr_t addr;
- status = _zx_vmar_map_old(_zx_vmar_root_self(), 0, vmo, 0, map_size,
- ZX_VM_FLAG_PERM_READ, &addr);
+ status = _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ, 0, vmo, 0,
+ map_size, &addr);
if (status == ZX_OK) {
*buff = reinterpret_cast<char *>(addr);
*buff_size = map_size;
diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h
index f8a405ba6..34022430d 100644
--- a/lib/sanitizer_common/sanitizer_internal_defs.h
+++ b/lib/sanitizer_common/sanitizer_internal_defs.h
@@ -105,8 +105,8 @@
//
// FIXME: do we have anything like this on Mac?
#ifndef SANITIZER_CAN_USE_PREINIT_ARRAY
-#if ((SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_OPENBSD) && \
- !defined(PIC)
+#if ((SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_OPENBSD || \
+ SANITIZER_FUCHSIA) && !defined(PIC)
#define SANITIZER_CAN_USE_PREINIT_ARRAY 1
// Before Solaris 11.4, .preinit_array is fully supported only with GNU ld.
// FIXME: Check for those conditions.
@@ -275,8 +275,6 @@ typedef thread_return_t (THREAD_CALLING_CONV *thread_callback_t)(void* arg);
// NOTE: Functions below must be defined in each run-time.
void NORETURN Die();
-// FIXME: No, this shouldn't be in the sanitizer interface.
-SANITIZER_INTERFACE_ATTRIBUTE
void NORETURN CheckFailed(const char *file, int line, const char *cond,
u64 v1, u64 v2);
@@ -431,6 +429,7 @@ namespace __scudo { using namespace __sanitizer; } // NOLINT
namespace __ubsan { using namespace __sanitizer; } // NOLINT
namespace __xray { using namespace __sanitizer; } // NOLINT
namespace __interception { using namespace __sanitizer; } // NOLINT
+namespace __hwasan { using namespace __sanitizer; } // NOLINT
#endif // SANITIZER_DEFS_H
diff --git a/lib/sanitizer_common/sanitizer_libc.cc b/lib/sanitizer_common/sanitizer_libc.cc
index 4b462bfe9..4032cb104 100644
--- a/lib/sanitizer_common/sanitizer_libc.cc
+++ b/lib/sanitizer_common/sanitizer_libc.cc
@@ -73,6 +73,18 @@ void *internal_memmove(void *dest, const void *src, uptr n) {
}
void *internal_memset(void* s, int c, uptr n) {
+ // Optimize for the most performance-critical case:
+ if ((reinterpret_cast<uptr>(s) % 16) == 0 && (n % 16) == 0) {
+ u64 *p = reinterpret_cast<u64*>(s);
+ u64 *e = p + n / 8;
+ u64 v = c;
+ v |= v << 8;
+ v |= v << 16;
+ v |= v << 32;
+ for (; p < e; p += 2)
+ p[0] = p[1] = v;
+ return s;
+ }
// The next line prevents Clang from making a call to memset() instead of the
// loop below.
// FIXME: building the runtime with -ffreestanding is a better idea. However
diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc
index 8d39c4d90..cd1e46ba9 100644
--- a/lib/sanitizer_common/sanitizer_linux.cc
+++ b/lib/sanitizer_common/sanitizer_linux.cc
@@ -31,10 +31,6 @@
#include <asm/param.h>
#endif
-#if SANITIZER_NETBSD
-#include <lwp.h>
-#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
@@ -69,6 +65,7 @@
#endif
#if SANITIZER_OPENBSD
#include <sys/futex.h>
+#include <sys/sysctl.h>
#endif
#include <unistd.h>
@@ -117,6 +114,9 @@ struct kernel_timeval {
// <linux/futex.h> is broken on some linux distributions.
const int FUTEX_WAIT = 0;
const int FUTEX_WAKE = 1;
+const int FUTEX_PRIVATE_FLAG = 128;
+const int FUTEX_WAIT_PRIVATE = FUTEX_WAIT | FUTEX_PRIVATE_FLAG;
+const int FUTEX_WAKE_PRIVATE = FUTEX_WAKE | FUTEX_PRIVATE_FLAG;
#endif // SANITIZER_LINUX
// Are we using 32-bit or 64-bit Linux syscalls?
@@ -170,14 +170,11 @@ namespace __sanitizer {
#endif
// --------------- sanitizer_libc.h
-#if !SANITIZER_SOLARIS
+#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
#if !SANITIZER_S390 && !SANITIZER_OPENBSD
uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
OFF_T offset) {
-#if SANITIZER_NETBSD
- return internal_syscall64(SYSCALL(mmap), addr, length, prot, flags, fd,
- (long)0, offset);
-#elif SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
+#if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd,
offset);
#else
@@ -191,11 +188,11 @@ uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
#if !SANITIZER_OPENBSD
uptr internal_munmap(void *addr, uptr length) {
- return internal_syscall_ptr(SYSCALL(munmap), (uptr)addr, length);
+ return internal_syscall(SYSCALL(munmap), (uptr)addr, length);
}
int internal_mprotect(void *addr, uptr length, int prot) {
- return internal_syscall_ptr(SYSCALL(mprotect), (uptr)addr, length, prot);
+ return internal_syscall(SYSCALL(mprotect), (uptr)addr, length, prot);
}
#endif
@@ -207,7 +204,7 @@ uptr internal_open(const char *filename, int flags) {
#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags);
#else
- return internal_syscall_ptr(SYSCALL(open), (uptr)filename, flags);
+ return internal_syscall(SYSCALL(open), (uptr)filename, flags);
#endif
}
@@ -216,32 +213,28 @@ uptr internal_open(const char *filename, int flags, u32 mode) {
return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags,
mode);
#else
- return internal_syscall_ptr(SYSCALL(open), (uptr)filename, flags, mode);
+ return internal_syscall(SYSCALL(open), (uptr)filename, flags, mode);
#endif
}
uptr internal_read(fd_t fd, void *buf, uptr count) {
sptr res;
- HANDLE_EINTR(res, (sptr)internal_syscall_ptr(SYSCALL(read), fd, (uptr)buf,
- count));
+ HANDLE_EINTR(res,
+ (sptr)internal_syscall(SYSCALL(read), fd, (uptr)buf, count));
return res;
}
uptr internal_write(fd_t fd, const void *buf, uptr count) {
sptr res;
- HANDLE_EINTR(res, (sptr)internal_syscall_ptr(SYSCALL(write), fd, (uptr)buf,
- count));
+ HANDLE_EINTR(res,
+ (sptr)internal_syscall(SYSCALL(write), fd, (uptr)buf, count));
return res;
}
uptr internal_ftruncate(fd_t fd, uptr size) {
sptr res;
-#if SANITIZER_NETBSD
- HANDLE_EINTR(res, internal_syscall64(SYSCALL(ftruncate), fd, 0, (s64)size));
-#else
HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd,
(OFF_T)size));
-#endif
return res;
}
@@ -313,9 +306,8 @@ static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) {
#endif
uptr internal_stat(const char *path, void *buf) {
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
- return internal_syscall_ptr(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf,
- 0);
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
+ return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0);
#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf,
0);
@@ -338,9 +330,7 @@ uptr internal_stat(const char *path, void *buf) {
}
uptr internal_lstat(const char *path, void *buf) {
-#if SANITIZER_NETBSD
- return internal_syscall_ptr(SYSCALL(lstat), path, buf);
-#elif SANITIZER_FREEBSD || SANITIZER_OPENBSD
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf,
AT_SYMLINK_NOFOLLOW);
#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
@@ -365,16 +355,16 @@ uptr internal_lstat(const char *path, void *buf) {
}
uptr internal_fstat(fd_t fd, void *buf) {
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD || \
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD || \
SANITIZER_LINUX_USES_64BIT_SYSCALLS
-#if SANITIZER_MIPS64 && !SANITIZER_NETBSD && !SANITIZER_OPENBSD
+#if SANITIZER_MIPS64 && !SANITIZER_OPENBSD
// For mips64, fstat syscall fills buffer in the format of kernel_stat
struct kernel_stat kbuf;
int res = internal_syscall(SYSCALL(fstat), fd, &kbuf);
kernel_stat_to_stat(&kbuf, (struct stat *)buf);
return res;
# else
- return internal_syscall_ptr(SYSCALL(fstat), fd, (uptr)buf);
+ return internal_syscall(SYSCALL(fstat), fd, (uptr)buf);
# endif
#else
struct stat64 buf64;
@@ -404,10 +394,10 @@ uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
return internal_syscall(SYSCALL(readlinkat), AT_FDCWD, (uptr)path, (uptr)buf,
bufsize);
#elif SANITIZER_OPENBSD
- return internal_syscall_ptr(SYSCALL(readlinkat), AT_FDCWD, (uptr)path,
- (uptr)buf, bufsize);
+ return internal_syscall(SYSCALL(readlinkat), AT_FDCWD, (uptr)path, (uptr)buf,
+ bufsize);
#else
- return internal_syscall_ptr(SYSCALL(readlink), path, buf, bufsize);
+ return internal_syscall(SYSCALL(readlink), path, buf, bufsize);
#endif
}
@@ -415,7 +405,7 @@ uptr internal_unlink(const char *path) {
#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS || SANITIZER_OPENBSD
return internal_syscall(SYSCALL(unlinkat), AT_FDCWD, (uptr)path, 0);
#else
- return internal_syscall_ptr(SYSCALL(unlink), (uptr)path);
+ return internal_syscall(SYSCALL(unlink), (uptr)path);
#endif
}
@@ -424,7 +414,7 @@ uptr internal_rename(const char *oldpath, const char *newpath) {
return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD,
(uptr)newpath);
#else
- return internal_syscall_ptr(SYSCALL(rename), (uptr)oldpath, (uptr)newpath);
+ return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath);
#endif
}
@@ -433,7 +423,7 @@ uptr internal_sched_yield() {
}
void internal__exit(int exitcode) {
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
internal_syscall(SYSCALL(exit), exitcode);
#else
internal_syscall(SYSCALL(exit_group), exitcode);
@@ -445,17 +435,17 @@ unsigned int internal_sleep(unsigned int seconds) {
struct timespec ts;
ts.tv_sec = 1;
ts.tv_nsec = 0;
- int res = internal_syscall_ptr(SYSCALL(nanosleep), &ts, &ts);
+ int res = internal_syscall(SYSCALL(nanosleep), &ts, &ts);
if (res) return ts.tv_sec;
return 0;
}
uptr internal_execve(const char *filename, char *const argv[],
char *const envp[]) {
- return internal_syscall_ptr(SYSCALL(execve), (uptr)filename, (uptr)argv,
- (uptr)envp);
+ return internal_syscall(SYSCALL(execve), (uptr)filename, (uptr)argv,
+ (uptr)envp);
}
-#endif // !SANITIZER_SOLARIS
+#endif // !SANITIZER_SOLARIS && !SANITIZER_NETBSD
// ----------------- sanitizer_common.h
bool FileExists(const char *filename) {
@@ -470,6 +460,7 @@ bool FileExists(const char *filename) {
return S_ISREG(st.st_mode);
}
+#if !SANITIZER_NETBSD
tid_t GetTid() {
#if SANITIZER_FREEBSD
long Tid;
@@ -477,8 +468,6 @@ tid_t GetTid() {
return Tid;
#elif SANITIZER_OPENBSD
return internal_syscall(SYSCALL(getthrid));
-#elif SANITIZER_NETBSD
- return _lwp_self();
#elif SANITIZER_SOLARIS
return thr_self();
#else
@@ -486,28 +475,43 @@ tid_t GetTid() {
#endif
}
-#if !SANITIZER_SOLARIS
+int TgKill(pid_t pid, tid_t tid, int sig) {
+#if SANITIZER_LINUX
+ return internal_syscall(SYSCALL(tgkill), pid, tid, sig);
+#elif SANITIZER_FREEBSD
+ return internal_syscall(SYSCALL(thr_kill2), pid, tid, sig);
+#elif SANITIZER_OPENBSD
+ (void)pid;
+ return internal_syscall(SYSCALL(thrkill), tid, sig, nullptr);
+#elif SANITIZER_SOLARIS
+ (void)pid;
+ return thr_kill(tid, sig);
+#endif
+}
+#endif
+
+#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
u64 NanoTime() {
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
timeval tv;
#else
kernel_timeval tv;
#endif
internal_memset(&tv, 0, sizeof(tv));
- internal_syscall_ptr(SYSCALL(gettimeofday), &tv, 0);
+ internal_syscall(SYSCALL(gettimeofday), &tv, 0);
return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000;
}
uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {
- return internal_syscall_ptr(SYSCALL(clock_gettime), clk_id, tp);
+ return internal_syscall(SYSCALL(clock_gettime), clk_id, tp);
}
-#endif // !SANITIZER_SOLARIS
+#endif // !SANITIZER_SOLARIS && !SANITIZER_NETBSD
// Like getenv, but reads env directly from /proc (on Linux) or parses the
// 'environ' array (on some others) and does not use libc. This function
// should be called first inside __asan_init.
const char *GetEnv(const char *name) {
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD || \
+#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD || \
SANITIZER_SOLARIS
if (::environ != 0) {
uptr NameLen = internal_strlen(name);
@@ -585,8 +589,8 @@ static void GetArgsAndEnv(char ***argv, char ***envp) {
// kern.ps_strings sysctl, which returns a pointer to a structure containing
// this information. See also <sys/exec.h>.
ps_strings *pss;
- size_t sz = sizeof(pss);
- if (sysctlbyname("kern.ps_strings", &pss, &sz, NULL, 0) == -1) {
+ uptr sz = sizeof(pss);
+ if (internal_sysctlbyname("kern.ps_strings", &pss, &sz, NULL, 0) == -1) {
Printf("sysctl kern.ps_strings failed\n");
Die();
}
@@ -628,10 +632,10 @@ void ReExec() {
CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME,
};
char path[400];
- size_t len;
+ uptr len;
len = sizeof(path);
- if (sysctl(name, ARRAY_SIZE(name), path, &len, NULL, 0) != -1)
+ if (internal_sysctl(name, ARRAY_SIZE(name), path, &len, NULL, 0) != -1)
pathname = path;
#elif SANITIZER_SOLARIS
pathname = getexecname();
@@ -669,7 +673,8 @@ void BlockingMutex::Lock() {
#elif SANITIZER_NETBSD
sched_yield(); /* No userspace futex-like synchronization */
#else
- internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAIT, MtxSleeping, 0, 0, 0);
+ internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAIT_PRIVATE, MtxSleeping,
+ 0, 0, 0);
#endif
}
}
@@ -684,7 +689,7 @@ void BlockingMutex::Unlock() {
#elif SANITIZER_NETBSD
/* No userspace futex-like synchronization */
#else
- internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAKE, 1, 0, 0, 0);
+ internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAKE_PRIVATE, 1, 0, 0, 0);
#endif
}
}
@@ -699,7 +704,9 @@ void BlockingMutex::CheckLocked() {
// The actual size of this structure is specified by d_reclen.
// Note that getdents64 uses a different structure format. We only provide the
// 32-bit syscall here.
-#if SANITIZER_NETBSD || SANITIZER_OPENBSD
+#if SANITIZER_NETBSD
+// Not used
+#elif SANITIZER_OPENBSD
// struct dirent is different for Linux and us. At this moment, we use only
// d_fileno (Linux call this d_ino), d_reclen, and d_name.
struct linux_dirent {
@@ -726,27 +733,15 @@ struct linux_dirent {
};
#endif
-#if !SANITIZER_SOLARIS
+#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
// Syscall wrappers.
uptr internal_ptrace(int request, int pid, void *addr, void *data) {
-#if SANITIZER_NETBSD
- // XXX We need additional work for ptrace:
- // - for request, we use PT_FOO whereas Linux uses PTRACE_FOO
- // - data is int for us, but void * for Linux
- // - Linux sometimes uses data in the case where we use addr instead
- // At this moment, this function is used only within
- // "#if SANITIZER_LINUX && defined(__x86_64__)" block in
- // sanitizer_stoptheworld_linux_libcdep.cc.
- return internal_syscall_ptr(SYSCALL(ptrace), request, pid, (uptr)addr,
- (uptr)data);
-#else
return internal_syscall(SYSCALL(ptrace), request, pid, (uptr)addr,
(uptr)data);
-#endif
}
uptr internal_waitpid(int pid, int *status, int options) {
- return internal_syscall_ptr(SYSCALL(wait4), pid, (uptr)status, options,
+ return internal_syscall(SYSCALL(wait4), pid, (uptr)status, options,
0 /* rusage */);
}
@@ -764,16 +759,12 @@ uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) {
#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
return internal_syscall(SYSCALL(getdents64), fd, (uptr)dirp, count);
#else
- return internal_syscall_ptr(SYSCALL(getdents), fd, (uptr)dirp, count);
+ return internal_syscall(SYSCALL(getdents), fd, (uptr)dirp, count);
#endif
}
uptr internal_lseek(fd_t fd, OFF_T offset, int whence) {
-#if SANITIZER_NETBSD
- return internal_syscall64(SYSCALL(lseek), fd, 0, offset, whence);
-#else
return internal_syscall(SYSCALL(lseek), fd, offset, whence);
-#endif
}
#if SANITIZER_LINUX
@@ -783,7 +774,7 @@ uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {
#endif
uptr internal_sigaltstack(const void *ss, void *oss) {
- return internal_syscall_ptr(SYSCALL(sigaltstack), (uptr)ss, (uptr)oss);
+ return internal_syscall(SYSCALL(sigaltstack), (uptr)ss, (uptr)oss);
}
int internal_fork() {
@@ -794,6 +785,28 @@ int internal_fork() {
#endif
}
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
+int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
+ uptr *oldlenp, const void *newp, uptr newlen) {
+#if SANITIZER_OPENBSD
+ return sysctl(name, namelen, oldp, (size_t *)oldlenp, (void *)newp,
+ (size_t)newlen);
+#else
+ return sysctl(name, namelen, oldp, (size_t *)oldlenp, newp, (size_t)newlen);
+#endif
+}
+
+int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
+ const void *newp, uptr newlen) {
+#if SANITIZER_OPENBSD
+ return sysctlbyname(sname, oldp, (size_t *)oldlenp, (void *)newp,
+ (size_t)newlen);
+#else
+ return sysctlbyname(sname, oldp, (size_t *)oldlenp, newp, (size_t)newlen);
+#endif
+}
+#endif
+
#if SANITIZER_LINUX
#define SA_RESTORER 0x04000000
// Doesn't set sa_restorer if the caller did not set it, so use with caution
@@ -863,8 +876,8 @@ int internal_sigaction_syscall(int signum, const void *act, void *oldact) {
uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
__sanitizer_sigset_t *oldset) {
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
- return internal_syscall_ptr(SYSCALL(sigprocmask), how, set, oldset);
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
+ return internal_syscall(SYSCALL(sigprocmask), how, set, oldset);
#else
__sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
__sanitizer_kernel_sigset_t *k_oldset = (__sanitizer_kernel_sigset_t *)oldset;
@@ -902,9 +915,20 @@ bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
const uptr bit = signum % (sizeof(k_set->sig[0]) * 8);
return k_set->sig[idx] & (1 << bit);
}
-#endif // SANITIZER_LINUX
+#elif SANITIZER_FREEBSD
+void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {
+ sigset_t *rset = reinterpret_cast<sigset_t *>(set);
+ sigdelset(rset, signum);
+}
+
+bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
+ sigset_t *rset = reinterpret_cast<sigset_t *>(set);
+ return sigismember(rset, signum);
+}
+#endif
#endif // !SANITIZER_SOLARIS
+#if !SANITIZER_NETBSD
// ThreadLister implementation.
ThreadLister::ThreadLister(pid_t pid) : pid_(pid), buffer_(4096) {
char task_directory_path[80];
@@ -991,6 +1015,7 @@ ThreadLister::~ThreadLister() {
if (!internal_iserror(descriptor_))
internal_close(descriptor_);
}
+#endif
#if SANITIZER_WORDSIZE == 32
// Take care of unusable kernel area in top gigabyte.
@@ -1093,8 +1118,9 @@ uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
const int Mib[4] = {CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME};
#endif
const char *default_module_name = "kern.proc.pathname";
- size_t Size = buf_len;
- bool IsErr = (sysctl(Mib, ARRAY_SIZE(Mib), buf, &Size, NULL, 0) != 0);
+ uptr Size = buf_len;
+ bool IsErr =
+ (internal_sysctl(Mib, ARRAY_SIZE(Mib), buf, &Size, NULL, 0) != 0);
int readlink_error = IsErr ? errno : 0;
uptr module_name_len = Size;
#else
@@ -1636,6 +1662,16 @@ static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size,
static atomic_uint32_t android_api_level;
+static AndroidApiLevel AndroidDetectApiLevelStatic() {
+#if __ANDROID_API__ <= 19
+ return ANDROID_KITKAT;
+#elif __ANDROID_API__ <= 22
+ return ANDROID_LOLLIPOP_MR1;
+#else
+ return ANDROID_POST_LOLLIPOP;
+#endif
+}
+
static AndroidApiLevel AndroidDetectApiLevel() {
if (!&dl_iterate_phdr)
return ANDROID_KITKAT; // K or lower
@@ -1648,11 +1684,14 @@ static AndroidApiLevel AndroidDetectApiLevel() {
// interesting to detect.
}
+extern "C" __attribute__((weak)) void* _DYNAMIC;
+
AndroidApiLevel AndroidGetApiLevel() {
AndroidApiLevel level =
(AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed);
if (level) return level;
- level = AndroidDetectApiLevel();
+ level = &_DYNAMIC == nullptr ? AndroidDetectApiLevelStatic()
+ : AndroidDetectApiLevel();
atomic_store(&android_api_level, level, memory_order_relaxed);
return level;
}
@@ -1959,13 +1998,13 @@ void CheckASLR() {
#if SANITIZER_NETBSD
int mib[3];
int paxflags;
- size_t len = sizeof(paxflags);
+ uptr len = sizeof(paxflags);
mib[0] = CTL_PROC;
mib[1] = internal_getpid();
mib[2] = PROC_PID_PAXFLAGS;
- if (UNLIKELY(sysctl(mib, 3, &paxflags, &len, NULL, 0) == -1)) {
+ if (UNLIKELY(internal_sysctl(mib, 3, &paxflags, &len, NULL, 0) == -1)) {
Printf("sysctl failed\n");
Die();
}
diff --git a/lib/sanitizer_common/sanitizer_linux.h b/lib/sanitizer_common/sanitizer_linux.h
index 975d6541d..8322f4e08 100644
--- a/lib/sanitizer_common/sanitizer_linux.h
+++ b/lib/sanitizer_common/sanitizer_linux.h
@@ -69,6 +69,8 @@ void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr);
#endif
+#elif SANITIZER_FREEBSD
+void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
#endif // SANITIZER_LINUX
// This class reads thread IDs from /proc/<pid>/task using only syscalls.
diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
index 4962ff832..7859557c8 100644
--- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
@@ -652,10 +652,10 @@ u32 GetNumberOfCPUs() {
#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
u32 ncpu;
int req[2];
- size_t len = sizeof(ncpu);
+ uptr len = sizeof(ncpu);
req[0] = CTL_HW;
req[1] = HW_NCPU;
- CHECK_EQ(sysctl(req, 2, &ncpu, &len, NULL, 0), 0);
+ CHECK_EQ(internal_sysctl(req, 2, &ncpu, &len, NULL, 0), 0);
return ncpu;
#elif SANITIZER_ANDROID && !defined(CPU_COUNT) && !defined(__aarch64__)
// Fall back to /sys/devices/system/cpu on Android when cpu_set_t doesn't
diff --git a/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc
index 180d7c199..90bd08815 100644
--- a/lib/sanitizer_common/sanitizer_mac.cc
+++ b/lib/sanitizer_common/sanitizer_mac.cc
@@ -213,6 +213,18 @@ int internal_fork() {
return fork();
}
+int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
+ uptr *oldlenp, const void *newp, uptr newlen) {
+ return sysctl(const_cast<int *>(name), namelen, oldp, (size_t *)oldlenp,
+ const_cast<void *>(newp), (size_t)newlen);
+}
+
+int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
+ const void *newp, uptr newlen) {
+ return sysctlbyname(sname, oldp, (size_t *)oldlenp, const_cast<void *>(newp),
+ (size_t)newlen);
+}
+
int internal_forkpty(int *amaster) {
int master, slave;
if (openpty(&master, &slave, nullptr, nullptr, nullptr) == -1) return -1;
@@ -499,9 +511,9 @@ MacosVersion GetMacosVersionInternal() {
uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]);
for (uptr i = 0; i < maxlen; i++) version[i] = '\0';
// Get the version length.
- CHECK_NE(sysctl(mib, 2, 0, &len, 0, 0), -1);
+ CHECK_NE(internal_sysctl(mib, 2, 0, &len, 0, 0), -1);
CHECK_LT(len, maxlen);
- CHECK_NE(sysctl(mib, 2, version, &len, 0, 0), -1);
+ CHECK_NE(internal_sysctl(mib, 2, version, &len, 0, 0), -1);
switch (version[0]) {
case '9': return MACOS_VERSION_LEOPARD;
case '1': {
@@ -511,6 +523,10 @@ MacosVersion GetMacosVersionInternal() {
case '2': return MACOS_VERSION_MOUNTAIN_LION;
case '3': return MACOS_VERSION_MAVERICKS;
case '4': return MACOS_VERSION_YOSEMITE;
+ case '5': return MACOS_VERSION_EL_CAPITAN;
+ case '6': return MACOS_VERSION_SIERRA;
+ case '7': return MACOS_VERSION_HIGH_SIERRA;
+ case '8': return MACOS_VERSION_MOJAVE;
default:
if (IsDigit(version[1]))
return MACOS_VERSION_UNKNOWN_NEWER;
@@ -890,10 +906,10 @@ struct __sanitizer_task_vm_info {
(sizeof(__sanitizer_task_vm_info) / sizeof(natural_t)))
uptr GetTaskInfoMaxAddress() {
- __sanitizer_task_vm_info vm_info = {};
+ __sanitizer_task_vm_info vm_info = {} /* zero initialize */;
mach_msg_type_number_t count = __SANITIZER_TASK_VM_INFO_COUNT;
int err = task_info(mach_task_self(), TASK_VM_INFO, (int *)&vm_info, &count);
- if (err == 0) {
+ if (err == 0 && vm_info.max_address != 0) {
return vm_info.max_address - 1;
} else {
// xnu cannot provide vm address limit
@@ -1060,14 +1076,16 @@ void CheckNoDeepBind(const char *filename, int flag) {
// Do nothing.
}
-// FIXME: implement on this platform.
bool GetRandom(void *buffer, uptr length, bool blocking) {
- UNIMPLEMENTED();
+ if (!buffer || !length || length > 256)
+ return false;
+ // arc4random never fails.
+ arc4random_buf(buffer, length);
+ return true;
}
-// FIXME: implement on this platform.
u32 GetNumberOfCPUs() {
- UNIMPLEMENTED();
+ return (u32)sysconf(_SC_NPROCESSORS_ONLN);
}
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_mac.h b/lib/sanitizer_common/sanitizer_mac.h
index e022a2c03..58423a774 100644
--- a/lib/sanitizer_common/sanitizer_mac.h
+++ b/lib/sanitizer_common/sanitizer_mac.h
@@ -40,6 +40,10 @@ enum MacosVersion {
MACOS_VERSION_MOUNTAIN_LION,
MACOS_VERSION_MAVERICKS,
MACOS_VERSION_YOSEMITE,
+ MACOS_VERSION_EL_CAPITAN,
+ MACOS_VERSION_SIERRA,
+ MACOS_VERSION_HIGH_SIERRA,
+ MACOS_VERSION_MOJAVE,
MACOS_VERSION_UNKNOWN_NEWER
};
diff --git a/lib/sanitizer_common/sanitizer_netbsd.cc b/lib/sanitizer_common/sanitizer_netbsd.cc
new file mode 100644
index 000000000..83beb008f
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_netbsd.cc
@@ -0,0 +1,330 @@
+//===-- sanitizer_netbsd.cc -----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between Sanitizer run-time libraries and implements
+// NetBSD-specific functions from sanitizer_libc.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_NETBSD
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_getauxval.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_linux.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <sys/exec.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <link.h>
+#include <lwp.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+extern "C" void *__mmap(void *, size_t, int, int, int, int,
+ off_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int __sysctl(const int *, unsigned int, void *, size_t *,
+ const void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys_close(int) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys_open(const char *, int, ...) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" ssize_t _sys_read(int, void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" ssize_t _sys_write(int, const void *,
+ size_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int __ftruncate(int, int, off_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" ssize_t _sys_readlink(const char *, char *,
+ size_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys_sched_yield() SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys___nanosleep50(const void *,
+ void *) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys_execve(const char *, char *const[],
+ char *const[]) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" off_t __lseek(int, int, off_t, int) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int __fork() SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys___sigprocmask14(int, const void *,
+ void *) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys___wait450(int wpid, int *, int,
+ void *) SANITIZER_WEAK_ATTRIBUTE;
+
+namespace __sanitizer {
+
+static void *GetRealLibcAddress(const char *symbol) {
+ void *real = dlsym(RTLD_NEXT, symbol);
+ if (!real)
+ real = dlsym(RTLD_DEFAULT, symbol);
+ if (!real) {
+ Printf("GetRealLibcAddress failed for symbol=%s", symbol);
+ Die();
+ }
+ return real;
+}
+
+#define _REAL(func, ...) real##_##func(__VA_ARGS__)
+#define DEFINE__REAL(ret_type, func, ...) \
+ static ret_type (*real_##func)(__VA_ARGS__) = NULL; \
+ if (!real_##func) { \
+ real_##func = (ret_type(*)(__VA_ARGS__))GetRealLibcAddress(#func); \
+ } \
+ CHECK(real_##func);
+
+// --------------- sanitizer_libc.h
+uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
+ OFF_T offset) {
+ CHECK(&__mmap);
+ return (uptr)__mmap(addr, length, prot, flags, fd, 0, offset);
+}
+
+uptr internal_munmap(void *addr, uptr length) {
+ DEFINE__REAL(int, munmap, void *a, uptr b);
+ return _REAL(munmap, addr, length);
+}
+
+int internal_mprotect(void *addr, uptr length, int prot) {
+ DEFINE__REAL(int, mprotect, void *a, uptr b, int c);
+ return _REAL(mprotect, addr, length, prot);
+}
+
+uptr internal_close(fd_t fd) {
+ CHECK(&_sys_close);
+ return _sys_close(fd);
+}
+
+uptr internal_open(const char *filename, int flags) {
+ CHECK(&_sys_open);
+ return _sys_open(filename, flags);
+}
+
+uptr internal_open(const char *filename, int flags, u32 mode) {
+ CHECK(&_sys_open);
+ return _sys_open(filename, flags, mode);
+}
+
+uptr internal_read(fd_t fd, void *buf, uptr count) {
+ sptr res;
+ CHECK(&_sys_read);
+ HANDLE_EINTR(res, (sptr)_sys_read(fd, buf, (size_t)count));
+ return res;
+}
+
+uptr internal_write(fd_t fd, const void *buf, uptr count) {
+ sptr res;
+ CHECK(&_sys_write);
+ HANDLE_EINTR(res, (sptr)_sys_write(fd, buf, count));
+ return res;
+}
+
+uptr internal_ftruncate(fd_t fd, uptr size) {
+ sptr res;
+ CHECK(&__ftruncate);
+ HANDLE_EINTR(res, __ftruncate(fd, 0, (s64)size));
+ return res;
+}
+
+uptr internal_stat(const char *path, void *buf) {
+ DEFINE__REAL(int, __stat50, const char *a, void *b);
+ return _REAL(__stat50, path, buf);
+}
+
+uptr internal_lstat(const char *path, void *buf) {
+ DEFINE__REAL(int, __lstat50, const char *a, void *b);
+ return _REAL(__lstat50, path, buf);
+}
+
+uptr internal_fstat(fd_t fd, void *buf) {
+ DEFINE__REAL(int, __fstat50, int a, void *b);
+ return _REAL(__fstat50, fd, buf);
+}
+
+uptr internal_filesize(fd_t fd) {
+ struct stat st;
+ if (internal_fstat(fd, &st))
+ return -1;
+ return (uptr)st.st_size;
+}
+
+uptr internal_dup2(int oldfd, int newfd) {
+ DEFINE__REAL(int, dup2, int a, int b);
+ return _REAL(dup2, oldfd, newfd);
+}
+
+uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
+ CHECK(&_sys_readlink);
+ return (uptr)_sys_readlink(path, buf, bufsize);
+}
+
+uptr internal_unlink(const char *path) {
+ DEFINE__REAL(int, unlink, const char *a);
+ return _REAL(unlink, path);
+}
+
+uptr internal_rename(const char *oldpath, const char *newpath) {
+ DEFINE__REAL(int, rename, const char *a, const char *b);
+ return _REAL(rename, oldpath, newpath);
+}
+
+uptr internal_sched_yield() {
+ CHECK(&_sys_sched_yield);
+ return _sys_sched_yield();
+}
+
+void internal__exit(int exitcode) {
+ DEFINE__REAL(void, _exit, int a);
+ _REAL(_exit, exitcode);
+ Die(); // Unreachable.
+}
+
+unsigned int internal_sleep(unsigned int seconds) {
+ struct timespec ts;
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ CHECK(&_sys___nanosleep50);
+ int res = _sys___nanosleep50(&ts, &ts);
+ if (res)
+ return ts.tv_sec;
+ return 0;
+}
+
+uptr internal_execve(const char *filename, char *const argv[],
+ char *const envp[]) {
+ CHECK(&_sys_execve);
+ return _sys_execve(filename, argv, envp);
+}
+
+tid_t GetTid() {
+ DEFINE__REAL(int, _lwp_self);
+ return _REAL(_lwp_self);
+}
+
+int TgKill(pid_t pid, tid_t tid, int sig) {
+ DEFINE__REAL(int, _lwp_kill, int a, int b);
+ (void)pid;
+ return _REAL(_lwp_kill, tid, sig);
+}
+
+u64 NanoTime() {
+ timeval tv;
+ DEFINE__REAL(int, __gettimeofday50, void *a, void *b);
+ internal_memset(&tv, 0, sizeof(tv));
+ _REAL(__gettimeofday50, &tv, 0);
+ return (u64)tv.tv_sec * 1000 * 1000 * 1000 + tv.tv_usec * 1000;
+}
+
+uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {
+ DEFINE__REAL(int, __clock_gettime50, __sanitizer_clockid_t a, void *b);
+ return _REAL(__clock_gettime50, clk_id, tp);
+}
+
+uptr internal_ptrace(int request, int pid, void *addr, void *data) {
+ Printf("internal_ptrace not implemented for NetBSD");
+ Die();
+ return 0;
+}
+
+uptr internal_waitpid(int pid, int *status, int options) {
+ CHECK(&_sys___wait450);
+ return _sys___wait450(pid, status, options, 0 /* rusage */);
+}
+
+uptr internal_getpid() {
+ DEFINE__REAL(int, getpid);
+ return _REAL(getpid);
+}
+
+uptr internal_getppid() {
+ DEFINE__REAL(int, getppid);
+ return _REAL(getppid);
+}
+
+uptr internal_getdents(fd_t fd, void *dirp, unsigned int count) {
+ DEFINE__REAL(int, __getdents30, int a, void *b, size_t c);
+ return _REAL(__getdents30, fd, dirp, count);
+}
+
+uptr internal_lseek(fd_t fd, OFF_T offset, int whence) {
+ CHECK(&__lseek);
+ return __lseek(fd, 0, offset, whence);
+}
+
+uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {
+ Printf("internal_prctl not implemented for NetBSD");
+ Die();
+ return 0;
+}
+
+uptr internal_sigaltstack(const void *ss, void *oss) {
+ DEFINE__REAL(int, __sigaltstack14, const void *a, void *b);
+ return _REAL(__sigaltstack14, ss, oss);
+}
+
+int internal_fork() {
+ CHECK(&__fork);
+ return __fork();
+}
+
+int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
+ uptr *oldlenp, const void *newp, uptr newlen) {
+ CHECK(&__sysctl);
+ return __sysctl(name, namelen, oldp, (size_t *)oldlenp, newp, (size_t)newlen);
+}
+
+int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
+ const void *newp, uptr newlen) {
+ DEFINE__REAL(int, sysctlbyname, const char *a, void *b, size_t *c,
+ const void *d, size_t e);
+ return _REAL(sysctlbyname, sname, oldp, (size_t *)oldlenp, newp,
+ (size_t)newlen);
+}
+
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+ __sanitizer_sigset_t *oldset) {
+ CHECK(&_sys___sigprocmask14);
+ return _sys___sigprocmask14(how, set, oldset);
+}
+
+void internal_sigfillset(__sanitizer_sigset_t *set) {
+ DEFINE__REAL(int, __sigfillset14, const void *a);
+ (void)_REAL(__sigfillset14, set);
+}
+
+void internal_sigemptyset(__sanitizer_sigset_t *set) {
+ DEFINE__REAL(int, __sigemptyset14, const void *a);
+ (void)_REAL(__sigemptyset14, set);
+}
+
+uptr intrnal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ Printf("internal_clone not implemented for NetBSD");
+ Die();
+ return 0;
+}
+
+} // namespace __sanitizer
+
+#endif
diff --git a/lib/sanitizer_common/sanitizer_openbsd.cc b/lib/sanitizer_common/sanitizer_openbsd.cc
index 2aea7cb14..dc955109a 100644
--- a/lib/sanitizer_common/sanitizer_openbsd.cc
+++ b/lib/sanitizer_common/sanitizer_openbsd.cc
@@ -54,9 +54,9 @@ int internal_mprotect(void *addr, uptr length, int prot) {
uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
// On OpenBSD we cannot get the full path
struct kinfo_proc kp;
- size_t kl;
+ uptr kl;
const int Mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
- if (sysctl(Mib, ARRAY_SIZE(Mib), &kp, &kl, NULL, 0) != -1)
+ if (internal_sysctl(Mib, ARRAY_SIZE(Mib), &kp, &kl, NULL, 0) != -1)
return internal_snprintf(buf,
(KI_MAXCOMLEN < buf_len ? KI_MAXCOMLEN : buf_len),
"%s", kp.p_comm);
@@ -64,23 +64,23 @@ uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
}
static void GetArgsAndEnv(char ***argv, char ***envp) {
- size_t nargv;
- size_t nenv;
+ uptr nargv;
+ uptr nenv;
int argvmib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV};
int envmib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ENV};
- if (sysctl(argvmib, 4, NULL, &nargv, NULL, 0) == -1) {
+ if (internal_sysctl(argvmib, 4, NULL, &nargv, NULL, 0) == -1) {
Printf("sysctl KERN_PROC_NARGV failed\n");
Die();
}
- if (sysctl(envmib, 4, NULL, &nenv, NULL, 0) == -1) {
+ if (internal_sysctl(envmib, 4, NULL, &nenv, NULL, 0) == -1) {
Printf("sysctl KERN_PROC_NENV failed\n");
Die();
}
- if (sysctl(argvmib, 4, &argv, &nargv, NULL, 0) == -1) {
+ if (internal_sysctl(argvmib, 4, &argv, &nargv, NULL, 0) == -1) {
Printf("sysctl KERN_PROC_ARGV failed\n");
Die();
}
- if (sysctl(envmib, 4, &envp, &nenv, NULL, 0) == -1) {
+ if (internal_sysctl(envmib, 4, &envp, &nenv, NULL, 0) == -1) {
Printf("sysctl KERN_PROC_ENV failed\n");
Die();
}
diff --git a/lib/sanitizer_common/sanitizer_platform.h b/lib/sanitizer_common/sanitizer_platform.h
index d81e25580..106a147e5 100644
--- a/lib/sanitizer_common/sanitizer_platform.h
+++ b/lib/sanitizer_common/sanitizer_platform.h
@@ -235,7 +235,12 @@
#if defined(__mips__)
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 40)
#elif defined(__aarch64__)
-# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 48)
+# if SANITIZER_MAC
+// Darwin iOS/ARM64 has a 36-bit VMA, 64GiB VM
+# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 36)
+# else
+# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 48)
+# endif
#else
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47)
#endif
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
index c27055f2a..cd1b73d58 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
@@ -1037,7 +1037,11 @@ CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
-#if SANITIZER_LINUX && (!defined(__ANDROID__) || __ANDROID_API__ >= 21)
+#ifndef __GLIBC_PREREQ
+#define __GLIBC_PREREQ(x, y) 0
+#endif
+
+#if SANITIZER_LINUX && (__ANDROID_API__ >= 21 || __GLIBC_PREREQ (2, 14))
CHECK_TYPE_SIZE(mmsghdr);
CHECK_SIZE_AND_OFFSET(mmsghdr, msg_hdr);
CHECK_SIZE_AND_OFFSET(mmsghdr, msg_len);
@@ -1078,9 +1082,6 @@ COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
// Can't write checks for sa_handler and sa_sigaction due to them being
// preprocessor macros.
CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
-#ifndef __GLIBC_PREREQ
-#define __GLIBC_PREREQ(x, y) 0
-#endif
#if !defined(__s390x__) || __GLIBC_PREREQ (2, 20)
// On s390x glibc 2.19 and earlier sa_flags was unsigned long, and sa_resv
// didn't exist.
@@ -1206,7 +1207,7 @@ CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
#endif
#if SANITIZER_LINUX
-COMPILER_CHECK(sizeof(__sanitizer_mallinfo) == sizeof(struct mallinfo));
+COMPILER_CHECK(sizeof(__sanitizer_struct_mallinfo) == sizeof(struct mallinfo));
#endif
#if !SANITIZER_ANDROID
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
index f89a11312..3f67916ad 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.h
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
@@ -187,13 +187,13 @@ namespace __sanitizer {
#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
#if SANITIZER_ANDROID
- struct __sanitizer_mallinfo {
+ struct __sanitizer_struct_mallinfo {
uptr v[10];
};
#endif
#if SANITIZER_LINUX && !SANITIZER_ANDROID
- struct __sanitizer_mallinfo {
+ struct __sanitizer_struct_mallinfo {
int v[10];
};
diff --git a/lib/sanitizer_common/sanitizer_posix.h b/lib/sanitizer_common/sanitizer_posix.h
index da447002b..3075a13b9 100644
--- a/lib/sanitizer_common/sanitizer_posix.h
+++ b/lib/sanitizer_common/sanitizer_posix.h
@@ -60,6 +60,11 @@ uptr internal_waitpid(int pid, int *status, int options);
int internal_fork();
int internal_forkpty(int *amaster);
+int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
+ uptr *oldlenp, const void *newp, uptr newlen);
+int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
+ const void *newp, uptr newlen);
+
// These functions call appropriate pthread_ functions directly, bypassing
// the interceptor. They are weak and may not be present in some tools.
SANITIZER_WEAK_ATTRIBUTE
diff --git a/lib/sanitizer_common/sanitizer_procmaps_bsd.cc b/lib/sanitizer_common/sanitizer_procmaps_bsd.cc
index e41dc987d..4cebd9858 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_bsd.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_bsd.cc
@@ -67,8 +67,8 @@ void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
#endif
};
- size_t Size = 0;
- int Err = sysctl(Mib, ARRAY_SIZE(Mib), NULL, &Size, NULL, 0);
+ uptr Size = 0;
+ int Err = internal_sysctl(Mib, ARRAY_SIZE(Mib), NULL, &Size, NULL, 0);
CHECK_EQ(Err, 0);
CHECK_GT(Size, 0);
@@ -76,7 +76,7 @@ void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
size_t MmapedSize = Size * 4 / 3;
void *VmMap = MmapOrDie(MmapedSize, "ReadProcMaps()");
Size = MmapedSize;
- Err = sysctl(Mib, ARRAY_SIZE(Mib), VmMap, &Size, NULL, 0);
+ Err = internal_sysctl(Mib, ARRAY_SIZE(Mib), VmMap, &Size, NULL, 0);
CHECK_EQ(Err, 0);
proc_maps->data = (char *)VmMap;
#else
@@ -88,7 +88,7 @@ void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
if (Size > 0x10000)
Size = 0x10000;
Size = (Size / sizeof(struct kinfo_vmentry)) * sizeof(struct kinfo_vmentry);
- Err = sysctl(Mib, ARRAY_SIZE(Mib), Mem, &Size, NULL, 0);
+ Err = internal_sysctl(Mib, ARRAY_SIZE(Mib), Mem, &Size, NULL, 0);
CHECK_EQ(Err, 0);
MmapedSize = Size;
proc_maps->data = Mem;
diff --git a/lib/sanitizer_common/sanitizer_procmaps_mac.cc b/lib/sanitizer_common/sanitizer_procmaps_mac.cc
index 0167ab18b..d90e4b993 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_mac.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_mac.cc
@@ -140,12 +140,6 @@ void MemoryMappingLayout::LoadFromCache() {
// early in the process, when dyld is one of the only images loaded,
// so it will be hit after only a few iterations.
static mach_header *get_dyld_image_header() {
- mach_port_name_t port;
- if (task_for_pid(mach_task_self(), internal_getpid(), &port) !=
- KERN_SUCCESS) {
- return nullptr;
- }
-
unsigned depth = 1;
vm_size_t size = 0;
vm_address_t address = 0;
@@ -154,7 +148,7 @@ static mach_header *get_dyld_image_header() {
while (true) {
struct vm_region_submap_info_64 info;
- err = vm_region_recurse_64(port, &address, &size, &depth,
+ err = vm_region_recurse_64(mach_task_self(), &address, &size, &depth,
(vm_region_info_t)&info, &count);
if (err != KERN_SUCCESS) return nullptr;
diff --git a/lib/sanitizer_common/sanitizer_ring_buffer.h b/lib/sanitizer_common/sanitizer_ring_buffer.h
new file mode 100644
index 000000000..d15f27fd4
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_ring_buffer.h
@@ -0,0 +1,162 @@
+//===-- sanitizer_ring_buffer.h ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Simple ring buffer.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_RING_BUFFER_H
+#define SANITIZER_RING_BUFFER_H
+
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+// RingBuffer<T>: fixed-size ring buffer optimized for speed of push().
+// T should be a POD type and sizeof(T) should be divisible by sizeof(void*).
+// At creation, all elements are zero.
+template<class T>
+class RingBuffer {
+ public:
+ COMPILER_CHECK(sizeof(T) % sizeof(void *) == 0);
+ static RingBuffer *New(uptr Size) {
+ void *Ptr = MmapOrDie(SizeInBytes(Size), "RingBuffer");
+ RingBuffer *RB = reinterpret_cast<RingBuffer*>(Ptr);
+ uptr End = reinterpret_cast<uptr>(Ptr) + SizeInBytes(Size);
+ RB->last_ = RB->next_ = reinterpret_cast<T*>(End - sizeof(T));
+ return RB;
+ }
+ void Delete() {
+ UnmapOrDie(this, SizeInBytes(size()));
+ }
+ uptr size() const {
+ return last_ + 1 -
+ reinterpret_cast<T *>(reinterpret_cast<uptr>(this) +
+ 2 * sizeof(T *));
+ }
+
+ static uptr SizeInBytes(uptr Size) {
+ return Size * sizeof(T) + 2 * sizeof(T*);
+ }
+
+ uptr SizeInBytes() { return SizeInBytes(size()); }
+
+ void push(T t) {
+ *next_ = t;
+ next_--;
+ // The condition below works only if sizeof(T) is divisible by sizeof(T*).
+ if (next_ <= reinterpret_cast<T*>(&next_))
+ next_ = last_;
+ }
+
+ T operator[](uptr Idx) const {
+ CHECK_LT(Idx, size());
+ sptr IdxNext = Idx + 1;
+ if (IdxNext > last_ - next_)
+ IdxNext -= size();
+ return next_[IdxNext];
+ }
+
+ private:
+ RingBuffer() {}
+ ~RingBuffer() {}
+ RingBuffer(const RingBuffer&) = delete;
+
+ // Data layout:
+ // LNDDDDDDDD
+ // D: data elements.
+ // L: last_, always points to the last data element.
+ // N: next_, initially equals to last_, is decremented on every push,
+ // wraps around if it's less or equal than its own address.
+ T *last_;
+ T *next_;
+ T data_[1]; // flexible array.
+};
+
+// A ring buffer with externally provided storage that encodes its state in 8
+// bytes. Has significant constraints on size and alignment of storage.
+// See a comment in hwasan/hwasan_thread_list.h for the motivation behind this.
+#if SANITIZER_WORDSIZE == 64
+template <class T>
+class CompactRingBuffer {
+ // Top byte of long_ stores the buffer size in pages.
+ // Lower bytes store the address of the next buffer element.
+ static constexpr int kPageSizeBits = 12;
+ static constexpr int kSizeShift = 56;
+ static constexpr uptr kNextMask = (1ULL << kSizeShift) - 1;
+
+ uptr GetStorageSize() const { return (long_ >> kSizeShift) << kPageSizeBits; }
+
+ void Init(void *storage, uptr size) {
+ CHECK_EQ(sizeof(CompactRingBuffer<T>), sizeof(void *));
+ CHECK(IsPowerOfTwo(size));
+ CHECK_GE(size, 1 << kPageSizeBits);
+ CHECK_LE(size, 128 << kPageSizeBits);
+ CHECK_EQ(size % 4096, 0);
+ CHECK_EQ(size % sizeof(T), 0);
+ CHECK_EQ((uptr)storage % (size * 2), 0);
+ long_ = (uptr)storage | ((size >> kPageSizeBits) << kSizeShift);
+ }
+
+ void SetNext(const T *next) {
+ long_ = (long_ & ~kNextMask) | (uptr)next;
+ }
+
+ public:
+ CompactRingBuffer(void *storage, uptr size) {
+ Init(storage, size);
+ }
+
+ // A copy constructor of sorts.
+ CompactRingBuffer(const CompactRingBuffer &other, void *storage) {
+ uptr size = other.GetStorageSize();
+ internal_memcpy(storage, other.StartOfStorage(), size);
+ Init(storage, size);
+ uptr Idx = other.Next() - (const T *)other.StartOfStorage();
+ SetNext((const T *)storage + Idx);
+ }
+
+ T *Next() const { return (T *)(long_ & kNextMask); }
+
+ void *StartOfStorage() const {
+ return (void *)((uptr)Next() & ~(GetStorageSize() - 1));
+ }
+
+ void *EndOfStorage() const {
+ return (void *)((uptr)StartOfStorage() + GetStorageSize());
+ }
+
+ uptr size() const { return GetStorageSize() / sizeof(T); }
+
+ void push(T t) {
+ T *next = Next();
+ *next = t;
+ next++;
+ next = (T *)((uptr)next & ~GetStorageSize());
+ SetNext(next);
+ }
+
+ T operator[](uptr Idx) const {
+ CHECK_LT(Idx, size());
+ const T *Begin = (const T *)StartOfStorage();
+ sptr StorageIdx = Next() - Begin;
+ StorageIdx -= (sptr)(Idx + 1);
+ if (StorageIdx < 0)
+ StorageIdx += size();
+ return Begin[StorageIdx];
+ }
+
+ public:
+ ~CompactRingBuffer() {}
+ CompactRingBuffer(const CompactRingBuffer &) = delete;
+
+ uptr long_;
+};
+#endif
+} // namespace __sanitizer
+
+#endif // SANITIZER_RING_BUFFER_H
diff --git a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
index 747a4a701..c87b18e1b 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
@@ -114,11 +114,25 @@ void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
return;
}
InternalScopedString frame_desc(GetPageSizeCached());
- RenderFrame(&frame_desc, fmt, 0, frame->info,
- common_flags()->symbolize_vs_style,
- common_flags()->strip_path_prefix);
- internal_strncpy(out_buf, frame_desc.data(), out_buf_size);
- out_buf[out_buf_size - 1] = 0;
+ uptr frame_num = 0;
+ // Reserve one byte for the final 0.
+ char *out_end = out_buf + out_buf_size - 1;
+ for (SymbolizedStack *cur = frame; cur && out_buf < out_end;
+ cur = cur->next) {
+ frame_desc.clear();
+ RenderFrame(&frame_desc, fmt, frame_num++, cur->info,
+ common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
+ if (!frame_desc.length())
+ continue;
+ // Reserve one byte for the terminating 0.
+ uptr n = out_end - out_buf - 1;
+ internal_strncpy(out_buf, frame_desc.data(), n);
+ out_buf += __sanitizer::Min<uptr>(n, frame_desc.length());
+ *out_buf++ = 0;
+ }
+ CHECK(out_buf <= out_end);
+ *out_buf = 0;
}
SANITIZER_INTERFACE_ATTRIBUTE
diff --git a/lib/sanitizer_common/sanitizer_syscall_generic.inc b/lib/sanitizer_common/sanitizer_syscall_generic.inc
index e4ed1b4de..ddfb26397 100644
--- a/lib/sanitizer_common/sanitizer_syscall_generic.inc
+++ b/lib/sanitizer_common/sanitizer_syscall_generic.inc
@@ -11,46 +11,21 @@
//
//===----------------------------------------------------------------------===//
-#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_NETBSD || \
- SANITIZER_OPENBSD || SANITIZER_SOLARIS
+// NetBSD uses libc calls directly
+#if !SANITIZER_NETBSD
+
+#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_OPENBSD || SANITIZER_SOLARIS
# define SYSCALL(name) SYS_ ## name
#else
# define SYSCALL(name) __NR_ ## name
#endif
-#if SANITIZER_NETBSD
-// We use 3 kinds of internal_syscall's for different types of retval in order
-// to address differences in calling conventions (e.g. registers to place the
-// return value in).
-// - internal_syscall for 32-bit length (int, pid_t)
-// - internal_syscall64 for 64-bit length (off_t)
-// - internal_syscall_ptr for pointer and (s)size_t
-# define internal_syscall syscall
-# define internal_syscall64 __syscall
-// Handle syscall renames manually
-# define SYS_stat SYS___stat50
-# define SYS_lstat SYS___lstat50
-# define SYS_fstat SYS___fstat50
-# define SYS_gettimeofday SYS___gettimeofday50
-# define SYS_wait4 SYS___wait450
-# define SYS_getdents SYS___getdents30
-# define SYS_sigaltstack SYS___sigaltstack14
-# define SYS_sigprocmask SYS___sigprocmask14
-# define SYS_nanosleep SYS___nanosleep50
-# define SYS_clock_gettime SYS___clock_gettime50
-# if SANITIZER_WORDSIZE == 64
-# define internal_syscall_ptr __syscall
-# else
-# define internal_syscall_ptr syscall
-# endif
-#elif defined(__x86_64__) && (SANITIZER_FREEBSD || SANITIZER_MAC)
+#if defined(__x86_64__) && (SANITIZER_FREEBSD || SANITIZER_MAC)
# define internal_syscall __syscall
-# define internal_syscall64 __syscall
-# define internal_syscall_ptr __syscall
# else
# define internal_syscall syscall
-# define internal_syscall64 syscall
-# define internal_syscall_ptr syscall
+#endif
+
#endif
bool internal_iserror(uptr retval, int *rverrno) {
diff --git a/lib/sanitizer_common/sanitizer_syscall_linux_aarch64.inc b/lib/sanitizer_common/sanitizer_syscall_linux_aarch64.inc
index 1f05ed9b6..7ab1d7641 100644
--- a/lib/sanitizer_common/sanitizer_syscall_linux_aarch64.inc
+++ b/lib/sanitizer_common/sanitizer_syscall_linux_aarch64.inc
@@ -127,9 +127,6 @@ static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3,
#define internal_syscall(...) __SYSCALL_DISP(__internal_syscall, __VA_ARGS__)
-#define internal_syscall_ptr internal_syscall
-#define internal_syscall64 internal_syscall
-
// Helper function used to avoid cobbler errno.
bool internal_iserror(uptr retval, int *rverrno) {
if (retval >= (uptr)-4095) {
diff --git a/lib/sanitizer_common/sanitizer_syscall_linux_arm.inc b/lib/sanitizer_common/sanitizer_syscall_linux_arm.inc
index a3fdb9e60..b4fd0962a 100644
--- a/lib/sanitizer_common/sanitizer_syscall_linux_arm.inc
+++ b/lib/sanitizer_common/sanitizer_syscall_linux_arm.inc
@@ -127,9 +127,6 @@ static uptr __internal_syscall(u32 nr, u32 arg1, long arg2, long arg3,
#define internal_syscall(...) __SYSCALL_DISP(__internal_syscall, __VA_ARGS__)
-#define internal_syscall_ptr internal_syscall
-#define internal_syscall64 internal_syscall
-
// Helper function used to avoid cobbler errno.
bool internal_iserror(uptr retval, int *rverrno) {
if (retval >= (uptr)-4095) {
diff --git a/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc b/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc
index 327aaa80a..9853a6a67 100644
--- a/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc
+++ b/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc
@@ -20,9 +20,6 @@ static uptr internal_syscall(u64 nr) {
return retval;
}
-#define internal_syscall_ptr internal_syscall
-#define internal_syscall64 internal_syscall
-
template <typename T1>
static uptr internal_syscall(u64 nr, T1 arg1) {
u64 retval;
diff --git a/lib/sanitizer_common/sanitizer_termination.cc b/lib/sanitizer_common/sanitizer_termination.cc
index 8243fc05d..35e4403ad 100644
--- a/lib/sanitizer_common/sanitizer_termination.cc
+++ b/lib/sanitizer_common/sanitizer_termination.cc
@@ -84,3 +84,12 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond,
}
} // namespace __sanitizer
+
+using namespace __sanitizer; // NOLINT
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_set_death_callback(void (*callback)(void)) {
+ SetUserDieCallback(callback);
+}
+} // extern "C"
diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc
index 38e567d9a..e8f5919db 100644
--- a/lib/sanitizer_common/sanitizer_win.cc
+++ b/lib/sanitizer_common/sanitizer_win.cc
@@ -29,6 +29,10 @@
#include "sanitizer_placement_new.h"
#include "sanitizer_win_defs.h"
+#if defined(PSAPI_VERSION) && PSAPI_VERSION == 1
+#pragma comment(lib, "psapi")
+#endif
+
// A macro to tell the compiler that this part of the code cannot be reached,
// if the compiler supports this feature. Since we're using this in
// code that is called when terminating the process, the expansion of the
diff --git a/lib/sanitizer_common/sanitizer_win_defs.h b/lib/sanitizer_common/sanitizer_win_defs.h
index 077ff9ccc..10fc2d021 100644
--- a/lib/sanitizer_common/sanitizer_win_defs.h
+++ b/lib/sanitizer_common/sanitizer_win_defs.h
@@ -17,17 +17,27 @@
#if SANITIZER_WINDOWS
#ifndef WINAPI
-#ifdef _M_IX86
+#if defined(_M_IX86) || defined(__i386__)
#define WINAPI __stdcall
#else
#define WINAPI
#endif
#endif
-#if defined(_WIN64)
+#if defined(_M_IX86) || defined(__i386__)
+#define WIN_SYM_PREFIX "_"
+#else
#define WIN_SYM_PREFIX
+#endif
+
+// For MinGW, the /export: directives contain undecorated symbols, contrary to
+// link/lld-link. The GNU linker doesn't support /alternatename and /include
+// though, thus lld-link in MinGW mode interprets them in the same way as
+// in the default mode.
+#ifdef __MINGW32__
+#define WIN_EXPORT_PREFIX
#else
-#define WIN_SYM_PREFIX "_"
+#define WIN_EXPORT_PREFIX WIN_SYM_PREFIX
#endif
// Intermediate macro to ensure the parameter is expanded before stringified.
@@ -62,8 +72,8 @@
__pragma(comment(linker, "/include:" WIN_SYM_PREFIX STRINGIFY(Name)))
#define WIN_EXPORT(ExportedName, Name) \
- __pragma(comment(linker, "/export:" WIN_SYM_PREFIX STRINGIFY(ExportedName) \
- "=" WIN_SYM_PREFIX STRINGIFY(Name)))
+ __pragma(comment(linker, "/export:" WIN_EXPORT_PREFIX STRINGIFY(ExportedName)\
+ "=" WIN_EXPORT_PREFIX STRINGIFY(Name)))
// We cannot define weak functions on Windows, but we can use WIN_WEAK_ALIAS()
// which defines an alias to a default implementation, and only works when
diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt
index 401682baa..4642c59ec 100644
--- a/lib/sanitizer_common/tests/CMakeLists.txt
+++ b/lib/sanitizer_common/tests/CMakeLists.txt
@@ -26,6 +26,7 @@ set(SANITIZER_UNITTESTS
sanitizer_posix_test.cc
sanitizer_printf_test.cc
sanitizer_procmaps_test.cc
+ sanitizer_ring_buffer_test.cc
sanitizer_quarantine_test.cc
sanitizer_stackdepot_test.cc
sanitizer_stacktrace_printer_test.cc
diff --git a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
index ef4c10b8d..05fef252b 100644
--- a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
@@ -89,11 +89,20 @@ struct AP64VeryCompact {
static const uptr kFlags = 0;
};
+struct AP64Dense {
+ static const uptr kSpaceBeg = kAllocatorSpace;
+ static const uptr kSpaceSize = kAllocatorSize;
+ static const uptr kMetadataSize = 16;
+ typedef DenseSizeClassMap SizeClassMap;
+ typedef NoOpMapUnmapCallback MapUnmapCallback;
+ static const uptr kFlags = 0;
+};
typedef SizeClassAllocator64<AP64> Allocator64;
typedef SizeClassAllocator64<AP64Dyn> Allocator64Dynamic;
typedef SizeClassAllocator64<AP64Compact> Allocator64Compact;
typedef SizeClassAllocator64<AP64VeryCompact> Allocator64VeryCompact;
+typedef SizeClassAllocator64<AP64Dense> Allocator64Dense;
#elif defined(__mips64)
static const u64 kAddressSpaceSize = 1ULL << 40;
#elif defined(__aarch64__)
@@ -144,6 +153,10 @@ TEST(SanitizerCommon, InternalSizeClassMap) {
TestSizeClassMap<InternalSizeClassMap>();
}
+TEST(SanitizerCommon, DenseSizeClassMap) {
+ TestSizeClassMap<VeryCompactSizeClassMap>();
+}
+
template <class Allocator>
void TestSizeClassAllocator() {
Allocator *a = new Allocator;
@@ -226,9 +239,14 @@ TEST(SanitizerCommon, SizeClassAllocator64Dynamic) {
}
#if !SANITIZER_ANDROID
+//FIXME(kostyak): find values so that those work on Android as well.
TEST(SanitizerCommon, SizeClassAllocator64Compact) {
TestSizeClassAllocator<Allocator64Compact>();
}
+
+TEST(SanitizerCommon, SizeClassAllocator64Dense) {
+ TestSizeClassAllocator<Allocator64Dense>();
+}
#endif
TEST(SanitizerCommon, SizeClassAllocator64VeryCompact) {
diff --git a/lib/sanitizer_common/tests/sanitizer_common_test.cc b/lib/sanitizer_common/tests/sanitizer_common_test.cc
index 0177484a5..6b091de60 100644
--- a/lib/sanitizer_common/tests/sanitizer_common_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_common_test.cc
@@ -354,7 +354,8 @@ TEST(SanitizerCommon, InternalScopedString) {
EXPECT_STREQ("012345678", str.data());
}
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD || \
+ SANITIZER_OPENBSD || SANITIZER_MAC || SANITIZER_IOS
TEST(SanitizerCommon, GetRandom) {
u8 buffer_1[32], buffer_2[32];
for (bool blocking : { false, true }) {
diff --git a/lib/sanitizer_common/tests/sanitizer_ring_buffer_test.cc b/lib/sanitizer_common/tests/sanitizer_ring_buffer_test.cc
new file mode 100644
index 000000000..80aa57c52
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_ring_buffer_test.cc
@@ -0,0 +1,99 @@
+//===-- sanitizer_vector_test.cc ------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of *Sanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_ring_buffer.h"
+#include "gtest/gtest.h"
+
+namespace __sanitizer {
+
+struct LargeStruct {
+ int64_t v;
+ int64_t extra[3];
+
+ explicit LargeStruct(int64_t v) : v(v) {}
+ operator int64_t() { return v; }
+};
+
+struct Struct10Bytes {
+ short t[3];
+};
+
+TEST(RingBuffer, Construct) {
+ RingBuffer<int64_t> *RBlong = RingBuffer<int64_t>::New(20);
+ EXPECT_EQ(RBlong->size(), 20U);
+ RBlong->Delete();
+}
+
+template <class T> void TestRB() {
+ RingBuffer<T> *RB;
+ const size_t Sizes[] = {1, 2, 3, 5, 8, 16, 20, 40, 10000};
+ for (size_t Size : Sizes) {
+ RB = RingBuffer<T>::New(Size);
+ EXPECT_EQ(RB->size(), Size);
+ RB->Delete();
+ }
+
+ RB = RingBuffer<T>::New(4);
+ EXPECT_EQ(RB->size(), 4U);
+#define EXPECT_RING_BUFFER(a0, a1, a2, a3) \
+ EXPECT_EQ((int64_t)(*RB)[0], (int64_t)a0); \
+ EXPECT_EQ((int64_t)(*RB)[1], (int64_t)a1); \
+ EXPECT_EQ((int64_t)(*RB)[2], (int64_t)a2); \
+ EXPECT_EQ((int64_t)(*RB)[3], (int64_t)a3);
+
+ RB->push(T(1)); EXPECT_RING_BUFFER(1, 0, 0, 0);
+ RB->push(T(2)); EXPECT_RING_BUFFER(2, 1, 0, 0);
+ RB->push(T(3)); EXPECT_RING_BUFFER(3, 2, 1, 0);
+ RB->push(T(4)); EXPECT_RING_BUFFER(4, 3, 2, 1);
+ RB->push(T(5)); EXPECT_RING_BUFFER(5, 4, 3, 2);
+ RB->push(T(6)); EXPECT_RING_BUFFER(6, 5, 4, 3);
+ RB->push(T(7)); EXPECT_RING_BUFFER(7, 6, 5, 4);
+ RB->push(T(8)); EXPECT_RING_BUFFER(8, 7, 6, 5);
+ RB->push(T(9)); EXPECT_RING_BUFFER(9, 8, 7, 6);
+ RB->push(T(10)); EXPECT_RING_BUFFER(10, 9, 8, 7);
+ RB->push(T(11)); EXPECT_RING_BUFFER(11, 10, 9, 8);
+ RB->push(T(12)); EXPECT_RING_BUFFER(12, 11, 10, 9);
+
+#undef EXPECT_RING_BUFFER
+}
+
+#if SANITIZER_WORDSIZE == 64
+TEST(RingBuffer, int64) {
+ TestRB<int64_t>();
+}
+
+TEST(RingBuffer, LargeStruct) {
+ TestRB<LargeStruct>();
+}
+
+template<typename T>
+CompactRingBuffer<T> *AllocCompactRingBuffer(size_t count) {
+ size_t sz = sizeof(T) * count;
+ EXPECT_EQ(0ULL, sz % 4096);
+ void *p = MmapAlignedOrDieOnFatalError(sz, sz * 2, "CompactRingBuffer");
+ return new CompactRingBuffer<T>(p, sz);
+}
+
+TEST(CompactRingBuffer, int64) {
+ const size_t page_sizes[] = {1, 2, 4, 128};
+
+ for (size_t pages : page_sizes) {
+ size_t count = 4096 * pages / sizeof(int64_t);
+ auto R = AllocCompactRingBuffer<int64_t>(count);
+ int64_t top = count * 3 + 13;
+ for (int64_t i = 0; i < top; ++i) R->push(i);
+ for (int64_t i = 0; i < (int64_t)count; ++i)
+ EXPECT_EQ(top - i - 1, (*R)[i]);
+ }
+}
+#endif
+} // namespace __sanitizer
diff --git a/lib/scudo/CMakeLists.txt b/lib/scudo/CMakeLists.txt
index 0646c3dd4..79f69e934 100644
--- a/lib/scudo/CMakeLists.txt
+++ b/lib/scudo/CMakeLists.txt
@@ -17,6 +17,14 @@ set(SCUDO_DYNAMIC_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS})
# Use gc-sections by default to avoid unused code being pulled in.
list(APPEND SCUDO_DYNAMIC_LINK_FLAGS -Wl,--gc-sections)
+if(ANDROID)
+# Put most Sanitizer shared libraries in the global group. For more details, see
+# android-changes-for-ndk-developers.md#changes-to-library-search-order
+ if (COMPILER_RT_HAS_Z_GLOBAL)
+ list(APPEND SCUDO_DYNAMIC_LINK_FLAGS -Wl,-z,global)
+ endif()
+endif()
+
# The minimal Scudo runtime does not inlude the UBSan runtime.
set(SCUDO_MINIMAL_OBJECT_LIBS
RTSanitizerCommonNoTermination
diff --git a/lib/scudo/scudo_allocator.cpp b/lib/scudo/scudo_allocator.cpp
index 4a11bf5fc..fb04fb281 100644
--- a/lib/scudo/scudo_allocator.cpp
+++ b/lib/scudo/scudo_allocator.cpp
@@ -129,16 +129,9 @@ namespace Chunk {
computeChecksum(Ptr, &NewUnpackedHeader));
}
- // Nulls out a chunk header. When returning the chunk to the backend, there
- // is no need to store a valid ChunkAvailable header, as this would be
- // computationally expensive. Zeroing out serves the same purpose by making
- // the header invalid. In the extremely rare event where 0 would be a valid
- // checksum for the chunk, the state of the chunk is ChunkAvailable anyway.
+ // Ensure that ChunkAvailable is 0, so that if a 0 checksum is ever valid
+ // for a fully nulled out header, its state will be available anyway.
COMPILER_CHECK(ChunkAvailable == 0);
- static INLINE void eraseHeader(void *Ptr) {
- const PackedHeader NullPackedHeader = 0;
- atomic_store_relaxed(getAtomicHeader(Ptr), NullPackedHeader);
- }
// Loads and unpacks the header, verifying the checksum in the process.
static INLINE
@@ -185,7 +178,9 @@ struct QuarantineCallback {
Chunk::loadHeader(Ptr, &Header);
if (UNLIKELY(Header.State != ChunkQuarantine))
dieWithMessage("invalid chunk state when recycling address %p\n", Ptr);
- Chunk::eraseHeader(Ptr);
+ UnpackedHeader NewHeader = Header;
+ NewHeader.State = ChunkAvailable;
+ Chunk::compareExchangeHeader(Ptr, &NewHeader, &Header);
void *BackendPtr = Chunk::getBackendPtr(Ptr, &Header);
if (Header.ClassId)
getBackend().deallocatePrimary(Cache_, BackendPtr, Header.ClassId);
@@ -264,7 +259,8 @@ struct Allocator {
Quarantine.Init(
static_cast<uptr>(getFlags()->QuarantineSizeKb) << 10,
static_cast<uptr>(getFlags()->ThreadLocalQuarantineSizeKb) << 10);
- QuarantineChunksUpToSize = getFlags()->QuarantineChunksUpToSize;
+ QuarantineChunksUpToSize = (Quarantine.GetCacheSize() == 0) ? 0 :
+ getFlags()->QuarantineChunksUpToSize;
DeallocationTypeMismatch = getFlags()->DeallocationTypeMismatch;
DeleteSizeMismatch = getFlags()->DeleteSizeMismatch;
ZeroContents = getFlags()->ZeroContents;
@@ -389,10 +385,11 @@ struct Allocator {
// quarantine chunk size threshold.
void quarantineOrDeallocateChunk(void *Ptr, UnpackedHeader *Header,
uptr Size) {
- const bool BypassQuarantine = (Quarantine.GetCacheSize() == 0) ||
- (Size > QuarantineChunksUpToSize);
+ const bool BypassQuarantine = !Size || (Size > QuarantineChunksUpToSize);
if (BypassQuarantine) {
- Chunk::eraseHeader(Ptr);
+ UnpackedHeader NewHeader = *Header;
+ NewHeader.State = ChunkAvailable;
+ Chunk::compareExchangeHeader(Ptr, &NewHeader, Header);
void *BackendPtr = Chunk::getBackendPtr(Ptr, Header);
if (Header->ClassId) {
bool UnlockRequired;
@@ -675,7 +672,7 @@ void *scudoValloc(uptr Size) {
}
void *scudoPvalloc(uptr Size) {
- uptr PageSize = GetPageSizeCached();
+ const uptr PageSize = GetPageSizeCached();
if (UNLIKELY(CheckForPvallocOverflow(Size, PageSize))) {
errno = ENOMEM;
if (Instance.canReturnNull())
diff --git a/lib/tsan/CMakeLists.txt b/lib/tsan/CMakeLists.txt
index 4a2ea3f4a..d501d0cba 100644
--- a/lib/tsan/CMakeLists.txt
+++ b/lib/tsan/CMakeLists.txt
@@ -158,6 +158,15 @@ else()
VERBATIM)
elseif(arch STREQUAL "aarch64")
add_asm_sources(TSAN_ASM_SOURCES rtl/tsan_rtl_aarch64.S)
+ # Sanity check for Go runtime.
+ set(BUILDGO_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/go/buildgo.sh)
+ add_custom_target(GotsanRuntimeCheck
+ COMMAND env "CC=${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}"
+ 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)
elseif(arch MATCHES "powerpc64|powerpc64le")
add_asm_sources(TSAN_ASM_SOURCES rtl/tsan_rtl_ppc64.S)
# Sanity check for Go runtime.
diff --git a/lib/tsan/check_analyze.sh b/lib/tsan/check_analyze.sh
index 9b5abc317..b2beb8578 100755
--- a/lib/tsan/check_analyze.sh
+++ b/lib/tsan/check_analyze.sh
@@ -34,16 +34,16 @@ check() {
fi
}
-for f in write1 write2 write4 write8; do
+for f in write1 write2 write4 write8 read2 read4 read8; do
check $f rsp 1
- check $f push 2
- check $f pop 12
+ check $f push 1
+ check $f pop 6
done
-for f in read1 read2 read4 read8; do
+for f in read1; do
check $f rsp 1
- check $f push 3
- check $f pop 18
+ check $f push 2
+ check $f pop 12
done
for f in func_entry func_exit; do
diff --git a/lib/tsan/go/buildgo.sh b/lib/tsan/go/buildgo.sh
index 7f570ca81..eec4cf15e 100755
--- a/lib/tsan/go/buildgo.sh
+++ b/lib/tsan/go/buildgo.sh
@@ -55,13 +55,19 @@ if [ "`uname -a | grep Linux`" != "" ]; then
"
if [ "`uname -a | grep ppc64le`" != "" ]; then
SUFFIX="linux_ppc64le"
+ ARCHCFLAGS="-m64"
elif [ "`uname -a | grep x86_64`" != "" ]; then
SUFFIX="linux_amd64"
+ ARCHCFLAGS="-m64"
OSCFLAGS="$OSCFLAGS -ffreestanding -Wno-unused-const-variable -Werror -Wno-unknown-warning-option"
+ elif [ "`uname -a | grep aarch64`" != "" ]; then
+ SUFFIX="linux_arm64"
+ ARCHCFLAGS=""
fi
elif [ "`uname -a | grep FreeBSD`" != "" ]; then
SUFFIX="freebsd_amd64"
OSCFLAGS="-fno-strict-aliasing -fPIC -Werror"
+ ARCHCFLAGS="-m64"
OSLDFLAGS="-lpthread -fPIC -fpie"
SRCS="
$SRCS
@@ -77,6 +83,7 @@ elif [ "`uname -a | grep FreeBSD`" != "" ]; then
elif [ "`uname -a | grep NetBSD`" != "" ]; then
SUFFIX="netbsd_amd64"
OSCFLAGS="-fno-strict-aliasing -fPIC -Werror"
+ ARCHCFLAGS="-m64"
OSLDFLAGS="-lpthread -fPIC -fpie"
SRCS="
$SRCS
@@ -87,11 +94,13 @@ elif [ "`uname -a | grep NetBSD`" != "" ]; then
../../sanitizer_common/sanitizer_procmaps_common.cc
../../sanitizer_common/sanitizer_linux.cc
../../sanitizer_common/sanitizer_linux_libcdep.cc
+ ../../sanitizer_common/sanitizer_netbsd.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 -mmacosx-version-min=10.7"
+ ARCHCFLAGS="-m64"
OSLDFLAGS="-lpthread -fPIC -fpie -mmacosx-version-min=10.7"
SRCS="
$SRCS
@@ -104,6 +113,7 @@ elif [ "`uname -a | grep Darwin`" != "" ]; then
elif [ "`uname -a | grep MINGW`" != "" ]; then
SUFFIX="windows_amd64"
OSCFLAGS="-Wno-error=attributes -Wno-attributes -Wno-unused-const-variable -Wno-unknown-warning-option"
+ ARCHCFLAGS="-m64"
OSLDFLAGS=""
SRCS="
$SRCS
@@ -136,7 +146,7 @@ for F in $SRCS; do
cat $F >> $DIR/gotsan.cc
done
-FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -std=c++11 -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO=1 -DSANITIZER_DEADLOCK_DETECTOR_VERSION=2 $OSCFLAGS"
+FLAGS=" -I../rtl -I../.. -I../../sanitizer_common -I../../../include -std=c++11 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO=1 -DSANITIZER_DEADLOCK_DETECTOR_VERSION=2 $OSCFLAGS $ARCHCFLAGS"
if [ "$DEBUG" = "" ]; then
FLAGS="$FLAGS -DSANITIZER_DEBUG=0 -O3 -fomit-frame-pointer"
if [ "$SUFFIX" = "linux_ppc64le" ]; then
@@ -153,7 +163,7 @@ if [ "$SILENT" != "1" ]; then
fi
$CC $DIR/gotsan.cc -c -o $DIR/race_$SUFFIX.syso $FLAGS $CFLAGS
-$CC $OSCFLAGS test.c $DIR/race_$SUFFIX.syso -m64 -g -o $DIR/test $OSLDFLAGS $LDFLAGS
+$CC $OSCFLAGS $ARCHCFLAGS test.c $DIR/race_$SUFFIX.syso -g -o $DIR/test $OSLDFLAGS $LDFLAGS
export GORACE="exitcode=0 atexit_sleep_ms=0"
if [ "$SILENT" != "1" ]; then
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
index 901997b3e..5e64d11f3 100644
--- a/lib/tsan/rtl/tsan_interceptors.cc
+++ b/lib/tsan/rtl/tsan_interceptors.cc
@@ -508,7 +508,8 @@ static void LongJmp(ThreadState *thr, uptr *env) {
uptr mangled_sp = env[6];
#elif SANITIZER_MAC
# ifdef __aarch64__
- uptr mangled_sp = env[13];
+ uptr mangled_sp =
+ (GetMacosVersion() >= MACOS_VERSION_MOJAVE) ? env[12] : env[13];
# else
uptr mangled_sp = env[2];
# endif
diff --git a/lib/tsan/rtl/tsan_libdispatch_mac.cc b/lib/tsan/rtl/tsan_libdispatch_mac.cc
index d6c1ca662..df22888b3 100644
--- a/lib/tsan/rtl/tsan_libdispatch_mac.cc
+++ b/lib/tsan/rtl/tsan_libdispatch_mac.cc
@@ -185,11 +185,8 @@ static void invoke_and_release_block(void *param) {
TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, \
DISPATCH_NOESCAPE dispatch_block_t block) { \
SCOPED_TSAN_INTERCEPTOR(name, q, block); \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
- dispatch_block_t heap_block = Block_copy(block); \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
tsan_block_context_t new_context = { \
- q, heap_block, &invoke_and_release_block, false, true, barrier, 0}; \
+ q, block, &invoke_block, false, true, barrier, 0}; \
Release(thr, pc, (uptr)&new_context); \
SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
REAL(name##_f)(q, &new_context, dispatch_callback_wrap); \
diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h
index 6b3e6bac2..70ae6170a 100644
--- a/lib/tsan/rtl/tsan_platform.h
+++ b/lib/tsan/rtl/tsan_platform.h
@@ -458,6 +458,32 @@ struct Mapping47 {
static const uptr kAppMemEnd = 0x00e000000000ull;
};
+#elif SANITIZER_GO && defined(__aarch64__)
+
+/* Go on linux/aarch64 (48-bit VMA)
+0000 0000 1000 - 0000 1000 0000: executable
+0000 1000 0000 - 00c0 0000 0000: -
+00c0 0000 0000 - 00e0 0000 0000: heap
+00e0 0000 0000 - 2000 0000 0000: -
+2000 0000 0000 - 3000 0000 0000: shadow
+3000 0000 0000 - 3000 0000 0000: -
+3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects)
+4000 0000 0000 - 6000 0000 0000: -
+6000 0000 0000 - 6200 0000 0000: traces
+6200 0000 0000 - 8000 0000 0000: -
+*/
+
+struct Mapping48 {
+ static const uptr kMetaShadowBeg = 0x300000000000ull;
+ static const uptr kMetaShadowEnd = 0x400000000000ull;
+ static const uptr kTraceMemBeg = 0x600000000000ull;
+ static const uptr kTraceMemEnd = 0x620000000000ull;
+ static const uptr kShadowBeg = 0x200000000000ull;
+ static const uptr kShadowEnd = 0x300000000000ull;
+ static const uptr kAppMemBeg = 0x000000001000ull;
+ static const uptr kAppMemEnd = 0x00e000000000ull;
+};
+
// Indicates the runtime will define the memory regions at runtime.
#define TSAN_RUNTIME_VMA 1
@@ -525,8 +551,10 @@ template<int Type>
uptr MappingArchImpl(void) {
#if defined(__aarch64__) && !defined(__APPLE__)
switch (vmaSize) {
+#if !SANITIZER_GO
case 39: return MappingImpl<Mapping39, Type>();
case 42: return MappingImpl<Mapping42, Type>();
+#endif
case 48: return MappingImpl<Mapping48, Type>();
}
DCHECK(0);
@@ -682,8 +710,10 @@ ALWAYS_INLINE
bool IsAppMem(uptr mem) {
#if defined(__aarch64__) && !defined(__APPLE__)
switch (vmaSize) {
+#if !SANITIZER_GO
case 39: return IsAppMemImpl<Mapping39>(mem);
case 42: return IsAppMemImpl<Mapping42>(mem);
+#endif
case 48: return IsAppMemImpl<Mapping48>(mem);
}
DCHECK(0);
@@ -713,8 +743,10 @@ ALWAYS_INLINE
bool IsShadowMem(uptr mem) {
#if defined(__aarch64__) && !defined(__APPLE__)
switch (vmaSize) {
+#if !SANITIZER_GO
case 39: return IsShadowMemImpl<Mapping39>(mem);
case 42: return IsShadowMemImpl<Mapping42>(mem);
+#endif
case 48: return IsShadowMemImpl<Mapping48>(mem);
}
DCHECK(0);
@@ -744,8 +776,10 @@ ALWAYS_INLINE
bool IsMetaMem(uptr mem) {
#if defined(__aarch64__) && !defined(__APPLE__)
switch (vmaSize) {
+#if !SANITIZER_GO
case 39: return IsMetaMemImpl<Mapping39>(mem);
case 42: return IsMetaMemImpl<Mapping42>(mem);
+#endif
case 48: return IsMetaMemImpl<Mapping48>(mem);
}
DCHECK(0);
@@ -785,8 +819,10 @@ ALWAYS_INLINE
uptr MemToShadow(uptr x) {
#if defined(__aarch64__) && !defined(__APPLE__)
switch (vmaSize) {
+#if !SANITIZER_GO
case 39: return MemToShadowImpl<Mapping39>(x);
case 42: return MemToShadowImpl<Mapping42>(x);
+#endif
case 48: return MemToShadowImpl<Mapping48>(x);
}
DCHECK(0);
@@ -828,8 +864,10 @@ ALWAYS_INLINE
u32 *MemToMeta(uptr x) {
#if defined(__aarch64__) && !defined(__APPLE__)
switch (vmaSize) {
+#if !SANITIZER_GO
case 39: return MemToMetaImpl<Mapping39>(x);
case 42: return MemToMetaImpl<Mapping42>(x);
+#endif
case 48: return MemToMetaImpl<Mapping48>(x);
}
DCHECK(0);
@@ -884,8 +922,10 @@ ALWAYS_INLINE
uptr ShadowToMem(uptr s) {
#if defined(__aarch64__) && !defined(__APPLE__)
switch (vmaSize) {
+#if !SANITIZER_GO
case 39: return ShadowToMemImpl<Mapping39>(s);
case 42: return ShadowToMemImpl<Mapping42>(s);
+#endif
case 48: return ShadowToMemImpl<Mapping48>(s);
}
DCHECK(0);
@@ -923,8 +963,10 @@ ALWAYS_INLINE
uptr GetThreadTrace(int tid) {
#if defined(__aarch64__) && !defined(__APPLE__)
switch (vmaSize) {
+#if !SANITIZER_GO
case 39: return GetThreadTraceImpl<Mapping39>(tid);
case 42: return GetThreadTraceImpl<Mapping42>(tid);
+#endif
case 48: return GetThreadTraceImpl<Mapping48>(tid);
}
DCHECK(0);
@@ -957,8 +999,10 @@ ALWAYS_INLINE
uptr GetThreadTraceHeader(int tid) {
#if defined(__aarch64__) && !defined(__APPLE__)
switch (vmaSize) {
+#if !SANITIZER_GO
case 39: return GetThreadTraceHeaderImpl<Mapping39>(tid);
case 42: return GetThreadTraceHeaderImpl<Mapping42>(tid);
+#endif
case 48: return GetThreadTraceHeaderImpl<Mapping48>(tid);
}
DCHECK(0);
diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc
index de989b780..d2ce60709 100644
--- a/lib/tsan/rtl/tsan_platform_linux.cc
+++ b/lib/tsan/rtl/tsan_platform_linux.cc
@@ -212,11 +212,19 @@ void InitializePlatformEarly() {
vmaSize =
(MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
#if defined(__aarch64__)
+# if !SANITIZER_GO
if (vmaSize != 39 && vmaSize != 42 && vmaSize != 48) {
Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
Printf("FATAL: Found %zd - Supported 39, 42 and 48\n", vmaSize);
Die();
}
+#else
+ if (vmaSize != 48) {
+ Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
+ Printf("FATAL: Found %zd - Supported 48\n", vmaSize);
+ Die();
+ }
+#endif
#elif defined(__powerpc64__)
# if !SANITIZER_GO
if (vmaSize != 44 && vmaSize != 46 && vmaSize != 47) {
diff --git a/lib/tsan/rtl/tsan_platform_mac.cc b/lib/tsan/rtl/tsan_platform_mac.cc
index f8d7324b7..7e3a47387 100644
--- a/lib/tsan/rtl/tsan_platform_mac.cc
+++ b/lib/tsan/rtl/tsan_platform_mac.cc
@@ -240,6 +240,9 @@ void InitializePlatformEarly() {
#endif
}
+static const uptr kPthreadSetjmpXorKeySlot = 0x7;
+extern "C" uptr __tsan_darwin_setjmp_xor_key = 0;
+
void InitializePlatform() {
DisableCoreDumperIfNecessary();
#if !SANITIZER_GO
@@ -251,6 +254,11 @@ void InitializePlatform() {
prev_pthread_introspection_hook =
pthread_introspection_hook_install(&my_pthread_introspection_hook);
#endif
+
+ if (GetMacosVersion() >= MACOS_VERSION_MOJAVE) {
+ __tsan_darwin_setjmp_xor_key =
+ (uptr)pthread_getspecific(kPthreadSetjmpXorKeySlot);
+ }
}
#if !SANITIZER_GO
diff --git a/lib/tsan/rtl/tsan_rtl_aarch64.S b/lib/tsan/rtl/tsan_rtl_aarch64.S
index 844c2e23d..3d02bf22f 100644
--- a/lib/tsan/rtl/tsan_rtl_aarch64.S
+++ b/lib/tsan/rtl/tsan_rtl_aarch64.S
@@ -120,8 +120,10 @@ ASM_SYMBOL_INTERCEPTOR(setjmp):
add x0, x29, 32
eor x1, x2, x0
#else
+ adrp x2, ___tsan_darwin_setjmp_xor_key@page
+ ldr x2, [x2, ___tsan_darwin_setjmp_xor_key@pageoff]
add x0, x29, 32
- mov x1, x0
+ eor x1, x2, x0
#endif
// call tsan interceptor
@@ -178,8 +180,10 @@ ASM_SYMBOL_INTERCEPTOR(_setjmp):
add x0, x29, 32
eor x1, x2, x0
#else
+ adrp x2, ___tsan_darwin_setjmp_xor_key@page
+ ldr x2, [x2, ___tsan_darwin_setjmp_xor_key@pageoff]
add x0, x29, 32
- mov x1, x0
+ eor x1, x2, x0
#endif
// call tsan interceptor
@@ -238,8 +242,10 @@ ASM_SYMBOL_INTERCEPTOR(sigsetjmp):
add x0, x29, 32
eor x1, x2, x0
#else
+ adrp x2, ___tsan_darwin_setjmp_xor_key@page
+ ldr x2, [x2, ___tsan_darwin_setjmp_xor_key@pageoff]
add x0, x29, 32
- mov x1, x0
+ eor x1, x2, x0
#endif
// call tsan interceptor
diff --git a/lib/tsan/rtl/tsan_rtl_amd64.S b/lib/tsan/rtl/tsan_rtl_amd64.S
index 8af61bf0e..34ef51c2a 100644
--- a/lib/tsan/rtl/tsan_rtl_amd64.S
+++ b/lib/tsan/rtl/tsan_rtl_amd64.S
@@ -196,6 +196,7 @@ ASM_SYMBOL_INTERCEPTOR(setjmp):
#elif defined(__APPLE__)
lea 16(%rsp), %rdi
mov %rdi, %rsi
+ xorq ___tsan_darwin_setjmp_xor_key(%rip), %rsi
#elif defined(__linux__)
lea 16(%rsp), %rdi
mov %rdi, %rsi
@@ -244,6 +245,7 @@ ASM_SYMBOL_INTERCEPTOR(_setjmp):
#elif defined(__APPLE__)
lea 16(%rsp), %rdi
mov %rdi, %rsi
+ xorq ___tsan_darwin_setjmp_xor_key(%rip), %rsi
#elif defined(__linux__)
lea 16(%rsp), %rdi
mov %rdi, %rsi
@@ -299,6 +301,7 @@ ASM_SYMBOL_INTERCEPTOR(sigsetjmp):
#elif defined(__APPLE__)
lea 32(%rsp), %rdi
mov %rdi, %rsi
+ xorq ___tsan_darwin_setjmp_xor_key(%rip), %rsi
#elif defined(__linux__)
lea 32(%rsp), %rdi
mov %rdi, %rsi
diff --git a/lib/xray/CMakeLists.txt b/lib/xray/CMakeLists.txt
index 8e18f5565..4bb25d454 100644
--- a/lib/xray/CMakeLists.txt
+++ b/lib/xray/CMakeLists.txt
@@ -147,7 +147,7 @@ if (APPLE)
add_compiler_rt_object_libraries(RTXray
OS ${XRAY_SUPPORTED_OS}
ARCHS ${XRAY_SUPPORTED_ARCH}
- SOURCES ${x86_64_SOURCES}
+ SOURCES ${XRAY_SOURCES} ${x86_64_SOURCES}
ADDITIONAL_HEADERS ${XRAY_IMPL_HEADERS}
CFLAGS ${XRAY_CFLAGS}
DEFS ${XRAY_COMMON_DEFINITIONS}
diff --git a/lib/xray/tests/CMakeLists.txt b/lib/xray/tests/CMakeLists.txt
index 11f373167..2f167e3ae 100644
--- a/lib/xray/tests/CMakeLists.txt
+++ b/lib/xray/tests/CMakeLists.txt
@@ -19,9 +19,16 @@ set(XRAY_UNITTEST_CFLAGS
${XRAY_CFLAGS}
${COMPILER_RT_UNITTEST_CFLAGS}
${COMPILER_RT_GTEST_CFLAGS}
+ ${COMPILER_RT_GMOCK_CFLAGS}
-I${COMPILER_RT_SOURCE_DIR}/include
-I${COMPILER_RT_SOURCE_DIR}/lib/xray
- -I${COMPILER_RT_SOURCE_DIR}/lib)
+ -I${COMPILER_RT_SOURCE_DIR}/lib
+ )
+
+# We add the include directories one at a time in our CFLAGS.
+foreach (DIR ${LLVM_INCLUDE_DIR} ${LLVM_MAIN_INCLUDE_DIR})
+ list(APPEND XRAY_UNITTEST_CFLAGS -I${DIR})
+endforeach()
function(add_xray_lib library)
add_library(${library} STATIC ${ARGN})
@@ -42,10 +49,27 @@ endfunction()
set(XRAY_TEST_ARCH ${XRAY_SUPPORTED_ARCH})
set(XRAY_UNITTEST_LINK_FLAGS
${CMAKE_THREAD_LIBS_INIT}
- -l${SANITIZER_CXX_ABI_LIBRARY}
- -fxray-instrument
- )
+ -l${SANITIZER_CXX_ABI_LIBRARY})
+
if (NOT APPLE)
+ # Needed by LLVMSupport.
+ append_list_if(
+ COMPILER_RT_HAS_TERMINFO
+ -l${COMPILER_RT_TERMINFO_LIB} XRAY_UNITTEST_LINK_FLAGS)
+
+ if (COMPILER_RT_STANDALONE_BUILD)
+ append_list_if(COMPILER_RT_HAS_LLVMXRAY ${LLVM_XRAY_LDFLAGS} XRAY_UNITTEST_LINK_FLAGS)
+ append_list_if(COMPILER_RT_HAS_LLVMXRAY ${LLVM_XRAY_LIBLIST} XRAY_UNITTEST_LINK_FLAGS)
+ else()
+ # We add the library directories one at a time in our CFLAGS.
+ foreach (DIR ${LLVM_LIBRARY_DIR})
+ list(APPEND XRAY_UNITTEST_LINK_FLAGS -L${DIR})
+ endforeach()
+
+ # We also add the actual libraries to link as dependencies.
+ list(APPEND XRAY_UNITTEST_LINK_FLAGS -lLLVMXRay -lLLVMSupport -lLLVMTestingSupport)
+ endif()
+
append_list_if(COMPILER_RT_HAS_LIBM -lm XRAY_UNITTEST_LINK_FLAGS)
append_list_if(COMPILER_RT_HAS_LIBRT -lrt XRAY_UNITTEST_LINK_FLAGS)
append_list_if(COMPILER_RT_HAS_LIBDL -ldl XRAY_UNITTEST_LINK_FLAGS)
@@ -62,17 +86,21 @@ macro(add_xray_unittest testname)
generate_compiler_rt_tests(TEST_OBJECTS
XRayUnitTests "${testname}-${arch}-Test" "${arch}"
SOURCES ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}
+ ${COMPILER_RT_GMOCK_SOURCE}
+
# Note that any change in the implementations will cause all the unit
# tests to be re-built. This is by design, but may be cumbersome during
# the build/test cycle.
COMPILE_DEPS ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}
${XRAY_HEADERS} ${XRAY_ALL_SOURCE_FILES_ABS_PATHS}
RUNTIME "${XRAY_RUNTIME_LIBS}"
- DEPS gtest xray llvm-xray
+ DEPS gtest xray llvm-xray LLVMXRay LLVMTestingSupport
CFLAGS ${XRAY_UNITTEST_CFLAGS}
- LINK_FLAGS ${TARGET_LINK_FLAGS} ${XRAY_UNITTEST_LINK_FLAGS})
+ LINK_FLAGS ${TARGET_LINK_FLAGS} ${XRAY_UNITTEST_LINK_FLAGS}
+ )
set_target_properties(XRayUnitTests
- PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
endforeach()
endif()
endmacro()
diff --git a/lib/xray/tests/unit/CMakeLists.txt b/lib/xray/tests/unit/CMakeLists.txt
index b42eb50d0..d0ead947d 100644
--- a/lib/xray/tests/unit/CMakeLists.txt
+++ b/lib/xray/tests/unit/CMakeLists.txt
@@ -1,12 +1,8 @@
-add_xray_unittest(XRayBufferQueueTest SOURCES
- buffer_queue_test.cc xray_unit_test_main.cc)
-add_xray_unittest(XRayFDRLoggingTest SOURCES
- fdr_logging_test.cc xray_unit_test_main.cc)
-add_xray_unittest(XRayAllocatorTest SOURCES
- allocator_test.cc xray_unit_test_main.cc)
-add_xray_unittest(XRaySegmentedArrayTest SOURCES
- segmented_array_test.cc xray_unit_test_main.cc)
-add_xray_unittest(XRayFunctionCallTrieTest SOURCES
- function_call_trie_test.cc xray_unit_test_main.cc)
-add_xray_unittest(XRayProfileCollectorTest SOURCES
- profile_collector_test.cc xray_unit_test_main.cc)
+add_xray_unittest(XRayTest SOURCES
+ buffer_queue_test.cc
+ allocator_test.cc
+ segmented_array_test.cc
+ function_call_trie_test.cc
+ profile_collector_test.cc
+ fdr_log_writer_test.cc
+ xray_unit_test_main.cc)
diff --git a/lib/xray/tests/unit/fdr_log_writer_test.cc b/lib/xray/tests/unit/fdr_log_writer_test.cc
new file mode 100644
index 000000000..3a2138cd8
--- /dev/null
+++ b/lib/xray/tests/unit/fdr_log_writer_test.cc
@@ -0,0 +1,92 @@
+//===-- fdr_log_writer_test.cc --------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of XRay, a function call tracing system.
+//
+//===----------------------------------------------------------------------===//
+#include <time.h>
+
+#include "xray/xray_records.h"
+#include "xray_fdr_log_writer.h"
+#include "llvm/Support/DataExtractor.h"
+#include "llvm/Testing/Support/Error.h"
+#include "llvm/XRay/Trace.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace __xray {
+namespace {
+
+static constexpr size_t kSize = 4096;
+
+using ::llvm::HasValue;
+using ::testing::Eq;
+using ::testing::SizeIs;
+
+// Exercise the common code path where we initialize a buffer and are able to
+// write some records successfully.
+TEST(FdrLogWriterTest, WriteSomeRecords) {
+ bool Success = false;
+ BufferQueue Buffers(kSize, 1, Success);
+ BufferQueue::Buffer B;
+ ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok);
+
+ FDRLogWriter Writer(B);
+ MetadataRecord Preamble[] = {
+ createMetadataRecord<MetadataRecord::RecordKinds::NewBuffer>(int32_t{1}),
+ createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>(
+ int64_t{1}, int32_t{2}),
+ createMetadataRecord<MetadataRecord::RecordKinds::Pid>(int32_t{1}),
+ };
+ ASSERT_THAT(Writer.writeMetadataRecords(Preamble),
+ Eq(sizeof(MetadataRecord) * 3));
+ ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(1));
+ ASSERT_TRUE(
+ Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Enter, 1, 1));
+ ASSERT_TRUE(
+ Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, 1, 1));
+ ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok);
+ ASSERT_EQ(B.Data, nullptr);
+ ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok);
+
+ // We then need to go through each element of the Buffers, and re-create a
+ // flat buffer that we would see if they were laid out in a file. This also
+ // means we need to write out the header manually.
+ // TODO: Isolate the file header writing.
+ std::string Serialized;
+ std::aligned_storage<sizeof(XRayFileHeader), alignof(XRayFileHeader)>::type
+ HeaderStorage;
+ auto *Header = reinterpret_cast<XRayFileHeader *>(&HeaderStorage);
+ new (Header) XRayFileHeader();
+ Header->Version = 3;
+ Header->Type = FileTypes::FDR_LOG;
+ Header->CycleFrequency = 3e9;
+ Header->ConstantTSC = 1;
+ Header->NonstopTSC = 1;
+ Serialized.append(reinterpret_cast<const char *>(&HeaderStorage),
+ sizeof(XRayFileHeader));
+ size_t BufferCount = 0;
+ Buffers.apply([&](const BufferQueue::Buffer &B) {
+ ++BufferCount;
+ auto Size = atomic_load_relaxed(&B.Extents);
+ auto Extents =
+ createMetadataRecord<MetadataRecord::RecordKinds::BufferExtents>(Size);
+ Serialized.append(reinterpret_cast<const char *>(&Extents),
+ sizeof(Extents));
+ Serialized.append(reinterpret_cast<const char *>(B.Data), Size);
+ });
+ ASSERT_EQ(BufferCount, 1u);
+
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(SizeIs(2)));
+}
+
+} // namespace
+} // namespace __xray
diff --git a/lib/xray/tests/unit/fdr_logging_test.cc b/lib/xray/tests/unit/fdr_logging_test.cc
deleted file mode 100644
index b6961efbc..000000000
--- a/lib/xray/tests/unit/fdr_logging_test.cc
+++ /dev/null
@@ -1,202 +0,0 @@
-//===-- fdr_logging_test.cc -----------------------------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of XRay, a function call tracing system.
-//
-//===----------------------------------------------------------------------===//
-#include "sanitizer_common/sanitizer_common.h"
-#include "xray_fdr_logging.h"
-#include "gtest/gtest.h"
-
-#include <array>
-#include <fcntl.h>
-#include <iostream>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <system_error>
-#include <thread>
-#include <unistd.h>
-
-#include "xray/xray_records.h"
-
-namespace __xray {
-namespace {
-
-constexpr auto kBufferSize = 16384;
-constexpr auto kBufferMax = 10;
-
-struct ScopedFileCloserAndDeleter {
- explicit ScopedFileCloserAndDeleter(int Fd, const char *Filename)
- : Fd(Fd), Filename(Filename) {}
-
- ~ScopedFileCloserAndDeleter() {
- if (Map)
- munmap(Map, Size);
- if (Fd) {
- close(Fd);
- unlink(Filename);
- }
- }
-
- void registerMap(void *M, size_t S) {
- Map = M;
- Size = S;
- }
-
- int Fd;
- const char *Filename;
- void *Map = nullptr;
- size_t Size = 0;
-};
-
-TEST(FDRLoggingTest, Simple) {
- FDRLoggingOptions Options;
- Options.ReportErrors = true;
- char TmpFilename[] = "fdr-logging-test.XXXXXX";
- Options.Fd = mkstemp(TmpFilename);
- ASSERT_NE(Options.Fd, -1);
- ASSERT_EQ(fdrLoggingInit(kBufferSize, kBufferMax, &Options,
- sizeof(FDRLoggingOptions)),
- XRayLogInitStatus::XRAY_LOG_INITIALIZED);
- fdrLoggingHandleArg0(1, XRayEntryType::ENTRY);
- fdrLoggingHandleArg0(1, XRayEntryType::EXIT);
- ASSERT_EQ(fdrLoggingFinalize(), XRayLogInitStatus::XRAY_LOG_FINALIZED);
- ASSERT_EQ(fdrLoggingFlush(), XRayLogFlushStatus::XRAY_LOG_FLUSHED);
-
- // To do this properly, we have to close the file descriptor then re-open the
- // file for reading this time.
- ASSERT_EQ(close(Options.Fd), 0);
- int Fd = open(TmpFilename, O_RDONLY);
- ASSERT_NE(-1, Fd);
- ScopedFileCloserAndDeleter Guard(Fd, TmpFilename);
- auto Size = lseek(Fd, 0, SEEK_END);
- ASSERT_NE(Size, 0);
- // Map the file contents.
- void *Map = mmap(NULL, Size, PROT_READ, MAP_PRIVATE, Fd, 0);
- const char *Contents = static_cast<const char *>(Map);
- Guard.registerMap(Map, Size);
- ASSERT_NE(Contents, nullptr);
-
- XRayFileHeader H;
- memcpy(&H, Contents, sizeof(XRayFileHeader));
- ASSERT_EQ(H.Version, 3);
- ASSERT_EQ(H.Type, FileTypes::FDR_LOG);
-
- // We require one buffer at least to have the "extents" metadata record,
- // followed by the NewBuffer record.
- MetadataRecord MDR0, MDR1;
- memcpy(&MDR0, Contents + sizeof(XRayFileHeader), sizeof(MetadataRecord));
- memcpy(&MDR1, Contents + sizeof(XRayFileHeader) + sizeof(MetadataRecord),
- sizeof(MetadataRecord));
- ASSERT_EQ(MDR0.RecordKind,
- uint8_t(MetadataRecord::RecordKinds::BufferExtents));
- ASSERT_EQ(MDR1.RecordKind, uint8_t(MetadataRecord::RecordKinds::NewBuffer));
-}
-
-TEST(FDRLoggingTest, Multiple) {
- FDRLoggingOptions Options;
- char TmpFilename[] = "fdr-logging-test.XXXXXX";
- Options.Fd = mkstemp(TmpFilename);
- ASSERT_NE(Options.Fd, -1);
- ASSERT_EQ(fdrLoggingInit(kBufferSize, kBufferMax, &Options,
- sizeof(FDRLoggingOptions)),
- XRayLogInitStatus::XRAY_LOG_INITIALIZED);
- for (uint64_t I = 0; I < 100; ++I) {
- fdrLoggingHandleArg0(1, XRayEntryType::ENTRY);
- fdrLoggingHandleArg0(1, XRayEntryType::EXIT);
- }
- ASSERT_EQ(fdrLoggingFinalize(), XRayLogInitStatus::XRAY_LOG_FINALIZED);
- ASSERT_EQ(fdrLoggingFlush(), XRayLogFlushStatus::XRAY_LOG_FLUSHED);
-
- // To do this properly, we have to close the file descriptor then re-open the
- // file for reading this time.
- ASSERT_EQ(close(Options.Fd), 0);
- int Fd = open(TmpFilename, O_RDONLY);
- ASSERT_NE(-1, Fd);
- ScopedFileCloserAndDeleter Guard(Fd, TmpFilename);
- auto Size = lseek(Fd, 0, SEEK_END);
- ASSERT_NE(Size, 0);
- // Map the file contents.
- void *Map = mmap(NULL, Size, PROT_READ, MAP_PRIVATE, Fd, 0);
- const char *Contents = static_cast<const char *>(Map);
- Guard.registerMap(Map, Size);
- ASSERT_NE(Contents, nullptr);
-
- XRayFileHeader H;
- memcpy(&H, Contents, sizeof(XRayFileHeader));
- ASSERT_EQ(H.Version, 3);
- ASSERT_EQ(H.Type, FileTypes::FDR_LOG);
-
- MetadataRecord MDR0, MDR1;
- memcpy(&MDR0, Contents + sizeof(XRayFileHeader), sizeof(MetadataRecord));
- memcpy(&MDR1, Contents + sizeof(XRayFileHeader) + sizeof(MetadataRecord),
- sizeof(MetadataRecord));
- ASSERT_EQ(MDR0.RecordKind,
- uint8_t(MetadataRecord::RecordKinds::BufferExtents));
- ASSERT_EQ(MDR1.RecordKind, uint8_t(MetadataRecord::RecordKinds::NewBuffer));
-}
-
-TEST(FDRLoggingTest, MultiThreadedCycling) {
- FDRLoggingOptions Options;
- char TmpFilename[] = "fdr-logging-test.XXXXXX";
- Options.Fd = mkstemp(TmpFilename);
- ASSERT_NE(Options.Fd, -1);
- ASSERT_EQ(fdrLoggingInit(kBufferSize, 1, &Options, sizeof(FDRLoggingOptions)),
- XRayLogInitStatus::XRAY_LOG_INITIALIZED);
-
- // Now we want to create one thread, do some logging, then create another one,
- // in succession and making sure that we're able to get thread records from
- // the latest thread (effectively being able to recycle buffers).
- std::array<tid_t, 2> Threads;
- for (uint64_t I = 0; I < 2; ++I) {
- std::thread t{[I, &Threads] {
- fdrLoggingHandleArg0(I + 1, XRayEntryType::ENTRY);
- fdrLoggingHandleArg0(I + 1, XRayEntryType::EXIT);
- Threads[I] = GetTid();
- }};
- t.join();
- }
- ASSERT_EQ(fdrLoggingFinalize(), XRayLogInitStatus::XRAY_LOG_FINALIZED);
- ASSERT_EQ(fdrLoggingFlush(), XRayLogFlushStatus::XRAY_LOG_FLUSHED);
-
- // To do this properly, we have to close the file descriptor then re-open the
- // file for reading this time.
- ASSERT_EQ(close(Options.Fd), 0);
- int Fd = open(TmpFilename, O_RDONLY);
- ASSERT_NE(-1, Fd);
- ScopedFileCloserAndDeleter Guard(Fd, TmpFilename);
- auto Size = lseek(Fd, 0, SEEK_END);
- ASSERT_NE(Size, 0);
- // Map the file contents.
- void *Map = mmap(NULL, Size, PROT_READ, MAP_PRIVATE, Fd, 0);
- const char *Contents = static_cast<const char *>(Map);
- Guard.registerMap(Map, Size);
- ASSERT_NE(Contents, nullptr);
-
- XRayFileHeader H;
- memcpy(&H, Contents, sizeof(XRayFileHeader));
- ASSERT_EQ(H.Version, 3);
- ASSERT_EQ(H.Type, FileTypes::FDR_LOG);
-
- MetadataRecord MDR0, MDR1;
- memcpy(&MDR0, Contents + sizeof(XRayFileHeader), sizeof(MetadataRecord));
- memcpy(&MDR1, Contents + sizeof(XRayFileHeader) + sizeof(MetadataRecord),
- sizeof(MetadataRecord));
- ASSERT_EQ(MDR0.RecordKind,
- uint8_t(MetadataRecord::RecordKinds::BufferExtents));
- ASSERT_EQ(MDR1.RecordKind, uint8_t(MetadataRecord::RecordKinds::NewBuffer));
- int32_t Latest = 0;
- memcpy(&Latest, MDR1.Data, sizeof(int32_t));
- ASSERT_EQ(Latest, static_cast<int32_t>(Threads[1]));
-}
-
-} // namespace
-} // namespace __xray
diff --git a/lib/xray/xray_allocator.h b/lib/xray/xray_allocator.h
index 824481528..f77bccbd9 100644
--- a/lib/xray/xray_allocator.h
+++ b/lib/xray/xray_allocator.h
@@ -20,18 +20,70 @@
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_posix.h"
+#include "xray_defs.h"
#include "xray_utils.h"
-#include <sys/mman.h>
#include <cstddef>
#include <cstdint>
-
-#ifndef MAP_NORESERVE
-// no-op on NetBSD (at least), unsupported flag on FreeBSD basically because unneeded
-#define MAP_NORESERVE 0
-#endif
+#include <sys/mman.h>
namespace __xray {
+// We implement our own memory allocation routine which will bypass the
+// internal allocator. This allows us to manage the memory directly, using
+// mmap'ed memory to back the allocators.
+template <class T> T *allocate() XRAY_NEVER_INSTRUMENT {
+ uptr RoundedSize = RoundUpTo(sizeof(T), GetPageSizeCached());
+ uptr B = internal_mmap(NULL, RoundedSize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ int ErrNo;
+ if (UNLIKELY(internal_iserror(B, &ErrNo))) {
+ if (Verbosity())
+ Report(
+ "XRay Profiling: Failed to allocate memory of size %d; Error = %d.\n",
+ RoundedSize, B);
+ return nullptr;
+ }
+ return reinterpret_cast<T *>(B);
+}
+
+template <class T> void deallocate(T *B) XRAY_NEVER_INSTRUMENT {
+ if (B == nullptr)
+ return;
+ uptr RoundedSize = RoundUpTo(sizeof(T), GetPageSizeCached());
+ internal_munmap(B, RoundedSize);
+}
+
+template <class T = uint8_t> T *allocateBuffer(size_t S) XRAY_NEVER_INSTRUMENT {
+ uptr RoundedSize = RoundUpTo(S * sizeof(T), GetPageSizeCached());
+ uptr B = internal_mmap(NULL, RoundedSize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ int ErrNo;
+ if (UNLIKELY(internal_iserror(B, &ErrNo))) {
+ if (Verbosity())
+ Report(
+ "XRay Profiling: Failed to allocate memory of size %d; Error = %d.\n",
+ RoundedSize, B);
+ return nullptr;
+ }
+ return reinterpret_cast<T *>(B);
+}
+
+template <class T> void deallocateBuffer(T *B, size_t S) XRAY_NEVER_INSTRUMENT {
+ if (B == nullptr)
+ return;
+ uptr RoundedSize = RoundUpTo(S * sizeof(T), GetPageSizeCached());
+ internal_munmap(B, RoundedSize);
+}
+
+template <class T, class... U>
+T *initArray(size_t N, U &&... Us) XRAY_NEVER_INSTRUMENT {
+ auto A = allocateBuffer<T>(N);
+ if (A != nullptr)
+ while (N > 0)
+ new (A + (--N)) T(std::forward<U>(Us)...);
+ return A;
+}
+
/// The Allocator type hands out fixed-sized chunks of memory that are
/// cache-line aligned and sized. This is useful for placement of
/// performance-sensitive data in memory that's frequently accessed. The
@@ -59,19 +111,16 @@ template <size_t N> struct Allocator {
private:
const size_t MaxMemory{0};
- void *BackingStore = nullptr;
- void *AlignedNextBlock = nullptr;
+ uint8_t *BackingStore = nullptr;
+ uint8_t *AlignedNextBlock = nullptr;
size_t AllocatedBlocks = 0;
SpinMutex Mutex{};
- void *Alloc() {
+ void *Alloc() XRAY_NEVER_INSTRUMENT {
SpinMutexLock Lock(&Mutex);
if (UNLIKELY(BackingStore == nullptr)) {
- BackingStore = reinterpret_cast<void *>(
- internal_mmap(NULL, MaxMemory, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, 0, 0));
- if (BackingStore == MAP_FAILED) {
- BackingStore = nullptr;
+ BackingStore = allocateBuffer(MaxMemory);
+ if (BackingStore == nullptr) {
if (Verbosity())
Report("XRay Profiling: Failed to allocate memory for allocator.\n");
return nullptr;
@@ -84,7 +133,7 @@ private:
auto AlignedNextBlockNum = nearest_boundary(
reinterpret_cast<uintptr_t>(AlignedNextBlock), kCacheLineSize);
if (diff(AlignedNextBlockNum, BackingStoreNum) > ptrdiff_t(MaxMemory)) {
- munmap(BackingStore, MaxMemory);
+ deallocateBuffer(BackingStore, MaxMemory);
AlignedNextBlock = BackingStore = nullptr;
if (Verbosity())
Report("XRay Profiling: Cannot obtain enough memory from "
@@ -92,7 +141,7 @@ private:
return nullptr;
}
- AlignedNextBlock = reinterpret_cast<void *>(AlignedNextBlockNum);
+ AlignedNextBlock = reinterpret_cast<uint8_t *>(AlignedNextBlockNum);
// Assert that AlignedNextBlock is cache-line aligned.
DCHECK_EQ(reinterpret_cast<uintptr_t>(AlignedNextBlock) % kCacheLineSize,
@@ -105,21 +154,21 @@ private:
// Align the pointer we'd like to return to an appropriate alignment, then
// advance the pointer from where to start allocations.
void *Result = AlignedNextBlock;
- AlignedNextBlock = reinterpret_cast<void *>(
- reinterpret_cast<char *>(AlignedNextBlock) + N);
+ AlignedNextBlock = reinterpret_cast<uint8_t *>(
+ reinterpret_cast<uint8_t *>(AlignedNextBlock) + N);
++AllocatedBlocks;
return Result;
}
public:
- explicit Allocator(size_t M)
+ explicit Allocator(size_t M) XRAY_NEVER_INSTRUMENT
: MaxMemory(nearest_boundary(M, kCacheLineSize)) {}
- Block Allocate() { return {Alloc()}; }
+ Block Allocate() XRAY_NEVER_INSTRUMENT { return {Alloc()}; }
- ~Allocator() NOEXCEPT {
+ ~Allocator() NOEXCEPT XRAY_NEVER_INSTRUMENT {
if (BackingStore != nullptr) {
- internal_munmap(BackingStore, MaxMemory);
+ deallocateBuffer(BackingStore, MaxMemory);
}
}
};
diff --git a/lib/xray/xray_basic_logging.cc b/lib/xray/xray_basic_logging.cc
index 585ca641c..ee28d598f 100644
--- a/lib/xray/xray_basic_logging.cc
+++ b/lib/xray/xray_basic_logging.cc
@@ -38,8 +38,9 @@
namespace __xray {
-SpinMutex LogMutex;
+static SpinMutex LogMutex;
+namespace {
// We use elements of this type to record the entry TSC of every function ID we
// see as we're tracing a particular thread's execution.
struct alignas(16) StackEntry {
@@ -62,11 +63,18 @@ struct alignas(64) ThreadLocalData {
int Fd = -1;
};
+struct BasicLoggingOptions {
+ int DurationFilterMicros = 0;
+ size_t MaxStackDepth = 0;
+ size_t ThreadBufferSize = 0;
+};
+} // namespace
+
static pthread_key_t PThreadKey;
static atomic_uint8_t BasicInitialized{0};
-BasicLoggingOptions GlobalOptions;
+struct BasicLoggingOptions GlobalOptions;
thread_local atomic_uint8_t Guard{0};
@@ -360,8 +368,8 @@ static void TLDDestructor(void *P) XRAY_NEVER_INSTRUMENT {
fsync(TLD.Fd);
}
-XRayLogInitStatus basicLoggingInit(size_t BufferSize, size_t BufferMax,
- void *Options,
+XRayLogInitStatus basicLoggingInit(UNUSED size_t BufferSize,
+ UNUSED size_t BufferMax, void *Options,
size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
uint8_t Expected = 0;
if (!atomic_compare_exchange_strong(&BasicInitialized, &Expected, 1,
@@ -385,43 +393,32 @@ XRayLogInitStatus basicLoggingInit(size_t BufferSize, size_t BufferMax,
"using emulation instead.\n");
});
- if (BufferSize == 0 && BufferMax == 0 && Options != nullptr) {
- FlagParser P;
- BasicFlags F;
- F.setDefaults();
- registerXRayBasicFlags(&P, &F);
- P.ParseString(useCompilerDefinedBasicFlags());
- auto *EnvOpts = GetEnv("XRAY_BASIC_OPTIONS");
- if (EnvOpts == nullptr)
- EnvOpts = "";
-
- P.ParseString(EnvOpts);
-
- // If XRAY_BASIC_OPTIONS was not defined, then we use the deprecated options
- // set through XRAY_OPTIONS instead.
- if (internal_strlen(EnvOpts) == 0) {
- F.func_duration_threshold_us =
- flags()->xray_naive_log_func_duration_threshold_us;
- F.max_stack_depth = flags()->xray_naive_log_max_stack_depth;
- F.thread_buffer_size = flags()->xray_naive_log_thread_buffer_size;
- }
-
- P.ParseString(static_cast<const char *>(Options));
- GlobalOptions.ThreadBufferSize = F.thread_buffer_size;
- GlobalOptions.DurationFilterMicros = F.func_duration_threshold_us;
- GlobalOptions.MaxStackDepth = F.max_stack_depth;
- *basicFlags() = F;
- } else if (OptionsSize != sizeof(BasicLoggingOptions)) {
- Report("Invalid options size, potential ABI mismatch; expected %d got %d",
- sizeof(BasicLoggingOptions), OptionsSize);
- return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
- } else {
- if (Verbosity())
- Report("XRay Basic: struct-based init is deprecated, please use "
- "string-based configuration instead.\n");
- GlobalOptions = *reinterpret_cast<BasicLoggingOptions *>(Options);
+ FlagParser P;
+ BasicFlags F;
+ F.setDefaults();
+ registerXRayBasicFlags(&P, &F);
+ P.ParseString(useCompilerDefinedBasicFlags());
+ auto *EnvOpts = GetEnv("XRAY_BASIC_OPTIONS");
+ if (EnvOpts == nullptr)
+ EnvOpts = "";
+
+ P.ParseString(EnvOpts);
+
+ // If XRAY_BASIC_OPTIONS was not defined, then we use the deprecated options
+ // set through XRAY_OPTIONS instead.
+ if (internal_strlen(EnvOpts) == 0) {
+ F.func_duration_threshold_us =
+ flags()->xray_naive_log_func_duration_threshold_us;
+ F.max_stack_depth = flags()->xray_naive_log_max_stack_depth;
+ F.thread_buffer_size = flags()->xray_naive_log_thread_buffer_size;
}
+ P.ParseString(static_cast<const char *>(Options));
+ GlobalOptions.ThreadBufferSize = F.thread_buffer_size;
+ GlobalOptions.DurationFilterMicros = F.func_duration_threshold_us;
+ GlobalOptions.MaxStackDepth = F.max_stack_depth;
+ *basicFlags() = F;
+
atomic_store(&ThresholdTicks,
atomic_load(&TicksPerSec, memory_order_acquire) *
GlobalOptions.DurationFilterMicros / 1000000,
diff --git a/lib/xray/xray_buffer_queue.cc b/lib/xray/xray_buffer_queue.cc
index 3ce728900..5a88ecd33 100644
--- a/lib/xray/xray_buffer_queue.cc
+++ b/lib/xray/xray_buffer_queue.cc
@@ -16,87 +16,42 @@
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_posix.h"
+#include "xray_allocator.h"
+#include "xray_defs.h"
#include <memory>
#include <sys/mman.h>
-#ifndef MAP_NORESERVE
-// no-op on NetBSD (at least), unsupported flag on FreeBSD
-#define MAP_NORESERVE 0
-#endif
-
using namespace __xray;
using namespace __sanitizer;
-template <class T> static T *allocRaw(size_t N) {
- // TODO: Report errors?
- // We use MAP_NORESERVE on platforms where it's supported to ensure that the
- // pages we're allocating for XRay never end up in pages that can be swapped
- // in/out. We're doing this because for FDR mode, we want to ensure that
- // writes to the buffers stay resident in memory to prevent XRay itself from
- // causing swapping/thrashing.
- //
- // In the case when XRay pages cannot be swapped in/out or there's not enough
- // RAM to back these pages, we're willing to cause a segmentation fault
- // instead of introducing latency in the measurement. We assume here that
- // there are enough pages that are swappable in/out outside of the buffers
- // being used by FDR mode (which are bounded and configurable anyway) to allow
- // us to keep using always-resident memory.
- //
- // TODO: Make this configurable?
- void *A = reinterpret_cast<void *>(
- internal_mmap(NULL, N * sizeof(T), PROT_WRITE | PROT_READ,
- MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0));
- return (A == MAP_FAILED) ? nullptr : reinterpret_cast<T *>(A);
-}
-
-template <class T> static void deallocRaw(T *ptr, size_t N) {
- // TODO: Report errors?
- if (ptr != nullptr)
- internal_munmap(ptr, N);
-}
-
-template <class T> static T *initArray(size_t N) {
- auto A = allocRaw<T>(N);
- if (A != nullptr)
- while (N > 0)
- new (A + (--N)) T();
- return A;
-}
-
-BufferQueue::BufferQueue(size_t B, size_t N, bool &Success)
- : BufferSize(B), Buffers(initArray<BufferQueue::BufferRep>(N)),
- BufferCount(N), Finalizing{0}, OwnedBuffers(initArray<void *>(N)),
- Next(Buffers), First(Buffers), LiveBuffers(0) {
- if (Buffers == nullptr) {
+BufferQueue::BufferQueue(size_t B, size_t N,
+ bool &Success) XRAY_NEVER_INSTRUMENT
+ : BufferSize(B),
+ BufferCount(N),
+ Mutex(),
+ Finalizing{0},
+ BackingStore(allocateBuffer(B *N)),
+ Buffers(initArray<BufferQueue::BufferRep>(N)),
+ Next(Buffers),
+ First(Buffers),
+ LiveBuffers(0) {
+ if (BackingStore == nullptr) {
Success = false;
return;
}
- if (OwnedBuffers == nullptr) {
- // Clean up the buffers we've already allocated.
- for (auto B = Buffers, E = Buffers + BufferCount; B != E; ++B)
- B->~BufferRep();
- deallocRaw(Buffers, N);
+ if (Buffers == nullptr) {
+ deallocateBuffer(BackingStore, BufferSize * BufferCount);
Success = false;
return;
- };
+ }
for (size_t i = 0; i < N; ++i) {
auto &T = Buffers[i];
- void *Tmp = allocRaw<char>(BufferSize);
- if (Tmp == nullptr) {
- Success = false;
- return;
- }
- auto *Extents = allocRaw<BufferExtents>(1);
- if (Extents == nullptr) {
- Success = false;
- return;
- }
auto &Buf = T.Buff;
- Buf.Data = Tmp;
+ Buf.Data = reinterpret_cast<char *>(BackingStore) + (BufferSize * i);
Buf.Size = B;
- Buf.Extents = Extents;
- OwnedBuffers[i] = Tmp;
+ atomic_store(&Buf.Extents, 0, memory_order_release);
+ T.Used = false;
}
Success = true;
}
@@ -104,13 +59,17 @@ BufferQueue::BufferQueue(size_t B, size_t N, bool &Success)
BufferQueue::ErrorCode BufferQueue::getBuffer(Buffer &Buf) {
if (atomic_load(&Finalizing, memory_order_acquire))
return ErrorCode::QueueFinalizing;
+
SpinMutexLock Guard(&Mutex);
if (LiveBuffers == BufferCount)
return ErrorCode::NotEnoughMemory;
auto &T = *Next;
auto &B = T.Buff;
- Buf = B;
+ auto Extents = atomic_load(&B.Extents, memory_order_acquire);
+ atomic_store(&Buf.Extents, Extents, memory_order_release);
+ Buf.Data = B.Data;
+ Buf.Size = B.Size;
T.Used = true;
++LiveBuffers;
@@ -121,15 +80,11 @@ BufferQueue::ErrorCode BufferQueue::getBuffer(Buffer &Buf) {
}
BufferQueue::ErrorCode BufferQueue::releaseBuffer(Buffer &Buf) {
- // Blitz through the buffers array to find the buffer.
- bool Found = false;
- for (auto I = OwnedBuffers, E = OwnedBuffers + BufferCount; I != E; ++I) {
- if (*I == Buf.Data) {
- Found = true;
- break;
- }
- }
- if (!Found)
+ // Check whether the buffer being referred to is within the bounds of the
+ // backing store's range.
+ if (Buf.Data < BackingStore ||
+ Buf.Data >
+ reinterpret_cast<char *>(BackingStore) + (BufferCount * BufferSize))
return ErrorCode::UnrecognizedBuffer;
SpinMutexLock Guard(&Mutex);
@@ -140,10 +95,14 @@ BufferQueue::ErrorCode BufferQueue::releaseBuffer(Buffer &Buf) {
return ErrorCode::NotEnoughMemory;
// Now that the buffer has been released, we mark it as "used".
- First->Buff = Buf;
+ auto Extents = atomic_load(&Buf.Extents, memory_order_acquire);
+ atomic_store(&First->Buff.Extents, Extents, memory_order_release);
+ First->Buff.Data = Buf.Data;
+ First->Buff.Size = Buf.Size;
First->Used = true;
Buf.Data = nullptr;
Buf.Size = 0;
+ atomic_store(&Buf.Extents, 0, memory_order_release);
--LiveBuffers;
if (++First == (Buffers + BufferCount))
First = Buffers;
@@ -158,14 +117,8 @@ BufferQueue::ErrorCode BufferQueue::finalize() {
}
BufferQueue::~BufferQueue() {
- for (auto I = Buffers, E = Buffers + BufferCount; I != E; ++I) {
- auto &T = *I;
- auto &Buf = T.Buff;
- deallocRaw(Buf.Data, Buf.Size);
- deallocRaw(Buf.Extents, 1);
- }
for (auto B = Buffers, E = Buffers + BufferCount; B != E; ++B)
B->~BufferRep();
- deallocRaw(Buffers, BufferCount);
- deallocRaw(OwnedBuffers, BufferCount);
+ deallocateBuffer(Buffers, BufferCount);
+ deallocateBuffer(BackingStore, BufferSize * BufferCount);
}
diff --git a/lib/xray/xray_buffer_queue.h b/lib/xray/xray_buffer_queue.h
index e76fa7983..c1fa9fab7 100644
--- a/lib/xray/xray_buffer_queue.h
+++ b/lib/xray/xray_buffer_queue.h
@@ -18,7 +18,9 @@
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_mutex.h"
+#include "xray_defs.h"
#include <cstddef>
+#include <cstdint>
namespace __xray {
@@ -29,14 +31,10 @@ namespace __xray {
/// trace collection.
class BufferQueue {
public:
- struct alignas(64) BufferExtents {
- atomic_uint64_t Size;
- };
-
struct Buffer {
+ atomic_uint64_t Extents{0};
void *Data = nullptr;
size_t Size = 0;
- BufferExtents *Extents;
};
struct BufferRep {
@@ -76,8 +74,10 @@ private:
T *operator->() const { return &(Buffers[Offset].Buff); }
- Iterator(BufferRep *Root, size_t O, size_t M)
- : Buffers(Root), Offset(O), Max(M) {
+ Iterator(BufferRep *Root, size_t O, size_t M) XRAY_NEVER_INSTRUMENT
+ : Buffers(Root),
+ Offset(O),
+ Max(M) {
// We want to advance to the first Offset where the 'Used' property is
// true, or to the end of the list/queue.
while (!Buffers[Offset].Used && Offset != Max) {
@@ -107,16 +107,18 @@ private:
// Size of each individual Buffer.
size_t BufferSize;
- BufferRep *Buffers;
-
// Amount of pre-allocated buffers.
size_t BufferCount;
SpinMutex Mutex;
atomic_uint8_t Finalizing;
- // Pointers to buffers managed/owned by the BufferQueue.
- void **OwnedBuffers;
+ // A pointer to a contiguous block of memory to serve as the backing store for
+ // all the individual buffers handed out.
+ uint8_t *BackingStore;
+
+ // A dynamically allocated array of BufferRep instances.
+ BufferRep *Buffers;
// Pointer to the next buffer to be handed out.
BufferRep *Next;
@@ -198,7 +200,7 @@ public:
/// Applies the provided function F to each Buffer in the queue, only if the
/// Buffer is marked 'used' (i.e. has been the result of getBuffer(...) and a
/// releaseBuffer(...) operation).
- template <class F> void apply(F Fn) {
+ template <class F> void apply(F Fn) XRAY_NEVER_INSTRUMENT {
SpinMutexLock G(&Mutex);
for (auto I = begin(), E = end(); I != E; ++I)
Fn(*I);
diff --git a/lib/xray/xray_fdr_log_records.h b/lib/xray/xray_fdr_log_records.h
index 87096d4fc..e7b1ee562 100644
--- a/lib/xray/xray_fdr_log_records.h
+++ b/lib/xray/xray_fdr_log_records.h
@@ -12,6 +12,9 @@
//===----------------------------------------------------------------------===//
#ifndef XRAY_XRAY_FDR_LOG_RECORDS_H
#define XRAY_XRAY_FDR_LOG_RECORDS_H
+#include <cstdint>
+
+namespace __xray {
enum class RecordType : uint8_t { Function, Metadata };
@@ -68,4 +71,6 @@ struct alignas(8) FunctionRecord {
static_assert(sizeof(FunctionRecord) == 8, "Wrong size for FunctionRecord.");
+} // namespace __xray
+
#endif // XRAY_XRAY_FDR_LOG_RECORDS_H
diff --git a/lib/xray/xray_fdr_log_writer.h b/lib/xray/xray_fdr_log_writer.h
new file mode 100644
index 000000000..28af356e7
--- /dev/null
+++ b/lib/xray/xray_fdr_log_writer.h
@@ -0,0 +1,119 @@
+//===-- xray_fdr_log_writer.h ---------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of XRay, a function call tracing system.
+//
+//===----------------------------------------------------------------------===//
+#ifndef COMPILER_RT_LIB_XRAY_XRAY_FDR_LOG_WRITER_H_
+#define COMPILER_RT_LIB_XRAY_XRAY_FDR_LOG_WRITER_H_
+
+#include "xray_buffer_queue.h"
+#include "xray_fdr_log_records.h"
+#include <functional>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+namespace __xray {
+
+template <size_t Index> struct SerializerImpl {
+ template <class Tuple,
+ typename std::enable_if<
+ Index<std::tuple_size<
+ typename std::remove_reference<Tuple>::type>::value,
+ int>::type = 0> static void serializeTo(char *Buffer,
+ Tuple &&T) {
+ auto P = reinterpret_cast<const char *>(&std::get<Index>(T));
+ constexpr auto Size = sizeof(std::get<Index>(T));
+ internal_memcpy(Buffer, P, Size);
+ SerializerImpl<Index + 1>::serializeTo(Buffer + Size,
+ std::forward<Tuple>(T));
+ }
+
+ template <class Tuple,
+ typename std::enable_if<
+ Index >= std::tuple_size<typename std::remove_reference<
+ Tuple>::type>::value,
+ int>::type = 0>
+ static void serializeTo(char *, Tuple &&){};
+};
+
+using Serializer = SerializerImpl<0>;
+
+template <MetadataRecord::RecordKinds Kind, class... DataTypes>
+MetadataRecord createMetadataRecord(DataTypes &&... Ds) {
+ MetadataRecord R;
+ R.Type = 1;
+ R.RecordKind = static_cast<uint8_t>(Kind);
+ Serializer::serializeTo(R.Data,
+ std::make_tuple(std::forward<DataTypes>(Ds)...));
+ return R;
+}
+
+class FDRLogWriter {
+ BufferQueue::Buffer &Buffer;
+ char *NextRecord = nullptr;
+
+ template <class T> void writeRecord(const T &R) {
+ internal_memcpy(NextRecord, reinterpret_cast<const char *>(&R), sizeof(T));
+ NextRecord += sizeof(T);
+ atomic_fetch_add(&Buffer.Extents, sizeof(T), memory_order_acq_rel);
+ }
+
+public:
+ explicit FDRLogWriter(BufferQueue::Buffer &B, char *P)
+ : Buffer(B), NextRecord(P) {
+ DCHECK_NE(Buffer.Data, nullptr);
+ DCHECK_NE(NextRecord, nullptr);
+ }
+
+ explicit FDRLogWriter(BufferQueue::Buffer &B)
+ : FDRLogWriter(B, static_cast<char *>(B.Data)) {}
+
+ template <MetadataRecord::RecordKinds Kind, class... Data>
+ bool writeMetadata(Data &&... Ds) {
+ // TODO: Check boundary conditions:
+ // 1) Buffer is full, and cannot handle one metadata record.
+ // 2) Buffer queue is finalising.
+ writeRecord(createMetadataRecord<Kind>(std::forward<Data>(Ds)...));
+ return true;
+ }
+
+ template <size_t N> size_t writeMetadataRecords(MetadataRecord (&Recs)[N]) {
+ constexpr auto Size = sizeof(MetadataRecord) * N;
+ internal_memcpy(NextRecord, reinterpret_cast<const char *>(Recs), Size);
+ NextRecord += Size;
+ atomic_fetch_add(&Buffer.Extents, Size, memory_order_acq_rel);
+ return Size;
+ }
+
+ enum class FunctionRecordKind : uint8_t {
+ Enter = 0x00,
+ Exit = 0x01,
+ TailExit = 0x02,
+ EnterArg = 0x03,
+ };
+
+ bool writeFunction(FunctionRecordKind Kind, int32_t FuncId, int32_t Delta) {
+ FunctionRecord R;
+ R.Type = 0;
+ R.RecordKind = uint8_t(Kind);
+ R.FuncId = FuncId;
+ R.TSCDelta = Delta;
+ writeRecord(R);
+ return true;
+ }
+
+ char *getNextRecord() const { return NextRecord; }
+
+}; // namespace __xray
+
+} // namespace __xray
+
+#endif // COMPILER-RT_LIB_XRAY_XRAY_FDR_LOG_WRITER_H_
diff --git a/lib/xray/xray_fdr_logging.cc b/lib/xray/xray_fdr_logging.cc
index 6cb2dfa0c..2d6af443d 100644
--- a/lib/xray/xray_fdr_logging.cc
+++ b/lib/xray/xray_fdr_logging.cc
@@ -30,9 +30,11 @@
#include "sanitizer_common/sanitizer_common.h"
#include "xray/xray_interface.h"
#include "xray/xray_records.h"
+#include "xray_allocator.h"
#include "xray_buffer_queue.h"
#include "xray_defs.h"
#include "xray_fdr_flags.h"
+#include "xray_fdr_log_writer.h"
#include "xray_flags.h"
#include "xray_recursion_guard.h"
#include "xray_tsc.h"
@@ -40,14 +42,16 @@
namespace __xray {
-atomic_sint32_t LoggingStatus = {XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
+static atomic_sint32_t LoggingStatus = {
+ XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
+namespace {
// Group together thread-local-data in a struct, then hide it behind a function
// call so that it can be initialized on first use instead of as a global. We
// force the alignment to 64-bytes for x86 cache line alignment, as this
// structure is used in the hot path of implementation.
struct alignas(64) ThreadLocalData {
- BufferQueue::Buffer Buffer;
+ BufferQueue::Buffer Buffer{};
char *RecordPtr = nullptr;
// The number of FunctionEntry records immediately preceding RecordPtr.
uint8_t NumConsecutiveFnEnters = 0;
@@ -70,6 +74,7 @@ struct alignas(64) ThreadLocalData {
// FDRLogging, and that we're going to clean it up when the thread exits.
BufferQueue *BQ = nullptr;
};
+} // namespace
static_assert(std::is_trivially_destructible<ThreadLocalData>::value,
"ThreadLocalData must be trivially destructible");
@@ -81,15 +86,12 @@ static constexpr auto FunctionRecSize = sizeof(FunctionRecord);
static pthread_key_t Key;
// Global BufferQueue.
+static std::aligned_storage<sizeof(BufferQueue)>::type BufferQueueStorage;
static BufferQueue *BQ = nullptr;
static atomic_sint32_t LogFlushStatus = {
XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
-static FDRLoggingOptions FDROptions;
-
-static SpinMutex FDROptionsMutex;
-
// This function will initialize the thread-local data structure used by the FDR
// logging implementation and return a reference to it. The implementation
// details require a bit of care to maintain.
@@ -140,60 +142,35 @@ static ThreadLocalData &getThreadLocalData() {
static void writeNewBufferPreamble(tid_t Tid, timespec TS,
pid_t Pid) XRAY_NEVER_INSTRUMENT {
- static constexpr int InitRecordsCount = 3;
+ static_assert(sizeof(time_t) <= 8, "time_t needs to be at most 8 bytes");
auto &TLD = getThreadLocalData();
- MetadataRecord Metadata[InitRecordsCount];
- {
- // Write out a MetadataRecord to signify that this is the start of a new
- // buffer, associated with a particular thread, with a new CPU. For the
- // data, we have 15 bytes to squeeze as much information as we can. At this
- // point we only write down the following bytes:
- // - Thread ID (tid_t, cast to 4 bytes type due to Darwin being 8 bytes)
- auto &NewBuffer = Metadata[0];
- NewBuffer.Type = uint8_t(RecordType::Metadata);
- NewBuffer.RecordKind = uint8_t(MetadataRecord::RecordKinds::NewBuffer);
- int32_t tid = static_cast<int32_t>(Tid);
- internal_memcpy(&NewBuffer.Data, &tid, sizeof(tid));
- }
-
- // Also write the WalltimeMarker record.
- {
- static_assert(sizeof(time_t) <= 8, "time_t needs to be at most 8 bytes");
- auto &WalltimeMarker = Metadata[1];
- WalltimeMarker.Type = uint8_t(RecordType::Metadata);
- WalltimeMarker.RecordKind =
- uint8_t(MetadataRecord::RecordKinds::WalltimeMarker);
-
- // We only really need microsecond precision here, and enforce across
- // platforms that we need 64-bit seconds and 32-bit microseconds encoded in
- // the Metadata record.
- int32_t Micros = TS.tv_nsec / 1000;
- int64_t Seconds = TS.tv_sec;
- internal_memcpy(WalltimeMarker.Data, &Seconds, sizeof(Seconds));
- internal_memcpy(WalltimeMarker.Data + sizeof(Seconds), &Micros,
- sizeof(Micros));
- }
-
- // Also write the Pid record.
- {
- // Write out a MetadataRecord that contains the current pid
- auto &PidMetadata = Metadata[2];
- PidMetadata.Type = uint8_t(RecordType::Metadata);
- PidMetadata.RecordKind = uint8_t(MetadataRecord::RecordKinds::Pid);
- int32_t pid = static_cast<int32_t>(Pid);
- internal_memcpy(&PidMetadata.Data, &pid, sizeof(pid));
- }
+ MetadataRecord Metadata[] = {
+ // Write out a MetadataRecord to signify that this is the start of a new
+ // buffer, associated with a particular thread, with a new CPU. For the
+ // data, we have 15 bytes to squeeze as much information as we can. At
+ // this point we only write down the following bytes:
+ // - Thread ID (tid_t, cast to 4 bytes type due to Darwin being 8 bytes)
+ createMetadataRecord<MetadataRecord::RecordKinds::NewBuffer>(
+ static_cast<int32_t>(Tid)),
+
+ // Also write the WalltimeMarker record. We only really need microsecond
+ // precision here, and enforce across platforms that we need 64-bit
+ // seconds and 32-bit microseconds encoded in the Metadata record.
+ createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>(
+ static_cast<int64_t>(TS.tv_sec),
+ static_cast<int32_t>(TS.tv_nsec / 1000)),
+
+ // Also write the Pid record.
+ createMetadataRecord<MetadataRecord::RecordKinds::Pid>(
+ static_cast<int32_t>(Pid)),
+ };
TLD.NumConsecutiveFnEnters = 0;
TLD.NumTailCalls = 0;
if (TLD.BQ == nullptr || TLD.BQ->finalizing())
return;
- internal_memcpy(TLD.RecordPtr, Metadata, sizeof(Metadata));
- TLD.RecordPtr += sizeof(Metadata);
- // Since we write out the extents as the first metadata record of the
- // buffer, we need to write out the extents including the extents record.
- atomic_store(&TLD.Buffer.Extents->Size, sizeof(Metadata),
- memory_order_release);
+ FDRLogWriter Writer(TLD.Buffer);
+ TLD.RecordPtr += Writer.writeMetadataRecords(Metadata);
}
static void setupNewBuffer(int (*wall_clock_reader)(
@@ -201,6 +178,7 @@ static void setupNewBuffer(int (*wall_clock_reader)(
auto &TLD = getThreadLocalData();
auto &B = TLD.Buffer;
TLD.RecordPtr = static_cast<char *>(B.Data);
+ atomic_store(&B.Extents, 0, memory_order_release);
tid_t Tid = GetTid();
timespec TS{0, 0};
pid_t Pid = internal_getpid();
@@ -213,91 +191,90 @@ static void setupNewBuffer(int (*wall_clock_reader)(
static void incrementExtents(size_t Add) {
auto &TLD = getThreadLocalData();
- atomic_fetch_add(&TLD.Buffer.Extents->Size, Add, memory_order_acq_rel);
+ atomic_fetch_add(&TLD.Buffer.Extents, Add, memory_order_acq_rel);
}
static void decrementExtents(size_t Subtract) {
auto &TLD = getThreadLocalData();
- atomic_fetch_sub(&TLD.Buffer.Extents->Size, Subtract, memory_order_acq_rel);
+ atomic_fetch_sub(&TLD.Buffer.Extents, Subtract, memory_order_acq_rel);
}
static void writeNewCPUIdMetadata(uint16_t CPU,
uint64_t TSC) XRAY_NEVER_INSTRUMENT {
auto &TLD = getThreadLocalData();
- MetadataRecord NewCPUId;
- NewCPUId.Type = uint8_t(RecordType::Metadata);
- NewCPUId.RecordKind = uint8_t(MetadataRecord::RecordKinds::NewCPUId);
+ FDRLogWriter W(TLD.Buffer, TLD.RecordPtr);
// The data for the New CPU will contain the following bytes:
// - CPU ID (uint16_t, 2 bytes)
// - Full TSC (uint64_t, 8 bytes)
// Total = 10 bytes.
- internal_memcpy(&NewCPUId.Data, &CPU, sizeof(CPU));
- internal_memcpy(&NewCPUId.Data[sizeof(CPU)], &TSC, sizeof(TSC));
- internal_memcpy(TLD.RecordPtr, &NewCPUId, sizeof(MetadataRecord));
- TLD.RecordPtr += sizeof(MetadataRecord);
+ W.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(CPU, TSC);
+ TLD.RecordPtr = W.getNextRecord();
TLD.NumConsecutiveFnEnters = 0;
TLD.NumTailCalls = 0;
- incrementExtents(sizeof(MetadataRecord));
}
static void writeTSCWrapMetadata(uint64_t TSC) XRAY_NEVER_INSTRUMENT {
auto &TLD = getThreadLocalData();
- MetadataRecord TSCWrap;
- TSCWrap.Type = uint8_t(RecordType::Metadata);
- TSCWrap.RecordKind = uint8_t(MetadataRecord::RecordKinds::TSCWrap);
+ FDRLogWriter W(TLD.Buffer, TLD.RecordPtr);
// The data for the TSCWrap record contains the following bytes:
// - Full TSC (uint64_t, 8 bytes)
// Total = 8 bytes.
- internal_memcpy(&TSCWrap.Data, &TSC, sizeof(TSC));
- internal_memcpy(TLD.RecordPtr, &TSCWrap, sizeof(MetadataRecord));
- TLD.RecordPtr += sizeof(MetadataRecord);
+ W.writeMetadata<MetadataRecord::RecordKinds::TSCWrap>(TSC);
+ TLD.RecordPtr = W.getNextRecord();
TLD.NumConsecutiveFnEnters = 0;
TLD.NumTailCalls = 0;
- incrementExtents(sizeof(MetadataRecord));
}
// Call Argument metadata records store the arguments to a function in the
// order of their appearance; holes are not supported by the buffer format.
static void writeCallArgumentMetadata(uint64_t A) XRAY_NEVER_INSTRUMENT {
auto &TLD = getThreadLocalData();
- MetadataRecord CallArg;
- CallArg.Type = uint8_t(RecordType::Metadata);
- CallArg.RecordKind = uint8_t(MetadataRecord::RecordKinds::CallArgument);
-
- internal_memcpy(CallArg.Data, &A, sizeof(A));
- internal_memcpy(TLD.RecordPtr, &CallArg, sizeof(MetadataRecord));
- TLD.RecordPtr += sizeof(MetadataRecord);
- incrementExtents(sizeof(MetadataRecord));
+ FDRLogWriter W(TLD.Buffer, TLD.RecordPtr);
+ W.writeMetadata<MetadataRecord::RecordKinds::CallArgument>(A);
+ TLD.RecordPtr = W.getNextRecord();
}
-static void writeFunctionRecord(int FuncId, uint32_t TSCDelta,
+static void writeFunctionRecord(int32_t FuncId, uint32_t TSCDelta,
XRayEntryType EntryType) XRAY_NEVER_INSTRUMENT {
- FunctionRecord FuncRecord;
- FuncRecord.Type = uint8_t(RecordType::Function);
- // Only take 28 bits of the function id.
- FuncRecord.FuncId = FuncId & ~(0x0F << 28);
- FuncRecord.TSCDelta = TSCDelta;
+ constexpr int32_t MaxFuncId = (1 << 29) - 1;
+ if (UNLIKELY(FuncId > MaxFuncId)) {
+ if (Verbosity())
+ Report("Warning: Function ID '%d' > max function id: '%d'", FuncId,
+ MaxFuncId);
+ return;
+ }
auto &TLD = getThreadLocalData();
+ FDRLogWriter W(TLD.Buffer, TLD.RecordPtr);
+
+ // Only take 28 bits of the function id.
+ //
+ // We need to be careful about the sign bit and the bitwise operations being
+ // performed here. In effect, we want to truncate the value of the function id
+ // to the first 28 bits. To do this properly, this means we need to mask the
+ // function id with (2 ^ 28) - 1 == 0x0fffffff.
+ //
+ auto TruncatedId = FuncId & MaxFuncId;
+ auto Kind = FDRLogWriter::FunctionRecordKind::Enter;
+
switch (EntryType) {
case XRayEntryType::ENTRY:
++TLD.NumConsecutiveFnEnters;
- FuncRecord.RecordKind = uint8_t(FunctionRecord::RecordKinds::FunctionEnter);
break;
case XRayEntryType::LOG_ARGS_ENTRY:
// We should not rewind functions with logged args.
TLD.NumConsecutiveFnEnters = 0;
TLD.NumTailCalls = 0;
- FuncRecord.RecordKind = uint8_t(FunctionRecord::RecordKinds::FunctionEnter);
+ Kind = FDRLogWriter::FunctionRecordKind::EnterArg;
break;
case XRayEntryType::EXIT:
// If we've decided to log the function exit, we will never erase the log
// before it.
TLD.NumConsecutiveFnEnters = 0;
TLD.NumTailCalls = 0;
- FuncRecord.RecordKind = uint8_t(FunctionRecord::RecordKinds::FunctionExit);
+ Kind = FDRLogWriter::FunctionRecordKind::Exit;
break;
case XRayEntryType::TAIL:
// If we just entered the function we're tail exiting from or erased every
@@ -312,8 +289,7 @@ static void writeFunctionRecord(int FuncId, uint32_t TSCDelta,
TLD.NumTailCalls = 0;
TLD.NumConsecutiveFnEnters = 0;
}
- FuncRecord.RecordKind =
- uint8_t(FunctionRecord::RecordKinds::FunctionTailExit);
+ Kind = FDRLogWriter::FunctionRecordKind::TailExit;
break;
case XRayEntryType::CUSTOM_EVENT: {
// This is a bug in patching, so we'll report it once and move on.
@@ -334,9 +310,8 @@ static void writeFunctionRecord(int FuncId, uint32_t TSCDelta,
}
}
- internal_memcpy(TLD.RecordPtr, &FuncRecord, sizeof(FunctionRecord));
- TLD.RecordPtr += sizeof(FunctionRecord);
- incrementExtents(sizeof(FunctionRecord));
+ W.writeFunction(Kind, TruncatedId, TSCDelta);
+ TLD.RecordPtr = W.getNextRecord();
}
static atomic_uint64_t TicksPerSec{0};
@@ -345,7 +320,8 @@ static atomic_uint64_t ThresholdTicks{0};
// Re-point the thread local pointer into this thread's Buffer before the recent
// "Function Entry" record and any "Tail Call Exit" records after that.
static void rewindRecentCall(uint64_t TSC, uint64_t &LastTSC,
- uint64_t &LastFunctionEntryTSC, int32_t FuncId) {
+ uint64_t &LastFunctionEntryTSC,
+ int32_t FuncId) XRAY_NEVER_INSTRUMENT {
auto &TLD = getThreadLocalData();
TLD.RecordPtr -= FunctionRecSize;
decrementExtents(FunctionRecSize);
@@ -410,6 +386,9 @@ static void rewindRecentCall(uint64_t TSC, uint64_t &LastTSC,
static bool releaseThreadLocalBuffer(BufferQueue &BQArg) {
auto &TLD = getThreadLocalData();
auto EC = BQArg.releaseBuffer(TLD.Buffer);
+ if (TLD.Buffer.Data == nullptr)
+ return true;
+
if (EC != BufferQueue::ErrorCode::Ok) {
Report("Failed to release buffer at %p; error=%s\n", TLD.Buffer.Data,
BufferQueue::getErrorString(EC));
@@ -521,6 +500,7 @@ static uint32_t writeCurrentCPUTSC(ThreadLocalData &TLD, uint64_t TSC,
writeNewCPUIdMetadata(CPU, TSC);
return 0;
}
+
// If the delta is greater than the range for a uint32_t, then we write out
// the TSC wrap metadata entry with the full TSC, and the TSC for the
// function record be 0.
@@ -570,7 +550,7 @@ static void processFunctionHook(int32_t FuncId, XRayEntryType Entry,
auto &TLD = getThreadLocalData();
- if (TLD.BQ == nullptr)
+ if (TLD.BQ == nullptr && BQ != nullptr)
TLD.BQ = BQ;
if (!isLogInitializedAndReady(TLD.BQ, TSC, CPU, wall_clock_reader))
@@ -596,14 +576,16 @@ static void processFunctionHook(int32_t FuncId, XRayEntryType Entry,
// 1. When the delta between the TSC we get and the previous TSC for the
// same CPU is outside of the uint32_t range, we end up having to
// write a MetadataRecord to indicate a "tsc wrap" before the actual
- // FunctionRecord.
+ // FunctionRecord. This means we have: 1 MetadataRecord + 1 Function
+ // Record.
// 2. When we learn that we've moved CPUs, we need to write a
// MetadataRecord to indicate a "cpu change", and thus write out the
// current TSC for that CPU before writing out the actual
- // FunctionRecord.
- // 3. When we learn about a new CPU ID, we need to write down a "new cpu
- // id" MetadataRecord before writing out the actual FunctionRecord.
- // 4. The second MetadataRecord is the optional function call argument.
+ // FunctionRecord. This means we have: 1 MetadataRecord + 1 Function
+ // Record.
+ // 3. Given the previous two cases, in addition we can add at most one
+ // function argument record. This means we have: 2 MetadataRecord + 1
+ // Function Record.
//
// So the math we need to do is to determine whether writing 40 bytes past the
// current pointer exceeds the buffer's maximum size. If we don't have enough
@@ -615,20 +597,21 @@ static void processFunctionHook(int32_t FuncId, XRayEntryType Entry,
return;
}
- // By this point, we are now ready to write up to 40 bytes (explained above).
- DCHECK((TLD.RecordPtr + MaxSize) - static_cast<char *>(TLD.Buffer.Data) >=
- static_cast<ptrdiff_t>(MetadataRecSize) &&
- "Misconfigured BufferQueue provided; Buffer size not large enough.");
-
auto RecordTSCDelta = writeCurrentCPUTSC(TLD, TSC, CPU);
TLD.LastTSC = TSC;
TLD.CurrentCPU = CPU;
switch (Entry) {
case XRayEntryType::ENTRY:
- case XRayEntryType::LOG_ARGS_ENTRY:
// Update the thread local state for the next invocation.
TLD.LastFunctionEntryTSC = TSC;
break;
+ case XRayEntryType::LOG_ARGS_ENTRY:
+ // Update the thread local state for the next invocation, but also prevent
+ // rewinding when we have arguments logged.
+ TLD.LastFunctionEntryTSC = TSC;
+ TLD.NumConsecutiveFnEnters = 0;
+ TLD.NumTailCalls = 0;
+ break;
case XRayEntryType::TAIL:
case XRayEntryType::EXIT:
// Break out and write the exit record if we can't erase any functions.
@@ -741,7 +724,8 @@ XRayBuffer fdrIterator(const XRayBuffer B) {
static BufferQueue::const_iterator It{};
static BufferQueue::const_iterator End{};
- static void *CurrentBuffer{nullptr};
+ static uint8_t *CurrentBuffer{nullptr};
+ static size_t SerializedBufferSize = 0;
if (B.Data == static_cast<void *>(&Header) && B.Size == sizeof(Header)) {
// From this point on, we provide raw access to the raw buffer we're getting
// from the BufferQueue. We're relying on the iterators from the current
@@ -751,7 +735,7 @@ XRayBuffer fdrIterator(const XRayBuffer B) {
}
if (CurrentBuffer != nullptr) {
- InternalFree(CurrentBuffer);
+ deallocateBuffer(CurrentBuffer, SerializedBufferSize);
CurrentBuffer = nullptr;
}
@@ -762,9 +746,9 @@ XRayBuffer fdrIterator(const XRayBuffer B) {
// out to disk. The difference here would be that we still write "empty"
// buffers, or at least go through the iterators faithfully to let the
// handlers see the empty buffers in the queue.
- auto BufferSize = atomic_load(&It->Extents->Size, memory_order_acquire);
- auto SerializedBufferSize = BufferSize + sizeof(MetadataRecord);
- CurrentBuffer = InternalAlloc(SerializedBufferSize);
+ auto BufferSize = atomic_load(&It->Extents, memory_order_acquire);
+ SerializedBufferSize = BufferSize + sizeof(MetadataRecord);
+ CurrentBuffer = allocateBuffer(SerializedBufferSize);
if (CurrentBuffer == nullptr)
return {nullptr, 0};
@@ -832,7 +816,6 @@ XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
if (TLD.RecordPtr != nullptr && TLD.BQ != nullptr)
releaseThreadLocalBuffer(*TLD.BQ);
BQ->~BufferQueue();
- InternalFree(BQ);
BQ = nullptr;
}
});
@@ -855,15 +838,7 @@ XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
// (fixed-sized) and let the tools reading the buffers deal with the data
// afterwards.
//
- int Fd = -1;
- {
- // FIXME: Remove this section of the code, when we remove the struct-based
- // configuration API.
- SpinMutexLock Guard(&FDROptionsMutex);
- Fd = FDROptions.Fd;
- }
- if (Fd == -1)
- Fd = getLogFD();
+ int Fd = getLogFD();
if (Fd == -1) {
auto Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
atomic_store(&LogFlushStatus, Result, memory_order_release);
@@ -875,6 +850,13 @@ XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
retryingWriteAll(Fd, reinterpret_cast<char *>(&Header),
reinterpret_cast<char *>(&Header) + sizeof(Header));
+ // Release the current thread's buffer before we attempt to write out all the
+ // buffers. This ensures that in case we had only a single thread going, that
+ // we are able to capture the data nonetheless.
+ auto &TLD = getThreadLocalData();
+ if (TLD.RecordPtr != nullptr && TLD.BQ != nullptr)
+ releaseThreadLocalBuffer(*TLD.BQ);
+
BQ->apply([&](const BufferQueue::Buffer &B) {
// Starting at version 2 of the FDR logging implementation, we only write
// the records identified by the extents of the buffer. We use the Extents
@@ -882,7 +864,7 @@ XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
// still use a Metadata record, but fill in the extents instead for the
// data.
MetadataRecord ExtentsRecord;
- auto BufferExtents = atomic_load(&B.Extents->Size, memory_order_acquire);
+ auto BufferExtents = atomic_load(&B.Extents, memory_order_acquire);
DCHECK(BufferExtents <= B.Size);
ExtentsRecord.Type = uint8_t(RecordType::Metadata);
ExtentsRecord.RecordKind =
@@ -914,7 +896,12 @@ XRayLogInitStatus fdrLoggingFinalize() XRAY_NEVER_INSTRUMENT {
// Do special things to make the log finalize itself, and not allow any more
// operations to be performed until re-initialized.
- BQ->finalize();
+ if (BQ == nullptr) {
+ if (Verbosity())
+ Report("Attempting to finalize an uninitialized global buffer!\n");
+ } else {
+ BQ->finalize();
+ }
atomic_store(&LoggingStatus, XRayLogInitStatus::XRAY_LOG_FINALIZED,
memory_order_release);
@@ -986,11 +973,17 @@ void fdrLoggingHandleCustomEvent(void *Event,
// - The metadata record we're going to write. (16 bytes)
// - The additional data we're going to write. Currently, that's the size
// of the event we're going to dump into the log as free-form bytes.
- if (!prepareBuffer(TSC, CPU, clock_gettime, MetadataRecSize + EventSize)) {
+ if (!prepareBuffer(TSC, CPU, clock_gettime,
+ MetadataRecSize + ReducedEventSize)) {
TLD.BQ = nullptr;
return;
}
+ // We need to reset the counts for the number of functions we're able to
+ // rewind.
+ TLD.NumConsecutiveFnEnters = 0;
+ TLD.NumTailCalls = 0;
+
// Write the custom event metadata record, which consists of the following
// information:
// - 8 bytes (64-bits) for the full TSC when the event started.
@@ -1001,11 +994,12 @@ void fdrLoggingHandleCustomEvent(void *Event,
uint8_t(MetadataRecord::RecordKinds::CustomEventMarker);
constexpr auto TSCSize = sizeof(TC.TSC);
internal_memcpy(&CustomEvent.Data, &ReducedEventSize, sizeof(int32_t));
- internal_memcpy(&CustomEvent.Data[sizeof(int32_t)], &TSC, TSCSize);
+ internal_memcpy(&CustomEvent.Data + sizeof(int32_t), &TSC, TSCSize);
internal_memcpy(TLD.RecordPtr, &CustomEvent, sizeof(CustomEvent));
TLD.RecordPtr += sizeof(CustomEvent);
internal_memcpy(TLD.RecordPtr, Event, ReducedEventSize);
- incrementExtents(MetadataRecSize + EventSize);
+ TLD.RecordPtr += ReducedEventSize;
+ incrementExtents(MetadataRecSize + ReducedEventSize);
endBufferIfFull();
}
@@ -1031,7 +1025,8 @@ void fdrLoggingHandleTypedEvent(
// - The metadata record we're going to write. (16 bytes)
// - The additional data we're going to write. Currently, that's the size
// of the event we're going to dump into the log as free-form bytes.
- if (!prepareBuffer(TSC, CPU, clock_gettime, MetadataRecSize + EventSize)) {
+ if (!prepareBuffer(TSC, CPU, clock_gettime,
+ MetadataRecSize + ReducedEventSize)) {
TLD.BQ = nullptr;
return;
}
@@ -1056,12 +1051,13 @@ void fdrLoggingHandleTypedEvent(
TLD.RecordPtr += sizeof(TypedEvent);
internal_memcpy(TLD.RecordPtr, Event, ReducedEventSize);
+ TLD.RecordPtr += ReducedEventSize;
incrementExtents(MetadataRecSize + EventSize);
endBufferIfFull();
}
-XRayLogInitStatus fdrLoggingInit(size_t BufferSize, size_t BufferMax,
- void *Options,
+XRayLogInitStatus fdrLoggingInit(UNUSED size_t BufferSize,
+ UNUSED size_t BufferMax, void *Options,
size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
if (Options == nullptr)
return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
@@ -1075,76 +1071,51 @@ XRayLogInitStatus fdrLoggingInit(size_t BufferSize, size_t BufferMax,
return static_cast<XRayLogInitStatus>(CurrentStatus);
}
- // Because of __xray_log_init_mode(...) which guarantees that this will be
- // called with BufferSize == 0 and BufferMax == 0 we parse the configuration
- // provided in the Options pointer as a string instead.
- if (BufferSize == 0 && BufferMax == 0) {
- if (Verbosity())
- Report("Initializing FDR mode with options: %s\n",
- static_cast<const char *>(Options));
-
- // TODO: Factor out the flags specific to the FDR mode implementation. For
- // now, use the global/single definition of the flags, since the FDR mode
- // flags are already defined there.
- FlagParser FDRParser;
- FDRFlags FDRFlags;
- registerXRayFDRFlags(&FDRParser, &FDRFlags);
- FDRFlags.setDefaults();
-
- // Override first from the general XRAY_DEFAULT_OPTIONS compiler-provided
- // options until we migrate everyone to use the XRAY_FDR_OPTIONS
- // compiler-provided options.
- FDRParser.ParseString(useCompilerDefinedFlags());
- FDRParser.ParseString(useCompilerDefinedFDRFlags());
- auto *EnvOpts = GetEnv("XRAY_FDR_OPTIONS");
- if (EnvOpts == nullptr)
- EnvOpts = "";
- FDRParser.ParseString(EnvOpts);
-
- // FIXME: Remove this when we fully remove the deprecated flags.
- if (internal_strlen(EnvOpts) == 0) {
- FDRFlags.func_duration_threshold_us =
- flags()->xray_fdr_log_func_duration_threshold_us;
- FDRFlags.grace_period_ms = flags()->xray_fdr_log_grace_period_ms;
- }
-
- // The provided options should always override the compiler-provided and
- // environment-variable defined options.
- FDRParser.ParseString(static_cast<const char *>(Options));
- *fdrFlags() = FDRFlags;
- BufferSize = FDRFlags.buffer_size;
- BufferMax = FDRFlags.buffer_max;
- SpinMutexLock Guard(&FDROptionsMutex);
- FDROptions.Fd = -1;
- FDROptions.ReportErrors = true;
- } else if (OptionsSize != sizeof(FDRLoggingOptions)) {
- // FIXME: This is deprecated, and should really be removed.
- // At this point we use the flag parser specific to the FDR mode
- // implementation.
- if (Verbosity())
- Report("Cannot initialize FDR logging; wrong size for options: %d\n",
- OptionsSize);
- return static_cast<XRayLogInitStatus>(
- atomic_load(&LoggingStatus, memory_order_acquire));
- } else {
- if (Verbosity())
- Report("XRay FDR: struct-based init is deprecated, please use "
- "string-based configuration instead.\n");
- SpinMutexLock Guard(&FDROptionsMutex);
- internal_memcpy(&FDROptions, Options, OptionsSize);
+ if (Verbosity())
+ Report("Initializing FDR mode with options: %s\n",
+ static_cast<const char *>(Options));
+
+ // TODO: Factor out the flags specific to the FDR mode implementation. For
+ // now, use the global/single definition of the flags, since the FDR mode
+ // flags are already defined there.
+ FlagParser FDRParser;
+ FDRFlags FDRFlags;
+ registerXRayFDRFlags(&FDRParser, &FDRFlags);
+ FDRFlags.setDefaults();
+
+ // Override first from the general XRAY_DEFAULT_OPTIONS compiler-provided
+ // options until we migrate everyone to use the XRAY_FDR_OPTIONS
+ // compiler-provided options.
+ FDRParser.ParseString(useCompilerDefinedFlags());
+ FDRParser.ParseString(useCompilerDefinedFDRFlags());
+ auto *EnvOpts = GetEnv("XRAY_FDR_OPTIONS");
+ if (EnvOpts == nullptr)
+ EnvOpts = "";
+ FDRParser.ParseString(EnvOpts);
+
+ // FIXME: Remove this when we fully remove the deprecated flags.
+ if (internal_strlen(EnvOpts) == 0) {
+ FDRFlags.func_duration_threshold_us =
+ flags()->xray_fdr_log_func_duration_threshold_us;
+ FDRFlags.grace_period_ms = flags()->xray_fdr_log_grace_period_ms;
}
+ // The provided options should always override the compiler-provided and
+ // environment-variable defined options.
+ FDRParser.ParseString(static_cast<const char *>(Options));
+ *fdrFlags() = FDRFlags;
+ BufferSize = FDRFlags.buffer_size;
+ BufferMax = FDRFlags.buffer_max;
+
bool Success = false;
if (BQ != nullptr) {
BQ->~BufferQueue();
- InternalFree(BQ);
BQ = nullptr;
}
if (BQ == nullptr) {
- BQ = reinterpret_cast<BufferQueue *>(
- InternalAlloc(sizeof(BufferQueue), nullptr, 64));
+ BQ = reinterpret_cast<BufferQueue *>(&BufferQueueStorage);
new (BQ) BufferQueue(BufferSize, BufferMax, Success);
}
@@ -1152,7 +1123,6 @@ XRayLogInitStatus fdrLoggingInit(size_t BufferSize, size_t BufferMax,
Report("BufferQueue init failed.\n");
if (BQ != nullptr) {
BQ->~BufferQueue();
- InternalFree(BQ);
BQ = nullptr;
}
return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
@@ -1170,6 +1140,8 @@ XRayLogInitStatus fdrLoggingInit(size_t BufferSize, size_t BufferMax,
auto &TLD = *reinterpret_cast<ThreadLocalData *>(TLDPtr);
if (TLD.BQ == nullptr)
return;
+ if (TLD.Buffer.Data == nullptr)
+ return;
auto EC = TLD.BQ->releaseBuffer(TLD.Buffer);
if (EC != BufferQueue::ErrorCode::Ok)
Report("At thread exit, failed to release buffer at %p; error=%s\n",
@@ -1209,11 +1181,22 @@ bool fdrLogDynamicInitializer() XRAY_NEVER_INSTRUMENT {
};
auto RegistrationResult = __xray_log_register_mode("xray-fdr", Impl);
if (RegistrationResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&
- Verbosity())
+ Verbosity()) {
Report("Cannot register XRay FDR mode to 'xray-fdr'; error = %d\n",
RegistrationResult);
- if (flags()->xray_fdr_log || !internal_strcmp(flags()->xray_mode, "xray-fdr"))
- __xray_set_log_impl(Impl);
+ return false;
+ }
+
+ if (flags()->xray_fdr_log ||
+ !internal_strcmp(flags()->xray_mode, "xray-fdr")) {
+ auto SelectResult = __xray_log_select_mode("xray-fdr");
+ if (SelectResult != XRayLogRegisterStatus::XRAY_REGISTRATION_OK &&
+ Verbosity()) {
+ Report("Cannot select XRay FDR mode as 'xray-fdr'; error = %d\n",
+ SelectResult);
+ return false;
+ }
+ }
return true;
}
diff --git a/lib/xray/xray_function_call_trie.h b/lib/xray/xray_function_call_trie.h
index 2acf14aa5..f4c2fc335 100644
--- a/lib/xray/xray_function_call_trie.h
+++ b/lib/xray/xray_function_call_trie.h
@@ -15,7 +15,7 @@
#ifndef XRAY_FUNCTION_CALL_TRIE_H
#define XRAY_FUNCTION_CALL_TRIE_H
-#include "sanitizer_common/sanitizer_allocator_internal.h"
+#include "xray_defs.h"
#include "xray_profiling_flags.h"
#include "xray_segmented_array.h"
#include <memory> // For placement new.
@@ -120,9 +120,11 @@ public:
// We add a constructor here to allow us to inplace-construct through
// Array<...>'s AppendEmplace.
Node(Node *P, NodeIdPairAllocatorType &A, int64_t CC, int64_t CLT,
- int32_t F)
- : Parent(P), Callees(A), CallCount(CC), CumulativeLocalTime(CLT),
- FId(F) {}
+ int32_t F) XRAY_NEVER_INSTRUMENT : Parent(P),
+ Callees(A),
+ CallCount(CC),
+ CumulativeLocalTime(CLT),
+ FId(F) {}
// TODO: Include the compact histogram.
};
@@ -134,7 +136,8 @@ private:
// We add a constructor here to allow us to inplace-construct through
// Array<...>'s AppendEmplace.
- ShadowStackEntry(uint64_t T, Node *N) : EntryTSC{T}, NodePtr{N} {}
+ ShadowStackEntry(uint64_t T, Node *N) XRAY_NEVER_INSTRUMENT : EntryTSC{T},
+ NodePtr{N} {}
};
using NodeArray = Array<Node>;
@@ -158,8 +161,9 @@ public:
Allocators(const Allocators &) = delete;
Allocators &operator=(const Allocators &) = delete;
- Allocators(Allocators &&O)
- : NodeAllocator(O.NodeAllocator), RootAllocator(O.RootAllocator),
+ Allocators(Allocators &&O) XRAY_NEVER_INSTRUMENT
+ : NodeAllocator(O.NodeAllocator),
+ RootAllocator(O.RootAllocator),
ShadowStackAllocator(O.ShadowStackAllocator),
NodeIdPairAllocator(O.NodeIdPairAllocator) {
O.NodeAllocator = nullptr;
@@ -168,7 +172,7 @@ public:
O.NodeIdPairAllocator = nullptr;
}
- Allocators &operator=(Allocators &&O) {
+ Allocators &operator=(Allocators &&O) XRAY_NEVER_INSTRUMENT {
{
auto Tmp = O.NodeAllocator;
O.NodeAllocator = this->NodeAllocator;
@@ -192,58 +196,54 @@ public:
return *this;
}
- ~Allocators() {
+ ~Allocators() XRAY_NEVER_INSTRUMENT {
// Note that we cannot use delete on these pointers, as they need to be
// returned to the sanitizer_common library's internal memory tracking
// system.
if (NodeAllocator != nullptr) {
NodeAllocator->~NodeAllocatorType();
- InternalFree(NodeAllocator);
+ deallocate(NodeAllocator);
NodeAllocator = nullptr;
}
if (RootAllocator != nullptr) {
RootAllocator->~RootAllocatorType();
- InternalFree(RootAllocator);
+ deallocate(RootAllocator);
RootAllocator = nullptr;
}
if (ShadowStackAllocator != nullptr) {
ShadowStackAllocator->~ShadowStackAllocatorType();
- InternalFree(ShadowStackAllocator);
+ deallocate(ShadowStackAllocator);
ShadowStackAllocator = nullptr;
}
if (NodeIdPairAllocator != nullptr) {
NodeIdPairAllocator->~NodeIdPairAllocatorType();
- InternalFree(NodeIdPairAllocator);
+ deallocate(NodeIdPairAllocator);
NodeIdPairAllocator = nullptr;
}
}
};
// TODO: Support configuration of options through the arguments.
- static Allocators InitAllocators() {
+ static Allocators InitAllocators() XRAY_NEVER_INSTRUMENT {
return InitAllocatorsCustom(profilingFlags()->per_thread_allocator_max);
}
- static Allocators InitAllocatorsCustom(uptr Max) {
+ static Allocators InitAllocatorsCustom(uptr Max) XRAY_NEVER_INSTRUMENT {
Allocators A;
- auto NodeAllocator = reinterpret_cast<Allocators::NodeAllocatorType *>(
- InternalAlloc(sizeof(Allocators::NodeAllocatorType)));
+ auto NodeAllocator = allocate<Allocators::NodeAllocatorType>();
new (NodeAllocator) Allocators::NodeAllocatorType(Max);
A.NodeAllocator = NodeAllocator;
- auto RootAllocator = reinterpret_cast<Allocators::RootAllocatorType *>(
- InternalAlloc(sizeof(Allocators::RootAllocatorType)));
+ auto RootAllocator = allocate<Allocators::RootAllocatorType>();
new (RootAllocator) Allocators::RootAllocatorType(Max);
A.RootAllocator = RootAllocator;
auto ShadowStackAllocator =
- reinterpret_cast<Allocators::ShadowStackAllocatorType *>(
- InternalAlloc(sizeof(Allocators::ShadowStackAllocatorType)));
+ allocate<Allocators::ShadowStackAllocatorType>();
new (ShadowStackAllocator) Allocators::ShadowStackAllocatorType(Max);
A.ShadowStackAllocator = ShadowStackAllocator;
- auto NodeIdPairAllocator = reinterpret_cast<NodeIdPairAllocatorType *>(
- InternalAlloc(sizeof(NodeIdPairAllocatorType)));
+ auto NodeIdPairAllocator = allocate<NodeIdPairAllocatorType>();
new (NodeIdPairAllocator) NodeIdPairAllocatorType(Max);
A.NodeIdPairAllocator = NodeIdPairAllocator;
return A;
@@ -256,12 +256,13 @@ private:
NodeIdPairAllocatorType *NodeIdPairAllocator = nullptr;
public:
- explicit FunctionCallTrie(const Allocators &A)
- : Nodes(*A.NodeAllocator), Roots(*A.RootAllocator),
+ explicit FunctionCallTrie(const Allocators &A) XRAY_NEVER_INSTRUMENT
+ : Nodes(*A.NodeAllocator),
+ Roots(*A.RootAllocator),
ShadowStack(*A.ShadowStackAllocator),
NodeIdPairAllocator(A.NodeIdPairAllocator) {}
- void enterFunction(const int32_t FId, uint64_t TSC) {
+ void enterFunction(const int32_t FId, uint64_t TSC) XRAY_NEVER_INSTRUMENT {
DCHECK_NE(FId, 0);
// This function primarily deals with ensuring that the ShadowStack is
// consistent and ready for when an exit event is encountered.
@@ -301,7 +302,7 @@ public:
return;
}
- void exitFunction(int32_t FId, uint64_t TSC) {
+ void exitFunction(int32_t FId, uint64_t TSC) XRAY_NEVER_INSTRUMENT {
// When we exit a function, we look up the ShadowStack to see whether we've
// entered this function before. We do as little processing here as we can,
// since most of the hard work would have already been done at function
@@ -323,7 +324,7 @@ public:
}
}
- const RootArray &getRoots() const { return Roots; }
+ const RootArray &getRoots() const XRAY_NEVER_INSTRUMENT { return Roots; }
// The deepCopyInto operation will update the provided FunctionCallTrie by
// re-creating the contents of this particular FunctionCallTrie in the other
@@ -338,7 +339,7 @@ public:
// synchronisation of both "this" and |O|.
//
// This function must *not* be called with a non-empty FunctionCallTrie |O|.
- void deepCopyInto(FunctionCallTrie &O) const {
+ void deepCopyInto(FunctionCallTrie &O) const XRAY_NEVER_INSTRUMENT {
DCHECK(O.getRoots().empty());
// We then push the root into a stack, to use as the parent marker for new
@@ -394,7 +395,7 @@ public:
//
// This function is *not* thread-safe, and may require external
// synchronisation of both "this" and |O|.
- void mergeInto(FunctionCallTrie &O) const {
+ void mergeInto(FunctionCallTrie &O) const XRAY_NEVER_INSTRUMENT {
struct NodeAndTarget {
FunctionCallTrie::Node *OrigNode;
FunctionCallTrie::Node *TargetNode;
diff --git a/lib/xray/xray_init.cc b/lib/xray/xray_init.cc
index b4e069795..8886a600d 100644
--- a/lib/xray/xray_init.cc
+++ b/lib/xray/xray_init.cc
@@ -27,6 +27,15 @@ extern const XRaySledEntry __start_xray_instr_map[] __attribute__((weak));
extern const XRaySledEntry __stop_xray_instr_map[] __attribute__((weak));
extern const XRayFunctionSledIndex __start_xray_fn_idx[] __attribute__((weak));
extern const XRayFunctionSledIndex __stop_xray_fn_idx[] __attribute__((weak));
+
+#if SANITIZER_MAC
+// HACK: This is a temporary workaround to make XRay build on
+// Darwin, but it will probably not work at runtime.
+const XRaySledEntry __start_xray_instr_map[] = {};
+extern const XRaySledEntry __stop_xray_instr_map[] = {};
+extern const XRayFunctionSledIndex __start_xray_fn_idx[] = {};
+extern const XRayFunctionSledIndex __stop_xray_fn_idx[] = {};
+#endif
}
using namespace __xray;
diff --git a/lib/xray/xray_profile_collector.cc b/lib/xray/xray_profile_collector.cc
index 17a611eea..a2a8f1ffe 100644
--- a/lib/xray/xray_profile_collector.cc
+++ b/lib/xray/xray_profile_collector.cc
@@ -13,10 +13,11 @@
//
//===----------------------------------------------------------------------===//
#include "xray_profile_collector.h"
-#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_vector.h"
+#include "xray_allocator.h"
+#include "xray_defs.h"
#include "xray_profiling_flags.h"
+#include "xray_segmented_array.h"
#include <memory>
#include <pthread.h>
#include <utility>
@@ -29,7 +30,7 @@ namespace {
SpinMutex GlobalMutex;
struct ThreadTrie {
tid_t TId;
- FunctionCallTrie *Trie;
+ typename std::aligned_storage<sizeof(FunctionCallTrie)>::type TrieStorage;
};
struct ProfileBuffer {
@@ -56,65 +57,51 @@ struct BlockHeader {
u64 ThreadId;
};
-// These need to be pointers that point to heap/internal-allocator-allocated
-// objects because these are accessed even at program exit.
-Vector<ThreadTrie> *ThreadTries = nullptr;
-Vector<ProfileBuffer> *ProfileBuffers = nullptr;
-FunctionCallTrie::Allocators *GlobalAllocators = nullptr;
+using ThreadTriesArray = Array<ThreadTrie>;
+using ProfileBufferArray = Array<ProfileBuffer>;
+using ThreadTriesArrayAllocator = typename ThreadTriesArray::AllocatorType;
+using ProfileBufferArrayAllocator = typename ProfileBufferArray::AllocatorType;
+
+// These need to be global aligned storage to avoid dynamic initialization. We
+// need these to be aligned to allow us to placement new objects into the
+// storage, and have pointers to those objects be appropriately aligned.
+static typename std::aligned_storage<sizeof(FunctionCallTrie::Allocators)>::type
+ AllocatorStorage;
+static typename std::aligned_storage<sizeof(ThreadTriesArray)>::type
+ ThreadTriesStorage;
+static typename std::aligned_storage<sizeof(ProfileBufferArray)>::type
+ ProfileBuffersStorage;
+static typename std::aligned_storage<sizeof(ThreadTriesArrayAllocator)>::type
+ ThreadTriesArrayAllocatorStorage;
+static typename std::aligned_storage<sizeof(ProfileBufferArrayAllocator)>::type
+ ProfileBufferArrayAllocatorStorage;
+
+static ThreadTriesArray *ThreadTries = nullptr;
+static ThreadTriesArrayAllocator *ThreadTriesAllocator = nullptr;
+static ProfileBufferArray *ProfileBuffers = nullptr;
+static ProfileBufferArrayAllocator *ProfileBuffersAllocator = nullptr;
+static FunctionCallTrie::Allocators *GlobalAllocators = nullptr;
} // namespace
-void post(const FunctionCallTrie &T, tid_t TId) {
+void post(const FunctionCallTrie &T, tid_t TId) XRAY_NEVER_INSTRUMENT {
static pthread_once_t Once = PTHREAD_ONCE_INIT;
- pthread_once(&Once, +[] {
- SpinMutexLock Lock(&GlobalMutex);
- GlobalAllocators = reinterpret_cast<FunctionCallTrie::Allocators *>(
- InternalAlloc(sizeof(FunctionCallTrie::Allocators)));
- new (GlobalAllocators) FunctionCallTrie::Allocators();
- *GlobalAllocators = FunctionCallTrie::InitAllocatorsCustom(
- profilingFlags()->global_allocator_max);
- ThreadTries = reinterpret_cast<Vector<ThreadTrie> *>(
- InternalAlloc(sizeof(Vector<ThreadTrie>)));
- new (ThreadTries) Vector<ThreadTrie>();
- ProfileBuffers = reinterpret_cast<Vector<ProfileBuffer> *>(
- InternalAlloc(sizeof(Vector<ProfileBuffer>)));
- new (ProfileBuffers) Vector<ProfileBuffer>();
- });
- DCHECK_NE(GlobalAllocators, nullptr);
- DCHECK_NE(ThreadTries, nullptr);
- DCHECK_NE(ProfileBuffers, nullptr);
+ pthread_once(&Once, +[] { reset(); });
ThreadTrie *Item = nullptr;
{
SpinMutexLock Lock(&GlobalMutex);
- if (GlobalAllocators == nullptr)
+ if (GlobalAllocators == nullptr || ThreadTries == nullptr)
return;
- Item = ThreadTries->PushBack();
+ Item = ThreadTries->Append({});
Item->TId = TId;
-
- // Here we're using the internal allocator instead of the managed allocator
- // because:
- //
- // 1) We're not using the segmented array data structure to host
- // FunctionCallTrie objects. We're using a Vector (from sanitizer_common)
- // which works like a std::vector<...> keeping elements contiguous in
- // memory. The segmented array data structure assumes that elements are
- // trivially destructible, where FunctionCallTrie isn't.
- //
- // 2) Using a managed allocator means we need to manage that separately,
- // which complicates the nature of this code. To get around that, we're
- // using the internal allocator instead, which has its own global state
- // and is decoupled from the lifetime management required by the managed
- // allocator we have in XRay.
- //
- Item->Trie = reinterpret_cast<FunctionCallTrie *>(InternalAlloc(
- sizeof(FunctionCallTrie), nullptr, alignof(FunctionCallTrie)));
- DCHECK_NE(Item->Trie, nullptr);
- new (Item->Trie) FunctionCallTrie(*GlobalAllocators);
+ auto Trie = reinterpret_cast<FunctionCallTrie *>(&Item->TrieStorage);
+ new (Trie) FunctionCallTrie(*GlobalAllocators);
}
- T.deepCopyInto(*Item->Trie);
+ auto Trie = reinterpret_cast<FunctionCallTrie *>(&Item->TrieStorage);
+ T.deepCopyInto(*Trie);
}
// A PathArray represents the function id's representing a stack trace. In this
@@ -127,17 +114,13 @@ struct ProfileRecord {
// The Path in this record is the function id's from the leaf to the root of
// the function call stack as represented from a FunctionCallTrie.
- PathArray *Path = nullptr;
+ PathArray Path;
const FunctionCallTrie::Node *Node = nullptr;
// Constructor for in-place construction.
- ProfileRecord(PathAllocator &A, const FunctionCallTrie::Node *N)
- : Path([&] {
- auto P =
- reinterpret_cast<PathArray *>(InternalAlloc(sizeof(PathArray)));
- new (P) PathArray(A);
- return P;
- }()),
+ ProfileRecord(PathAllocator &A,
+ const FunctionCallTrie::Node *N) XRAY_NEVER_INSTRUMENT
+ : Path(A),
Node(N) {}
};
@@ -147,9 +130,9 @@ using ProfileRecordArray = Array<ProfileRecord>;
// Walk a depth-first traversal of each root of the FunctionCallTrie to generate
// the path(s) and the data associated with the path.
-static void populateRecords(ProfileRecordArray &PRs,
- ProfileRecord::PathAllocator &PA,
- const FunctionCallTrie &Trie) {
+static void
+populateRecords(ProfileRecordArray &PRs, ProfileRecord::PathAllocator &PA,
+ const FunctionCallTrie &Trie) XRAY_NEVER_INSTRUMENT {
using StackArray = Array<const FunctionCallTrie::Node *>;
using StackAllocator = typename StackArray::AllocatorType;
StackAllocator StackAlloc(profilingFlags()->stack_allocator_max);
@@ -167,8 +150,8 @@ static void populateRecords(ProfileRecordArray &PRs,
// Traverse the Node's parents and as we're doing so, get the FIds in
// the order they appear.
for (auto N = Node; N != nullptr; N = N->Parent)
- Record->Path->Append(N->FId);
- DCHECK(!Record->Path->empty());
+ Record->Path.Append(N->FId);
+ DCHECK(!Record->Path.empty());
for (const auto C : Node->Callees)
DFSStack.Append(C.NodePtr);
@@ -177,52 +160,58 @@ static void populateRecords(ProfileRecordArray &PRs,
}
static void serializeRecords(ProfileBuffer *Buffer, const BlockHeader &Header,
- const ProfileRecordArray &ProfileRecords) {
- auto NextPtr = static_cast<char *>(
+ const ProfileRecordArray &ProfileRecords)
+ XRAY_NEVER_INSTRUMENT {
+ auto NextPtr = static_cast<uint8_t *>(
internal_memcpy(Buffer->Data, &Header, sizeof(Header))) +
sizeof(Header);
for (const auto &Record : ProfileRecords) {
// List of IDs follow:
- for (const auto FId : *Record.Path)
+ for (const auto FId : Record.Path)
NextPtr =
- static_cast<char *>(internal_memcpy(NextPtr, &FId, sizeof(FId))) +
+ static_cast<uint8_t *>(internal_memcpy(NextPtr, &FId, sizeof(FId))) +
sizeof(FId);
// Add the sentinel here.
constexpr int32_t SentinelFId = 0;
- NextPtr = static_cast<char *>(
+ NextPtr = static_cast<uint8_t *>(
internal_memset(NextPtr, SentinelFId, sizeof(SentinelFId))) +
sizeof(SentinelFId);
// Add the node data here.
NextPtr =
- static_cast<char *>(internal_memcpy(NextPtr, &Record.Node->CallCount,
- sizeof(Record.Node->CallCount))) +
+ static_cast<uint8_t *>(internal_memcpy(
+ NextPtr, &Record.Node->CallCount, sizeof(Record.Node->CallCount))) +
sizeof(Record.Node->CallCount);
- NextPtr = static_cast<char *>(
+ NextPtr = static_cast<uint8_t *>(
internal_memcpy(NextPtr, &Record.Node->CumulativeLocalTime,
sizeof(Record.Node->CumulativeLocalTime))) +
sizeof(Record.Node->CumulativeLocalTime);
}
- DCHECK_EQ(NextPtr - static_cast<char *>(Buffer->Data), Buffer->Size);
+ DCHECK_EQ(NextPtr - static_cast<uint8_t *>(Buffer->Data), Buffer->Size);
}
} // namespace
-void serialize() {
+void serialize() XRAY_NEVER_INSTRUMENT {
SpinMutexLock Lock(&GlobalMutex);
- // Clear out the global ProfileBuffers.
- for (uptr I = 0; I < ProfileBuffers->Size(); ++I)
- InternalFree((*ProfileBuffers)[I].Data);
- ProfileBuffers->Reset();
+ if (GlobalAllocators == nullptr || ThreadTries == nullptr ||
+ ProfileBuffers == nullptr)
+ return;
+
+ // Clear out the global ProfileBuffers, if it's not empty.
+ for (auto &B : *ProfileBuffers)
+ deallocateBuffer(reinterpret_cast<uint8_t *>(B.Data), B.Size);
+ ProfileBuffers->trim(ProfileBuffers->size());
- if (ThreadTries->Size() == 0)
+ if (ThreadTries->empty())
return;
// Then repopulate the global ProfileBuffers.
- for (u32 I = 0; I < ThreadTries->Size(); ++I) {
+ u32 I = 0;
+ for (const auto &ThreadTrie : *ThreadTries) {
using ProfileRecordAllocator = typename ProfileRecordArray::AllocatorType;
ProfileRecordAllocator PRAlloc(profilingFlags()->global_allocator_max);
ProfileRecord::PathAllocator PathAlloc(
@@ -233,9 +222,11 @@ void serialize() {
// use a local allocator and an __xray::Array<...> to store the intermediary
// data, then compute the size as we're going along. Then we'll allocate the
// contiguous space to contain the thread buffer data.
- const auto &Trie = *(*ThreadTries)[I].Trie;
+ const auto &Trie =
+ *reinterpret_cast<const FunctionCallTrie *>(&(ThreadTrie.TrieStorage));
if (Trie.getRoots().empty())
continue;
+
populateRecords(ProfileRecords, PathAlloc, Trie);
DCHECK(!Trie.getRoots().empty());
DCHECK(!ProfileRecords.empty());
@@ -251,68 +242,71 @@ void serialize() {
// + end of record (8 bytes)
u32 CumulativeSizes = 0;
for (const auto &Record : ProfileRecords)
- CumulativeSizes += 20 + (4 * Record.Path->size());
+ CumulativeSizes += 20 + (4 * Record.Path.size());
- BlockHeader Header{16 + CumulativeSizes, I, (*ThreadTries)[I].TId};
- auto Buffer = ProfileBuffers->PushBack();
+ BlockHeader Header{16 + CumulativeSizes, I++, ThreadTrie.TId};
+ auto Buffer = ProfileBuffers->Append({});
Buffer->Size = sizeof(Header) + CumulativeSizes;
- Buffer->Data = InternalAlloc(Buffer->Size, nullptr, 64);
+ Buffer->Data = allocateBuffer(Buffer->Size);
DCHECK_NE(Buffer->Data, nullptr);
serializeRecords(Buffer, Header, ProfileRecords);
-
- // Now clean up the ProfileRecords array, one at a time.
- for (auto &Record : ProfileRecords) {
- Record.Path->~PathArray();
- InternalFree(Record.Path);
- }
}
}
-void reset() {
+void reset() XRAY_NEVER_INSTRUMENT {
SpinMutexLock Lock(&GlobalMutex);
+
if (ProfileBuffers != nullptr) {
// Clear out the profile buffers that have been serialized.
- for (uptr I = 0; I < ProfileBuffers->Size(); ++I)
- InternalFree((*ProfileBuffers)[I].Data);
- ProfileBuffers->Reset();
- InternalFree(ProfileBuffers);
- ProfileBuffers = nullptr;
+ for (auto &B : *ProfileBuffers)
+ deallocateBuffer(reinterpret_cast<uint8_t *>(B.Data), B.Size);
+ ProfileBuffers->trim(ProfileBuffers->size());
}
if (ThreadTries != nullptr) {
// Clear out the function call tries per thread.
- for (uptr I = 0; I < ThreadTries->Size(); ++I) {
- auto &T = (*ThreadTries)[I];
- T.Trie->~FunctionCallTrie();
- InternalFree(T.Trie);
+ for (auto &T : *ThreadTries) {
+ auto Trie = reinterpret_cast<FunctionCallTrie *>(&T.TrieStorage);
+ Trie->~FunctionCallTrie();
}
- ThreadTries->Reset();
- InternalFree(ThreadTries);
- ThreadTries = nullptr;
+ ThreadTries->trim(ThreadTries->size());
}
// Reset the global allocators.
- if (GlobalAllocators != nullptr) {
+ if (GlobalAllocators != nullptr)
GlobalAllocators->~Allocators();
- InternalFree(GlobalAllocators);
- GlobalAllocators = nullptr;
- }
- GlobalAllocators = reinterpret_cast<FunctionCallTrie::Allocators *>(
- InternalAlloc(sizeof(FunctionCallTrie::Allocators)));
+
+ GlobalAllocators =
+ reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorStorage);
new (GlobalAllocators) FunctionCallTrie::Allocators();
*GlobalAllocators = FunctionCallTrie::InitAllocators();
- ThreadTries = reinterpret_cast<Vector<ThreadTrie> *>(
- InternalAlloc(sizeof(Vector<ThreadTrie>)));
- new (ThreadTries) Vector<ThreadTrie>();
- ProfileBuffers = reinterpret_cast<Vector<ProfileBuffer> *>(
- InternalAlloc(sizeof(Vector<ProfileBuffer>)));
- new (ProfileBuffers) Vector<ProfileBuffer>();
+
+ if (ThreadTriesAllocator != nullptr)
+ ThreadTriesAllocator->~ThreadTriesArrayAllocator();
+
+ ThreadTriesAllocator = reinterpret_cast<ThreadTriesArrayAllocator *>(
+ &ThreadTriesArrayAllocatorStorage);
+ new (ThreadTriesAllocator)
+ ThreadTriesArrayAllocator(profilingFlags()->global_allocator_max);
+ ThreadTries = reinterpret_cast<ThreadTriesArray *>(&ThreadTriesStorage);
+ new (ThreadTries) ThreadTriesArray(*ThreadTriesAllocator);
+
+ if (ProfileBuffersAllocator != nullptr)
+ ProfileBuffersAllocator->~ProfileBufferArrayAllocator();
+
+ ProfileBuffersAllocator = reinterpret_cast<ProfileBufferArrayAllocator *>(
+ &ProfileBufferArrayAllocatorStorage);
+ new (ProfileBuffersAllocator)
+ ProfileBufferArrayAllocator(profilingFlags()->global_allocator_max);
+ ProfileBuffers =
+ reinterpret_cast<ProfileBufferArray *>(&ProfileBuffersStorage);
+ new (ProfileBuffers) ProfileBufferArray(*ProfileBuffersAllocator);
}
-XRayBuffer nextBuffer(XRayBuffer B) {
+XRayBuffer nextBuffer(XRayBuffer B) XRAY_NEVER_INSTRUMENT {
SpinMutexLock Lock(&GlobalMutex);
- if (ProfileBuffers == nullptr || ProfileBuffers->Size() == 0)
+ if (ProfileBuffers == nullptr || ProfileBuffers->size() == 0)
return {nullptr, 0};
static pthread_once_t Once = PTHREAD_ONCE_INIT;
@@ -336,7 +330,7 @@ XRayBuffer nextBuffer(XRayBuffer B) {
BlockHeader Header;
internal_memcpy(&Header, B.Data, sizeof(BlockHeader));
auto NextBlock = Header.BlockNum + 1;
- if (NextBlock < ProfileBuffers->Size())
+ if (NextBlock < ProfileBuffers->size())
return {(*ProfileBuffers)[NextBlock].Data,
(*ProfileBuffers)[NextBlock].Size};
return {nullptr, 0};
diff --git a/lib/xray/xray_profiling.cc b/lib/xray/xray_profiling.cc
index d4b4345d7..6615de1a3 100644
--- a/lib/xray/xray_profiling.cc
+++ b/lib/xray/xray_profiling.cc
@@ -19,7 +19,6 @@
#include "sanitizer_common/sanitizer_flags.h"
#include "xray/xray_interface.h"
#include "xray/xray_log_interface.h"
-
#include "xray_flags.h"
#include "xray_profile_collector.h"
#include "xray_profiling_flags.h"
@@ -40,16 +39,30 @@ atomic_sint32_t ProfilerLogStatus = {XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
SpinMutex ProfilerOptionsMutex;
struct alignas(64) ProfilingData {
- FunctionCallTrie::Allocators *Allocators = nullptr;
- FunctionCallTrie *FCT = nullptr;
+ FunctionCallTrie::Allocators *Allocators;
+ FunctionCallTrie *FCT;
};
static pthread_key_t ProfilingKey;
+thread_local std::aligned_storage<sizeof(FunctionCallTrie::Allocators)>::type
+ AllocatorsStorage;
+thread_local std::aligned_storage<sizeof(FunctionCallTrie)>::type
+ FunctionCallTrieStorage;
thread_local std::aligned_storage<sizeof(ProfilingData)>::type ThreadStorage{};
+
static ProfilingData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
thread_local auto ThreadOnce = [] {
new (&ThreadStorage) ProfilingData{};
+ auto *Allocators =
+ reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage);
+ new (Allocators) FunctionCallTrie::Allocators();
+ *Allocators = FunctionCallTrie::InitAllocators();
+ auto *FCT = reinterpret_cast<FunctionCallTrie *>(&FunctionCallTrieStorage);
+ new (FCT) FunctionCallTrie(*Allocators);
+ auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
+ TLD.Allocators = Allocators;
+ TLD.FCT = FCT;
pthread_setspecific(ProfilingKey, &ThreadStorage);
return false;
}();
@@ -57,25 +70,18 @@ static ProfilingData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
- // We need to check whether the global flag to finalizing/finalized has been
- // switched. If it is, then we ought to not actually initialise the data.
- auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
- if (Status == XRayLogInitStatus::XRAY_LOG_FINALIZING ||
- Status == XRayLogInitStatus::XRAY_LOG_FINALIZED)
- return TLD;
-
- // If we're live, then we re-initialize TLD if the pointers are not null.
- if (UNLIKELY(TLD.Allocators == nullptr && TLD.FCT == nullptr)) {
- TLD.Allocators = reinterpret_cast<FunctionCallTrie::Allocators *>(
- InternalAlloc(sizeof(FunctionCallTrie::Allocators)));
- new (TLD.Allocators) FunctionCallTrie::Allocators();
- *TLD.Allocators = FunctionCallTrie::InitAllocators();
- TLD.FCT = reinterpret_cast<FunctionCallTrie *>(
- InternalAlloc(sizeof(FunctionCallTrie)));
- new (TLD.FCT) FunctionCallTrie(*TLD.Allocators);
+ if (UNLIKELY(TLD.Allocators == nullptr || TLD.FCT == nullptr)) {
+ auto *Allocators =
+ reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage);
+ new (Allocators) FunctionCallTrie::Allocators();
+ *Allocators = FunctionCallTrie::InitAllocators();
+ auto *FCT = reinterpret_cast<FunctionCallTrie *>(&FunctionCallTrieStorage);
+ new (FCT) FunctionCallTrie(*Allocators);
+ TLD.Allocators = Allocators;
+ TLD.FCT = FCT;
}
- return TLD;
+ return *reinterpret_cast<ProfilingData *>(&ThreadStorage);
}
static void cleanupTLD() XRAY_NEVER_INSTRUMENT {
@@ -83,8 +89,6 @@ static void cleanupTLD() XRAY_NEVER_INSTRUMENT {
if (TLD.Allocators != nullptr && TLD.FCT != nullptr) {
TLD.FCT->~FunctionCallTrie();
TLD.Allocators->~Allocators();
- InternalFree(TLD.FCT);
- InternalFree(TLD.Allocators);
TLD.FCT = nullptr;
TLD.Allocators = nullptr;
}
@@ -162,11 +166,13 @@ namespace {
thread_local atomic_uint8_t ReentranceGuard{0};
-static void postCurrentThreadFCT(ProfilingData &TLD) {
+static void postCurrentThreadFCT(ProfilingData &TLD) XRAY_NEVER_INSTRUMENT {
if (TLD.Allocators == nullptr || TLD.FCT == nullptr)
return;
- profileCollectorService::post(*TLD.FCT, GetTid());
+ if (!TLD.FCT->getRoots().empty())
+ profileCollectorService::post(*TLD.FCT, GetTid());
+
cleanupTLD();
}
@@ -181,13 +187,14 @@ void profilingHandleArg0(int32_t FuncId,
return;
auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
- auto &TLD = getThreadLocalData();
if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_FINALIZED ||
Status == XRayLogInitStatus::XRAY_LOG_FINALIZING)) {
+ auto &TLD = getThreadLocalData();
postCurrentThreadFCT(TLD);
return;
}
+ auto &TLD = getThreadLocalData();
switch (Entry) {
case XRayEntryType::ENTRY:
case XRayEntryType::LOG_ARGS_ENTRY:
@@ -235,15 +242,8 @@ XRayLogInitStatus profilingFinalize() XRAY_NEVER_INSTRUMENT {
}
XRayLogInitStatus
-profilingLoggingInit(size_t BufferSize, size_t BufferMax, void *Options,
- size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
- if (BufferSize != 0 || BufferMax != 0) {
- if (Verbosity())
- Report("__xray_log_init() being used, and is unsupported. Use "
- "__xray_log_init_mode(...) instead. Bailing out.");
- return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
- }
-
+profilingLoggingInit(UNUSED size_t BufferSize, UNUSED size_t BufferMax,
+ void *Options, size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
XRayLogInitStatus::XRAY_LOG_INITIALIZING,
diff --git a/lib/xray/xray_segmented_array.h b/lib/xray/xray_segmented_array.h
index 11dd794fa..c723c7de0 100644
--- a/lib/xray/xray_segmented_array.h
+++ b/lib/xray/xray_segmented_array.h
@@ -88,7 +88,7 @@ private:
// segments when elements are trimmed off the end.
SegmentBase *Freelist = &SentinelSegment;
- Segment *NewSegment() {
+ Segment *NewSegment() XRAY_NEVER_INSTRUMENT {
// We need to handle the case in which enough elements have been trimmed to
// allow us to re-use segments we've allocated before. For this we look into
// the Freelist, to see whether we need to actually allocate new blocks or
@@ -111,7 +111,7 @@ private:
return S;
}
- Segment *InitHeadAndTail() {
+ Segment *InitHeadAndTail() XRAY_NEVER_INSTRUMENT {
DCHECK_EQ(Head, &SentinelSegment);
DCHECK_EQ(Tail, &SentinelSegment);
auto Segment = NewSegment();
@@ -123,7 +123,7 @@ private:
return Segment;
}
- Segment *AppendNewSegment() {
+ Segment *AppendNewSegment() XRAY_NEVER_INSTRUMENT {
auto S = NewSegment();
if (S == nullptr)
return nullptr;
@@ -144,16 +144,18 @@ private:
size_t Size = 0;
public:
- Iterator(SegmentBase *IS, size_t Off, size_t S)
- : S(IS), Offset(Off), Size(S) {}
- Iterator(const Iterator &) noexcept = default;
- Iterator() noexcept = default;
- Iterator(Iterator &&) noexcept = default;
- Iterator &operator=(const Iterator &) = default;
- Iterator &operator=(Iterator &&) = default;
- ~Iterator() = default;
-
- Iterator &operator++() {
+ Iterator(SegmentBase *IS, size_t Off, size_t S) XRAY_NEVER_INSTRUMENT
+ : S(IS),
+ Offset(Off),
+ Size(S) {}
+ Iterator(const Iterator &) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
+ Iterator() NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
+ Iterator(Iterator &&) NOEXCEPT XRAY_NEVER_INSTRUMENT = default;
+ Iterator &operator=(const Iterator &) XRAY_NEVER_INSTRUMENT = default;
+ Iterator &operator=(Iterator &&) XRAY_NEVER_INSTRUMENT = default;
+ ~Iterator() XRAY_NEVER_INSTRUMENT = default;
+
+ Iterator &operator++() XRAY_NEVER_INSTRUMENT {
if (++Offset % ElementsPerSegment || Offset == Size)
return *this;
@@ -168,7 +170,7 @@ private:
return *this;
}
- Iterator &operator--() {
+ Iterator &operator--() XRAY_NEVER_INSTRUMENT {
DCHECK_NE(S, &SentinelSegment);
DCHECK_GT(Offset, 0);
@@ -181,29 +183,31 @@ private:
return *this;
}
- Iterator operator++(int) {
+ Iterator operator++(int) XRAY_NEVER_INSTRUMENT {
Iterator Copy(*this);
++(*this);
return Copy;
}
- Iterator operator--(int) {
+ Iterator operator--(int) XRAY_NEVER_INSTRUMENT {
Iterator Copy(*this);
--(*this);
return Copy;
}
template <class V, class W>
- friend bool operator==(const Iterator<V> &L, const Iterator<W> &R) {
+ friend bool operator==(const Iterator<V> &L,
+ const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
return L.S == R.S && L.Offset == R.Offset;
}
template <class V, class W>
- friend bool operator!=(const Iterator<V> &L, const Iterator<W> &R) {
+ friend bool operator!=(const Iterator<V> &L,
+ const Iterator<W> &R) XRAY_NEVER_INSTRUMENT {
return !(L == R);
}
- U &operator*() const {
+ U &operator*() const XRAY_NEVER_INSTRUMENT {
DCHECK_NE(S, &SentinelSegment);
auto RelOff = Offset % ElementsPerSegment;
@@ -214,11 +218,11 @@ private:
return *reinterpret_cast<U *>(AlignedOffset);
}
- U *operator->() const { return &(**this); }
+ U *operator->() const XRAY_NEVER_INSTRUMENT { return &(**this); }
};
public:
- explicit Array(AllocatorType &A) : Alloc(&A) {}
+ explicit Array(AllocatorType &A) XRAY_NEVER_INSTRUMENT : Alloc(&A) {}
Array(const Array &) = delete;
Array(Array &&O) NOEXCEPT : Alloc(O.Alloc),
@@ -230,16 +234,16 @@ public:
O.Size = 0;
}
- bool empty() const { return Size == 0; }
+ bool empty() const XRAY_NEVER_INSTRUMENT { return Size == 0; }
- AllocatorType &allocator() const {
+ AllocatorType &allocator() const XRAY_NEVER_INSTRUMENT {
DCHECK_NE(Alloc, nullptr);
return *Alloc;
}
- size_t size() const { return Size; }
+ size_t size() const XRAY_NEVER_INSTRUMENT { return Size; }
- T *Append(const T &E) {
+ T *Append(const T &E) XRAY_NEVER_INSTRUMENT {
if (UNLIKELY(Head == &SentinelSegment))
if (InitHeadAndTail() == nullptr)
return nullptr;
@@ -257,7 +261,8 @@ public:
return Position;
}
- template <class... Args> T *AppendEmplace(Args &&... args) {
+ template <class... Args>
+ T *AppendEmplace(Args &&... args) XRAY_NEVER_INSTRUMENT {
if (UNLIKELY(Head == &SentinelSegment))
if (InitHeadAndTail() == nullptr)
return nullptr;
@@ -281,7 +286,7 @@ public:
return reinterpret_cast<T *>(Position);
}
- T &operator[](size_t Offset) const {
+ T &operator[](size_t Offset) const XRAY_NEVER_INSTRUMENT {
DCHECK_LE(Offset, Size);
// We need to traverse the array enough times to find the element at Offset.
auto S = Head;
@@ -296,13 +301,13 @@ public:
return *reinterpret_cast<T *>(Position);
}
- T &front() const {
+ T &front() const XRAY_NEVER_INSTRUMENT {
DCHECK_NE(Head, &SentinelSegment);
DCHECK_NE(Size, 0u);
return *begin();
}
- T &back() const {
+ T &back() const XRAY_NEVER_INSTRUMENT {
DCHECK_NE(Tail, &SentinelSegment);
DCHECK_NE(Size, 0u);
auto It = end();
@@ -310,7 +315,8 @@ public:
return *It;
}
- template <class Predicate> T *find_element(Predicate P) const {
+ template <class Predicate>
+ T *find_element(Predicate P) const XRAY_NEVER_INSTRUMENT {
if (empty())
return nullptr;
@@ -324,7 +330,10 @@ public:
/// Remove N Elements from the end. This leaves the blocks behind, and not
/// require allocation of new blocks for new elements added after trimming.
- void trim(size_t Elements) {
+ void trim(size_t Elements) XRAY_NEVER_INSTRUMENT {
+ if (Elements == 0)
+ return;
+
DCHECK_LE(Elements, Size);
DCHECK_GT(Size, 0);
auto OldSize = Size;
@@ -357,10 +366,18 @@ public:
}
// Provide iterators.
- Iterator<T> begin() const { return Iterator<T>(Head, 0, Size); }
- Iterator<T> end() const { return Iterator<T>(Tail, Size, Size); }
- Iterator<const T> cbegin() const { return Iterator<const T>(Head, 0, Size); }
- Iterator<const T> cend() const { return Iterator<const T>(Tail, Size, Size); }
+ Iterator<T> begin() const XRAY_NEVER_INSTRUMENT {
+ return Iterator<T>(Head, 0, Size);
+ }
+ Iterator<T> end() const XRAY_NEVER_INSTRUMENT {
+ return Iterator<T>(Tail, Size, Size);
+ }
+ Iterator<const T> cbegin() const XRAY_NEVER_INSTRUMENT {
+ return Iterator<const T>(Head, 0, Size);
+ }
+ Iterator<const T> cend() const XRAY_NEVER_INSTRUMENT {
+ return Iterator<const T>(Tail, Size, Size);
+ }
};
// We need to have this storage definition out-of-line so that the compiler can
diff --git a/lib/xray/xray_trampoline_x86_64.S b/lib/xray/xray_trampoline_x86_64.S
index 99ad3966e..9dffae048 100644
--- a/lib/xray/xray_trampoline_x86_64.S
+++ b/lib/xray/xray_trampoline_x86_64.S
@@ -19,6 +19,7 @@
.macro SAVE_REGISTERS
+ pushfq
subq $240, %rsp
CFI_DEF_CFA_OFFSET(248)
movq %rbp, 232(%rsp)
@@ -69,6 +70,7 @@
movq 8(%rsp), %r14
movq 0(%rsp), %r15
addq $240, %rsp
+ popfq
CFI_DEF_CFA_OFFSET(8)
.endm
diff --git a/lib/xray/xray_utils.cc b/lib/xray/xray_utils.cc
index 68f4e8c10..5753354a4 100644
--- a/lib/xray/xray_utils.cc
+++ b/lib/xray/xray_utils.cc
@@ -103,10 +103,9 @@ int getLogFD() XRAY_NEVER_INSTRUMENT {
if (LastSlash != nullptr)
Progname = LastSlash + 1;
- const int HalfLength = sizeof(TmpFilename) / 2 - sizeof(TmpWildcardPattern);
int NeededLength = internal_snprintf(
- TmpFilename, sizeof(TmpFilename), "%.*s%.*s.%s", HalfLength,
- flags()->xray_logfile_base, HalfLength, Progname, TmpWildcardPattern);
+ TmpFilename, sizeof(TmpFilename), "%s%s.%s",
+ flags()->xray_logfile_base, Progname, TmpWildcardPattern);
if (NeededLength > int(sizeof(TmpFilename))) {
Report("XRay log file name too long (%d): %s\n", NeededLength, TmpFilename);
return -1;
diff --git a/lib/xray/xray_x86_64.cc b/lib/xray/xray_x86_64.cc
index 51dc4ce43..d0411d0fb 100644
--- a/lib/xray/xray_x86_64.cc
+++ b/lib/xray/xray_x86_64.cc
@@ -1,9 +1,10 @@
#include "cpuid.h"
#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_posix.h"
#include "xray_defs.h"
#include "xray_interface_internal.h"
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
+#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD || SANITIZER_MAC
#include <sys/types.h>
#if SANITIZER_OPENBSD
#include <sys/time.h>
@@ -81,17 +82,20 @@ uint64_t getTSCFrequency() XRAY_NEVER_INSTRUMENT {
}
return TSCFrequency == -1 ? 0 : static_cast<uint64_t>(TSCFrequency);
}
-#elif SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
+#elif SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD || SANITIZER_MAC
uint64_t getTSCFrequency() XRAY_NEVER_INSTRUMENT {
long long TSCFrequency = -1;
size_t tscfreqsz = sizeof(TSCFrequency);
#if SANITIZER_OPENBSD
int Mib[2] = { CTL_MACHDEP, CPU_TSCFREQ };
- if (sysctl(Mib, 2, &TSCFrequency, &tscfreqsz, NULL, 0) != -1) {
+ if (internal_sysctl(Mib, 2, &TSCFrequency, &tscfreqsz, NULL, 0) != -1) {
+#elif SANITIZER_MAC
+ if (internal_sysctlbyname("machdep.tsc.frequency", &TSCFrequency,
+ &tscfreqsz, NULL, 0) != -1) {
#else
- if (sysctlbyname("machdep.tsc_freq", &TSCFrequency, &tscfreqsz,
- NULL, 0) != -1) {
+ if (internal_sysctlbyname("machdep.tsc_freq", &TSCFrequency, &tscfreqsz,
+ NULL, 0) != -1) {
#endif
return static_cast<uint64_t>(TSCFrequency);
} else {
diff --git a/test/asan/TestCases/Darwin/segv_read_write.c b/test/asan/TestCases/Darwin/segv_read_write.c
index d8e2d215f..127365d2f 100644
--- a/test/asan/TestCases/Darwin/segv_read_write.c
+++ b/test/asan/TestCases/Darwin/segv_read_write.c
@@ -9,11 +9,8 @@ static volatile int sink;
__attribute__((noinline)) void Read(int *ptr) { sink = *ptr; }
__attribute__((noinline)) void Write(int *ptr) { *ptr = 0; }
int main(int argc, char **argv) {
- // Writes to shadow are detected as reads from shadow gap (because of how the
- // shadow mapping works). This is kinda hard to fix. Test a random address in
- // the application part of the address space.
void *volatile p =
- mmap(nullptr, 4096, PROT_READ, MAP_PRIVATE | MAP_ANON, 0, 0);
+ mmap(nullptr, 4096, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0);
munmap(p, 4096);
if (argc == 1)
Read((int *)p);
diff --git a/test/asan/TestCases/Linux/coverage-missing.cc b/test/asan/TestCases/Linux/coverage-missing.cc
index 32aada645..10acef9af 100644
--- a/test/asan/TestCases/Linux/coverage-missing.cc
+++ b/test/asan/TestCases/Linux/coverage-missing.cc
@@ -45,7 +45,15 @@
// RUN: diff bar.txt foo-missing.txt > %t.log || true
// RUN: not grep "^<" %t.log
-// REQUIRES: x86-target-arch
+// FIXME %sancov GetInstrumentedPCs relies on objdump -d to
+// obtain the number of instrumented PCs. The i386
+// %dynamiclib has .plt entries that are not recognized by
+// objdump,
+// "sancov.py: found 0 instrumented PCs in *.so",
+// causing AddressSanitizer-i386-linux to fail.
+// Change it back to x86-target-arch after %sancov switches to a more robust approach.
+
+// REQUIRES: x86_64-target-arch
// XFAIL: android
#include <stdio.h>
diff --git a/test/asan/TestCases/Posix/stack-use-after-return.cc b/test/asan/TestCases/Posix/stack-use-after-return.cc
index 237c880f8..528fa94b0 100644
--- a/test/asan/TestCases/Posix/stack-use-after-return.cc
+++ b/test/asan/TestCases/Posix/stack-use-after-return.cc
@@ -78,9 +78,11 @@ int main(int argc, char **argv) {
pthread_attr_init(&attr);
if (kStackSize > 0) {
size_t desired_stack_size = kStackSize;
+#ifdef PTHREAD_STACK_MIN
if (desired_stack_size < PTHREAD_STACK_MIN) {
desired_stack_size = PTHREAD_STACK_MIN;
}
+#endif
int ret = pthread_attr_setstacksize(&attr, desired_stack_size);
if (ret != 0) {
diff --git a/test/asan/TestCases/intercept-rethrow-exception.cc b/test/asan/TestCases/intercept-rethrow-exception.cc
index e81dc5398..019092a9e 100644
--- a/test/asan/TestCases/intercept-rethrow-exception.cc
+++ b/test/asan/TestCases/intercept-rethrow-exception.cc
@@ -4,6 +4,10 @@
// RUN: %clangxx_asan -fexceptions -O0 %s -o %t
// RUN: %run %t
+// The current implementation of this functionality requires special
+// combination of libraries that are not used by default on NetBSD
+// XFAIL: netbsd
+
#include <assert.h>
#include <exception>
#include <sanitizer/asan_interface.h>
diff --git a/test/asan/TestCases/throw_catch.cc b/test/asan/TestCases/throw_catch.cc
index 01083510c..d7f00ea9e 100644
--- a/test/asan/TestCases/throw_catch.cc
+++ b/test/asan/TestCases/throw_catch.cc
@@ -21,6 +21,7 @@ void ThrowAndCatch() {
}
}
+__attribute__((noinline))
void TestThrow() {
char x[32];
fprintf(stderr, "Before: %p poisoned: %d\n", &x,
@@ -36,6 +37,7 @@ void TestThrow() {
assert(!__asan_address_is_poisoned(x + 32));
}
+__attribute__((noinline))
void TestThrowInline() {
char x[32];
fprintf(stderr, "Before: %p poisoned: %d\n", &x,
diff --git a/test/builtins/Unit/compiler_rt_logb_test.c b/test/builtins/Unit/compiler_rt_logb_test.c
new file mode 100644
index 000000000..796765980
--- /dev/null
+++ b/test/builtins/Unit/compiler_rt_logb_test.c
@@ -0,0 +1,63 @@
+// RUN: %clang_builtins %s %librt -o %t && %run %t
+//===-- compiler_rt_logb_test.c - Test __compiler_rt_logb -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file checks __compiler_rt_logb from the compiler_rt library for
+// conformance against libm.
+//
+//===----------------------------------------------------------------------===//
+
+#define DOUBLE_PRECISION
+#include <math.h>
+#include <stdio.h>
+#include "fp_lib.h"
+
+int test__compiler_rt_logb(fp_t x) {
+ fp_t crt_value = __compiler_rt_logb(x);
+ fp_t libm_value = logb(x);
+ // Compare actual rep, e.g. to avoid NaN != the same NaN
+ if (toRep(crt_value) != toRep(libm_value)) {
+ printf("error: in __compiler_rt_logb(%a [%lX]) = %a [%lX] != %a [%lX]\n",
+ x, toRep(x), crt_value, toRep(crt_value), libm_value,
+ toRep(libm_value));
+ return 1;
+ }
+ return 0;
+}
+
+double cases[] = {
+ 1.e-6, -1.e-6, NAN, -NAN, INFINITY, -INFINITY, -1,
+ -0.0, 0.0, 1, -2, 2, -0.5, 0.5,
+};
+
+int main() {
+ const unsigned N = sizeof(cases) / sizeof(cases[0]);
+ unsigned i;
+ for (i = 0; i < N; ++i) {
+ if (test__compiler_rt_logb(cases[i])) return 1;
+ }
+
+ // Test a moving 1 bit, especially to handle denormal values.
+ // Test the negation as well.
+ rep_t x = signBit;
+ while (x) {
+ if (test__compiler_rt_logb(fromRep(x))) return 1;
+ if (test__compiler_rt_logb(fromRep(signBit ^ x))) return 1;
+ x >>= 1;
+ }
+ // Also try a couple moving ones
+ x = signBit | (signBit >> 1) | (signBit >> 2);
+ while (x) {
+ if (test__compiler_rt_logb(fromRep(x))) return 1;
+ if (test__compiler_rt_logb(fromRep(signBit ^ x))) return 1;
+ x >>= 1;
+ }
+
+ return 0;
+}
diff --git a/test/builtins/Unit/compiler_rt_logbf_test.c b/test/builtins/Unit/compiler_rt_logbf_test.c
new file mode 100644
index 000000000..5a4979a4b
--- /dev/null
+++ b/test/builtins/Unit/compiler_rt_logbf_test.c
@@ -0,0 +1,63 @@
+// RUN: %clang_builtins %s %librt -o %t && %run %t
+//===-- compiler_rt_logbf_test.c - Test __compiler_rt_logbf ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file checks __compiler_rt_logbf from the compiler_rt library for
+// conformance against libm.
+//
+//===----------------------------------------------------------------------===//
+
+#define SINGLE_PRECISION
+#include <math.h>
+#include <stdio.h>
+#include "fp_lib.h"
+
+int test__compiler_rt_logbf(fp_t x) {
+ fp_t crt_value = __compiler_rt_logbf(x);
+ fp_t libm_value = logbf(x);
+ // Compare actual rep, e.g. to avoid NaN != the same NaN
+ if (toRep(crt_value) != toRep(libm_value)) {
+ printf("error: in __compiler_rt_logb(%a [%X]) = %a [%X] != %a [%X]\n", x,
+ toRep(x), crt_value, toRep(crt_value), libm_value,
+ toRep(libm_value));
+ return 1;
+ }
+ return 0;
+}
+
+double cases[] = {
+ 1.e-6, -1.e-6, NAN, -NAN, INFINITY, -INFINITY, -1,
+ -0.0, 0.0, 1, -2, 2, -0.5, 0.5,
+};
+
+int main() {
+ const unsigned N = sizeof(cases) / sizeof(cases[0]);
+ unsigned i;
+ for (i = 0; i < N; ++i) {
+ if (test__compiler_rt_logbf(cases[i])) return 1;
+ }
+
+ // Test a moving 1 bit, especially to handle denormal values.
+ // Test the negation as well.
+ rep_t x = signBit;
+ while (x) {
+ if (test__compiler_rt_logbf(fromRep(x))) return 1;
+ if (test__compiler_rt_logbf(fromRep(signBit ^ x))) return 1;
+ x >>= 1;
+ }
+ // Also try a couple moving ones
+ x = signBit | (signBit >> 1) | (signBit >> 2);
+ while (x) {
+ if (test__compiler_rt_logbf(fromRep(x))) return 1;
+ if (test__compiler_rt_logbf(fromRep(signBit ^ x))) return 1;
+ x >>= 1;
+ }
+
+ return 0;
+}
diff --git a/test/builtins/Unit/compiler_rt_logbl_test.c b/test/builtins/Unit/compiler_rt_logbl_test.c
new file mode 100644
index 000000000..2617748e9
--- /dev/null
+++ b/test/builtins/Unit/compiler_rt_logbl_test.c
@@ -0,0 +1,79 @@
+// RUN: %clang_builtins %s %librt -o %t && %run %t
+//===-- compiler_rt_logbl_test.c - Test __compiler_rt_logbl ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file checks __compiler_rt_logbl from the compiler_rt library for
+// conformance against libm.
+//
+//===----------------------------------------------------------------------===//
+
+#define QUAD_PRECISION
+#include <math.h>
+#include <stdio.h>
+#include "fp_lib.h"
+#include "int_lib.h"
+
+#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT)
+
+int test__compiler_rt_logbl(fp_t x) {
+ fp_t crt_value = __compiler_rt_logbl(x);
+ fp_t libm_value = logbl(x);
+ // Compare actual rep, e.g. to avoid NaN != the same NaN
+ if (toRep(crt_value) != toRep(libm_value)) {
+ // Split expected values into two for printf
+ twords x_t, crt_value_t, libm_value_t;
+ x_t.all = toRep(x);
+ crt_value_t.all = toRep(crt_value);
+ libm_value_t.all = toRep(libm_value);
+ printf(
+ "error: in __compiler_rt_logb(%a [%llX %llX]) = %a [%llX %llX] != %a "
+ "[%llX %llX]\n",
+ x, x_t.s.high, x_t.s.low, crt_value, crt_value_t.s.high,
+ crt_value_t.s.low, libm_value, libm_value_t.s.high, libm_value_t.s.low);
+ return 1;
+ }
+ return 0;
+}
+
+double cases[] = {
+ 1.e-6, -1.e-6, NAN, -NAN, INFINITY, -INFINITY, -1,
+ -0.0, 0.0, 1, -2, 2, -0.5, 0.5,
+};
+
+#endif
+
+int main() {
+#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT)
+ const unsigned N = sizeof(cases) / sizeof(cases[0]);
+ unsigned i;
+ for (i = 0; i < N; ++i) {
+ if (test__compiler_rt_logbl(cases[i])) return 1;
+ }
+
+ // Test a moving 1 bit, especially to handle denormal values.
+ // Test the negation as well.
+ rep_t x = signBit;
+ while (x) {
+ if (test__compiler_rt_logbl(fromRep(x))) return 1;
+ if (test__compiler_rt_logbl(fromRep(signBit ^ x))) return 1;
+ x >>= 1;
+ }
+ // Also try a couple moving ones
+ x = signBit | (signBit >> 1) | (signBit >> 2);
+ while (x) {
+ if (test__compiler_rt_logbl(fromRep(x))) return 1;
+ if (test__compiler_rt_logbl(fromRep(signBit ^ x))) return 1;
+ x >>= 1;
+ }
+#else
+ printf("skipped\n");
+#endif
+
+ return 0;
+}
diff --git a/test/cfi/cross-dso/lit.local.cfg b/test/cfi/cross-dso/lit.local.cfg
index afdac4246..245d434fa 100644
--- a/test/cfi/cross-dso/lit.local.cfg
+++ b/test/cfi/cross-dso/lit.local.cfg
@@ -5,7 +5,7 @@ def getRoot(config):
root = getRoot(config)
-if root.host_os not in ['Linux']:
+if root.host_os not in ['Linux', 'FreeBSD', 'NetBSD']:
config.unsupported = True
# Android O (API level 26) has support for cross-dso cfi in libdl.so.
diff --git a/test/esan/TestCases/large-stack-linux.c b/test/esan/TestCases/large-stack-linux.c
index 1af32f8ba..17d88674b 100644
--- a/test/esan/TestCases/large-stack-linux.c
+++ b/test/esan/TestCases/large-stack-linux.c
@@ -1,5 +1,7 @@
// RUN: %clang_esan_wset -O0 %s -o %t 2>&1
// RUN: %env_esan_opts="verbosity=1 record_snapshots=0" %run %t %t 2>&1 | FileCheck %s
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <assert.h>
#include <stdio.h>
diff --git a/test/esan/TestCases/workingset-early-fault.c b/test/esan/TestCases/workingset-early-fault.c
index 1c420c368..971285b3f 100644
--- a/test/esan/TestCases/workingset-early-fault.c
+++ b/test/esan/TestCases/workingset-early-fault.c
@@ -3,6 +3,8 @@
//
// RUN: %clang_esan_wset %s -o %t
// RUN: %run %t 2>&1 | FileCheck %s
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <stdio.h>
#include <stdlib.h>
diff --git a/test/esan/TestCases/workingset-memset.cpp b/test/esan/TestCases/workingset-memset.cpp
index 9c972ec7a..56ed2f5b7 100644
--- a/test/esan/TestCases/workingset-memset.cpp
+++ b/test/esan/TestCases/workingset-memset.cpp
@@ -1,5 +1,7 @@
// RUN: %clang_esan_wset -O0 %s -o %t 2>&1
// RUN: %run %t 2>&1 | FileCheck %s
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <stdlib.h>
#include <string.h>
diff --git a/test/esan/TestCases/workingset-midreport.cpp b/test/esan/TestCases/workingset-midreport.cpp
index 38c376554..acd1eed17 100644
--- a/test/esan/TestCases/workingset-midreport.cpp
+++ b/test/esan/TestCases/workingset-midreport.cpp
@@ -6,6 +6,8 @@
// FIXME: Re-enable once PR33590 is fixed.
// UNSUPPORTED: x86_64
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <sanitizer/esan_interface.h>
#include <sched.h>
diff --git a/test/esan/TestCases/workingset-samples.cpp b/test/esan/TestCases/workingset-samples.cpp
index d97b62ba4..1f8e97dad 100644
--- a/test/esan/TestCases/workingset-samples.cpp
+++ b/test/esan/TestCases/workingset-samples.cpp
@@ -3,6 +3,8 @@
// FIXME: Re-enable once PR33590 is fixed.
// UNSUPPORTED: x86_64
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <sanitizer/esan_interface.h>
#include <sched.h>
diff --git a/test/esan/TestCases/workingset-signal-posix.cpp b/test/esan/TestCases/workingset-signal-posix.cpp
index ba776fc02..6f9787bd7 100644
--- a/test/esan/TestCases/workingset-signal-posix.cpp
+++ b/test/esan/TestCases/workingset-signal-posix.cpp
@@ -1,5 +1,7 @@
// RUN: %clang_esan_wset -O0 %s -o %t 2>&1
// RUN: %run %t 2>&1 | FileCheck %s
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <assert.h>
#include <setjmp.h>
diff --git a/test/esan/TestCases/workingset-simple.cpp b/test/esan/TestCases/workingset-simple.cpp
index f1ac2ecfe..dc17bcfd5 100644
--- a/test/esan/TestCases/workingset-simple.cpp
+++ b/test/esan/TestCases/workingset-simple.cpp
@@ -3,6 +3,8 @@
// FIXME: Re-enable once PR33590 is fixed.
// UNSUPPORTED: x86_64
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <stdlib.h>
#include <string.h>
diff --git a/test/esan/lit.cfg b/test/esan/lit.cfg
index 8b8457d66..1bb34ee08 100644
--- a/test/esan/lit.cfg
+++ b/test/esan/lit.cfg
@@ -39,6 +39,5 @@ config.substitutions.append(('%env_esan_opts=',
# Default test suffixes.
config.suffixes = ['.c', '.cpp']
-# EfficiencySanitizer tests are currently supported on Linux x86-64 only.
-if config.host_os not in ['Linux'] or config.target_arch not in ['x86_64', 'mips64'] :
+if config.host_os not in ['Linux', 'FreeBSD'] or config.target_arch not in ['x86_64', 'mips64'] :
config.unsupported = True
diff --git a/test/fuzzer/InitializeTest.cpp b/test/fuzzer/InitializeTest.cpp
index a93c2a525..5022c9efa 100644
--- a/test/fuzzer/InitializeTest.cpp
+++ b/test/fuzzer/InitializeTest.cpp
@@ -9,7 +9,7 @@
#include <stdlib.h>
#include <string.h>
-static char *argv0;
+static char *argv0 = NULL;
extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
assert(*argc > 0);
@@ -20,8 +20,7 @@ extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
assert(argv0);
- if (Size == strlen(argv0) &&
- !memmem(Data, Size, argv0, Size)) {
+ if (argv0 && Size >= 4 && !memcmp(Data, "fuzz", 4)) {
fprintf(stderr, "BINGO %s\n", argv0);
exit(1);
}
diff --git a/test/fuzzer/SymbolizeDeadlock.cpp b/test/fuzzer/SymbolizeDeadlock.cpp
index 5be1be804..b9ece38b2 100644
--- a/test/fuzzer/SymbolizeDeadlock.cpp
+++ b/test/fuzzer/SymbolizeDeadlock.cpp
@@ -8,7 +8,6 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
-#include <unistd.h>
#include "Bingo.h"
diff --git a/test/fuzzer/afl-driver-extra-stats.test b/test/fuzzer/afl-driver-extra-stats.test
index cddb683e6..2f5641daf 100644
--- a/test/fuzzer/afl-driver-extra-stats.test
+++ b/test/fuzzer/afl-driver-extra-stats.test
@@ -1,3 +1,5 @@
+# AFL doesn't work on Windows. No reason to test the driver.
+UNSUPPORTED: windows
XFAIL: ios
RUN: %no_fuzzer_cpp_compiler %S/AFLDriverTest.cpp %libfuzzer_src/afl/afl_driver.cpp -o %t-AFLDriverTest
diff --git a/test/fuzzer/afl-driver-stderr.test b/test/fuzzer/afl-driver-stderr.test
index d3d739d3b..5e3007e54 100644
--- a/test/fuzzer/afl-driver-stderr.test
+++ b/test/fuzzer/afl-driver-stderr.test
@@ -1,5 +1,6 @@
+# AFL doesn't work on Windows. No reason to test the driver.
+UNSUPPORTED: freebsd, windows
XFAIL: ios
-UNSUPPORTED: freebsd
RUN: %no_fuzzer_cpp_compiler %S/AFLDriverTest.cpp %libfuzzer_src/afl/afl_driver.cpp -o %t-AFLDriverTest
; Test that not specifying a stderr file isn't broken.
diff --git a/test/fuzzer/coverage.test b/test/fuzzer/coverage.test
index 3b2341f21..ff7a436e3 100644
--- a/test/fuzzer/coverage.test
+++ b/test/fuzzer/coverage.test
@@ -1,4 +1,5 @@
-UNSUPPORTED: aarch64
+# FIXME: Disabled on Windows because -fPIC cannot be used to compile for Windows.
+UNSUPPORTED: windows
RUN: %cpp_compiler -mllvm -use-unknown-locations=Disable %S/NullDerefTest.cpp -o %t-NullDerefTest
RUN: %cpp_compiler -mllvm -use-unknown-locations=Disable %S/DSO1.cpp -fPIC %ld_flags_rpath_so1 -shared -o %dynamiclib1
RUN: %cpp_compiler -mllvm -use-unknown-locations=Disable %S/DSO2.cpp -fPIC %ld_flags_rpath_so2 -shared -o %dynamiclib2
diff --git a/test/fuzzer/dso.test b/test/fuzzer/dso.test
index fc1fe2381..60ef8a6ac 100644
--- a/test/fuzzer/dso.test
+++ b/test/fuzzer/dso.test
@@ -1,3 +1,5 @@
+# FIXME: Disabled on Windows because -fPIC cannot be used to compile for Windows.
+UNSUPPORTED: windows
RUN: %cpp_compiler %S/DSO1.cpp -fPIC %ld_flags_rpath_so1 -shared -o %dynamiclib1
RUN: %cpp_compiler %S/DSO2.cpp -fPIC %ld_flags_rpath_so2 -shared -o %dynamiclib2
RUN: %cpp_compiler %S/DSOTestMain.cpp %S/DSOTestExtra.cpp %ld_flags_rpath_exe1 %ld_flags_rpath_exe2 -o %t-DSOTest
diff --git a/test/fuzzer/dump_coverage.test b/test/fuzzer/dump_coverage.test
index 41e193824..803a4fbb8 100644
--- a/test/fuzzer/dump_coverage.test
+++ b/test/fuzzer/dump_coverage.test
@@ -1,4 +1,5 @@
-UNSUPPORTED: freebsd
+# FIXME: Disabled on Windows because -fPIC cannot be used to compile for Windows.
+UNSUPPORTED: freebsd, windows
RUN: %cpp_compiler -fsanitize-coverage=0 -fsanitize-coverage=trace-pc-guard %S/DSO1.cpp -fPIC -shared -o %dynamiclib1 %ld_flags_rpath_so1
RUN: %cpp_compiler -fsanitize-coverage=0 -fsanitize-coverage=trace-pc-guard %S/DSO2.cpp -fPIC -shared -o %dynamiclib2 %ld_flags_rpath_so2
RUN: %cpp_compiler -fsanitize-coverage=0 -fsanitize-coverage=trace-pc-guard %S/DSOTestMain.cpp %S/DSOTestExtra.cpp %ld_flags_rpath_exe1 %ld_flags_rpath_exe2 -o %t-DSOTest
@@ -7,7 +8,7 @@ RUN: %cpp_compiler -fsanitize-coverage=0 -fsanitize-coverage=trace-pc-guard %S/N
RUN: rm -rf %t_workdir && mkdir -p %t_workdir
RUN: env ASAN_OPTIONS=coverage_dir='"%t_workdir"' not %run %t-NullDerefTest -dump_coverage=1 2>&1 | FileCheck %s
-RUN: sancov -covered-functions %t-NullDerefTest* %t_workdir/*.sancov | FileCheck %s --check-prefix=SANCOV
+RUN: sancov -covered-functions %t-NullDerefTest %t_workdir/*.sancov | FileCheck %s --check-prefix=SANCOV
RUN: env ASAN_OPTIONS=coverage_dir='"%t_workdir"' %run %t-DSOTest -dump_coverage=1 -runs=0 2>&1 | FileCheck -allow-deprecated-dag-overlap %s --check-prefix=DSO
RUN: env ASAN_OPTIONS=coverage_dir='"%t_workdir"' not %run %t-NullDerefTest -dump_coverage=0 2>&1 | FileCheck %s --check-prefix=NOCOV
diff --git a/test/fuzzer/exit_on_src_pos.test b/test/fuzzer/exit_on_src_pos.test
index ad0fa0a7c..c08c01410 100644
--- a/test/fuzzer/exit_on_src_pos.test
+++ b/test/fuzzer/exit_on_src_pos.test
@@ -1,9 +1,11 @@
# Temporary use -mllvm -use-unknown-locations=Disable so that
# all instructions have debug info (file line numbers) attached.
# TODO: Find out why test fails on Darwin with -O2.
-RUN: %cpp_compiler -O0 %S/SimpleTest.cpp -o %t-SimpleTest -mllvm -use-unknown-locations=Disable
-RUN: %cpp_compiler -O0 %S/ShrinkControlFlowTest.cpp -o %t-ShrinkControlFlowTest
+# Binaries must end in .exe or else symbolization will break on Windows because of how periods
+# in expansion of %t cause the compiler to overwrite .lib and .exp files.
+RUN: %cpp_compiler -O0 %S/SimpleTest.cpp -o %t-SimpleTest.exe -mllvm -use-unknown-locations=Disable
+RUN: %cpp_compiler -O0 %S/ShrinkControlFlowTest.cpp -o %t-ShrinkControlFlowTest.exe
-RUN: %run %t-SimpleTest -exit_on_src_pos=SimpleTest.cpp:18 2>&1 | FileCheck %s --check-prefix=EXIT_ON_SRC_POS
-RUN: %run %t-ShrinkControlFlowTest -exit_on_src_pos=Foo 2>&1 | FileCheck %s --check-prefix=EXIT_ON_SRC_POS
+RUN: %run %t-SimpleTest.exe -exit_on_src_pos=SimpleTest.cpp:18 2>&1 | FileCheck %s --check-prefix=EXIT_ON_SRC_POS
+RUN: %run %t-ShrinkControlFlowTest.exe -exit_on_src_pos=Foo 2>&1 | FileCheck %s --check-prefix=EXIT_ON_SRC_POS
EXIT_ON_SRC_POS: INFO: found line matching '{{.*}}', exiting.
diff --git a/test/fuzzer/fuzzer-implicit-integer-truncation.test b/test/fuzzer/fuzzer-implicit-integer-truncation.test
index 212559bdc..c968ea7d6 100644
--- a/test/fuzzer/fuzzer-implicit-integer-truncation.test
+++ b/test/fuzzer/fuzzer-implicit-integer-truncation.test
@@ -1,5 +1,5 @@
RUN: rm -f %t-ImplicitIntegerTruncationTest-Ubsan
RUN: %cpp_compiler -fsanitize=implicit-integer-truncation -fno-sanitize-recover=all %S/ImplicitIntegerTruncationTest.cpp -o %t-ImplicitIntegerTruncationTest-Ubsan
RUN: not %run %t-ImplicitIntegerTruncationTest-Ubsan 2>&1 | FileCheck %s
-CHECK: runtime error: implicit conversion from type 'int' of value 256 (32-bit, signed) to type 'unsigned char' changed the value to 0 (8-bit, unsigned)
+CHECK: ImplicitIntegerTruncationTest.cpp:22:17: runtime error: implicit conversion from type 'int' of value 256 (32-bit, signed) to type 'unsigned char' changed the value to 0 (8-bit, unsigned)
CHECK: Test unit written to ./crash-
diff --git a/test/fuzzer/fuzzer-mutationstats.test b/test/fuzzer/fuzzer-mutationstats.test
deleted file mode 100644
index cb75b3637..000000000
--- a/test/fuzzer/fuzzer-mutationstats.test
+++ /dev/null
@@ -1,10 +0,0 @@
-RUN: %cpp_compiler %S/SimpleTest.cpp -o %t-MutationStatsTest
-RUN: not %run %t-MutationStatsTest -print_mutation_stats=1 2>&1 | FileCheck %s --check-prefix=STAT
-
-# Ensures there are some non-zero values in the usefulness percentages printed.
-STAT: stat::mutation_usefulness: {{[0-9]+\.[0-9]+}}
-
-# Weighted mutations only trigger after first 10,000 runs, hence flag.
-RUN: not %run %t-MutationStatsTest -use_weighted_mutations=1 -seed=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=WEIGHTED
-
-WEIGHTED: BINGO
diff --git a/test/fuzzer/fuzzer-oom.test b/test/fuzzer/fuzzer-oom.test
index e82fb47c5..362ec314b 100644
--- a/test/fuzzer/fuzzer-oom.test
+++ b/test/fuzzer/fuzzer-oom.test
@@ -1,17 +1,21 @@
UNSUPPORTED: aarch64
-RUN: %cpp_compiler %S/OutOfMemoryTest.cpp -o %t-OutOfMemoryTest
-RUN: %cpp_compiler %S/OutOfMemorySingleLargeMallocTest.cpp -o %t-OutOfMemorySingleLargeMallocTest
-RUN: %cpp_compiler %S/AccumulateAllocationsTest.cpp -o %t-AccumulateAllocationsTest
+# Tests break on windows unless exe extension is used (because there are periods
+# in expansion of %t, the string after the period is interpreted as the file
+# extension, so each compilation will clobber the previous one's lib and exp
+# files causing symbolization to break).
+RUN: %cpp_compiler %S/OutOfMemoryTest.cpp -o %t-OutOfMemoryTest.exe
+RUN: %cpp_compiler %S/OutOfMemorySingleLargeMallocTest.cpp -o %t-OutOfMemorySingleLargeMallocTest.exe
+RUN: %cpp_compiler %S/AccumulateAllocationsTest.cpp -o %t-AccumulateAllocationsTest.exe
-RUN: not %run %t-OutOfMemoryTest -rss_limit_mb=300 2>&1 | FileCheck %s
+RUN: not %run %t-OutOfMemoryTest.exe -rss_limit_mb=300 2>&1 | FileCheck %s
CHECK: ERROR: libFuzzer: out-of-memory (used: {{.*}}; limit: 300Mb)
CHECK: Test unit written to ./oom-
SUMMARY: libFuzzer: out-of-memory
-RUN: not %run %t-OutOfMemorySingleLargeMallocTest -rss_limit_mb=300 2>&1 | FileCheck %s --check-prefix=SINGLE_LARGE_MALLOC
-RUN: not %run %t-OutOfMemorySingleLargeMallocTest -malloc_limit_mb=300 2>&1 | FileCheck %s --check-prefix=SINGLE_LARGE_MALLOC
-RUN: not %run %t-OutOfMemorySingleLargeMallocTest -rss_limit_mb=1000 -malloc_limit_mb=300 2>&1 | FileCheck %s --check-prefix=SINGLE_LARGE_MALLOC
+RUN: not %run %t-OutOfMemorySingleLargeMallocTest.exe -rss_limit_mb=300 2>&1 | FileCheck %s --check-prefix=SINGLE_LARGE_MALLOC
+RUN: not %run %t-OutOfMemorySingleLargeMallocTest.exe -malloc_limit_mb=300 2>&1 | FileCheck %s --check-prefix=SINGLE_LARGE_MALLOC
+RUN: not %run %t-OutOfMemorySingleLargeMallocTest.exe -rss_limit_mb=1000 -malloc_limit_mb=300 2>&1 | FileCheck %s --check-prefix=SINGLE_LARGE_MALLOC
We used to check for "out-of-memory (malloc(53{{.*}}))", but that would fail
sometimes, so now we accept any OOM message.
@@ -20,4 +24,4 @@ SINGLE_LARGE_MALLOC: libFuzzer: out-of-memory
SINGLE_LARGE_MALLOC: in LLVMFuzzerTestOneInput
# Check that -rss_limit_mb=0 means no limit.
-RUN: %run %t-AccumulateAllocationsTest -runs=1000 -rss_limit_mb=0
+RUN: %run %t-AccumulateAllocationsTest.exe -runs=1000 -rss_limit_mb=0
diff --git a/test/fuzzer/gc-sections.test b/test/fuzzer/gc-sections.test
index b8abfbbdf..e915c4cc9 100644
--- a/test/fuzzer/gc-sections.test
+++ b/test/fuzzer/gc-sections.test
@@ -8,8 +8,13 @@ With gc-sections. Currently, we can't remove unused code except with LLD.
RUN: %cpp_compiler %S/GcSectionsTest.cpp -o %t -fuse-ld=lld -ffunction-sections -Wl,-gc-sections
RUN: nm %t | not grep UnusedFunctionShouldBeRemovedByLinker
RUN: %run %t -runs=0 2>&1 | FileCheck %s
-CHECK-NOT: ERROR: The size of coverage PC tables does not match
With gc sections, with trace-pc. Unused code is removed.
RUN: %cpp_compiler %S/GcSectionsTest.cpp -o %t -fsanitize-coverage=0 -fsanitize-coverage=trace-pc -ffunction-sections -Wl,-gc-sections
RUN: nm %t | not grep UnusedFunctionShouldBeRemovedByLinker
+
+RUN: %cpp_compiler %S/GcSectionsTest.cpp -o %t -fsanitize-coverage=0 -fsanitize-coverage=trace-pc-guard,pc-table -fuse-ld=lld -ffunction-sections -Wl,-gc-sections
+RUN: nm %t | not grep UnusedFunctionShouldBeRemovedByLinker
+RUN: %run %t -runs=0 2>&1 | FileCheck %s
+
+CHECK-NOT: ERROR: The size of coverage PC tables does not match
diff --git a/test/fuzzer/handle-unstable.test b/test/fuzzer/handle-unstable.test
index a0b7c8303..d20224689 100644
--- a/test/fuzzer/handle-unstable.test
+++ b/test/fuzzer/handle-unstable.test
@@ -1,5 +1,6 @@
# Tests -handle_unstable
-UNSUPPORTED: aarch64
+# FIXME: Disabled on Windows until symbolization works properly.
+UNSUPPORTED: windows
RUN: %cpp_compiler %S/PrintUnstableStatsTest.cpp -o %t-HandleUnstableTest
diff --git a/test/fuzzer/lit.cfg b/test/fuzzer/lit.cfg
index 8a44860d4..608991c07 100644
--- a/test/fuzzer/lit.cfg
+++ b/test/fuzzer/lit.cfg
@@ -24,15 +24,18 @@ else:
# the test runner updated.
config.test_format = lit.formats.ShTest(execute_external)
-# LeakSanitizer is not supported on OSX right now.
-if sys.platform.startswith('darwin') or sys.platform.startswith('freebsd'):
+# LeakSanitizer is not supported on OSX or Windows right now.
+if (sys.platform.startswith('darwin') or
+ sys.platform.startswith('freebsd') or
+ sys.platform.startswith('netbsd') or
+ sys.platform.startswith('win')):
lit_config.note('lsan feature unavailable')
else:
lit_config.note('lsan feature available')
config.available_features.add('lsan')
-# MemorySanitizer is not supported on OSX right now
-if sys.platform.startswith('darwin'):
+# MemorySanitizer is not supported on OSX or Windows right now
+if sys.platform.startswith('darwin') or sys.platform.startswith('win'):
lit_config.note('msan feature unavailable')
assert 'msan' not in config.available_features
else:
@@ -67,10 +70,18 @@ def generate_compiler_cmd(is_cpp=True, fuzzer_enabled=True, msan_enabled=False):
config.runtime_library_dir)
elif any(x in config.target_triple for x in ('darwin', 'freebsd')):
link_cmd = '-lc++'
+ elif 'windows-msvc' in config.target_triple:
+ link_cmd = ''
else:
link_cmd = '-lstdc++'
- std_cmd = '--driver-mode=g++ -std=c++11' if is_cpp else ''
+ if is_cpp and 'windows-msvc' in config.target_triple:
+ std_cmd = '--driver-mode=cl'
+ elif is_cpp:
+ std_cmd = '--driver-mode=g++ -std=c++11'
+ else:
+ std_cmd = ''
+
if msan_enabled:
sanitizers = ['memory']
else:
diff --git a/test/fuzzer/merge-control-file.test b/test/fuzzer/merge-control-file.test
index 64b747116..60b2a6a62 100644
--- a/test/fuzzer/merge-control-file.test
+++ b/test/fuzzer/merge-control-file.test
@@ -1,6 +1,8 @@
XFAIL: ios
RUN: mkdir -p %t
-RUN: %cpp_compiler %S/FullCoverageSetTest.cpp -o %t/T
+# Use a ".exe" extension because it is needed on Windows to call system()
+# to execute itself again.
+RUN: %cpp_compiler %S/FullCoverageSetTest.cpp -o %t/T.exe
RUN: rm -rf %t/T0 %t/T1 %t/T2
RUN: mkdir -p %t/T0 %t/T1 %t/T2
@@ -11,9 +13,9 @@ RUN: echo ..Z... > %t/T0/3
# Test what happens if the control file is junk.
RUN: echo JUNK > %t/MCF
-RUN: not %run %t/T -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF 2>&1 | FileCheck %s --check-prefix=JUNK
+RUN: not %run %t/T.exe -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF 2>&1 | FileCheck %s --check-prefix=JUNK
RUN: echo 3 > %t/MCF; echo 0 >> %t/MCF; echo %t/T1/1 >> %t/MCF
-RUN: not %run %t/T -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF 2>&1 | FileCheck %s --check-prefix=JUNK
+RUN: not %run %t/T.exe -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF 2>&1 | FileCheck %s --check-prefix=JUNK
JUNK: MERGE-OUTER: non-empty control file provided: {{.*}}MCF
JUNK: MERGE-OUTER: bad control file, will overwrite it
@@ -22,18 +24,18 @@ JUNK: MERGE-OUTER: bad control file, will overwrite it
RUN: rm -f %t/T1/*; cp %t/T0/* %t/T1
RUN: echo 3 > %t/MCF; echo 0 >> %t/MCF; echo %t/T1/1 >> %t/MCF; echo %t/T1/2 >> %t/MCF; echo %t/T1/3 >> %t/MCF
-RUN: %run %t/T -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF 2>&1 | FileCheck %s --check-prefix=OK_0
+RUN: %run %t/T.exe -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF 2>&1 | FileCheck %s --check-prefix=OK_0
OK_0: MERGE-OUTER: control file ok, 3 files total, first not processed file 0
OK_0: MERGE-OUTER: 3 new files with {{.*}} new features added
RUN: rm -f %t/T1/*; cp %t/T0/* %t/T1
RUN: echo 3 > %t/MCF; echo 0 >> %t/MCF; echo %t/T1/1 >> %t/MCF; echo %t/T1/2 >> %t/MCF; echo %t/T1/3 >> %t/MCF
-RUN: %run %t/T -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF -save_coverage_summary=%t/SUMMARY 2>&1 | FileCheck %s --check-prefix=SAVE_SUMMARY
+RUN: %run %t/T.exe -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF -save_coverage_summary=%t/SUMMARY 2>&1 | FileCheck %s --check-prefix=SAVE_SUMMARY
SAVE_SUMMARY: MERGE-OUTER: writing coverage summary for 3 files to {{.*}}/SUMMARY
RUN: rm -f %t/T1/*; cp %t/T0/* %t/T1
RUN: echo 3 > %t/MCF; echo 0 >> %t/MCF; echo %t/T1/1 >> %t/MCF; echo %t/T1/2 >> %t/MCF; echo %t/T1/3 >> %t/MCF
-RUN: %run %t/T -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF -load_coverage_summary=%t/SUMMARY 2>&1 | FileCheck %s --check-prefix=LOAD_SUMMARY
+RUN: %run %t/T.exe -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF -load_coverage_summary=%t/SUMMARY 2>&1 | FileCheck %s --check-prefix=LOAD_SUMMARY
LOAD_SUMMARY: MERGE-OUTER: coverage summary loaded from
RUN: rm -f %t/T1/*; cp %t/T0/* %t/T1
@@ -42,7 +44,7 @@ RUN: echo STARTED 0 1 >> %t/MCF
RUN: echo DONE 0 11 >> %t/MCF
RUN: echo STARTED 1 2 >> %t/MCF
RUN: echo DONE 1 12 >> %t/MCF
-RUN: %run %t/T -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF 2>&1 | FileCheck %s --check-prefix=OK_2
+RUN: %run %t/T.exe -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF 2>&1 | FileCheck %s --check-prefix=OK_2
OK_2: MERGE-OUTER: control file ok, 3 files total, first not processed file 2
OK_2: MERGE-OUTER: 3 new files with {{.*}} new features added
@@ -54,5 +56,5 @@ RUN: echo STARTED 1 2 >> %t/MCF
RUN: echo DONE 1 12 >> %t/MCF
RUN: echo STARTED 2 2 >> %t/MCF
RUN: echo DONE 2 13 >> %t/MCF
-RUN: %run %t/T -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF 2>&1 | FileCheck %s --check-prefix=OK_3
+RUN: %run %t/T.exe -merge=1 %t/T1 %t/T2 -merge_control_file=%t/MCF 2>&1 | FileCheck %s --check-prefix=OK_3
OK_3: MERGE-OUTER: nothing to do, merge has been completed before
diff --git a/test/fuzzer/merge-posix.test b/test/fuzzer/merge-posix.test
index db0a48b54..883b7b6be 100644
--- a/test/fuzzer/merge-posix.test
+++ b/test/fuzzer/merge-posix.test
@@ -1,4 +1,5 @@
XFAIL: ios
+UNSUPPORTED: windows
RUN: %cpp_compiler %S/FullCoverageSetTest.cpp -o %t-FullCoverageSetTest
RUN: rm -rf %tmp/T1 %tmp/T2
diff --git a/test/fuzzer/merge-sigusr.test b/test/fuzzer/merge-sigusr.test
index a03e5440a..44448ca29 100644
--- a/test/fuzzer/merge-sigusr.test
+++ b/test/fuzzer/merge-sigusr.test
@@ -1,5 +1,7 @@
# Check that libFuzzer honors SIGUSR1/SIGUSR2
-UNSUPPORTED: darwin
+# FIXME: Disabled on Windows for now because of reliance on posix only features
+# (eg: export, "&", pkill).
+UNSUPPORTED: darwin, windows
RUN: rm -rf %t
RUN: mkdir -p %t
RUN: %cpp_compiler %S/SleepOneSecondTest.cpp -o %t/LFSIGUSR
diff --git a/test/fuzzer/minimize_crash.test b/test/fuzzer/minimize_crash.test
index de44b8747..dcab67bfd 100644
--- a/test/fuzzer/minimize_crash.test
+++ b/test/fuzzer/minimize_crash.test
@@ -1,3 +1,4 @@
+UNSUPPORTED: windows
RUN: %cpp_compiler %S/NullDerefTest.cpp -o %t-NullDerefTest
RUN: %cpp_compiler %S/SingleByteInputTest.cpp -o %t-SingleByteInputTest
RUN: mkdir -p %t.dir
diff --git a/test/fuzzer/minimize_two_crashes.test b/test/fuzzer/minimize_two_crashes.test
index 3c528f707..cba88eed1 100644
--- a/test/fuzzer/minimize_two_crashes.test
+++ b/test/fuzzer/minimize_two_crashes.test
@@ -1,5 +1,5 @@
-# Test that the minimizer stops when it sees a differe bug.
-UNSUPPORTED: freebsd
+# Test that the minimizer stops when it sees a different bug.
+UNSUPPORTED: freebsd,windows
# TODO: Find out why test fails on Darwin with -O2.
RUN: %cpp_compiler -O0 %S/TwoDifferentBugsTest.cpp -o %t-TwoDifferentBugsTest
diff --git a/test/fuzzer/null-deref-on-empty.test b/test/fuzzer/null-deref-on-empty.test
index f159a79f4..d576cc12b 100644
--- a/test/fuzzer/null-deref-on-empty.test
+++ b/test/fuzzer/null-deref-on-empty.test
@@ -1,3 +1,4 @@
+UNSUPPORTED: windows
RUN: %cpp_compiler %S/NullDerefOnEmptyTest.cpp -o %t-NullDerefOnEmptyTest
RUN: not %run %t-NullDerefOnEmptyTest -print_final_stats=1 2>&1 | FileCheck %s --check-prefix=NULL_DEREF_ON_EMPTY
diff --git a/test/fuzzer/null-deref.test b/test/fuzzer/null-deref.test
index 31eb5990d..e9926cab4 100644
--- a/test/fuzzer/null-deref.test
+++ b/test/fuzzer/null-deref.test
@@ -1,3 +1,4 @@
+UNSUPPORTED: windows
RUN: %cpp_compiler %S/NullDerefTest.cpp -o %t-NullDerefTest
RUN: not %run %t-NullDerefTest 2>&1 | FileCheck %s --check-prefix=NullDerefTest
diff --git a/test/fuzzer/print_unstable_stats.test b/test/fuzzer/print_unstable_stats.test
index c20efe940..41b279130 100644
--- a/test/fuzzer/print_unstable_stats.test
+++ b/test/fuzzer/print_unstable_stats.test
@@ -1,10 +1,11 @@
# Tests -print_unstable_stats
-UNSUPPORTED: aarch64
+# Disabled on Windows because of differences symbolizing and flakiness.
+UNSUPPORTED: aarch64, windows
RUN: %cpp_compiler %S/PrintUnstableStatsTest.cpp -o %t-PrintUnstableStatsTest
RUN: %run %t-PrintUnstableStatsTest -print_unstable_stats=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=LONG
-; We do not observe ini functions since we take the minimum hit counts, and minimum hit counts for ini is 0.
+# We do not observe ini functions since we take the minimum hit counts, and minimum hit counts for ini is 0.
LONG: UNSTABLE_FUNCTIONS:
LONG-NOT: det0()
LONG-NOT: det1()
diff --git a/test/fuzzer/sigusr.test b/test/fuzzer/sigusr.test
index 0b3ddc728..fa477a76e 100644
--- a/test/fuzzer/sigusr.test
+++ b/test/fuzzer/sigusr.test
@@ -1,4 +1,6 @@
-UNSUPPORTED: darwin
+# FIXME: Disabled on Windows for now because of reliance on posix only features
+# (eg: export, "&", pkill).
+UNSUPPORTED: darwin, windows
# Check that libFuzzer honors SIGUSR1/SIGUSR2
RUN: rm -rf %t
RUN: mkdir -p %t
diff --git a/test/fuzzer/trace-malloc-threaded.test b/test/fuzzer/trace-malloc-threaded.test
index 8f972d61f..f38005c1d 100644
--- a/test/fuzzer/trace-malloc-threaded.test
+++ b/test/fuzzer/trace-malloc-threaded.test
@@ -1,6 +1,7 @@
// FIXME: This test infinite loops on darwin because it crashes
// printing a stack trace repeatedly
-UNSUPPORTED: darwin, aarch64
+// FIXME: Disabled on Windows because of a crash (possibly related to above).
+UNSUPPORTED: darwin, aarch64, windows
RUN: %cpp_compiler %S/TraceMallocThreadedTest.cpp -o \
RUN: %t-TraceMallocThreadedTest
diff --git a/test/fuzzer/trace-malloc-unbalanced.test b/test/fuzzer/trace-malloc-unbalanced.test
index 193df01dd..c7b463214 100644
--- a/test/fuzzer/trace-malloc-unbalanced.test
+++ b/test/fuzzer/trace-malloc-unbalanced.test
@@ -6,14 +6,17 @@ UNSUPPORTED: darwin
RUN: %cpp_compiler %S/TraceMallocTest.cpp -o %t-TraceMallocTest
+# Specify python because we can't use the shebang line on Windows.
RUN: %run %t-TraceMallocTest -seed=1 -trace_malloc=1 -runs=200 2>&1 | \
-RUN: %libfuzzer_src/scripts/unbalanced_allocs.py --skip=5 | FileCheck %s
+RUN: python %libfuzzer_src/scripts/unbalanced_allocs.py --skip=5 | FileCheck %s
RUN: %run %t-TraceMallocTest -seed=1 -trace_malloc=2 -runs=200 2>&1 | \
-RUN: %libfuzzer_src/scripts/unbalanced_allocs.py --skip=5 | FileCheck %s --check-prefixes=CHECK,CHECK2
+RUN: python %libfuzzer_src/scripts/unbalanced_allocs.py --skip=5 | FileCheck %s --check-prefixes=CHECK,CHECK2
CHECK: MallocFreeTracer: START
-CHECK: Unbalanced MALLOC[{{[0-9]+}}] [[PTR:0x[0-9a-f]+]] 4
+# Behavior of the format string "%p" is implementation defined. Account for the
+# implementation on Windows and Linux.
+CHECK: Unbalanced MALLOC[{{[0-9]+}}] [[PTR:(:?0x)?[0-9a-fA-F]+]] 4
CHECK2-NEXT: {{ #0 0x[0-9a-f]+ in }}
CHECK2-NEXT: {{ #1 0x[0-9a-f]+ in }}
CHECK2-NEXT: {{ #2 0x[0-9a-f]+ in }}
diff --git a/test/fuzzer/ulimit.test b/test/fuzzer/ulimit.test
index 076866c50..7cf4c0a68 100644
--- a/test/fuzzer/ulimit.test
+++ b/test/fuzzer/ulimit.test
@@ -1,3 +1,5 @@
+# FIXME: Disabled on Windows for now because Windows has no ulimit command.
+UNSUPPORTED: windows
RUN: %cpp_compiler %S/SimpleTest.cpp -o %t-SimpleTest
RUN: ulimit -s 1000
RUN: not %run %t-SimpleTest
diff --git a/test/fuzzer/value-profile-cmp.test b/test/fuzzer/value-profile-cmp.test
index b927422d1..8d964b10b 100644
--- a/test/fuzzer/value-profile-cmp.test
+++ b/test/fuzzer/value-profile-cmp.test
@@ -1,3 +1,5 @@
+# FIXME: Disabled on Windows because of hangs.
+UNSUPPORTED: windows
CHECK: BINGO
RUN: %cpp_compiler %S/SimpleCmpTest.cpp -o %t-SimpleCmpTest
RUN: not %run %t-SimpleCmpTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s
diff --git a/test/fuzzer/value-profile-cmp2.test b/test/fuzzer/value-profile-cmp2.test
index 4bf119fcb..5935ed6d1 100644
--- a/test/fuzzer/value-profile-cmp2.test
+++ b/test/fuzzer/value-profile-cmp2.test
@@ -1,3 +1,4 @@
+UNSUPPORTED: ios
CHECK: BINGO
RUN: %cpp_compiler -fno-sanitize=address %S/SimpleHashTest.cpp -o %t-SimpleHashTest
RUN: not %run %t-SimpleHashTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 -max_len=64 2>&1 | FileCheck %s
diff --git a/test/fuzzer/value-profile-cmp3.test b/test/fuzzer/value-profile-cmp3.test
index 58ba18b90..fe715925f 100644
--- a/test/fuzzer/value-profile-cmp3.test
+++ b/test/fuzzer/value-profile-cmp3.test
@@ -1,3 +1,4 @@
+UNSUPPORTED: ios
CHECK: BINGO
RUN: %cpp_compiler %S/AbsNegAndConstantTest.cpp -o %t-AbsNegAndConstantTest
RUN: not %run %t-AbsNegAndConstantTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s
diff --git a/test/fuzzer/value-profile-cmp4.test b/test/fuzzer/value-profile-cmp4.test
index 05bc3f435..e5ac29f81 100644
--- a/test/fuzzer/value-profile-cmp4.test
+++ b/test/fuzzer/value-profile-cmp4.test
@@ -1,3 +1,5 @@
+# FIXME: Disabled on Windows because of hangs.
+UNSUPPORTED: windows
CHECK: BINGO
RUN: %cpp_compiler %S/AbsNegAndConstant64Test.cpp -o %t-AbsNegAndConstant64Test
RUN: not %run %t-AbsNegAndConstant64Test -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s
diff --git a/test/fuzzer/value-profile-div.test b/test/fuzzer/value-profile-div.test
index 59cc7c2f9..38f211241 100644
--- a/test/fuzzer/value-profile-div.test
+++ b/test/fuzzer/value-profile-div.test
@@ -1,4 +1,4 @@
-XFAIL: ios
+UNSUPPORTED: ios
UNSUPPORTED: aarch64
CHECK: AddressSanitizer: {{FPE|int-divide-by-zero}}
RUN: %cpp_compiler %S/DivTest.cpp -fsanitize-coverage=trace-div -o %t-DivTest
diff --git a/test/fuzzer/value-profile-load.test b/test/fuzzer/value-profile-load.test
index 607b81cd5..b6baf1320 100644
--- a/test/fuzzer/value-profile-load.test
+++ b/test/fuzzer/value-profile-load.test
@@ -1,3 +1,5 @@
+# FIXME: Disabled on Windows because of hangs.
+UNSUPPORTED: windows
CHECK: AddressSanitizer: global-buffer-overflow
RUN: %cpp_compiler %S/LoadTest.cpp -fsanitize-coverage=trace-gep -o %t-LoadTest
RUN: not %run %t-LoadTest -seed=2 -use_cmp=0 -use_value_profile=1 -runs=20000000 2>&1 | FileCheck %s
diff --git a/test/fuzzer/value-profile-mem.test b/test/fuzzer/value-profile-mem.test
index 57c844e92..7d68b8811 100644
--- a/test/fuzzer/value-profile-mem.test
+++ b/test/fuzzer/value-profile-mem.test
@@ -1,3 +1,4 @@
+UNSUPPORTED: ios
UNSUPPORTED: freebsd
CHECK: BINGO
RUN: %cpp_compiler %S/SingleMemcmpTest.cpp -o %t-SingleMemcmpTest
diff --git a/test/fuzzer/value-profile-set.test b/test/fuzzer/value-profile-set.test
index e55f1e4a8..7515e3651 100644
--- a/test/fuzzer/value-profile-set.test
+++ b/test/fuzzer/value-profile-set.test
@@ -1,3 +1,4 @@
+UNSUPPORTED: ios
CHECK: BINGO
RUN: %cpp_compiler %S/FourIndependentBranchesTest.cpp -o %t-FourIndependentBranchesTest
RUN: not %run %t-FourIndependentBranchesTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s
diff --git a/test/fuzzer/value-profile-strcmp.test b/test/fuzzer/value-profile-strcmp.test
index 647121f22..9b7a244d7 100644
--- a/test/fuzzer/value-profile-strcmp.test
+++ b/test/fuzzer/value-profile-strcmp.test
@@ -1,3 +1,4 @@
+UNSUPPORTED: ios
UNSUPPORTED: freebsd
CHECK: BINGO
RUN: %cpp_compiler %S/SingleStrcmpTest.cpp -o %t-SingleStrcmpTest
diff --git a/test/fuzzer/value-profile-strncmp.test b/test/fuzzer/value-profile-strncmp.test
index b60b97f86..98488df0a 100644
--- a/test/fuzzer/value-profile-strncmp.test
+++ b/test/fuzzer/value-profile-strncmp.test
@@ -1,4 +1,4 @@
-UNSUPPORTED: freebsd
+UNSUPPORTED: freebsd, aarch64
CHECK: BINGO
RUN: %cpp_compiler %S/SingleStrncmpTest.cpp -o %t-SingleStrncmpTest
RUN: not %run %t-SingleStrncmpTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s
diff --git a/test/fuzzer/value-profile-switch.test b/test/fuzzer/value-profile-switch.test
index cc3d4944c..a71682d79 100644
--- a/test/fuzzer/value-profile-switch.test
+++ b/test/fuzzer/value-profile-switch.test
@@ -1,4 +1,4 @@
-XFAIL: ios
+UNSUPPORTED: ios
CHECK: BINGO
RUN: %cpp_compiler %S/SwitchTest.cpp -o %t-SwitchTest
RUN: %cpp_compiler %S/Switch2Test.cpp -o %t-Switch2Test
diff --git a/test/hwasan/TestCases/deep-recursion.c b/test/hwasan/TestCases/deep-recursion.c
new file mode 100644
index 000000000..1ac0f5b15
--- /dev/null
+++ b/test/hwasan/TestCases/deep-recursion.c
@@ -0,0 +1,73 @@
+// RUN: %clang_hwasan -O1 %s -o %t
+// RUN: %env_hwasan_opts=stack_history_size=1 not %run %t 2>&1 | FileCheck %s --check-prefix=D1
+// RUN: %env_hwasan_opts=stack_history_size=2 not %run %t 2>&1 | FileCheck %s --check-prefix=D2
+// RUN: %env_hwasan_opts=stack_history_size=3 not %run %t 2>&1 | FileCheck %s --check-prefix=D3
+// RUN: %env_hwasan_opts=stack_history_size=5 not %run %t 2>&1 | FileCheck %s --check-prefix=D5
+// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=DEFAULT
+
+// REQUIRES: stable-runtime
+
+#include <stdlib.h>
+// At least -O1 is needed for this function to not have a stack frame on
+// AArch64.
+void USE(void *x) { // pretend_to_do_something(void *x)
+ __asm__ __volatile__("" : : "r" (x) : "memory");
+}
+
+volatile int four = 4;
+
+__attribute__((noinline)) void OOB() { int x[4]; x[four] = 0; USE(&x[0]); }
+__attribute__((noinline)) void FUNC1() { int x; USE(&x); OOB(); }
+__attribute__((noinline)) void FUNC2() { int x; USE(&x); FUNC1(); }
+__attribute__((noinline)) void FUNC3() { int x; USE(&x); FUNC2(); }
+__attribute__((noinline)) void FUNC4() { int x; USE(&x); FUNC3(); }
+__attribute__((noinline)) void FUNC5() { int x; USE(&x); FUNC4(); }
+__attribute__((noinline)) void FUNC6() { int x; USE(&x); FUNC5(); }
+__attribute__((noinline)) void FUNC7() { int x; USE(&x); FUNC6(); }
+__attribute__((noinline)) void FUNC8() { int x; USE(&x); FUNC7(); }
+__attribute__((noinline)) void FUNC9() { int x; USE(&x); FUNC8(); }
+__attribute__((noinline)) void FUNC10() { int x; USE(&x); FUNC9(); }
+
+int main() { FUNC10(); }
+
+// D1: Previosly allocated frames
+// D1: in OOB
+// D1-NOT: in FUNC
+// D1: Memory tags around the buggy address
+
+// D2: Previosly allocated frames
+// D2: in OOB
+// D2: in FUNC1
+// D2-NOT: in FUNC
+// D2: Memory tags around the buggy address
+
+// D3: Previosly allocated frames
+// D3: in OOB
+// D3: in FUNC1
+// D3: in FUNC2
+// D3-NOT: in FUNC
+// D3: Memory tags around the buggy address
+
+// D5: Previosly allocated frames
+// D5: in OOB
+// D5: in FUNC1
+// D5: in FUNC2
+// D5: in FUNC3
+// D5: in FUNC4
+// D5-NOT: in FUNC
+// D5: Memory tags around the buggy address
+
+// DEFAULT: Previosly allocated frames
+// DEFAULT: in OOB
+// DEFAULT: in FUNC1
+// DEFAULT: in FUNC2
+// DEFAULT: in FUNC3
+// DEFAULT: in FUNC4
+// DEFAULT: in FUNC5
+// DEFAULT: in FUNC6
+// DEFAULT: in FUNC7
+// DEFAULT: in FUNC8
+// DEFAULT: in FUNC9
+// DEFAULT: in FUNC10
+// DEFAULT-NOT: in FUNC
+// DEFAULT: Memory tags around the buggy address
diff --git a/test/hwasan/TestCases/double-free.c b/test/hwasan/TestCases/double-free.c
new file mode 100644
index 000000000..e97aae6ed
--- /dev/null
+++ b/test/hwasan/TestCases/double-free.c
@@ -0,0 +1,23 @@
+// RUN: %clang_hwasan %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+// REQUIRES: stable-runtime
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sanitizer/hwasan_interface.h>
+
+int main() {
+ __hwasan_enable_allocator_tagging();
+ char * volatile x = (char*)malloc(40);
+ free(x);
+ free(x);
+// CHECK: ERROR: HWAddressSanitizer: invalid-free on address
+// CHECK: tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem)
+// CHECK: freed by thread {{.*}} here:
+// CHECK: previously allocated here:
+// CHECK: Memory tags around the buggy address (one tag corresponds to 16 bytes):
+// CHECK: =>{{.*}}[[MEM_TAG]]
+ fprintf(stderr, "DONE\n");
+ __hwasan_disable_allocator_tagging();
+// CHECK-NOT: DONE
+}
diff --git a/test/hwasan/TestCases/heap-buffer-overflow.c b/test/hwasan/TestCases/heap-buffer-overflow.c
new file mode 100644
index 000000000..40b6e6e9d
--- /dev/null
+++ b/test/hwasan/TestCases/heap-buffer-overflow.c
@@ -0,0 +1,26 @@
+// RUN: %clang_hwasan %s -o %t
+// RUN: not %run %t 40 2>&1 | FileCheck %s --check-prefix=CHECK40
+// RUN: not %run %t 80 2>&1 | FileCheck %s --check-prefix=CHECK80
+// RUN: not %run %t -30 2>&1 | FileCheck %s --check-prefix=CHECKm30
+// RUN: not %run %t -30 1000000 2>&1 | FileCheck %s --check-prefix=CHECKMm30
+// RUN: not %run %t 1000000 1000000 2>&1 | FileCheck %s --check-prefix=CHECKM
+
+// REQUIRES: stable-runtime
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sanitizer/hwasan_interface.h>
+
+int main(int argc, char **argv) {
+ __hwasan_enable_allocator_tagging();
+ int offset = argc < 2 ? 40 : atoi(argv[1]);
+ int size = argc < 3 ? 30 : atoi(argv[2]);
+ char * volatile x = (char*)malloc(size);
+ x[offset] = 42;
+// CHECK40: is located 10 bytes to the right of 30-byte region
+// CHECK80: is located 50 bytes to the right of 30-byte region
+// CHECKm30: is located 30 bytes to the left of 30-byte region
+// CHECKMm30: is located 30 bytes to the left of 1000000-byte region
+// CHECKM: is located 0 bytes to the right of 1000000-byte region
+ free(x);
+}
diff --git a/test/hwasan/TestCases/hwasan-print-shadow.cc b/test/hwasan/TestCases/hwasan-print-shadow.cc
new file mode 100644
index 000000000..fa6330bbc
--- /dev/null
+++ b/test/hwasan/TestCases/hwasan-print-shadow.cc
@@ -0,0 +1,29 @@
+// RUN: %clangxx_hwasan -DSIZE=16 -O0 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// REQUIRES: stable-runtime
+
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sanitizer/hwasan_interface.h>
+
+int main() {
+ char *p = (char *)mmap(nullptr, 4096, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+ assert(p);
+
+ __hwasan_tag_memory(p, 1, 32);
+ __hwasan_tag_memory(p + 32, 3, 16);
+ __hwasan_tag_memory(p + 48, 0, 32);
+ __hwasan_tag_memory(p + 80, 4, 16);
+
+ char *q = (char *)__hwasan_tag_pointer(p, 7);
+ __hwasan_print_shadow(q + 5, 89 - 5);
+ // CHECK: HWASan shadow map for {{.*}}5 .. {{.*}}9 (pointer tag 7)
+ // CHECK-NEXT: {{.*}}0: 1
+ // CHECK-NEXT: {{.*}}0: 1
+ // CHECK-NEXT: {{.*}}0: 3
+ // CHECK-NEXT: {{.*}}0: 0
+ // CHECK-NEXT: {{.*}}0: 0
+ // CHECK-NEXT: {{.*}}0: 4
+}
diff --git a/test/hwasan/TestCases/longjmp.c b/test/hwasan/TestCases/longjmp.c
new file mode 100644
index 000000000..8d847b54b
--- /dev/null
+++ b/test/hwasan/TestCases/longjmp.c
@@ -0,0 +1,26 @@
+// RUN: %clang_hwasan -O0 -DNEGATIVE %s -o %t && %run %t 2>&1
+// RUN: %clang_hwasan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+// REQUIRES: stable-runtime
+
+#include <stdlib.h>
+#include <assert.h>
+#include <sanitizer/hwasan_interface.h>
+
+__attribute__((noinline))
+int f(void *caller_frame) {
+ int z = 0;
+ int *volatile p = &z;
+ // Tag of local is never zero.
+ assert(__hwasan_tag_pointer(p, 0) != p);
+#ifndef NEGATIVE
+ // This will destroy shadow of "z", and the following load will crash.
+ __hwasan_handle_longjmp(caller_frame);
+#endif
+ return p[0];
+}
+
+int main() {
+ return f(__builtin_frame_address(0));
+ // CHECK: READ of size 8 at {{.*}} tags: {{.*}}/00 (ptr/mem)
+}
diff --git a/test/hwasan/TestCases/malloc-test.c b/test/hwasan/TestCases/malloc-test.c
new file mode 100644
index 000000000..199464b9c
--- /dev/null
+++ b/test/hwasan/TestCases/malloc-test.c
@@ -0,0 +1,16 @@
+// Test basic malloc functionality.
+// RUN: %clang_hwasan %s -o %t
+// RUN: %run %t
+
+#include <stdlib.h>
+#include <assert.h>
+#include <sanitizer/hwasan_interface.h>
+#include <sanitizer/allocator_interface.h>
+
+int main() {
+ __hwasan_enable_allocator_tagging();
+ char *a1 = (char*)malloc(0);
+ assert(a1 != 0);
+ assert(__sanitizer_get_allocated_size(a1) == 0);
+ free(a1);
+}
diff --git a/test/hwasan/TestCases/malloc_fill.cc b/test/hwasan/TestCases/malloc_fill.cc
new file mode 100644
index 000000000..b8513b7e2
--- /dev/null
+++ b/test/hwasan/TestCases/malloc_fill.cc
@@ -0,0 +1,22 @@
+// Check that we fill malloc-ed memory correctly.
+// RUN: %clangxx_hwasan %s -o %t
+// RUN: %run %t | FileCheck %s
+// RUN: %env_hwasan_opts=max_malloc_fill_size=10:malloc_fill_byte=8 %run %t | FileCheck %s --check-prefix=CHECK-10-8
+// RUN: %env_hwasan_opts=max_malloc_fill_size=20:malloc_fill_byte=171 %run %t | FileCheck %s --check-prefix=CHECK-20-ab
+
+#include <stdio.h>
+int main(int argc, char **argv) {
+ // With asan allocator this makes sure we get memory from mmap.
+ static const int kSize = 1 << 25;
+ unsigned char *x = new unsigned char[kSize];
+ printf("-");
+ for (int i = 0; i <= 32; i++) {
+ printf("%02x", x[i]);
+ }
+ printf("-\n");
+ delete [] x;
+}
+
+// CHECK: -bebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebe-
+// CHECK-10-8: -080808080808080808080000000000000000000000000000000000000000000000-
+// CHECK-20-ab: -abababababababababababababababababababab00000000000000000000000000-
diff --git a/test/hwasan/TestCases/many-threads-uaf.c b/test/hwasan/TestCases/many-threads-uaf.c
new file mode 100644
index 000000000..3a79cb37b
--- /dev/null
+++ b/test/hwasan/TestCases/many-threads-uaf.c
@@ -0,0 +1,45 @@
+// RUN: %clang_hwasan %s -o %t && not %env_hwasan_opts=verbose_threads=1 %run %t 2>&1 | FileCheck %s
+// REQUIRES: stable-runtime
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <sanitizer/hwasan_interface.h>
+
+void *BoringThread(void *arg) {
+ char * volatile x = (char*)malloc(10);
+ x[5] = 0;
+ free(x);
+ return NULL;
+}
+
+// CHECK: Creating : T0
+// CHECK: Creating : T1
+// CHECK: Destroying: T1
+// CHECK: Creating : T1100
+// CHECK: Destroying: T1100
+// CHECK: Creating : T1101
+
+void *UAFThread(void *arg) {
+ char * volatile x = (char*)malloc(10);
+ fprintf(stderr, "ZZZ %p\n", x);
+ free(x);
+ x[5] = 42;
+ // CHECK: ERROR: HWAddressSanitizer: tag-mismatch on address
+ // CHECK: WRITE of size 1
+ // CHECK: many-threads-uaf.c:[[@LINE-3]]
+ // CHECK: Thread: T1101
+ return NULL;
+}
+
+int main() {
+ __hwasan_enable_allocator_tagging();
+ pthread_t t;
+ for (int i = 0; i < 1100; i++) {
+ pthread_create(&t, NULL, BoringThread, NULL);
+ pthread_join(t, NULL);
+ }
+ pthread_create(&t, NULL, UAFThread, NULL);
+ pthread_join(t, NULL);
+}
diff --git a/test/hwasan/TestCases/new-test.cc b/test/hwasan/TestCases/new-test.cc
new file mode 100644
index 000000000..3b1991e4d
--- /dev/null
+++ b/test/hwasan/TestCases/new-test.cc
@@ -0,0 +1,18 @@
+// Test basic new functionality.
+// RUN: %clangxx_hwasan %s -o %t
+// RUN: %run %t
+
+#include <stdlib.h>
+#include <assert.h>
+#include <sanitizer/hwasan_interface.h>
+#include <sanitizer/allocator_interface.h>
+
+int main() {
+ __hwasan_enable_allocator_tagging();
+
+ size_t volatile n = 0;
+ char *a1 = new char[n];
+ assert(a1 != nullptr);
+ assert(__sanitizer_get_allocated_size(a1) == 0);
+ delete[] a1;
+}
diff --git a/test/hwasan/TestCases/print-memory-usage-android.c b/test/hwasan/TestCases/print-memory-usage-android.c
new file mode 100644
index 000000000..5a057928b
--- /dev/null
+++ b/test/hwasan/TestCases/print-memory-usage-android.c
@@ -0,0 +1,21 @@
+// Tests __hwasan_print_memory_usage through /proc/$PID/maps.
+// RUN: %clang_hwasan %s -o %t && %env_hwasan_opts=export_memory_stats=1 %run %t 2>&1 | FileCheck %s
+// REQUIRES: android
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+// The function needs to be unsanitized in order for &cmd to be untagged. This
+// address is passed to system() and then to execve() syscall. The tests need to
+// run on unpatched linux kernel, which at this time does not accept tagged
+// pointers in system call arguments (but there is hope: see
+// https://lore.kernel.org/patchwork/cover/979328).
+__attribute__((no_sanitize("hwaddress")))
+int main() {
+ char cmd[1024];
+ snprintf(cmd, sizeof(cmd), "cat /proc/%d/maps", getpid());
+ system(cmd);
+ // CHECK: HWASAN pid: [[PID:[0-9]*]] rss: {{.*}} threads: 1 stacks: [[STACKS:[0-9]*]] thr_aux: {{.*}} stack_depot: {{.*}} uniq_stacks: [[UNIQ_STACKS:[0-9]*]] heap: [[HEAP:[0-9]*]]
+}
diff --git a/test/hwasan/TestCases/print-memory-usage.c b/test/hwasan/TestCases/print-memory-usage.c
new file mode 100644
index 000000000..df9d534f3
--- /dev/null
+++ b/test/hwasan/TestCases/print-memory-usage.c
@@ -0,0 +1,72 @@
+// Tests __hwasan_print_memory_usage.
+// RUN: %clang_hwasan %s -o %t
+// RUN: ulimit -s 1000
+// RUN: %run %t 2>&1 | FileCheck %s
+// REQUIRES: stable-runtime
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sanitizer/hwasan_interface.h>
+
+int state;
+__thread volatile char *sink;
+
+__attribute__((noinline))
+void *malloc_and_use(int size) {
+ char *x = (char*)malloc(size);
+ for (int i = 0; i < size; i++)
+ x[i] = 42; // make this memory used.
+ return x;
+}
+
+void *T1(void *arg) {
+
+ for (int i = 1; i <= (1 << 20); i *= 2)
+ sink = malloc_and_use(i);
+
+ __sync_fetch_and_add(&state, 1);
+ while (__sync_fetch_and_add(&state, 0) != 4) {}
+ return NULL;
+}
+
+void *T4(void *arg) { return NULL; }
+
+int main() {
+ __hwasan_enable_allocator_tagging();
+ sink = malloc_and_use(10);
+
+ __hwasan_print_memory_usage();
+ // CHECK: HWASAN pid: [[PID:[0-9]*]] rss: {{.*}} threads: 1 stacks: [[STACKS:[0-9]*]] thr_aux: {{.*}} stack_depot: {{.*}} uniq_stacks: [[UNIQ_STACKS:[0-9]*]] heap: [[HEAP:[0-9]*]]
+
+ void *one_meg = malloc_and_use(1 << 20);
+
+ __hwasan_print_memory_usage();
+ // CHECK: HWASAN pid: [[PID]] rss: {{.*}} threads: 1 stacks: [[STACKS]] thr_aux: {{.*}} stack_depot: {{.*}}
+
+ free(one_meg);
+
+ __hwasan_print_memory_usage();
+ // CHECK: HWASAN pid: [[PID]] rss: {{.*}} threads: 1 stacks: [[STACKS]] thr_aux: {{.*}} stack_depot: {{.*}} uniq_stacks: {{.*}} heap: [[HEAP]]
+
+ pthread_t t1, t2, t3, t4;
+
+ pthread_create(&t1, NULL, T1, NULL);
+ pthread_create(&t2, NULL, T1, NULL);
+ pthread_create(&t3, NULL, T1, NULL);
+ pthread_create(&t4, NULL, T4, NULL);
+ while (__sync_fetch_and_add(&state, 0) != 3) {}
+ pthread_join(t4, NULL);
+
+ __hwasan_print_memory_usage();
+ // CHECK: HWASAN pid: [[PID]] rss: {{.*}} threads: 4 stacks:
+
+ __sync_fetch_and_add(&state, 1);
+ pthread_join(t1, NULL);
+ pthread_join(t2, NULL);
+ pthread_join(t3, NULL);
+ __hwasan_print_memory_usage();
+ // CHECK: HWASAN pid: [[PID]] rss: {{.*}} threads: 1 stacks: [[STACKS]]
+}
diff --git a/test/hwasan/TestCases/pthread_exit.c b/test/hwasan/TestCases/pthread_exit.c
new file mode 100644
index 000000000..937e20c6a
--- /dev/null
+++ b/test/hwasan/TestCases/pthread_exit.c
@@ -0,0 +1,5 @@
+// Tests pthread_exit.
+// RUN: %clang_hwasan %s -o %t && %run %t
+// REQUIRES: stable-runtime
+#include <pthread.h>
+int main() { pthread_exit(NULL); }
diff --git a/test/hwasan/TestCases/realloc-after-free.c b/test/hwasan/TestCases/realloc-after-free.c
new file mode 100644
index 000000000..c4bc48c9c
--- /dev/null
+++ b/test/hwasan/TestCases/realloc-after-free.c
@@ -0,0 +1,28 @@
+// RUN: %clang_hwasan %s -o %t
+// RUN: not %run %t 50 2>&1 | FileCheck %s
+// RUN: not %run %t 40 2>&1 | FileCheck %s
+// RUN: not %run %t 30 2>&1 | FileCheck %s
+
+// REQUIRES: stable-runtime
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sanitizer/hwasan_interface.h>
+
+int main(int argc, char **argv) {
+ __hwasan_enable_allocator_tagging();
+ if (argc != 2) return 0;
+ int realloc_size = atoi(argv[1]);
+ char * volatile x = (char*)malloc(40);
+ free(x);
+ x = realloc(x, realloc_size);
+// CHECK: ERROR: HWAddressSanitizer: invalid-free on address
+// CHECK: tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem)
+// CHECK: freed by thread {{.*}} here:
+// CHECK: previously allocated here:
+// CHECK: Memory tags around the buggy address (one tag corresponds to 16 bytes):
+// CHECK: =>{{.*}}[[MEM_TAG]]
+ fprintf(stderr, "DONE\n");
+ __hwasan_disable_allocator_tagging();
+// CHECK-NOT: DONE
+}
diff --git a/test/hwasan/TestCases/realloc-test.cc b/test/hwasan/TestCases/realloc-test.cc
new file mode 100644
index 000000000..838790242
--- /dev/null
+++ b/test/hwasan/TestCases/realloc-test.cc
@@ -0,0 +1,37 @@
+// Test basic realloc functionality.
+// RUN: %clang_hwasan %s -o %t
+// RUN: %run %t
+
+#include <stdlib.h>
+#include <assert.h>
+#include <sanitizer/hwasan_interface.h>
+
+int main() {
+ __hwasan_enable_allocator_tagging();
+ char *x = (char*)realloc(nullptr, 4);
+ x[0] = 10;
+ x[1] = 20;
+ x[2] = 30;
+ x[3] = 40;
+ char *x1 = (char*)realloc(x, 5);
+ assert(x1 != x); // not necessary true for C,
+ // but true today for hwasan.
+ assert(x1[0] == 10 && x1[1] == 20 && x1[2] == 30 && x1[3] == 40);
+ x1[4] = 50;
+
+ char *x2 = (char*)realloc(x1, 6);
+ x2[5] = 60;
+ assert(x2 != x1);
+ assert(x2[0] == 10 && x2[1] == 20 && x2[2] == 30 && x2[3] == 40 &&
+ x2[4] == 50 && x2[5] == 60);
+
+ char *x3 = (char*)realloc(x2, 6);
+ assert(x3 != x2);
+ assert(x3[0] == 10 && x3[1] == 20 && x3[2] == 30 && x3[3] == 40 &&
+ x3[4] == 50 && x3[5] == 60);
+
+ char *x4 = (char*)realloc(x3, 5);
+ assert(x4 != x3);
+ assert(x4[0] == 10 && x4[1] == 20 && x4[2] == 30 && x4[3] == 40 &&
+ x4[4] == 50);
+}
diff --git a/test/hwasan/TestCases/rich-stack.c b/test/hwasan/TestCases/rich-stack.c
new file mode 100644
index 000000000..6787d5776
--- /dev/null
+++ b/test/hwasan/TestCases/rich-stack.c
@@ -0,0 +1,66 @@
+// Test how stack frames are reported (not fully implemented yet).
+// RUN: %clang_hwasan %s -o %t
+// RUN: not %run %t 3 2 -1 2>&1 | FileCheck %s --check-prefix=R321
+// REQUIRES: stable-runtime
+#include <stdint.h>
+#include <stdlib.h>
+void USE(void *x) { // pretend_to_do_something(void *x)
+ __asm__ __volatile__("" : : "r" (x) : "memory");
+}
+void USE2(void *a, void *b) { USE(a); USE(b); }
+void USE4(void *a, void *b, void *c, void *d) { USE2(a, b); USE2(c, d); }
+
+void BAR(int depth, int err_depth, int offset);
+
+uint64_t *leaked_ptr;
+
+void FOO(int depth, int err_depth, int offset) {
+ uint8_t v1;
+ uint16_t v2;
+ uint32_t v4;
+ uint64_t v8;
+ uint64_t v16[2];
+ uint64_t v32[4];
+ uint64_t v48[3];
+ USE4(&v1, &v2, &v4, &v8); USE4(&v16, &v32, &v48, 0);
+ leaked_ptr = &v16[0];
+ if (depth)
+ BAR(depth - 1, err_depth, offset);
+
+ if (err_depth == depth)
+ v16[offset] = 0; // maybe OOB.
+ if (err_depth == -depth)
+ leaked_ptr[offset] = 0; // maybe UAR.
+ USE(&v16);
+}
+
+void BAR(int depth, int err_depth, int offset) {
+ uint64_t x16[2];
+ uint64_t x32[4];
+ USE2(&x16, &x32);
+ leaked_ptr = &x16[0];
+ if (depth)
+ FOO(depth - 1, err_depth, offset);
+ if (err_depth == depth)
+ x16[offset] = 0; // maybe OOB
+ if (err_depth == -depth)
+ leaked_ptr[offset] = 0; // maybe UAR
+ USE(&x16);
+}
+
+
+int main(int argc, char **argv) {
+ if (argc != 4) return -1;
+ int depth = atoi(argv[1]);
+ int err_depth = atoi(argv[2]);
+ int offset = atoi(argv[3]);
+ FOO(depth, err_depth, offset);
+ return 0;
+}
+
+// R321: HWAddressSanitizer: tag-mismatch
+// R321-NEXT: WRITE of size 8
+// R321-NEXT: in BAR
+// R321-NEXT: in FOO
+// R321-NEXT: in main
+// R321: is located in stack of thread T0
diff --git a/test/hwasan/TestCases/sanitizer_malloc.cc b/test/hwasan/TestCases/sanitizer_malloc.cc
new file mode 100644
index 000000000..66ac9641e
--- /dev/null
+++ b/test/hwasan/TestCases/sanitizer_malloc.cc
@@ -0,0 +1,29 @@
+// Test allocator aliases.
+//
+// RUN: %clangxx_hwasan -O0 %s -o %t && %run %t
+
+#include <sanitizer/hwasan_interface.h>
+
+int main() {
+ void *volatile sink;
+ sink = (void *)&__sanitizer_posix_memalign;
+ sink = (void *)&__sanitizer_memalign;
+ sink = (void *)&__sanitizer_aligned_alloc;
+ sink = (void *)&__sanitizer___libc_memalign;
+ sink = (void *)&__sanitizer_valloc;
+ sink = (void *)&__sanitizer_pvalloc;
+ sink = (void *)&__sanitizer_free;
+ sink = (void *)&__sanitizer_cfree;
+ sink = (void *)&__sanitizer_malloc_usable_size;
+ sink = (void *)&__sanitizer_mallinfo;
+ sink = (void *)&__sanitizer_mallopt;
+ sink = (void *)&__sanitizer_malloc_stats;
+ sink = (void *)&__sanitizer_calloc;
+ sink = (void *)&__sanitizer_realloc;
+ sink = (void *)&__sanitizer_malloc;
+
+ // sanity check
+ void *p = __sanitizer_malloc(100);
+ p = __sanitizer_realloc(p, 200);
+ __sanitizer_free(p);
+}
diff --git a/test/hwasan/TestCases/sizes.cpp b/test/hwasan/TestCases/sizes.cpp
new file mode 100644
index 000000000..52217de74
--- /dev/null
+++ b/test/hwasan/TestCases/sizes.cpp
@@ -0,0 +1,80 @@
+// RUN: %clangxx_hwasan %s -lstdc++ -o %t
+// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-max
+// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t malloc 2>&1
+// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t malloc max 2>&1 | FileCheck %s --check-prefix=CHECK-max
+// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t malloc max 2>&1
+// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-calloc
+// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t calloc 2>&1
+// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t new 2>&1 | FileCheck %s --check-prefix=CHECK-max
+// RUN: %env_hwasan_opts=allocator_may_return_null=1 not %run %t new 2>&1 | FileCheck %s --check-prefix=CHECK-oom
+// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t new max 2>&1 | FileCheck %s --check-prefix=CHECK-max
+// RUN: %env_hwasan_opts=allocator_may_return_null=1 not %run %t new max 2>&1 | FileCheck %s --check-prefix=CHECK-oom
+// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 | FileCheck %s --check-prefix=CHECK-max
+// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1
+// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t new-nothrow max 2>&1 | FileCheck %s --check-prefix=CHECK-max
+// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t new-nothrow max 2>&1
+// RUN: %run %t usable 2>&1
+
+// Tests for various edge cases related to sizes, notably the maximum size the
+// allocator can allocate. Tests that an integer overflow in the parameters of
+// calloc is caught.
+
+#include <assert.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <limits>
+#include <new>
+
+#include <sanitizer/allocator_interface.h>
+
+int main(int argc, char **argv) {
+ assert(argc <= 3);
+ bool test_size_max = argc == 3 && !strcmp(argv[2], "max");
+
+ static const size_t kMaxAllowedMallocSize = 1ULL << 40;
+ static const size_t kChunkHeaderSize = 16;
+
+ size_t MallocSize = test_size_max ? std::numeric_limits<size_t>::max()
+ : kMaxAllowedMallocSize;
+
+ if (!strcmp(argv[1], "malloc")) {
+ void *p = malloc(MallocSize);
+ assert(!p);
+ p = malloc(kMaxAllowedMallocSize - kChunkHeaderSize);
+ assert(!p);
+ } else if (!strcmp(argv[1], "calloc")) {
+ // Trigger an overflow in calloc.
+ size_t size = std::numeric_limits<size_t>::max();
+ void *p = calloc((size / 0x1000) + 1, 0x1000);
+ assert(!p);
+ } else if (!strcmp(argv[1], "new")) {
+ void *p = operator new(MallocSize);
+ assert(!p);
+ } else if (!strcmp(argv[1], "new-nothrow")) {
+ void *p = operator new(MallocSize, std::nothrow);
+ assert(!p);
+ } else if (!strcmp(argv[1], "usable")) {
+ // Playing with the actual usable size of a chunk.
+ void *p = malloc(1007);
+ assert(p);
+ size_t size = __sanitizer_get_allocated_size(p);
+ assert(size >= 1007);
+ memset(p, 'A', size);
+ p = realloc(p, 2014);
+ assert(p);
+ size = __sanitizer_get_allocated_size(p);
+ assert(size >= 2014);
+ memset(p, 'B', size);
+ free(p);
+ } else {
+ assert(0);
+ }
+
+ return 0;
+}
+
+// CHECK-max: {{ERROR: HWAddressSanitizer: requested allocation size .* exceeds maximum supported size}}
+// CHECK-oom: ERROR: HWAddressSanitizer: allocator is out of memory
+// CHECK-calloc: ERROR: HWAddressSanitizer: calloc parameters overflow
diff --git a/test/hwasan/TestCases/stack-history-length.c b/test/hwasan/TestCases/stack-history-length.c
new file mode 100644
index 000000000..f4c0b036f
--- /dev/null
+++ b/test/hwasan/TestCases/stack-history-length.c
@@ -0,0 +1,36 @@
+// RUN: %clang_hwasan -O1 -DX=2046 %s -o %t.2046
+// RUN: %clang_hwasan -O1 -DX=2047 %s -o %t.2047
+// RUN: %env_hwasan_opts=stack_history_size=2048 not %run %t.2046 2>&1 | FileCheck %s --check-prefix=YES
+// RUN: %env_hwasan_opts=stack_history_size=2048 not %run %t.2047 2>&1 | FileCheck %s --check-prefix=NO
+
+// REQUIRES: stable-runtime
+
+#include <stdlib.h>
+
+void USE(void *x) { // pretend_to_do_something(void *x)
+ __asm__ __volatile__("" : : "r" (x) : "memory");
+}
+
+volatile int four = 4;
+__attribute__((noinline)) void FUNC0() { int x[4]; USE(&x[0]); }
+__attribute__((noinline)) void FUNC() { int x[4]; USE(&x[0]); }
+__attribute__((noinline)) void OOB() { int x[4]; x[four] = 0; USE(&x[0]); }
+
+int main() {
+ // FUNC0 is X+2's element of the ring buffer.
+ // If runtime buffer size is less than it, FUNC0 record will be lost.
+ FUNC0();
+ for (int i = 0; i < X; ++i)
+ FUNC();
+ OOB();
+}
+
+// YES: Previosly allocated frames
+// YES: OOB
+// YES: FUNC
+// YES: FUNC0
+
+// NO: Previosly allocated frames
+// NO: OOB
+// NO: FUNC
+// NO-NOT: FUNC0
diff --git a/test/hwasan/TestCases/stack-oob.c b/test/hwasan/TestCases/stack-oob.c
new file mode 100644
index 000000000..567af334e
--- /dev/null
+++ b/test/hwasan/TestCases/stack-oob.c
@@ -0,0 +1,25 @@
+// RUN: %clang_hwasan -DSIZE=16 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clang_hwasan -DSIZE=64 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clang_hwasan -DSIZE=0x1000 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+// REQUIRES: stable-runtime
+
+#include <stdlib.h>
+#include <sanitizer/hwasan_interface.h>
+
+__attribute__((noinline))
+int f() {
+ char z[SIZE];
+ char *volatile p = z;
+ return p[SIZE];
+}
+
+int main() {
+ return f();
+ // CHECK: READ of size 1 at
+ // CHECK: #0 {{.*}} in f{{.*}}stack-oob.c:14
+
+ // CHECK: is located in stack of threa
+
+ // CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in f
+}
diff --git a/test/hwasan/TestCases/stack-oob.cc b/test/hwasan/TestCases/stack-oob.cc
deleted file mode 100644
index 60b9a6295..000000000
--- a/test/hwasan/TestCases/stack-oob.cc
+++ /dev/null
@@ -1,25 +0,0 @@
-// RUN: %clangxx_hwasan -DSIZE=16 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
-// RUN: %clangxx_hwasan -DSIZE=64 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
-// RUN: %clangxx_hwasan -DSIZE=0x1000 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
-
-// REQUIRES: stable-runtime
-
-#include <stdlib.h>
-#include <sanitizer/hwasan_interface.h>
-
-__attribute__((noinline))
-int f() {
- char z[SIZE];
- char *volatile p = z;
- return p[SIZE];
-}
-
-int main() {
- return f();
- // CHECK: READ of size 1 at
- // CHECK: #0 {{.*}} in f{{.*}}stack-oob.cc:14
-
- // CHECK: HWAddressSanitizer can not describe address in more detail.
-
- // CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in f
-}
diff --git a/test/hwasan/TestCases/stack-uar.cc b/test/hwasan/TestCases/stack-uar.c
index e99dcceed..2c59b1785 100644
--- a/test/hwasan/TestCases/stack-uar.cc
+++ b/test/hwasan/TestCases/stack-uar.c
@@ -1,4 +1,4 @@
-// RUN: %clangxx_hwasan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clang_hwasan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
// REQUIRES: stable-runtime
@@ -15,9 +15,9 @@ char *f() {
int main() {
return *f();
// CHECK: READ of size 1 at
- // CHECK: #0 {{.*}} in main{{.*}}stack-uar.cc:16
+ // CHECK: #0 {{.*}} in main{{.*}}stack-uar.c:16
- // CHECK: HWAddressSanitizer can not describe address in more detail.
+ // CHECK: is located in stack of thread
// CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in main
}
diff --git a/test/hwasan/TestCases/thread-uaf.c b/test/hwasan/TestCases/thread-uaf.c
new file mode 100644
index 000000000..33cea1018
--- /dev/null
+++ b/test/hwasan/TestCases/thread-uaf.c
@@ -0,0 +1,56 @@
+// Tests UAF detection where Allocate/Deallocate/Use
+// happen in separate threads.
+// RUN: %clang_hwasan %s -o %t && not %run %t 2>&1 | FileCheck %s
+// REQUIRES: stable-runtime
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <sanitizer/hwasan_interface.h>
+
+char *volatile x;
+int state;
+
+void *Allocate(void *arg) {
+ x = (char*)malloc(10);
+ __sync_fetch_and_add(&state, 1);
+ while (__sync_fetch_and_add(&state, 0) != 3) {}
+ return NULL;
+}
+void *Deallocate(void *arg) {
+ free(x);
+ __sync_fetch_and_add(&state, 1);
+ while (__sync_fetch_and_add(&state, 0) != 3) {}
+ return NULL;
+}
+
+void *Use(void *arg) {
+ x[5] = 42;
+ // CHECK: ERROR: HWAddressSanitizer: tag-mismatch on address
+ // CHECK: WRITE of size 1 {{.*}} in thread T3
+ // CHECK: thread-uaf.c:[[@LINE-3]]
+ // CHECK: freed by thread T2 here
+ // CHECK: in Deallocate
+ // CHECK: previously allocated here:
+ // CHECK: in Allocate
+ // CHECK: Thread: T2 0x
+ // CHECK: Thread: T3 0x
+ __sync_fetch_and_add(&state, 1);
+ return NULL;
+}
+
+int main() {
+ __hwasan_enable_allocator_tagging();
+ pthread_t t1, t2, t3;
+
+ pthread_create(&t1, NULL, Allocate, NULL);
+ while (__sync_fetch_and_add(&state, 0) != 1) {}
+ pthread_create(&t2, NULL, Deallocate, NULL);
+ while (__sync_fetch_and_add(&state, 0) != 2) {}
+ pthread_create(&t3, NULL, Use, NULL);
+
+ pthread_join(t1, NULL);
+ pthread_join(t2, NULL);
+ pthread_join(t3, NULL);
+}
diff --git a/test/hwasan/TestCases/uaf_with_rb_distance.c b/test/hwasan/TestCases/uaf_with_rb_distance.c
new file mode 100644
index 000000000..25aae5256
--- /dev/null
+++ b/test/hwasan/TestCases/uaf_with_rb_distance.c
@@ -0,0 +1,27 @@
+// Checks how we print the developer note "hwasan_dev_note_heap_rb_distance".
+// RUN: %clang_hwasan %s -o %t
+// RUN: not %run %t 10 2>&1 | FileCheck %s --check-prefix=D10
+// RUN: not %run %t 42 2>&1 | FileCheck %s --check-prefix=D42
+
+// REQUIRES: stable-runtime
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sanitizer/hwasan_interface.h>
+
+
+void *p[100];
+
+int main(int argc, char **argv) {
+ __hwasan_enable_allocator_tagging();
+ int distance = argc >= 2 ? atoi(argv[1]) : 1;
+ for (int i = 0; i < 100; i++)
+ p[i] = malloc(i);
+ for (int i = 0; i < 100; i++)
+ free(p[i]);
+
+ *(int*)p[distance] = 0;
+}
+
+// D10: hwasan_dev_note_heap_rb_distance: 90 1023
+// D42: hwasan_dev_note_heap_rb_distance: 58 1023
diff --git a/test/hwasan/TestCases/use-after-free.c b/test/hwasan/TestCases/use-after-free.c
index b9f606011..3dae97b0c 100644
--- a/test/hwasan/TestCases/use-after-free.c
+++ b/test/hwasan/TestCases/use-after-free.c
@@ -1,13 +1,14 @@
-// RUN: %clang_hwasan -O0 -DLOAD %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,LOAD
-// RUN: %clang_hwasan -O1 -DLOAD %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,LOAD
-// RUN: %clang_hwasan -O2 -DLOAD %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,LOAD
-// RUN: %clang_hwasan -O3 -DLOAD %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,LOAD
+// RUN: %clang_hwasan -O0 -DISREAD=1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_hwasan -O1 -DISREAD=1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_hwasan -O2 -DISREAD=1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+// RUN: %clang_hwasan -O3 -DISREAD=1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
-// RUN: %clang_hwasan -O0 -DSTORE %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,STORE
+// RUN: %clang_hwasan -O0 -DISREAD=0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
// REQUIRES: stable-runtime
#include <stdlib.h>
+#include <stdio.h>
#include <sanitizer/hwasan_interface.h>
int main() {
@@ -15,25 +16,22 @@ int main() {
char * volatile x = (char*)malloc(10);
free(x);
__hwasan_disable_allocator_tagging();
-#ifdef STORE
- x[5] = 42;
-#endif
-#ifdef LOAD
- return x[5];
-#endif
- // LOAD: READ of size 1 at
- // LOAD: #0 {{.*}} in main {{.*}}use-after-free.c:22
-
- // STORE: WRITE of size 1 at
- // STORE: #0 {{.*}} in main {{.*}}use-after-free.c:19
-
- // CHECK: freed here:
+ fprintf(stderr, "Going to do a %s\n", ISREAD ? "READ" : "WRITE");
+ // CHECK: Going to do a [[TYPE:[A-Z]*]]
+ int r = 0;
+ if (ISREAD) r = x[5]; else x[5] = 42; // should be on the same line.
+ // CHECK: [[TYPE]] of size 1 at {{.*}} tags: [[PTR_TAG:[0-9a-f][0-9a-f]]]/[[MEM_TAG:[0-9a-f][0-9a-f]]] (ptr/mem)
+ // CHECK: #0 {{.*}} in main {{.*}}use-after-free.c:[[@LINE-2]]
+
+ // CHECK: freed by thread {{.*}} here:
// CHECK: #0 {{.*}} in {{.*}}free{{.*}} {{.*}}hwasan_interceptors.cc
- // CHECK: #1 {{.*}} in main {{.*}}use-after-free.c:16
+ // CHECK: #1 {{.*}} in main {{.*}}use-after-free.c:[[@LINE-11]]
// CHECK: previously allocated here:
// CHECK: #0 {{.*}} in {{.*}}malloc{{.*}} {{.*}}hwasan_interceptors.cc
- // CHECK: #1 {{.*}} in main {{.*}}use-after-free.c:15
-
+ // CHECK: #1 {{.*}} in main {{.*}}use-after-free.c:[[@LINE-16]]
+ // CHECK: Memory tags around the buggy address (one tag corresponds to 16 bytes):
+ // CHECK: =>{{.*}}[[MEM_TAG]]
// CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in main
+ return r;
}
diff --git a/test/lit.common.cfg b/test/lit.common.cfg
index 5274b49c6..659122712 100644
--- a/test/lit.common.cfg
+++ b/test/lit.common.cfg
@@ -106,6 +106,10 @@ config.substitutions.append(
if config.emulator:
config.substitutions.append( ('%run', config.emulator) )
config.substitutions.append( ('%env ', "env ") )
+ # TODO: Implement `%device_rm` to perform removal of files in the emulator.
+ # For now just make it a no-op.
+ lit_config.warning('%device_rm is not implemented')
+ config.substitutions.append( ('%device_rm', 'echo ') )
config.compile_wrapper = ""
elif config.host_os == 'Darwin' and config.apple_platform != "osx":
# Darwin tests can be targetting macOS, a device or a simulator. All devices
@@ -127,14 +131,18 @@ elif config.host_os == 'Darwin' and config.apple_platform != "osx":
ios_or_iossim = "iossim" if config.apple_platform.endswith("sim") else "ios"
config.available_features.add('ios')
+ device_id_env = "SANITIZER_" + ios_or_iossim.upper() + "_TEST_DEVICE_IDENTIFIER"
if ios_or_iossim == "iossim":
config.available_features.add('iossim')
+ if device_id_env not in os.environ:
+ lit_config.fatal(
+ '{} must be set in the environment when running iossim tests'.format(
+ device_id_env))
if config.apple_platform != "ios" and config.apple_platform != "iossim":
config.available_features.add(config.apple_platform)
ios_commands_dir = os.path.join(config.compiler_rt_src_root, "test", "sanitizer_common", "ios_commands")
- device_id_env = "SANITIZER_" + ios_or_iossim.upper() + "_TEST_DEVICE_IDENTIFIER"
run_wrapper = os.path.join(ios_commands_dir, ios_or_iossim + "_run.py")
env_wrapper = os.path.join(ios_commands_dir, ios_or_iossim + "_env.py")
compile_wrapper = os.path.join(ios_commands_dir, ios_or_iossim + "_compile.py")
@@ -144,9 +152,17 @@ elif config.host_os == 'Darwin' and config.apple_platform != "osx":
config.environment[device_id_env] = os.environ[device_id_env]
config.substitutions.append(('%run', run_wrapper))
config.substitutions.append(('%env ', env_wrapper + " "))
+ # Current implementation of %device_rm uses the run_wrapper to do
+ # the work.
+ config.substitutions.append(('%device_rm', '{} rm '.format(run_wrapper)))
config.compile_wrapper = compile_wrapper
- prepare_output = subprocess.check_output([prepare_script, config.apple_platform, config.clang]).strip()
+ try:
+ prepare_output = subprocess.check_output([prepare_script, config.apple_platform, config.clang]).strip()
+ except subprocess.CalledProcessError as e:
+ print("Command failed:")
+ print(e.output)
+ raise e
if len(prepare_output) > 0: print(prepare_output)
prepare_output_json = prepare_output.split("\n")[-1]
prepare_output = json.loads(prepare_output_json)
@@ -157,9 +173,15 @@ elif config.android:
config.compile_wrapper = compile_wrapper
config.substitutions.append( ('%run', "") )
config.substitutions.append( ('%env ', "env ") )
+ # TODO: Implement `%device_rm` to perform removal of files on a device. For
+ # now just make it a no-op.
+ lit_config.warning('%device_rm is not implemented')
+ config.substitutions.append( ('%device_rm', 'echo ') )
else:
config.substitutions.append( ('%run', "") )
config.substitutions.append( ('%env ', "env ") )
+ # When running locally %device_rm is a no-op.
+ config.substitutions.append( ('%device_rm', 'echo ') )
config.compile_wrapper = ""
# Define CHECK-%os to check for OS-dependent output.
@@ -295,7 +317,7 @@ if config.host_os == 'Darwin' and is_darwin_lto_supported():
config.lto_supported = True
config.lto_launch = ["env", "DYLD_LIBRARY_PATH=" + config.llvm_shlib_dir]
config.lto_flags = []
-elif config.host_os == 'Linux' and is_linux_lto_supported():
+elif config.host_os in ['Linux', 'FreeBSD', 'NetBSD'] and is_linux_lto_supported():
config.lto_supported = True
config.lto_launch = []
if config.use_lld:
diff --git a/test/lsan/TestCases/Linux/fork_and_leak.cc b/test/lsan/TestCases/Linux/fork_and_leak.cc
new file mode 100644
index 000000000..d7427ce3e
--- /dev/null
+++ b/test/lsan/TestCases/Linux/fork_and_leak.cc
@@ -0,0 +1,23 @@
+// Test that leaks detected after forking without exec().
+// RUN: %clangxx_lsan %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+int main() {
+ pid_t pid = fork();
+ assert(pid >= 0);
+ if (pid > 0) {
+ int status = 0;
+ waitpid(pid, &status, 0);
+ assert(WIFEXITED(status));
+ return WEXITSTATUS(status);
+ } else {
+ malloc(1337);
+ // CHECK: LeakSanitizer: detected memory leaks
+ }
+ return 0;
+}
+
diff --git a/test/lsan/TestCases/Linux/fork_with_threads.cc b/test/lsan/TestCases/Linux/fork_with_threads.cc
deleted file mode 100644
index 221c5d249..000000000
--- a/test/lsan/TestCases/Linux/fork_with_threads.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-// Test forked process does not run lsan.
-// RUN: %clangxx_lsan %s -o %t && %run %t 2>&1 | FileCheck %s
-
-#include <pthread.h>
-#include <stdlib.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-static pthread_barrier_t barrier;
-
-// CHECK-NOT: SUMMARY: {{(Leak|Address)}}Sanitizer:
-static void *thread_func(void *arg) {
- void *buffer = malloc(1337);
- pthread_barrier_wait(&barrier);
- for (;;)
- pthread_yield();
- return 0;
-}
-
-int main() {
- pthread_barrier_init(&barrier, 0, 2);
- pthread_t tid;
- int res = pthread_create(&tid, 0, thread_func, 0);
- pthread_barrier_wait(&barrier);
- pthread_barrier_destroy(&barrier);
-
- pid_t pid = fork();
- if (pid > 0) {
- int status = 0;
- waitpid(pid, &status, 0);
- }
- return 0;
-}
-
-// CHECK: WARNING: LeakSanitizer is disabled in forked process
diff --git a/test/msan/chained_origin_with_signals.cc b/test/msan/chained_origin_with_signals.cc
index 43dbdcca7..9c071569a 100644
--- a/test/msan/chained_origin_with_signals.cc
+++ b/test/msan/chained_origin_with_signals.cc
@@ -10,6 +10,9 @@
// RUN: not %run %t >%t.out 2>&1
// RUN: FileCheck %s < %t.out
+// Reported deadly signal due to stack-overflow
+// XFAIL: netbsd
+
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
diff --git a/test/msan/dtls_test.c b/test/msan/dtls_test.c
index b9021e0da..ee91be2ff 100644
--- a/test/msan/dtls_test.c
+++ b/test/msan/dtls_test.c
@@ -8,6 +8,9 @@
XFAIL: FreeBSD
UNSUPPORTED: powerpc
+
+ // Reports use-of-uninitialized-value, not analyzed
+ XFAIL: netbsd
*/
#ifndef BUILD_SO
diff --git a/test/msan/fork.cc b/test/msan/fork.cc
index e4dc54908..87d71f87e 100644
--- a/test/msan/fork.cc
+++ b/test/msan/fork.cc
@@ -14,6 +14,9 @@
// UNSUPPORTED: powerpc64-target-arch
// UNSUPPORTED: powerpc64le-target-arch
+// Sometimes hangs
+// UNSUPPORTED: netbsd
+
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
diff --git a/test/msan/ioctl_custom.cc b/test/msan/ioctl_custom.cc
index eaab63384..794f34535 100644
--- a/test/msan/ioctl_custom.cc
+++ b/test/msan/ioctl_custom.cc
@@ -4,6 +4,9 @@
// RUN: %clangxx_msan -DPOSITIVE -O0 -g %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_msan -DPOSITIVE -O3 -g %s -o %t && not %run %t 2>&1 | FileCheck %s
+// Reports different report (not analyzed)
+// XFAIL: netbsd
+
#include <assert.h>
#include <stdlib.h>
#include <net/if.h>
diff --git a/test/msan/lit.cfg b/test/msan/lit.cfg
index 550d04d08..e08673d5c 100644
--- a/test/msan/lit.cfg
+++ b/test/msan/lit.cfg
@@ -17,14 +17,20 @@ clang_msan_cflags = (["-fsanitize=memory",
config.debug_info_flags)
# Some Msan tests leverage backtrace() which requires libexecinfo on FreeBSD.
if config.host_os == 'FreeBSD':
- clang_msan_cflags += ["-lexecinfo"]
+ clang_msan_cflags += ["-lexecinfo", "-fPIC"]
clang_msan_cxxflags = config.cxx_mode_flags + clang_msan_cflags
+# Flags for KMSAN invocation. This is C-only, we're not interested in C++.
+clang_kmsan_cflags = (["-fsanitize=kernel-memory"] +
+ [config.target_cflags] +
+ config.debug_info_flags)
+
def build_invocation(compile_flags):
return " " + " ".join([config.clang] + compile_flags) + " "
config.substitutions.append( ("%clang_msan ", build_invocation(clang_msan_cflags)) )
config.substitutions.append( ("%clangxx_msan ", build_invocation(clang_msan_cxxflags)) )
+config.substitutions.append( ("%clang_kmsan ", build_invocation(clang_kmsan_cflags)) )
# Default test suffixes.
config.suffixes = ['.c', '.cc', '.cpp']
diff --git a/test/msan/signal_stress_test.cc b/test/msan/signal_stress_test.cc
index 5bc6f5921..dfbc580fa 100644
--- a/test/msan/signal_stress_test.cc
+++ b/test/msan/signal_stress_test.cc
@@ -2,6 +2,9 @@
//
// Test that va_arg shadow from a signal handler does not leak outside.
+// Reported deadly signal due to stack-overflow
+// XFAIL: netbsd
+
#include <signal.h>
#include <stdarg.h>
#include <sanitizer/msan_interface.h>
diff --git a/test/msan/tls_reuse.cc b/test/msan/tls_reuse.cc
index 9c2ee975c..78a328fa3 100644
--- a/test/msan/tls_reuse.cc
+++ b/test/msan/tls_reuse.cc
@@ -1,7 +1,6 @@
// RUN: %clangxx_msan -O0 %s -o %t && %run %t
// Check that when TLS block is reused between threads, its shadow is cleaned.
-// XFAIL: freebsd
#include <pthread.h>
#include <stdio.h>
diff --git a/test/msan/vararg.cc b/test/msan/vararg.cc
new file mode 100644
index 000000000..e1a7b1266
--- /dev/null
+++ b/test/msan/vararg.cc
@@ -0,0 +1,60 @@
+// RUN: %clangxx_msan -fsanitize-memory-track-origins=0 -O3 %s -o %t && \
+// RUN: not %run %t va_arg_tls >%t.out 2>&1
+// RUN: FileCheck %s --check-prefix=CHECK < %t.out
+
+// RUN: %clangxx_msan -fsanitize-memory-track-origins=0 -O3 %s -o %t && \
+// RUN: not %run %t overflow >%t.out 2>&1
+// RUN: FileCheck %s --check-prefix=CHECK < %t.out
+
+// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -O3 %s -o %t && \
+// RUN: not %run %t va_arg_tls >%t.out 2>&1
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-ORIGIN < %t.out
+
+// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -O3 %s -o %t && \
+// RUN: not %run %t overflow >%t.out 2>&1
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-ORIGIN < %t.out
+
+// Check that shadow and origin are passed through va_args.
+
+// Copying origins on AArch64, MIPS and PowerPC isn't supported yet.
+// XFAIL: aarch64
+// XFAIL: mips
+// XFAIL: powerpc64
+
+#include <stdarg.h>
+#include <string.h>
+
+__attribute__((noinline))
+int sum(int n, ...) {
+ va_list args;
+ int i, sum = 0, arg;
+ volatile int temp;
+ va_start(args, n);
+ for (i = 0; i < n; i++) {
+ arg = va_arg(args, int);
+ sum += arg;
+ }
+ va_end(args);
+ return sum;
+}
+
+int main(int argc, char *argv[]) {
+ volatile int uninit;
+ volatile int a = 1, b = 2;
+ if (argc == 2) {
+ // Shadow/origin will be passed via va_arg_tls/va_arg_origin_tls.
+ if (strcmp(argv[1], "va_arg_tls") == 0) {
+ return sum(3, uninit, a, b);
+ }
+ // Shadow/origin of |uninit| will be passed via overflow area.
+ if (strcmp(argv[1], "overflow") == 0) {
+ return sum(7,
+ a, a, a, a, a, a, uninit
+ );
+ }
+ }
+ return 0;
+}
+
+// CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value
+// CHECK-ORIGIN: Uninitialized value was created by an allocation of 'uninit' in the stack frame of function 'main'
diff --git a/test/profile/Inputs/instrprof-gcov-exceptions.cpp.gcov b/test/profile/Inputs/instrprof-gcov-exceptions.cpp.gcov
index 7caf50806..2a7b72488 100644
--- a/test/profile/Inputs/instrprof-gcov-exceptions.cpp.gcov
+++ b/test/profile/Inputs/instrprof-gcov-exceptions.cpp.gcov
@@ -6,7 +6,7 @@
// CHECK-NEXT: -: 1:#include <string>
// CHECK-NEXT: -: 2:
// CHECK-NEXT: -: 3:void asd(std::string i) {
-// CHECK-NEXT: 2: 4:}
+// CHECK-NEXT: 1: 4:}
// CHECK-NEXT: -: 5:
// CHECK-NEXT: -: 6:int main(void)
// CHECK-NEXT: -: 7:{
diff --git a/test/profile/Inputs/instrprof-gcov-switch1.c.gcov b/test/profile/Inputs/instrprof-gcov-switch1.c.gcov
index 7d136dc98..f19431e17 100644
--- a/test/profile/Inputs/instrprof-gcov-switch1.c.gcov
+++ b/test/profile/Inputs/instrprof-gcov-switch1.c.gcov
@@ -5,9 +5,9 @@
// CHECK-NEXT: -: 0:Programs:1
// CHECK-NEXT: -: 1:int main(void)
// CHECK-NEXT: -: 2:{
-// CHECK-NEXT: 2: 3: int i = 22;
+// CHECK-NEXT: 1: 3: int i = 22;
// CHECK-NEXT: -: 4:
-// CHECK-NEXT: 2: 5: switch (i) {
+// CHECK-NEXT: 1: 5: switch (i) {
// CHECK-NEXT: -: 6: case 7:
// CHECK-NEXT: #####: 7: break;
// CHECK-NEXT: -: 8:
diff --git a/test/profile/Inputs/instrprof-gcov-switch2.c.gcov b/test/profile/Inputs/instrprof-gcov-switch2.c.gcov
index 67f408606..0b85e0f50 100644
--- a/test/profile/Inputs/instrprof-gcov-switch2.c.gcov
+++ b/test/profile/Inputs/instrprof-gcov-switch2.c.gcov
@@ -5,9 +5,9 @@
// CHECK-NEXT: -: 0:Programs:1
// CHECK-NEXT: -: 1:int main(void)
// CHECK-NEXT: -: 2:{
-// CHECK-NEXT: 3: 3: int i = 22;
+// CHECK-NEXT: 1: 3: int i = 22;
// CHECK-NEXT: -: 4:
-// CHECK-NEXT: 3: 5: switch (i) {
+// CHECK-NEXT: 1: 5: switch (i) {
// CHECK-NEXT: -: 6: case 7:
// CHECK-NEXT: #####: 7: break;
// CHECK-NEXT: -: 8:
diff --git a/test/profile/Inputs/instrprof-shared-main.c.gcov b/test/profile/Inputs/instrprof-shared-main.c.gcov
index 70be36750..1636ca635 100644
--- a/test/profile/Inputs/instrprof-shared-main.c.gcov
+++ b/test/profile/Inputs/instrprof-shared-main.c.gcov
@@ -8,8 +8,8 @@
// CHECK-NEXT: -: 3:
// CHECK-NEXT: -: 4:int main() {
// CHECK-NEXT: -: 5: int i, j;
-// CHECK-NEXT: 2002: 6: for (i = 0; i < 1000; i++)
-// CHECK-NEXT: 2002000: 7: for (j = 0; j < 1000; j++)
+// CHECK-NEXT: 1001: 6: for (i = 0; i < 1000; i++)
+// CHECK-NEXT: 1001000: 7: for (j = 0; j < 1000; j++)
// CHECK-NEXT: 1001000: 8: foo(i * j);
// CHECK-NEXT: -: 9:
// CHECK-NEXT: 1: 10: if (g2 - g1 == 280001)
diff --git a/test/profile/instrprof-dlopen-dlclose-gcov.test b/test/profile/instrprof-dlopen-dlclose-gcov.test
index 0444fca26..8b1b2e294 100644
--- a/test/profile/instrprof-dlopen-dlclose-gcov.test
+++ b/test/profile/instrprof-dlopen-dlclose-gcov.test
@@ -7,6 +7,7 @@ RUN: %clang --coverage -o func3.shared -fPIC -shared %S/Inputs/instrprof-dlopen-
RUN: %clang --coverage -o %t -fPIC -rpath %t.d %S/Inputs/instrprof-dlopen-dlclose-main.c
# Test with two dlopened libraries.
+RUN: rm -f instrprof-dlopen-dlclose-main.gcda instrprof-dlopen-func.gcda instrprof-dlopen-func2.gcda
RUN: %run %t
RUN: llvm-cov gcov instrprof-dlopen-dlclose-main.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-dlopen-dlclose-main.c.gcov %S/Inputs/instrprof-dlopen-dlclose-main.c.gcov
@@ -14,10 +15,10 @@ RUN: llvm-cov gcov instrprof-dlopen-func.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-dlopen-func.c.gcov %S/Inputs/instrprof-dlopen-func.c.gcov
RUN: llvm-cov gcov instrprof-dlopen-func2.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-dlopen-func2.c.gcov %S/Inputs/instrprof-dlopen-func2.c.gcov
-RUN: rm instrprof-dlopen-dlclose-main.gcda instrprof-dlopen-func.gcda instrprof-dlopen-func2.gcda
# Test with three dlopened libraries.
RUN: %clang -DUSE_LIB3 --coverage -o %t -fPIC -rpath %t.d %S/Inputs/instrprof-dlopen-dlclose-main.c
+RUN: rm -f instrprof-dlopen-dlclose-main.gcda instrprof-dlopen-func.gcda instrprof-dlopen-func2.gcda instrprof-dlopen-func3.gcda
RUN: %run %t
RUN: llvm-cov gcov instrprof-dlopen-dlclose-main.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-dlopen-dlclose-main.c.gcov %S/Inputs/instrprof-dlopen-dlclose-main_three-libs.c.gcov
@@ -27,4 +28,3 @@ RUN: llvm-cov gcov instrprof-dlopen-func2.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-dlopen-func2.c.gcov %S/Inputs/instrprof-dlopen-func2.c.gcov
RUN: llvm-cov gcov instrprof-dlopen-func3.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-dlopen-func2.c.gcov %S/Inputs/instrprof-dlopen-func3.c.gcov
-RUN: rm instrprof-dlopen-dlclose-main.gcda instrprof-dlopen-func.gcda instrprof-dlopen-func2.gcda instrprof-dlopen-func3.gcda
diff --git a/test/profile/instrprof-gcov-multiple-bbs-single-line.test b/test/profile/instrprof-gcov-multiple-bbs-single-line.test
index 883945518..66b642920 100644
--- a/test/profile/instrprof-gcov-multiple-bbs-single-line.test
+++ b/test/profile/instrprof-gcov-multiple-bbs-single-line.test
@@ -1,5 +1,3 @@
-XFAIL: *
-
RUN: mkdir -p %t.d
RUN: cd %t.d
diff --git a/test/profile/instrprof-gcov-two-objects.test b/test/profile/instrprof-gcov-two-objects.test
index a53d51dce..4080caa7f 100644
--- a/test/profile/instrprof-gcov-two-objects.test
+++ b/test/profile/instrprof-gcov-two-objects.test
@@ -10,9 +10,9 @@ RUN: test -f instrprof-shared-main.gcno
RUN: %clang --coverage -o %t instrprof-shared-main.o instrprof-shared-lib.o
RUN: test -f %t
+RUN: rm -f instrprof-shared-main.gcda instrprof-shared-lib.gcda
RUN: %run %t
RUN: llvm-cov gcov instrprof-shared-main.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-shared-main.c.gcov %S/Inputs/instrprof-shared-main.c.gcov
RUN: llvm-cov gcov instrprof-shared-lib.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-shared-lib.c.gcov %S/Inputs/instrprof-shared-lib_in-loop.c.gcov
-RUN: rm instrprof-shared-main.gcda instrprof-shared-lib.gcda
diff --git a/test/profile/instrprof-shared-gcov-flush.test b/test/profile/instrprof-shared-gcov-flush.test
index 50292b633..542db0412 100644
--- a/test/profile/instrprof-shared-gcov-flush.test
+++ b/test/profile/instrprof-shared-gcov-flush.test
@@ -11,42 +11,42 @@ RUN: test -f instrprof-shared-lib.gcno
RUN: %clang -DEXIT_ABRUPTLY -DSHARED_CALL_BEFORE_GCOV_FLUSH -DSHARED_CALL_AFTER_GCOV_FLUSH --coverage -o %t -L%t.d -rpath %t.d -lfunc %S/Inputs/instrprof-shared-main-gcov-flush.c
RUN: test -f instrprof-shared-main-gcov-flush.gcno
+RUN: rm -f instrprof-shared-main-gcov-flush.gcda instrprof-shared-lib.gcda
RUN: %run %t
RUN: llvm-cov gcov instrprof-shared-main-gcov-flush.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-shared-main-gcov-flush.c.gcov %S/Inputs/instrprof-shared-main-gcov-flush_no-writeout.c.gcov
RUN: llvm-cov gcov instrprof-shared-lib.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-shared-lib.c.gcov %S/Inputs/instrprof-shared-lib.c.gcov
-RUN: rm instrprof-shared-main-gcov-flush.gcda instrprof-shared-lib.gcda
# Test the case where we exit normally and we have a call to the shared library function before __gcov_flush.
RUN: %clang -DSHARED_CALL_BEFORE_GCOV_FLUSH --coverage -o %t -L%t.d -rpath %t.d -lfunc %S/Inputs/instrprof-shared-main-gcov-flush.c
RUN: test -f instrprof-shared-main-gcov-flush.gcno
+RUN: rm -f instrprof-shared-main-gcov-flush.gcda instrprof-shared-lib.gcda
RUN: %run %t
RUN: llvm-cov gcov instrprof-shared-main-gcov-flush.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-shared-main-gcov-flush.c.gcov %S/Inputs/instrprof-shared-main-gcov-flush_shared-call-before.c.gcov
RUN: llvm-cov gcov instrprof-shared-lib.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-shared-lib.c.gcov %S/Inputs/instrprof-shared-lib.c.gcov
-RUN: rm instrprof-shared-main-gcov-flush.gcda instrprof-shared-lib.gcda
# Test the case where we exit normally and we have a call to the shared library function after __gcov_flush.
RUN: %clang -DSHARED_CALL_AFTER_GCOV_FLUSH --coverage -o %t -L%t.d -rpath %t.d -lfunc %S/Inputs/instrprof-shared-main-gcov-flush.c
RUN: test -f instrprof-shared-main-gcov-flush.gcno
+RUN: rm -f instrprof-shared-main-gcov-flush.gcda instrprof-shared-lib.gcda
RUN: %run %t
RUN: llvm-cov gcov instrprof-shared-main-gcov-flush.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-shared-main-gcov-flush.c.gcov %S/Inputs/instrprof-shared-main-gcov-flush_shared-call-after.c.gcov
RUN: llvm-cov gcov instrprof-shared-lib.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-shared-lib.c.gcov %S/Inputs/instrprof-shared-lib.c.gcov
-RUN: rm instrprof-shared-main-gcov-flush.gcda instrprof-shared-lib.gcda
# Test the case where we exit normally and we have calls to the shared library function before and after __gcov_flush.
RUN: %clang -DSHARED_CALL_BEFORE_GCOV_FLUSH -DSHARED_CALL_AFTER_GCOV_FLUSH --coverage -o %t -L%t.d -rpath %t.d -lfunc %S/Inputs/instrprof-shared-main-gcov-flush.c
RUN: test -f instrprof-shared-main-gcov-flush.gcno
+RUN: rm -f instrprof-shared-main-gcov-flush.gcda instrprof-shared-lib.gcda
RUN: %run %t
RUN: llvm-cov gcov instrprof-shared-main-gcov-flush.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-shared-main-gcov-flush.c.gcov %S/Inputs/instrprof-shared-main-gcov-flush_shared-call-before-after.c.gcov
RUN: llvm-cov gcov instrprof-shared-lib.gcda
RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-shared-lib.c.gcov %S/Inputs/instrprof-shared-lib_called-twice.c.gcov
-RUN: rm instrprof-shared-main-gcov-flush.gcda instrprof-shared-lib.gcda
diff --git a/test/safestack/lit.cfg b/test/safestack/lit.cfg
index 10cd8a5a5..f58ec4553 100644
--- a/test/safestack/lit.cfg
+++ b/test/safestack/lit.cfg
@@ -18,5 +18,5 @@ config.substitutions.append( ("%clang_safestack ", config.clang + " -O0 -fsaniti
if config.lto_supported:
config.substitutions.append((r"%clang_lto_safestack ", ' '.join(config.lto_launch + [config.clang] + config.lto_flags + ['-fsanitize=safe-stack '])))
-if config.host_os not in ['Linux', 'FreeBSD', 'Darwin', 'NetBSD']:
+if config.host_os not in ['Linux', 'FreeBSD', 'NetBSD']:
config.unsupported = True
diff --git a/test/safestack/pthread-cleanup.c b/test/safestack/pthread-cleanup.c
index 805366c9f..55c5b42dc 100644
--- a/test/safestack/pthread-cleanup.c
+++ b/test/safestack/pthread-cleanup.c
@@ -1,7 +1,9 @@
// RUN: %clang_safestack %s -pthread -o %t
-// RUN: not --crash %run %t
+// RUN: %run %t 0
+// RUN: not --crash %run %t 1
-// Test that unsafe stacks are deallocated correctly on thread exit.
+// Test unsafe stack deallocation. Unsafe stacks are not deallocated immediately
+// at thread exit. They are deallocated by following exiting threads.
#include <stdlib.h>
#include <string.h>
@@ -9,7 +11,7 @@
enum { kBufferSize = (1 << 15) };
-void *t1_start(void *ptr)
+void *start(void *ptr)
{
char buffer[kBufferSize];
return buffer;
@@ -17,15 +19,36 @@ void *t1_start(void *ptr)
int main(int argc, char **argv)
{
- pthread_t t1;
- char *buffer = NULL;
+ int arg = atoi(argv[1]);
- if (pthread_create(&t1, NULL, t1_start, NULL))
+ pthread_t t1, t2;
+ char *t1_buffer = NULL;
+
+ if (pthread_create(&t1, NULL, start, NULL))
abort();
- if (pthread_join(t1, &buffer))
+ if (pthread_join(t1, &t1_buffer))
abort();
- // should segfault here
- memset(buffer, 0, kBufferSize);
+ // Stack has not yet been deallocated
+ memset(t1_buffer, 0, kBufferSize);
+
+ if (arg == 0)
+ return 0;
+
+ for (int i = 0; i < 3; i++) {
+ if (pthread_create(&t2, NULL, start, NULL))
+ abort();
+ // Second thread destructor cleans up the first thread's stack.
+ if (pthread_join(t2, NULL))
+ abort();
+
+ // Should segfault here
+ memset(t1_buffer, 0, kBufferSize);
+
+ // PR39001: Re-try in the rare case that pthread_join() returns before the
+ // thread finishes exiting in the kernel--hence the tgkill() check for t1
+ // returns that it's alive despite pthread_join() returning.
+ sleep(1);
+ }
return 0;
}
diff --git a/test/safestack/pthread-stack-size.c b/test/safestack/pthread-stack-size.c
new file mode 100644
index 000000000..2fe7c680e
--- /dev/null
+++ b/test/safestack/pthread-stack-size.c
@@ -0,0 +1,53 @@
+// RUN: %clang_safestack %s -pthread -o %t
+// RUN: %run %t
+
+// Test unsafe stack deallocation with custom stack sizes, in particular ensure
+// that we correctly deallocate small stacks and don't accidentally deallocate
+// adjacent memory.
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+volatile int step = 0;
+
+void *wait_until(void *ptr) {
+ while ((int)ptr != step)
+ usleep(1000);
+
+ volatile char buf[64];
+ buf[0] = 0;
+
+ return NULL;
+}
+
+int main(int argc, char **argv) {
+ pthread_t t1, t2, t3;
+
+ pthread_attr_t small_stack_attr;
+ pthread_attr_init(&small_stack_attr);
+ pthread_attr_setstacksize(&small_stack_attr, 65536);
+
+ if (pthread_create(&t3, NULL, wait_until, (void *)3))
+ abort();
+ if (pthread_create(&t1, &small_stack_attr, wait_until, (void *)1))
+ abort();
+ if (pthread_create(&t2, NULL, wait_until, (void *)2))
+ abort();
+
+ step = 1;
+ if (pthread_join(t1, NULL))
+ abort();
+
+ step = 2;
+ if (pthread_join(t2, NULL))
+ abort();
+
+ step = 3;
+ if (pthread_join(t3, NULL))
+ abort();
+
+ pthread_attr_destroy(&small_stack_attr);
+ return 0;
+}
diff --git a/test/safestack/pthread.c b/test/safestack/pthread.c
index 416586ee1..1687c10a6 100644
--- a/test/safestack/pthread.c
+++ b/test/safestack/pthread.c
@@ -1,8 +1,6 @@
// RUN: %clang_safestack %s -pthread -o %t
// RUN: %run %t
-// XFAIL: darwin
-
// Test that pthreads receive their own unsafe stack.
#include <stdlib.h>
diff --git a/test/sanitizer_common/TestCases/Posix/devname_r.cc b/test/sanitizer_common/TestCases/Posix/devname_r.cc
index 826b7c92e..3a05dae72 100644
--- a/test/sanitizer_common/TestCases/Posix/devname_r.cc
+++ b/test/sanitizer_common/TestCases/Posix/devname_r.cc
@@ -17,8 +17,13 @@ int main(void) {
type = S_ISCHR(st.st_mode) ? S_IFCHR : S_IFBLK;
+#if defined(__NetBSD__)
+ if (devname_r(st.st_rdev, type, name, sizeof(name)))
+ exit(1);
+#else
if (!devname_r(st.st_rdev, type, name, sizeof(name)))
exit(1);
+#endif
printf("%s\n", name);
diff --git a/test/sanitizer_common/TestCases/get_module_and_offset_for_pc.cc b/test/sanitizer_common/TestCases/get_module_and_offset_for_pc.cc
index 0591d356f..b313df87c 100644
--- a/test/sanitizer_common/TestCases/get_module_and_offset_for_pc.cc
+++ b/test/sanitizer_common/TestCases/get_module_and_offset_for_pc.cc
@@ -1,6 +1,6 @@
// RUN: mkdir -p %t-dir
// RUN: %clangxx -DSHARED %s -shared -o %t-dir/get_module_and_offset_for_pc.so -fPIC
-// RUN: %clangxx -DSO_DIR=\"%t-dir\" -O0 %s -ldl -o %t
+// RUN: %clangxx -DSO_DIR=\"%t-dir\" -O0 %s -o %t
// RUN: %run %t 2>&1 | FileCheck %s
// UNSUPPORTED: i386-darwin
diff --git a/test/sanitizer_common/TestCases/pthread_mutexattr_get.cc b/test/sanitizer_common/TestCases/pthread_mutexattr_get.cc
index 26060f395..742ae72c4 100644
--- a/test/sanitizer_common/TestCases/pthread_mutexattr_get.cc
+++ b/test/sanitizer_common/TestCases/pthread_mutexattr_get.cc
@@ -1,5 +1,8 @@
// RUN: %clangxx -O0 %s -o %t && %run %t
+// pthread_mutexattr_setpshared and pthread_mutexattr_getpshared unavailable
+// UNSUPPORTED: netbsd
+
#include <assert.h>
#include <pthread.h>
diff --git a/test/sanitizer_common/TestCases/sanitizer_coverage_symbolize.cc b/test/sanitizer_common/TestCases/sanitizer_coverage_symbolize.cc
index 28e237802..daa994c81 100644
--- a/test/sanitizer_common/TestCases/sanitizer_coverage_symbolize.cc
+++ b/test/sanitizer_common/TestCases/sanitizer_coverage_symbolize.cc
@@ -7,7 +7,7 @@
// RUN: rm -rf $DIR
// RUN: mkdir -p $DIR
// RUN: cd $DIR
-// RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -ldl -o %t
+// RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -o %t
// RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s
// RUN: rm -rf $DIR
diff --git a/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc b/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc
index 1adbf653b..5e01a803b 100644
--- a/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc
+++ b/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc
@@ -9,7 +9,7 @@
// RUN: rm -rf $DIR
// RUN: mkdir -p $DIR
// RUN: cd $DIR
-// RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -ldl -o %t
+// RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -o %t
// RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s
// RUN: %sancovcc -covered-functions -strip_path_prefix=TestCases/ *.sancov %t 2>&1 | \
// RUN: FileCheck --check-prefix=CHECK-SANCOV %s
diff --git a/test/sanitizer_common/TestCases/symbolize_pc.cc b/test/sanitizer_common/TestCases/symbolize_pc.cc
index 68a6733f4..16a81f1bb 100644
--- a/test/sanitizer_common/TestCases/symbolize_pc.cc
+++ b/test/sanitizer_common/TestCases/symbolize_pc.cc
@@ -7,6 +7,17 @@
int GLOBAL_VAR_ABC;
+void SymbolizeSmallBuffer() {
+ char data[] = "abcdef";
+ __sanitizer_symbolize_pc(__builtin_return_address(0), "%p %F %L", data, 0);
+ printf("UNCHANGED '%s'\n", data);
+ __sanitizer_symbolize_pc(__builtin_return_address(0), "%p %F %L", data, 1);
+ printf("EMPTY '%s'\n", data);
+ __sanitizer_symbolize_pc(__builtin_return_address(0), "%p %F %L", data,
+ sizeof(data));
+ printf("PARTIAL '%s'\n", data);
+}
+
void SymbolizeCaller() {
char data[100];
__sanitizer_symbolize_pc(__builtin_return_address(0), "%p %F %L", data,
@@ -31,10 +42,16 @@ void SymbolizeData() {
printf("GLOBAL: %s\n", data);
}
-// CHECK: FIRST_FORMAT 0x{{.*}} in main symbolize_pc.cc:[[@LINE+3]]
-// CHECK: SECOND_FORMAT FUNC:main LINE:[[@LINE+2]] FILE:symbolize_pc.cc
int main() {
+ // CHECK: UNCHANGED 'abcdef'
+ // CHECK: EMPTY ''
+ // CHECK: PARTIAL '0x{{.*}}'
+ SymbolizeSmallBuffer();
+
+ // CHECK: FIRST_FORMAT 0x{{.*}} in main symbolize_pc.cc:[[@LINE+2]]
+ // CHECK: SECOND_FORMAT FUNC:main LINE:[[@LINE+1]] FILE:symbolize_pc.cc
SymbolizeCaller();
+
+ // CHECK: GLOBAL: GLOBAL_VAR_ABC
SymbolizeData();
}
-// CHECK: GLOBAL: GLOBAL_VAR_ABC
diff --git a/test/sanitizer_common/TestCases/symbolize_pc_inline.cc b/test/sanitizer_common/TestCases/symbolize_pc_inline.cc
new file mode 100644
index 000000000..9589a3af0
--- /dev/null
+++ b/test/sanitizer_common/TestCases/symbolize_pc_inline.cc
@@ -0,0 +1,32 @@
+// RUN: %clangxx -O3 %s -o %t
+// RUN: %env_tool_opts=strip_path_prefix=/TestCases/ %run %t 2>&1 | FileCheck %s
+// RUN: %env_tool_opts=strip_path_prefix=/TestCases/:symbolize_inline_frames=0 \
+// RUN: %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK-NOINLINE
+
+// XFAIL: darwin
+
+#include <sanitizer/common_interface_defs.h>
+#include <stdio.h>
+#include <string.h>
+
+char buffer[10000];
+
+__attribute__((noinline)) static void Symbolize() {
+ __sanitizer_symbolize_pc(__builtin_return_address(0), "%p %F %L", buffer,
+ sizeof(buffer));
+ for (char *p = buffer; strlen(p); p += strlen(p) + 1)
+ printf("%s\n", p);
+}
+
+// CHECK-NOINLINE: {{0x[0-9a-f]+}} in main symbolize_pc_inline.cc:[[@LINE+2]]
+// CHECK: [[ADDR:0x[0-9a-f]+]] in C2 symbolize_pc_inline.cc:[[@LINE+1]]
+static inline void C2() { Symbolize(); }
+
+// CHECK: [[ADDR]] in C3 symbolize_pc_inline.cc:[[@LINE+1]]
+static inline void C3() { C2(); }
+
+// CHECK: [[ADDR]] in C4 symbolize_pc_inline.cc:[[@LINE+1]]
+static inline void C4() { C3(); }
+
+// CHECK: [[ADDR]] in main symbolize_pc_inline.cc:[[@LINE+1]]
+int main() { C4(); }
diff --git a/test/sanitizer_common/ios_commands/iossim_run.py b/test/sanitizer_common/ios_commands/iossim_run.py
index 47b847f53..0d9ca935c 100755
--- a/test/sanitizer_common/ios_commands/iossim_run.py
+++ b/test/sanitizer_common/ios_commands/iossim_run.py
@@ -1,6 +1,6 @@
#!/usr/bin/python
-import os, sys, subprocess
+import glob, os, pipes, sys, subprocess
if not "SANITIZER_IOSSIM_TEST_DEVICE_IDENTIFIER" in os.environ:
@@ -8,11 +8,29 @@ if not "SANITIZER_IOSSIM_TEST_DEVICE_IDENTIFIER" in os.environ:
device_id = os.environ["SANITIZER_IOSSIM_TEST_DEVICE_IDENTIFIER"]
-for e in ["ASAN_OPTIONS", "TSAN_OPTIONS"]:
+for e in ["ASAN_OPTIONS", "TSAN_OPTIONS", "UBSAN_OPTIONS"]:
if e in os.environ:
os.environ["SIMCTL_CHILD_" + e] = os.environ[e]
-exitcode = subprocess.call(["xcrun", "simctl", "spawn", device_id] + sys.argv[1:])
+prog = sys.argv[1]
+exit_code = None
+if prog == 'rm':
+ # The simulator and host actually share the same file system so we can just
+ # execute directly on the host.
+ rm_args = []
+ for arg in sys.argv[2:]:
+ if '*' in arg or '?' in arg:
+ # Don't quote glob pattern
+ rm_args.append(arg)
+ else:
+ # FIXME(dliew): pipes.quote() is deprecated
+ rm_args.append(pipes.quote(arg))
+ rm_cmd_line = ["/bin/rm"] + rm_args
+ rm_cmd_line_str = ' '.join(rm_cmd_line)
+ # We use `shell=True` so that any wildcard globs get expanded by the shell.
+ exitcode = subprocess.call(rm_cmd_line_str, shell=True)
+else:
+ exitcode = subprocess.call(["xcrun", "simctl", "spawn", device_id] + sys.argv[1:])
if exitcode > 125:
exitcode = 126
sys.exit(exitcode)
diff --git a/test/sanitizer_common/lit.common.cfg b/test/sanitizer_common/lit.common.cfg
index 1b347bf2b..75cc7b77e 100644
--- a/test/sanitizer_common/lit.common.cfg
+++ b/test/sanitizer_common/lit.common.cfg
@@ -46,7 +46,13 @@ if default_tool_options_str:
config.environment[tool_options] = default_tool_options_str
default_tool_options_str += ':'
+if config.host_os in ['Linux']:
+ extra_link_flags = ["-ldl"]
+else:
+ extra_link_flags = []
+
clang_cflags = config.debug_info_flags + tool_cflags + [config.target_cflags]
+clang_cflags += extra_link_flags
clang_cxxflags = config.cxx_mode_flags + clang_cflags
def build_invocation(compile_flags):
diff --git a/test/scudo/dealloc-race.c b/test/scudo/dealloc-race.c
new file mode 100644
index 000000000..326e22f27
--- /dev/null
+++ b/test/scudo/dealloc-race.c
@@ -0,0 +1,69 @@
+// RUN: %clang_scudo %s -O2 -o %t
+// RUN: %env_scudo_opts="QuarantineChunksUpToSize=0" %run %t 2>&1
+
+// This test attempts to reproduce a race condition in the deallocation path
+// when bypassing the Quarantine. The old behavior was to zero-out the chunk
+// header after checking its checksum, state & various other things, but that
+// left a window during which 2 (or more) threads could deallocate the same
+// chunk, with a net result of having said chunk present in those distinct
+// thread caches.
+
+// A passing test means all the children died with an error. The failing
+// scenario involves winning a race, so repro can be scarce.
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+const int kNumThreads = 2;
+pthread_t tid[kNumThreads];
+
+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+char go = 0;
+
+// Frees the pointer passed when signaled to.
+void *thread_free(void *p) {
+ pthread_mutex_lock(&mutex);
+ while (!go)
+ pthread_cond_wait(&cond, &mutex);
+ pthread_mutex_unlock(&mutex);
+ free(p);
+ return 0;
+}
+
+// Allocates a chunk, and attempts to free it "simultaneously" by 2 threads.
+void child(void) {
+ void *p = malloc(16);
+ for (int i = 0; i < kNumThreads; i++)
+ pthread_create(&tid[i], 0, thread_free, p);
+ pthread_mutex_lock(&mutex);
+ go = 1;
+ pthread_cond_broadcast(&cond);
+ pthread_mutex_unlock(&mutex);
+ for (int i = 0; i < kNumThreads; i++)
+ pthread_join(tid[i], 0);
+}
+
+int main(int argc, char** argv) {
+ const int kChildren = 40;
+ pid_t pid;
+ for (int i = 0; i < kChildren; ++i) {
+ pid = fork();
+ if (pid < 0) {
+ exit(1);
+ } else if (pid == 0) {
+ child();
+ exit(0);
+ } else {
+ int status;
+ wait(&status);
+ // A 0 status means the child didn't die with an error. The race was won.
+ if (status == 0)
+ exit(1);
+ }
+ }
+ return 0;
+}
diff --git a/test/tsan/Darwin/gcd-sync-block-copy.mm b/test/tsan/Darwin/gcd-sync-block-copy.mm
new file mode 100644
index 000000000..87658d7f3
--- /dev/null
+++ b/test/tsan/Darwin/gcd-sync-block-copy.mm
@@ -0,0 +1,34 @@
+// This test verifies that dispatch_sync() doesn't actually copy the block under TSan (without TSan, it doesn't).
+
+// RUN: %clang_tsan -fno-sanitize=thread %s -o %t_no_tsan -framework Foundation
+// RUN: %run %t_no_tsan 2>&1 | FileCheck %s
+
+// RUN: %clang_tsan %s -o %t_with_tsan -framework Foundation
+// RUN: %run %t_with_tsan 2>&1 | FileCheck %s
+
+#import <Foundation/Foundation.h>
+
+@interface MyClass : NSObject
+@end
+
+@implementation MyClass
+- (instancetype)retain {
+ // Copying the dispatch_sync'd block below will increment the retain count of
+ // this object. Abort if that happens.
+ abort();
+}
+@end
+
+int main(int argc, const char* argv[]) {
+ dispatch_queue_t q = dispatch_queue_create("my.queue", NULL);
+ id object = [[MyClass alloc] init];
+ dispatch_sync(q, ^{
+ NSLog(@"%@", object);
+ });
+ [object release];
+ NSLog(@"Done.");
+ return 0;
+}
+
+// CHECK: Done.
+// CHECK-NOT: WARNING: ThreadSanitizer
diff --git a/test/tsan/large_malloc_meta.cc b/test/tsan/large_malloc_meta.cc
index e83004824..0d0eec34b 100644
--- a/test/tsan/large_malloc_meta.cc
+++ b/test/tsan/large_malloc_meta.cc
@@ -1,4 +1,7 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// UNSUPPORTED: ios
+
#include "test.h"
#include <sys/mman.h>
diff --git a/test/tsan/mmap_large.cc b/test/tsan/mmap_large.cc
index 764e954f2..c8d258e80 100644
--- a/test/tsan/mmap_large.cc
+++ b/test/tsan/mmap_large.cc
@@ -1,4 +1,7 @@
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// UNSUPPORTED: ios
+
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
diff --git a/test/ubsan/CMakeLists.txt b/test/ubsan/CMakeLists.txt
index fa8b16b80..351bf3b82 100644
--- a/test/ubsan/CMakeLists.txt
+++ b/test/ubsan/CMakeLists.txt
@@ -62,6 +62,31 @@ foreach(arch ${UBSAN_TEST_ARCH})
endif()
endforeach()
+macro(add_ubsan_device_testsuite test_mode sanitizer platform arch)
+ # Note we expect the caller to have already set UBSAN_TEST_TARGET_CFLAGS
+ set(UBSAN_LIT_TEST_MODE "${test_mode}")
+ set(CONFIG_NAME ${UBSAN_LIT_TEST_MODE}-${platform}-${arch})
+ set(UBSAN_TEST_TARGET_ARCH ${arch})
+ set(UBSAN_TEST_USE_LLD "False")
+ set(UBSAN_TEST_USE_THINLTO "False")
+ if (APPLE)
+ set(UBSAN_TEST_APPLE_PLATFORM "${platform}")
+ else()
+ unset(UBSAN_TEST_APPLE_PLATFORM)
+ endif()
+ configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg)
+ #list(APPEND UBSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME})
+ if(NOT COMPILER_RT_STANDALONE_BUILD)
+ list(APPEND UBSAN_TEST_DEPS ${sanitizer})
+ endif()
+ add_lit_testsuite(check-ubsan-${test_mode}-${platform}-${arch}
+ "UBSan ${CONFIG_NAME} tests"
+ ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/
+ DEPENDS ${UBSAN_TEST_DEPS})
+endmacro()
+
if(APPLE)
foreach(arch ${UBSAN_TEST_ARCH})
set(UBSAN_TEST_TARGET_ARCH ${arch})
@@ -69,9 +94,47 @@ if(APPLE)
set(UBSAN_TEST_TARGET_CFLAGS "${UBSAN_TEST_TARGET_CFLAGS} -lc++abi")
add_ubsan_testsuites("StandaloneStatic" ubsan ${arch})
endforeach()
+
+ # Device and simulator test suites.
+ # These are not added into "check-all", in order to run these tests, use
+ # "check-asan-iossim-x86_64" and similar. They also require that an extra env
+ # variable to select which iOS device or simulator to use, e.g.:
+ # SANITIZER_IOSSIM_TEST_DEVICE_IDENTIFIER="iPhone 6"
+ set(EXCLUDE_FROM_ALL ON)
+ set(UBSAN_APPLE_PLATFORMS "")
+ if (COMPILER_RT_ENABLE_IOS)
+ list(APPEND UBSAN_APPLE_PLATFORMS ios iossim)
+ endif()
+ if (COMPILER_RT_ENABLE_WATCHOS)
+ list(APPEND UBSAN_APPLE_PLATFORMS watchos watchossim)
+ endif()
+ if (COMPILER_RT_ENABLE_TVOS)
+ list(APPEND UBSAN_APPLE_PLATFORMS tvos tvossim)
+ endif()
+ foreach(platform ${UBSAN_APPLE_PLATFORMS})
+ foreach(arch ${DARWIN_${platform}_ARCHS})
+ get_target_flags_for_arch(${arch} UBSAN_TEST_TARGET_ARCH_FLAGS_AS_LIST)
+ string(REPLACE ";" " " UBSAN_TEST_TARGET_ARCH_FLAGS "${UBSAN_TEST_TARGET_ARCH_FLAGS_AS_LIST}")
+ set(UBSAN_TEST_TARGET_CFLAGS
+ "${UBSAN_TEST_TARGET_ARCH_FLAGS} -isysroot ${DARWIN_${platform}_SYSROOT}")
+ if (";${UBSAN_SUPPORTED_ARCH};" MATCHES ";${arch};")
+ add_ubsan_device_testsuite("Standalone" ubsan ${platform} ${arch})
+ endif()
+
+ if(COMPILER_RT_HAS_ASAN AND ";${ASAN_SUPPORTED_ARCH};" MATCHES ";${arch};")
+ add_ubsan_device_testsuite("AddressSanitizer" asan ${platform} ${arch})
+ endif()
+
+ if(COMPILER_RT_HAS_TSAN AND ";${TSAN_SUPPORTED_ARCH};" MATCHES ";${arch};")
+ add_ubsan_device_testsuite("ThreadSanitizer" tsan ${platform} ${arch})
+ endif()
+ endforeach()
+ endforeach()
+ set(EXCLUDE_FROM_ALL OFF)
endif()
add_lit_testsuite(check-ubsan "Running UndefinedBehaviorSanitizer tests"
${UBSAN_TESTSUITES}
DEPENDS ${UBSAN_TEST_DEPS})
set_target_properties(check-ubsan PROPERTIES FOLDER "Compiler-RT Misc")
+
diff --git a/test/ubsan/TestCases/ImplicitConversion/integer-conversion.c b/test/ubsan/TestCases/ImplicitConversion/integer-conversion.c
new file mode 100644
index 000000000..d49d04385
--- /dev/null
+++ b/test/ubsan/TestCases/ImplicitConversion/integer-conversion.c
@@ -0,0 +1,324 @@
+// RUN: %clang -x c -fsanitize=implicit-conversion %s -DV0 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V0
+// RUN: %clang -x c -fsanitize=implicit-conversion %s -DV1 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V1
+// RUN: %clang -x c -fsanitize=implicit-conversion %s -DV2 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V2
+// RUN: %clang -x c -fsanitize=implicit-conversion %s -DV3 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V3
+// RUN: %clang -x c -fsanitize=implicit-conversion %s -DV4 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V4
+// RUN: %clang -x c -fsanitize=implicit-conversion %s -DV5 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V5
+// RUN: %clang -x c -fsanitize=implicit-conversion %s -DV6 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V6
+// RUN: %clang -x c -fsanitize=implicit-conversion %s -DV7 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V7
+
+// RUN: %clang -x c++ -fsanitize=implicit-conversion %s -DV0 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V0
+// RUN: %clang -x c++ -fsanitize=implicit-conversion %s -DV1 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V1
+// RUN: %clang -x c++ -fsanitize=implicit-conversion %s -DV2 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V2
+// RUN: %clang -x c++ -fsanitize=implicit-conversion %s -DV3 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V3
+// RUN: %clang -x c++ -fsanitize=implicit-conversion %s -DV4 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V4
+// RUN: %clang -x c++ -fsanitize=implicit-conversion %s -DV5 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V5
+// RUN: %clang -x c++ -fsanitize=implicit-conversion %s -DV6 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V6
+// RUN: %clang -x c++ -fsanitize=implicit-conversion %s -DV7 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V7
+
+#include <stdint.h>
+
+// Test plan:
+// * Two types - int and char
+// * Two signs - signed and unsigned
+// * Square that - we have input and output types.
+// Thus, there are total of (2*2)^2 == 16 tests.
+// These are all the possible variations/combinations of casts.
+// However, not all of them should result in the check.
+// So here, we *only* check which should and which should not result in checks.
+
+unsigned int convert_unsigned_int_to_unsigned_int(unsigned int x) {
+#line 100
+ return x;
+}
+
+unsigned char convert_unsigned_char_to_unsigned_char(unsigned char x) {
+#line 200
+ return x;
+}
+
+signed int convert_signed_int_to_signed_int(signed int x) {
+#line 300
+ return x;
+}
+
+signed char convert_signed_char_to_signed_char(signed char x) {
+#line 400
+ return x;
+}
+
+unsigned char convert_unsigned_int_to_unsigned_char(unsigned int x) {
+#line 500
+ return x;
+}
+
+unsigned int convert_unsigned_char_to_unsigned_int(unsigned char x) {
+#line 600
+ return x;
+}
+
+signed int convert_unsigned_char_to_signed_int(unsigned char x) {
+#line 700
+ return x;
+}
+
+signed int convert_signed_char_to_signed_int(signed char x) {
+#line 800
+ return x;
+}
+
+signed int convert_unsigned_int_to_signed_int(unsigned int x) {
+#line 900
+ return x;
+}
+
+unsigned int convert_signed_int_to_unsigned_int(signed int x) {
+#line 1000
+ return x;
+}
+
+unsigned char convert_signed_int_to_unsigned_char(signed int x) {
+#line 1100
+ return x;
+}
+
+unsigned char convert_signed_char_to_unsigned_char(signed char x) {
+#line 1200
+ return x;
+}
+
+signed char convert_unsigned_char_to_signed_char(unsigned char x) {
+#line 1300
+ return x;
+}
+
+unsigned int convert_signed_char_to_unsigned_int(signed char x) {
+#line 1400
+ return x;
+}
+
+signed char convert_unsigned_int_to_signed_char(unsigned int x) {
+#line 1500
+ return x;
+}
+
+signed char convert_signed_int_to_signed_char(signed int x) {
+#line 1600
+ return x;
+}
+
+#line 1111 // !!!
+
+int main() {
+ // No bits set.
+ convert_unsigned_int_to_unsigned_int(0);
+ convert_unsigned_char_to_unsigned_char(0);
+ convert_signed_int_to_signed_int(0);
+ convert_signed_char_to_signed_char(0);
+ convert_unsigned_int_to_unsigned_char(0);
+ convert_unsigned_char_to_unsigned_int(0);
+ convert_unsigned_char_to_signed_int(0);
+ convert_signed_char_to_signed_int(0);
+ convert_unsigned_int_to_signed_int(0);
+ convert_signed_int_to_unsigned_int(0);
+ convert_signed_int_to_unsigned_char(0);
+ convert_signed_char_to_unsigned_char(0);
+ convert_unsigned_char_to_signed_char(0);
+ convert_signed_char_to_unsigned_int(0);
+ convert_unsigned_int_to_signed_char(0);
+ convert_signed_int_to_signed_char(0);
+
+ // One lowest bit set.
+ convert_unsigned_int_to_unsigned_int(1);
+ convert_unsigned_char_to_unsigned_char(1);
+ convert_signed_int_to_signed_int(1);
+ convert_signed_char_to_signed_char(1);
+ convert_unsigned_int_to_unsigned_char(1);
+ convert_unsigned_char_to_unsigned_int(1);
+ convert_unsigned_char_to_signed_int(1);
+ convert_signed_char_to_signed_int(1);
+ convert_unsigned_int_to_signed_int(1);
+ convert_signed_int_to_unsigned_int(1);
+ convert_signed_int_to_unsigned_char(1);
+ convert_signed_char_to_unsigned_char(1);
+ convert_unsigned_char_to_signed_char(1);
+ convert_signed_char_to_unsigned_int(1);
+ convert_unsigned_int_to_signed_char(1);
+ convert_signed_int_to_signed_char(1);
+
+#if defined(V0)
+ // All source bits set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)UINT32_MAX);
+ convert_unsigned_char_to_unsigned_char((uint8_t)UINT8_MAX);
+ convert_signed_int_to_signed_int((int32_t)(uint32_t)UINT32_MAX);
+ convert_signed_char_to_signed_char((int8_t)UINT8_MAX);
+ convert_unsigned_int_to_unsigned_char((uint32_t)UINT32_MAX);
+// CHECK-V0: {{.*}}integer-conversion.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 4294967295 (32-bit, unsigned) to type 'unsigned char' changed the value to 255 (8-bit, unsigned
+ convert_unsigned_char_to_unsigned_int((uint8_t)UINT8_MAX);
+ convert_unsigned_char_to_signed_int((uint8_t)UINT8_MAX);
+ convert_signed_char_to_signed_int((int8_t)UINT8_MAX);
+ convert_unsigned_int_to_signed_int((uint32_t)UINT32_MAX);
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)UINT32_MAX);
+ convert_signed_int_to_unsigned_char((int32_t)(uint32_t)UINT32_MAX);
+// CHECK-V0: {{.*}}integer-conversion.c:1100:10: runtime error: implicit conversion from type 'int' of value -1 (32-bit, signed) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)UINT8_MAX);
+ convert_unsigned_char_to_signed_char((uint8_t)UINT8_MAX);
+ convert_signed_char_to_unsigned_int((int8_t)UINT8_MAX);
+ convert_unsigned_int_to_signed_char((uint32_t)UINT32_MAX);
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)UINT32_MAX);
+#elif defined(V1)
+ // Source 'Sign' bit set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)INT32_MIN);
+ convert_unsigned_char_to_unsigned_char((uint8_t)INT8_MIN);
+ convert_signed_int_to_signed_int((int32_t)(uint32_t)INT32_MIN);
+ convert_signed_char_to_signed_char((int8_t)INT8_MIN);
+ convert_unsigned_int_to_unsigned_char((uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-conversion.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483648 (32-bit, unsigned) to type 'unsigned char' changed the value to 0 (8-bit, unsigned)
+ convert_unsigned_char_to_unsigned_int((uint8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_int((uint8_t)INT8_MIN);
+ convert_signed_char_to_signed_int((int8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_int((uint32_t)INT32_MIN);
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)INT32_MIN);
+ convert_signed_int_to_unsigned_char((int32_t)(uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-conversion.c:1100:10: runtime error: implicit conversion from type 'int' of value -2147483648 (32-bit, signed) to type 'unsigned char' changed the value to 0 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_char((uint8_t)INT8_MIN);
+ convert_signed_char_to_unsigned_int((int8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_char((uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-conversion.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483648 (32-bit, unsigned) to type 'signed char' changed the value to 0 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-conversion.c:1600:10: runtime error: implicit conversion from type 'int' of value -2147483648 (32-bit, signed) to type 'signed char' changed the value to 0 (8-bit, signed)
+#elif defined(V2)
+ // All bits except the source 'Sign' bit are set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)INT32_MAX);
+ convert_unsigned_char_to_unsigned_char((uint8_t)INT8_MAX);
+ convert_signed_int_to_signed_int((int32_t)(uint32_t)INT32_MAX);
+ convert_signed_char_to_signed_char((int8_t)INT8_MAX);
+ convert_unsigned_int_to_unsigned_char((uint32_t)INT32_MAX);
+// CHECK-V2: {{.*}}integer-conversion.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483647 (32-bit, unsigned) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
+ convert_unsigned_char_to_unsigned_int((uint8_t)INT8_MAX);
+ convert_unsigned_char_to_signed_int((uint8_t)INT8_MAX);
+ convert_signed_char_to_signed_int((int8_t)INT8_MAX);
+ convert_unsigned_int_to_signed_int((uint32_t)INT32_MAX);
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)INT32_MAX);
+ convert_signed_int_to_unsigned_char((int32_t)(uint32_t)INT32_MAX);
+// CHECK-V2: {{.*}}integer-conversion.c:1100:10: runtime error: implicit conversion from type 'int' of value 2147483647 (32-bit, signed) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)INT8_MAX);
+ convert_unsigned_char_to_signed_char((uint8_t)INT8_MAX);
+ convert_signed_char_to_unsigned_int((int8_t)INT8_MAX);
+ convert_unsigned_int_to_signed_char((uint32_t)INT32_MAX);
+// CHECK-V2: {{.*}}integer-conversion.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483647 (32-bit, unsigned) to type 'signed char' changed the value to -1 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)INT32_MAX);
+// CHECK-V2: {{.*}}integer-conversion.c:1600:10: runtime error: implicit conversion from type 'int' of value 2147483647 (32-bit, signed) to type 'signed char' changed the value to -1 (8-bit, signed)
+#elif defined(V3)
+ // All destination bits set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)UINT8_MAX);
+ convert_unsigned_char_to_unsigned_char((uint8_t)UINT8_MAX);
+ convert_signed_int_to_signed_int((int32_t)(uint32_t)UINT8_MAX);
+ convert_signed_char_to_signed_char((int8_t)UINT8_MAX);
+ convert_unsigned_int_to_unsigned_char((uint32_t)UINT8_MAX);
+ convert_unsigned_char_to_unsigned_int((uint8_t)UINT8_MAX);
+ convert_unsigned_char_to_signed_int((uint8_t)UINT8_MAX);
+ convert_signed_char_to_signed_int((int8_t)UINT8_MAX);
+ convert_unsigned_int_to_signed_int((uint32_t)UINT8_MAX);
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)UINT8_MAX);
+ convert_signed_int_to_unsigned_char((int32_t)(uint32_t)UINT8_MAX);
+ convert_signed_char_to_unsigned_char((int8_t)UINT8_MAX);
+ convert_unsigned_char_to_signed_char((uint8_t)UINT8_MAX);
+ convert_signed_char_to_unsigned_int((int8_t)UINT8_MAX);
+ convert_unsigned_int_to_signed_char((uint32_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-conversion.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 255 (32-bit, unsigned) to type 'signed char' changed the value to -1 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-conversion.c:1600:10: runtime error: implicit conversion from type 'int' of value 255 (32-bit, signed) to type 'signed char' changed the value to -1 (8-bit, signed)
+#elif defined(V4)
+ // Destination 'sign' bit set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)(uint8_t)INT8_MIN);
+ convert_unsigned_char_to_unsigned_char((uint8_t)(uint8_t)INT8_MIN);
+ convert_signed_int_to_signed_int((int32_t)(uint32_t)(uint8_t)INT8_MIN);
+ convert_signed_char_to_signed_char((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_int_to_unsigned_char((uint32_t)(uint8_t)INT8_MIN);
+ convert_unsigned_char_to_unsigned_int((uint8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_int((uint8_t)(uint8_t)INT8_MIN);
+ convert_signed_char_to_signed_int((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_int((uint32_t)(uint8_t)INT8_MIN);
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)(uint8_t)INT8_MIN);
+ convert_signed_int_to_unsigned_char((int32_t)(uint32_t)(uint8_t)INT8_MIN);
+ convert_signed_char_to_unsigned_char((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_char((uint8_t)(uint8_t)INT8_MIN);
+ convert_signed_char_to_unsigned_int((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_char((uint32_t)(uint8_t)INT8_MIN);
+// CHECK-V4: {{.*}}integer-conversion.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 128 (32-bit, unsigned) to type 'signed char' changed the value to -128 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)(uint8_t)INT8_MIN);
+// CHECK-V4: {{.*}}integer-conversion.c:1600:10: runtime error: implicit conversion from type 'int' of value 128 (32-bit, signed) to type 'signed char' changed the value to -128 (8-bit, signed)
+#elif defined(V5)
+ // All bits except the destination 'sign' bit are set.
+ convert_unsigned_int_to_unsigned_int((~((uint32_t)(uint8_t)INT8_MIN)));
+ convert_unsigned_char_to_unsigned_char((uint8_t)(uint8_t)INT8_MIN);
+ convert_signed_int_to_signed_int((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
+ convert_signed_char_to_signed_char((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_int_to_unsigned_char((~((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V5: {{.*}}integer-conversion.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 4294967167 (32-bit, unsigned) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+ convert_unsigned_char_to_unsigned_int((uint8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_int((uint8_t)(uint8_t)INT8_MIN);
+ convert_signed_char_to_signed_int((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_int((~((uint32_t)(uint8_t)INT8_MIN)));
+ convert_signed_int_to_unsigned_int((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
+ convert_signed_int_to_unsigned_char((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V5: {{.*}}integer-conversion.c:1100:10: runtime error: implicit conversion from type 'int' of value -129 (32-bit, signed) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_char((uint8_t)(uint8_t)INT8_MIN);
+ convert_signed_char_to_unsigned_int((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_char((~((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V5: {{.*}}integer-conversion.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 4294967167 (32-bit, unsigned) to type 'signed char' changed the value to 127 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V5: {{.*}}integer-conversion.c:1600:10: runtime error: implicit conversion from type 'int' of value -129 (32-bit, signed) to type 'signed char' changed the value to 127 (8-bit, signed)
+#elif defined(V6)
+ // Only the source and destination sign bits are set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)INT32_MIN);
+ convert_unsigned_char_to_unsigned_char((uint8_t)INT8_MIN);
+ convert_signed_int_to_signed_int((int32_t)INT32_MIN);
+ convert_signed_char_to_signed_char((int8_t)INT8_MIN);
+ convert_unsigned_int_to_unsigned_char(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN));
+// CHECK-V6: {{.*}}integer-conversion.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483776 (32-bit, unsigned) to type 'unsigned char' changed the value to 128 (8-bit, unsigned)
+ convert_unsigned_char_to_unsigned_int((uint8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_int((uint8_t)INT8_MIN);
+ convert_signed_char_to_signed_int((int8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_int((uint32_t)INT32_MIN);
+ convert_signed_int_to_unsigned_int((uint32_t)INT32_MIN);
+ convert_signed_int_to_unsigned_char((int32_t)(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V6: {{.*}}integer-conversion.c:1100:10: runtime error: implicit conversion from type 'int' of value -2147483520 (32-bit, signed) to type 'unsigned char' changed the value to 128 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_char((uint8_t)INT8_MIN);
+ convert_signed_char_to_unsigned_int((int8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_char((((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V6: {{.*}}integer-conversion.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483776 (32-bit, unsigned) to type 'signed char' changed the value to -128 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V6: {{.*}}integer-conversion.c:1600:10: runtime error: implicit conversion from type 'int' of value -2147483520 (32-bit, signed) to type 'signed char' changed the value to -128 (8-bit, signed)
+#elif defined(V7)
+ // All bits except the source and destination sign bits are set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)INT32_MAX);
+ convert_unsigned_char_to_unsigned_char((uint8_t)INT8_MAX);
+ convert_signed_int_to_signed_int((int32_t)INT32_MAX);
+ convert_signed_char_to_signed_char((int8_t)INT8_MAX);
+ convert_unsigned_int_to_unsigned_char(~(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V7: {{.*}}integer-conversion.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483519 (32-bit, unsigned) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+ convert_unsigned_char_to_unsigned_int((uint8_t)INT8_MAX);
+ convert_unsigned_char_to_signed_int((uint8_t)INT8_MAX);
+ convert_signed_char_to_signed_int((int8_t)INT8_MAX);
+ convert_unsigned_int_to_signed_int((uint32_t)INT32_MAX);
+ convert_signed_int_to_unsigned_int((uint32_t)INT32_MAX);
+ convert_signed_int_to_unsigned_char((int32_t)(~(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN))));
+// CHECK-V7: {{.*}}integer-conversion.c:1100:10: runtime error: implicit conversion from type 'int' of value 2147483519 (32-bit, signed) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)INT8_MAX);
+ convert_unsigned_char_to_signed_char((uint8_t)INT8_MAX);
+ convert_signed_char_to_unsigned_int((int8_t)INT8_MAX);
+ convert_unsigned_int_to_signed_char(~(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V7: {{.*}}integer-conversion.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483519 (32-bit, unsigned) to type 'signed char' changed the value to 127 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)~(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V7: {{.*}}integer-conversion.c:1600:10: runtime error: implicit conversion from type 'int' of value 2147483519 (32-bit, signed) to type 'signed char' changed the value to 127 (8-bit, signed)
+#else
+#error Some V* needs to be defined!
+#endif
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/ImplicitConversion/integer-truncation-blacklist.c b/test/ubsan/TestCases/ImplicitConversion/integer-truncation-blacklist.c
index 13d4dca4f..a3650e0a9 100644
--- a/test/ubsan/TestCases/ImplicitConversion/integer-truncation-blacklist.c
+++ b/test/ubsan/TestCases/ImplicitConversion/integer-truncation-blacklist.c
@@ -3,16 +3,22 @@
// XFAIL: android
// UNSUPPORTED: ios
+// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+
// RUN: rm -f %tmp
// RUN: echo "[implicit-integer-truncation]" >> %tmp
-// RUN: echo "fun:*implicitTruncation*" >> %tmp
-// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O0 %s -o %t && not %run %t 2>&1
-// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O1 %s -o %t && not %run %t 2>&1
-// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O2 %s -o %t && not %run %t 2>&1
-// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O3 %s -o %t && not %run %t 2>&1
+// RUN: echo "fun:implicitTruncation" >> %tmp
+// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O0 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O1 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O2 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O3 %s -o %t && not %run %t 2>&1 | not FileCheck %s
unsigned char implicitTruncation(unsigned int argc) {
return argc; // BOOM
+// CHECK: {{.*}}integer-truncation-blacklist.c:[[@LINE-1]]:10: runtime error: implicit conversion from type 'unsigned int' of value 4294967295 (32-bit, unsigned) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
}
int main(int argc, char **argv) {
diff --git a/test/ubsan/TestCases/ImplicitConversion/integer-truncation.c b/test/ubsan/TestCases/ImplicitConversion/integer-truncation.c
index 995eb7d0f..34a0f00de 100644
--- a/test/ubsan/TestCases/ImplicitConversion/integer-truncation.c
+++ b/test/ubsan/TestCases/ImplicitConversion/integer-truncation.c
@@ -1,63 +1,324 @@
-// RUN: %clang -x c -fsanitize=implicit-integer-truncation %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
-// RUN: %clangxx -x c++ -fsanitize=implicit-integer-truncation %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+// RUN: %clang -x c -fsanitize=implicit-integer-truncation %s -DV0 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V0
+// RUN: %clang -x c -fsanitize=implicit-integer-truncation %s -DV1 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V1
+// RUN: %clang -x c -fsanitize=implicit-integer-truncation %s -DV2 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V2
+// RUN: %clang -x c -fsanitize=implicit-integer-truncation %s -DV3 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V3
+// RUN: %clang -x c -fsanitize=implicit-integer-truncation %s -DV4 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V4
+// RUN: %clang -x c -fsanitize=implicit-integer-truncation %s -DV5 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V5
+// RUN: %clang -x c -fsanitize=implicit-integer-truncation %s -DV6 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V6
+// RUN: %clang -x c -fsanitize=implicit-integer-truncation %s -DV7 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V7
+
+// RUN: %clang -x c++ -fsanitize=implicit-integer-truncation %s -DV0 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V0
+// RUN: %clang -x c++ -fsanitize=implicit-integer-truncation %s -DV1 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V1
+// RUN: %clang -x c++ -fsanitize=implicit-integer-truncation %s -DV2 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V2
+// RUN: %clang -x c++ -fsanitize=implicit-integer-truncation %s -DV3 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V3
+// RUN: %clang -x c++ -fsanitize=implicit-integer-truncation %s -DV4 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V4
+// RUN: %clang -x c++ -fsanitize=implicit-integer-truncation %s -DV5 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V5
+// RUN: %clang -x c++ -fsanitize=implicit-integer-truncation %s -DV6 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V6
+// RUN: %clang -x c++ -fsanitize=implicit-integer-truncation %s -DV7 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V7
#include <stdint.h>
-#if !defined(__cplusplus)
-#define bool _Bool
-#endif
+// Test plan:
+// * Two types - int and char
+// * Two signs - signed and unsigned
+// * Square that - we have input and output types.
+// Thus, there are total of (2*2)^2 == 16 tests.
+// These are all the possible variations/combinations of casts.
+// However, not all of them should result in the check.
+// So here, we *only* check which should and which should not result in checks.
-int main() {
-// CHECK-NOT: integer-truncation.c
+unsigned int convert_unsigned_int_to_unsigned_int(unsigned int x) {
+#line 100
+ return x;
+}
- // Negative tests. Even if they produce unexpected results, this sanitizer does not care.
- int8_t n0 = (~((uint32_t)0)); // ~0 -> -1, but do not warn.
- uint8_t n2 = 128;
- uint8_t n3 = 255;
- // Bools do not count
- bool b0 = (~((uint32_t)0));
- bool b1 = 255;
+unsigned char convert_unsigned_char_to_unsigned_char(unsigned char x) {
+#line 200
+ return x;
+}
- // Explicit and-ing of bits will silence it.
- uint8_t nc0 = (~((uint32_t)0)) & 255;
+signed int convert_signed_int_to_signed_int(signed int x) {
+#line 300
+ return x;
+}
- // Explicit casts
- uint8_t i0 = (uint8_t)(~((uint32_t)0));
+signed char convert_signed_char_to_signed_char(signed char x) {
+#line 400
+ return x;
+}
-#if defined(__cplusplus)
- uint8_t i1 = uint8_t(~(uint32_t(0)));
- uint8_t i2 = static_cast<uint8_t>(~(uint32_t(0)));
-#endif
+unsigned char convert_unsigned_int_to_unsigned_char(unsigned int x) {
+#line 500
+ return x;
+}
+
+unsigned int convert_unsigned_char_to_unsigned_int(unsigned char x) {
+#line 600
+ return x;
+}
+
+signed int convert_unsigned_char_to_signed_int(unsigned char x) {
+#line 700
+ return x;
+}
+
+signed int convert_signed_char_to_signed_int(signed char x) {
+#line 800
+ return x;
+}
+
+signed int convert_unsigned_int_to_signed_int(unsigned int x) {
+#line 900
+ return x;
+}
+
+unsigned int convert_signed_int_to_unsigned_int(signed int x) {
+#line 1000
+ return x;
+}
+
+unsigned char convert_signed_int_to_unsigned_char(signed int x) {
+#line 1100
+ return x;
+}
+
+unsigned char convert_signed_char_to_unsigned_char(signed char x) {
+#line 1200
+ return x;
+}
+
+signed char convert_unsigned_char_to_signed_char(unsigned char x) {
+#line 1300
+ return x;
+}
+
+unsigned int convert_signed_char_to_unsigned_int(signed char x) {
+#line 1400
+ return x;
+}
- // Positive tests.
-
- uint8_t t_b0 = (~((uint16_t)(0)));
-// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:18: runtime error: implicit conversion from type 'int' of value -1 (32-bit, signed) to type 'uint8_t' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
-
- uint8_t t_b1 = (~((uint32_t)0));
-// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:18: runtime error: implicit conversion from type 'uint32_t' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type 'uint8_t' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
- uint16_t t_b2 = (~((uint32_t)0));
-// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:19: runtime error: implicit conversion from type 'uint32_t' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type 'uint16_t' (aka 'unsigned short') changed the value to 65535 (16-bit, unsigned)
-
- uint8_t t_b3 = ~((uint64_t)0);
-// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:18: runtime error: implicit conversion from type 'uint64_t' (aka 'unsigned long{{[^']*}}') of value 18446744073709551615 (64-bit, unsigned) to type 'uint8_t' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
- uint16_t t_b4 = ~((uint64_t)0);
-// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:19: runtime error: implicit conversion from type 'uint64_t' (aka 'unsigned long{{[^']*}}') of value 18446744073709551615 (64-bit, unsigned) to type 'uint16_t' (aka 'unsigned short') changed the value to 65535 (16-bit, unsigned)
- uint32_t t_b5 = ~((uint64_t)0);
-// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:19: runtime error: implicit conversion from type 'uint64_t' (aka 'unsigned long{{[^']*}}') of value 18446744073709551615 (64-bit, unsigned) to type 'uint32_t' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)
-
- int8_t t1 = 255;
-// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:15: runtime error: implicit conversion from type 'int' of value 255 (32-bit, signed) to type 'int8_t' (aka 'signed char') changed the value to -1 (8-bit, signed)
- uint8_t t2 = 256;
-// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:16: runtime error: implicit conversion from type 'int' of value 256 (32-bit, signed) to type 'uint8_t' (aka 'unsigned char') changed the value to 0 (8-bit, unsigned)
- int8_t t3 = 256;
-// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:15: runtime error: implicit conversion from type 'int' of value 256 (32-bit, signed) to type 'int8_t' (aka 'signed char') changed the value to 0 (8-bit, signed)
- uint8_t t4 = 257;
-// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:16: runtime error: implicit conversion from type 'int' of value 257 (32-bit, signed) to type 'uint8_t' (aka 'unsigned char') changed the value to 1 (8-bit, unsigned)
- int8_t t5 = 257;
-// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:15: runtime error: implicit conversion from type 'int' of value 257 (32-bit, signed) to type 'int8_t' (aka 'signed char') changed the value to 1 (8-bit, signed)
- int8_t t6 = 128;
-// CHECK: {{.*}}integer-truncation.c:[[@LINE-1]]:15: runtime error: implicit conversion from type 'int' of value 128 (32-bit, signed) to type 'int8_t' (aka 'signed char') changed the value to -128 (8-bit, signed)
+signed char convert_unsigned_int_to_signed_char(unsigned int x) {
+#line 1500
+ return x;
+}
+
+signed char convert_signed_int_to_signed_char(signed int x) {
+#line 1600
+ return x;
+}
+
+#line 1111 // !!!
+
+int main() {
+ // No bits set.
+ convert_unsigned_int_to_unsigned_int(0);
+ convert_unsigned_char_to_unsigned_char(0);
+ convert_signed_int_to_signed_int(0);
+ convert_signed_char_to_signed_char(0);
+ convert_unsigned_int_to_unsigned_char(0);
+ convert_unsigned_char_to_unsigned_int(0);
+ convert_unsigned_char_to_signed_int(0);
+ convert_signed_char_to_signed_int(0);
+ convert_unsigned_int_to_signed_int(0);
+ convert_signed_int_to_unsigned_int(0);
+ convert_signed_int_to_unsigned_char(0);
+ convert_signed_char_to_unsigned_char(0);
+ convert_unsigned_char_to_signed_char(0);
+ convert_signed_char_to_unsigned_int(0);
+ convert_unsigned_int_to_signed_char(0);
+ convert_signed_int_to_signed_char(0);
+
+ // One lowest bit set.
+ convert_unsigned_int_to_unsigned_int(1);
+ convert_unsigned_char_to_unsigned_char(1);
+ convert_signed_int_to_signed_int(1);
+ convert_signed_char_to_signed_char(1);
+ convert_unsigned_int_to_unsigned_char(1);
+ convert_unsigned_char_to_unsigned_int(1);
+ convert_unsigned_char_to_signed_int(1);
+ convert_signed_char_to_signed_int(1);
+ convert_unsigned_int_to_signed_int(1);
+ convert_signed_int_to_unsigned_int(1);
+ convert_signed_int_to_unsigned_char(1);
+ convert_signed_char_to_unsigned_char(1);
+ convert_unsigned_char_to_signed_char(1);
+ convert_signed_char_to_unsigned_int(1);
+ convert_unsigned_int_to_signed_char(1);
+ convert_signed_int_to_signed_char(1);
+
+#if defined(V0)
+ // All source bits set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)UINT32_MAX);
+ convert_unsigned_char_to_unsigned_char((uint8_t)UINT8_MAX);
+ convert_signed_int_to_signed_int((int32_t)(uint32_t)UINT32_MAX);
+ convert_signed_char_to_signed_char((int8_t)UINT8_MAX);
+ convert_unsigned_int_to_unsigned_char((uint32_t)UINT32_MAX);
+// CHECK-V0: {{.*}}integer-truncation.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 4294967295 (32-bit, unsigned) to type 'unsigned char' changed the value to 255 (8-bit, unsigned
+ convert_unsigned_char_to_unsigned_int((uint8_t)UINT8_MAX);
+ convert_unsigned_char_to_signed_int((uint8_t)UINT8_MAX);
+ convert_signed_char_to_signed_int((int8_t)UINT8_MAX);
+ convert_unsigned_int_to_signed_int((uint32_t)UINT32_MAX);
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)UINT32_MAX);
+ convert_signed_int_to_unsigned_char((int32_t)(uint32_t)UINT32_MAX);
+// CHECK-V0: {{.*}}integer-truncation.c:1100:10: runtime error: implicit conversion from type 'int' of value -1 (32-bit, signed) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)UINT8_MAX);
+ convert_unsigned_char_to_signed_char((uint8_t)UINT8_MAX);
+ convert_signed_char_to_unsigned_int((int8_t)UINT8_MAX);
+ convert_unsigned_int_to_signed_char((uint32_t)UINT32_MAX);
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)UINT32_MAX);
+#elif defined(V1)
+ // Source 'Sign' bit set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)INT32_MIN);
+ convert_unsigned_char_to_unsigned_char((uint8_t)INT8_MIN);
+ convert_signed_int_to_signed_int((int32_t)(uint32_t)INT32_MIN);
+ convert_signed_char_to_signed_char((int8_t)INT8_MIN);
+ convert_unsigned_int_to_unsigned_char((uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-truncation.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483648 (32-bit, unsigned) to type 'unsigned char' changed the value to 0 (8-bit, unsigned)
+ convert_unsigned_char_to_unsigned_int((uint8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_int((uint8_t)INT8_MIN);
+ convert_signed_char_to_signed_int((int8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_int((uint32_t)INT32_MIN);
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)INT32_MIN);
+ convert_signed_int_to_unsigned_char((int32_t)(uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-truncation.c:1100:10: runtime error: implicit conversion from type 'int' of value -2147483648 (32-bit, signed) to type 'unsigned char' changed the value to 0 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_char((uint8_t)INT8_MIN);
+ convert_signed_char_to_unsigned_int((int8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_char((uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-truncation.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483648 (32-bit, unsigned) to type 'signed char' changed the value to 0 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-truncation.c:1600:10: runtime error: implicit conversion from type 'int' of value -2147483648 (32-bit, signed) to type 'signed char' changed the value to 0 (8-bit, signed)
+#elif defined(V2)
+ // All bits except the source 'Sign' bit are set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)INT32_MAX);
+ convert_unsigned_char_to_unsigned_char((uint8_t)INT8_MAX);
+ convert_signed_int_to_signed_int((int32_t)(uint32_t)INT32_MAX);
+ convert_signed_char_to_signed_char((int8_t)INT8_MAX);
+ convert_unsigned_int_to_unsigned_char((uint32_t)INT32_MAX);
+// CHECK-V2: {{.*}}integer-truncation.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483647 (32-bit, unsigned) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
+ convert_unsigned_char_to_unsigned_int((uint8_t)INT8_MAX);
+ convert_unsigned_char_to_signed_int((uint8_t)INT8_MAX);
+ convert_signed_char_to_signed_int((int8_t)INT8_MAX);
+ convert_unsigned_int_to_signed_int((uint32_t)INT32_MAX);
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)INT32_MAX);
+ convert_signed_int_to_unsigned_char((int32_t)(uint32_t)INT32_MAX);
+// CHECK-V2: {{.*}}integer-truncation.c:1100:10: runtime error: implicit conversion from type 'int' of value 2147483647 (32-bit, signed) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)INT8_MAX);
+ convert_unsigned_char_to_signed_char((uint8_t)INT8_MAX);
+ convert_signed_char_to_unsigned_int((int8_t)INT8_MAX);
+ convert_unsigned_int_to_signed_char((uint32_t)INT32_MAX);
+// CHECK-V2: {{.*}}integer-truncation.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483647 (32-bit, unsigned) to type 'signed char' changed the value to -1 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)INT32_MAX);
+// CHECK-V2: {{.*}}integer-truncation.c:1600:10: runtime error: implicit conversion from type 'int' of value 2147483647 (32-bit, signed) to type 'signed char' changed the value to -1 (8-bit, signed)
+#elif defined(V3)
+ // All destination bits set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)UINT8_MAX);
+ convert_unsigned_char_to_unsigned_char((uint8_t)UINT8_MAX);
+ convert_signed_int_to_signed_int((int32_t)(uint32_t)UINT8_MAX);
+ convert_signed_char_to_signed_char((int8_t)UINT8_MAX);
+ convert_unsigned_int_to_unsigned_char((uint32_t)UINT8_MAX);
+ convert_unsigned_char_to_unsigned_int((uint8_t)UINT8_MAX);
+ convert_unsigned_char_to_signed_int((uint8_t)UINT8_MAX);
+ convert_signed_char_to_signed_int((int8_t)UINT8_MAX);
+ convert_unsigned_int_to_signed_int((uint32_t)UINT8_MAX);
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)UINT8_MAX);
+ convert_signed_int_to_unsigned_char((int32_t)(uint32_t)UINT8_MAX);
+ convert_signed_char_to_unsigned_char((int8_t)UINT8_MAX);
+ convert_unsigned_char_to_signed_char((uint8_t)UINT8_MAX);
+ convert_signed_char_to_unsigned_int((int8_t)UINT8_MAX);
+ convert_unsigned_int_to_signed_char((uint32_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-truncation.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 255 (32-bit, unsigned) to type 'signed char' changed the value to -1 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-truncation.c:1600:10: runtime error: implicit conversion from type 'int' of value 255 (32-bit, signed) to type 'signed char' changed the value to -1 (8-bit, signed)
+#elif defined(V4)
+ // Destination 'sign' bit set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)(uint8_t)INT8_MIN);
+ convert_unsigned_char_to_unsigned_char((uint8_t)(uint8_t)INT8_MIN);
+ convert_signed_int_to_signed_int((int32_t)(uint32_t)(uint8_t)INT8_MIN);
+ convert_signed_char_to_signed_char((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_int_to_unsigned_char((uint32_t)(uint8_t)INT8_MIN);
+ convert_unsigned_char_to_unsigned_int((uint8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_int((uint8_t)(uint8_t)INT8_MIN);
+ convert_signed_char_to_signed_int((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_int((uint32_t)(uint8_t)INT8_MIN);
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)(uint8_t)INT8_MIN);
+ convert_signed_int_to_unsigned_char((int32_t)(uint32_t)(uint8_t)INT8_MIN);
+ convert_signed_char_to_unsigned_char((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_char((uint8_t)(uint8_t)INT8_MIN);
+ convert_signed_char_to_unsigned_int((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_char((uint32_t)(uint8_t)INT8_MIN);
+// CHECK-V4: {{.*}}integer-truncation.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 128 (32-bit, unsigned) to type 'signed char' changed the value to -128 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)(uint8_t)INT8_MIN);
+// CHECK-V4: {{.*}}integer-truncation.c:1600:10: runtime error: implicit conversion from type 'int' of value 128 (32-bit, signed) to type 'signed char' changed the value to -128 (8-bit, signed)
+#elif defined(V5)
+ // All bits except the destination 'sign' bit are set.
+ convert_unsigned_int_to_unsigned_int((~((uint32_t)(uint8_t)INT8_MIN)));
+ convert_unsigned_char_to_unsigned_char((uint8_t)(uint8_t)INT8_MIN);
+ convert_signed_int_to_signed_int((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
+ convert_signed_char_to_signed_char((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_int_to_unsigned_char((~((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V5: {{.*}}integer-truncation.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 4294967167 (32-bit, unsigned) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+ convert_unsigned_char_to_unsigned_int((uint8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_int((uint8_t)(uint8_t)INT8_MIN);
+ convert_signed_char_to_signed_int((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_int((~((uint32_t)(uint8_t)INT8_MIN)));
+ convert_signed_int_to_unsigned_int((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
+ convert_signed_int_to_unsigned_char((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V5: {{.*}}integer-truncation.c:1100:10: runtime error: implicit conversion from type 'int' of value -129 (32-bit, signed) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_char((uint8_t)(uint8_t)INT8_MIN);
+ convert_signed_char_to_unsigned_int((int8_t)(uint8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_char((~((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V5: {{.*}}integer-truncation.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 4294967167 (32-bit, unsigned) to type 'signed char' changed the value to 127 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V5: {{.*}}integer-truncation.c:1600:10: runtime error: implicit conversion from type 'int' of value -129 (32-bit, signed) to type 'signed char' changed the value to 127 (8-bit, signed)
+#elif defined(V6)
+ // Only the source and destination sign bits are set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)INT32_MIN);
+ convert_unsigned_char_to_unsigned_char((uint8_t)INT8_MIN);
+ convert_signed_int_to_signed_int((int32_t)INT32_MIN);
+ convert_signed_char_to_signed_char((int8_t)INT8_MIN);
+ convert_unsigned_int_to_unsigned_char(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN));
+// CHECK-V6: {{.*}}integer-truncation.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483776 (32-bit, unsigned) to type 'unsigned char' changed the value to 128 (8-bit, unsigned)
+ convert_unsigned_char_to_unsigned_int((uint8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_int((uint8_t)INT8_MIN);
+ convert_signed_char_to_signed_int((int8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_int((uint32_t)INT32_MIN);
+ convert_signed_int_to_unsigned_int((uint32_t)INT32_MIN);
+ convert_signed_int_to_unsigned_char((int32_t)(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V6: {{.*}}integer-truncation.c:1100:10: runtime error: implicit conversion from type 'int' of value -2147483520 (32-bit, signed) to type 'unsigned char' changed the value to 128 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)INT8_MIN);
+ convert_unsigned_char_to_signed_char((uint8_t)INT8_MIN);
+ convert_signed_char_to_unsigned_int((int8_t)INT8_MIN);
+ convert_unsigned_int_to_signed_char((((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V6: {{.*}}integer-truncation.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483776 (32-bit, unsigned) to type 'signed char' changed the value to -128 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V6: {{.*}}integer-truncation.c:1600:10: runtime error: implicit conversion from type 'int' of value -2147483520 (32-bit, signed) to type 'signed char' changed the value to -128 (8-bit, signed)
+#elif defined(V7)
+ // All bits except the source and destination sign bits are set.
+ convert_unsigned_int_to_unsigned_int((uint32_t)INT32_MAX);
+ convert_unsigned_char_to_unsigned_char((uint8_t)INT8_MAX);
+ convert_signed_int_to_signed_int((int32_t)INT32_MAX);
+ convert_signed_char_to_signed_char((int8_t)INT8_MAX);
+ convert_unsigned_int_to_unsigned_char(~(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V7: {{.*}}integer-truncation.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483519 (32-bit, unsigned) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+ convert_unsigned_char_to_unsigned_int((uint8_t)INT8_MAX);
+ convert_unsigned_char_to_signed_int((uint8_t)INT8_MAX);
+ convert_signed_char_to_signed_int((int8_t)INT8_MAX);
+ convert_unsigned_int_to_signed_int((uint32_t)INT32_MAX);
+ convert_signed_int_to_unsigned_int((uint32_t)INT32_MAX);
+ convert_signed_int_to_unsigned_char((int32_t)(~(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN))));
+// CHECK-V7: {{.*}}integer-truncation.c:1100:10: runtime error: implicit conversion from type 'int' of value 2147483519 (32-bit, signed) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)INT8_MAX);
+ convert_unsigned_char_to_signed_char((uint8_t)INT8_MAX);
+ convert_signed_char_to_unsigned_int((int8_t)INT8_MAX);
+ convert_unsigned_int_to_signed_char(~(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V7: {{.*}}integer-truncation.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483519 (32-bit, unsigned) to type 'signed char' changed the value to 127 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)~(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V7: {{.*}}integer-truncation.c:1600:10: runtime error: implicit conversion from type 'int' of value 2147483519 (32-bit, signed) to type 'signed char' changed the value to 127 (8-bit, signed)
+#else
+#error Some V* needs to be defined!
+#endif
return 0;
}
diff --git a/test/ubsan/TestCases/Misc/log-path_test.cc b/test/ubsan/TestCases/Misc/log-path_test.cc
index bb89f1477..23ad7e27d 100644
--- a/test/ubsan/TestCases/Misc/log-path_test.cc
+++ b/test/ubsan/TestCases/Misc/log-path_test.cc
@@ -12,11 +12,13 @@
// Good log_path.
// RUN: rm -f %t.log.*
+// RUN: %device_rm -f '%t.log.*'
// RUN: %env_ubsan_opts=log_path='"%t.log"' %run %t -4 2> %t.out
// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.log.*
// Run w/o errors should not produce any log.
// RUN: rm -f %t.log.*
+// RUN: %device_rm -f '%t.log.*'
// RUN: %env_ubsan_opts=log_path='"%t.log"' %run %t 4
// RUN: not cat %t.log.*
diff --git a/test/ubsan/TestCases/Pointer/index-overflow.cpp b/test/ubsan/TestCases/Pointer/index-overflow.cpp
index eb7f95e85..f9b1fea08 100644
--- a/test/ubsan/TestCases/Pointer/index-overflow.cpp
+++ b/test/ubsan/TestCases/Pointer/index-overflow.cpp
@@ -1,7 +1,7 @@
// RUN: %clangxx -fsanitize=pointer-overflow %s -o %t
-// RUN: %t 1 2>&1 | FileCheck %s --check-prefix=ERR
-// RUN: %t 0 2>&1 | FileCheck %s --check-prefix=SAFE
-// RUN: %t -1 2>&1 | FileCheck %s --check-prefix=SAFE
+// RUN: %run %t 1 2>&1 | FileCheck %s --check-prefix=ERR
+// RUN: %run %t 0 2>&1 | FileCheck %s --check-prefix=SAFE
+// RUN: %run %t -1 2>&1 | FileCheck %s --check-prefix=SAFE
#include <stdio.h>
#include <stdint.h>
diff --git a/test/ubsan/TestCases/Pointer/unsigned-index-expression.cpp b/test/ubsan/TestCases/Pointer/unsigned-index-expression.cpp
index 0002c713f..5a1432625 100644
--- a/test/ubsan/TestCases/Pointer/unsigned-index-expression.cpp
+++ b/test/ubsan/TestCases/Pointer/unsigned-index-expression.cpp
@@ -1,5 +1,5 @@
// RUN: %clangxx -std=c++11 -fsanitize=pointer-overflow %s -o %t
-// RUN: %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
int main(int argc, char *argv[]) {
char c;
diff --git a/test/ubsan/lit.common.cfg b/test/ubsan/lit.common.cfg
index e20832bd6..9a7a8900e 100644
--- a/test/ubsan/lit.common.cfg
+++ b/test/ubsan/lit.common.cfg
@@ -74,3 +74,8 @@ if config.host_os not in ['Linux', 'Darwin', 'FreeBSD', 'Windows', 'NetBSD', 'Su
config.available_features.add('arch=' + config.target_arch)
config.excludes = ['Inputs']
+
+# Limit parallelism if necessary
+if config.host_os == 'Darwin':
+ if config.apple_platform != "osx" and not config.apple_platform.endswith("sim"):
+ config.parallelism_group = "darwin-ios-device-sanitizer"
diff --git a/test/ubsan/lit.site.cfg.in b/test/ubsan/lit.site.cfg.in
index a4d7b50ed..60c9c84e3 100644
--- a/test/ubsan/lit.site.cfg.in
+++ b/test/ubsan/lit.site.cfg.in
@@ -7,6 +7,7 @@ config.target_cflags = "@UBSAN_TEST_TARGET_CFLAGS@"
config.target_arch = "@UBSAN_TEST_TARGET_ARCH@"
config.use_lld = @UBSAN_TEST_USE_LLD@
config.use_thinlto = @UBSAN_TEST_USE_THINLTO@
+config.apple_platform = "@UBSAN_TEST_APPLE_PLATFORM@"
# Load common config for all compiler-rt lit tests.
lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
diff --git a/test/ubsan_minimal/TestCases/implicit-integer-truncation.c b/test/ubsan_minimal/TestCases/implicit-integer-truncation.c
index 1db6e6976..28711d2ee 100644
--- a/test/ubsan_minimal/TestCases/implicit-integer-truncation.c
+++ b/test/ubsan_minimal/TestCases/implicit-integer-truncation.c
@@ -3,7 +3,7 @@
#include <stdint.h>
int main() {
-// CHECK-NOT: integer-truncation.c
+// CHECK-NOT: implicit-conversion
// Negative tests. Even if they produce unexpected results, this sanitizer does not care.
int8_t n0 = (~((uint32_t)(0))); // ~0 -> -1, but do not warn.
@@ -19,6 +19,7 @@ int main() {
// Positive tests.
uint8_t t0 = (~((uint32_t)(0)));
// CHECK: implicit-conversion
+// CHECK-NOT: implicit-conversion
return 0;
}
diff --git a/test/xray/TestCases/Posix/argv0-log-file-name.cc b/test/xray/TestCases/Posix/argv0-log-file-name.cc
index 2f9a234f8..a2cb11b4d 100644
--- a/test/xray/TestCases/Posix/argv0-log-file-name.cc
+++ b/test/xray/TestCases/Posix/argv0-log-file-name.cc
@@ -4,7 +4,7 @@
// RUN: %clangxx_xray -std=c++11 %s -o %t
// RUN: XRAY_OPTIONS="patch_premain=true xray_naive_log=true" %run %t > xray.log.file.name 2>&1
// RUN: ls | FileCheck xray.log.file.name
-// RUN: rm xray-log.* xray.log.file.name
+// RUN: rm xray-log.argv0-log-file-name.* xray.log.file.name
// UNSUPPORTED: target-is-mips64,target-is-mips64el
diff --git a/test/xray/TestCases/Posix/clang-no-xray-instrument.cc b/test/xray/TestCases/Posix/clang-no-xray-instrument.cc
new file mode 100644
index 000000000..c2444b18a
--- /dev/null
+++ b/test/xray/TestCases/Posix/clang-no-xray-instrument.cc
@@ -0,0 +1,11 @@
+// Test that we cannot actually find XRay instrumentation when we build with
+// -fno-xray-instrument but have code that's marked as 'xray_always_instrument'.
+//
+// RUN: %clangxx -fno-xray-instrument -c %s -o %t.o
+// RUN: not %llvm_xray extract -symbolize %t.o 2>&1 | FileCheck %s
+// REQUIRES: x86_64-target-arch
+// REQUIRES: built-in-llvm-tree
+
+// CHECK: llvm-xray: Cannot extract instrumentation map
+// CHECK-NOT: {{.*always_instrumented.*}}
+[[clang::xray_always_instrument]] int always_instrumented() { return 42; }
diff --git a/test/xray/TestCases/Posix/fdr-mode.cc b/test/xray/TestCases/Posix/fdr-mode.cc
index b12d97c00..17d115a22 100644
--- a/test/xray/TestCases/Posix/fdr-mode.cc
+++ b/test/xray/TestCases/Posix/fdr-mode.cc
@@ -42,7 +42,6 @@ thread_local uint64_t var = 0;
void __attribute__((noinline)) fArg(int) { }
int main(int argc, char *argv[]) {
- using namespace __xray;
std::cout << "Logging before init." << std::endl;
// CHECK: Logging before init.
assert(__xray_log_select_mode("xray-fdr") ==
diff --git a/test/xray/TestCases/Posix/fdr-single-thread.cc b/test/xray/TestCases/Posix/fdr-single-thread.cc
index 480502b0a..fb65ad895 100644
--- a/test/xray/TestCases/Posix/fdr-single-thread.cc
+++ b/test/xray/TestCases/Posix/fdr-single-thread.cc
@@ -13,16 +13,10 @@
#include "xray/xray_log_interface.h"
#include <cassert>
-constexpr auto kBufferSize = 16384;
-constexpr auto kBufferMax = 10;
-
[[clang::xray_always_instrument]] void __attribute__((noinline)) fn() { }
int main(int argc, char *argv[]) {
- using namespace __xray;
- FDRLoggingOptions Opts;
-
- auto status = __xray_log_init(kBufferSize, kBufferMax, &Opts, sizeof(Opts));
+ auto status = __xray_log_init_mode("xray-fdr", "");
assert(status == XRayLogInitStatus::XRAY_LOG_INITIALIZED);
__xray_patch();
diff --git a/test/xray/TestCases/Posix/fdr-thread-order.cc b/test/xray/TestCases/Posix/fdr-thread-order.cc
index 1d6b01759..a5d7e6f36 100644
--- a/test/xray/TestCases/Posix/fdr-thread-order.cc
+++ b/test/xray/TestCases/Posix/fdr-thread-order.cc
@@ -1,15 +1,12 @@
-// RUN: %clangxx_xray -g -std=c++11 %s -o %t
-// RUN: rm fdr-thread-order.* || true
-// RUN: XRAY_OPTIONS="patch_premain=false xray_naive_log=false \
-// RUN: xray_logfile_base=fdr-thread-order. xray_fdr_log=true verbosity=1 \
-// RUN: xray_fdr_log_func_duration_threshold_us=0" %run %t 2>&1 | \
+// RUN: rm -rf %t && mkdir %t
+// RUN: %clangxx_xray -g -std=c++11 %s -o %t.exe
+// RUN: XRAY_OPTIONS="patch_premain=false \
+// RUN: xray_logfile_base=%t/ xray_mode=xray-fdr verbosity=1" \
+// RUN: XRAY_FDR_OPTIONS=func_duration_threshold_us=0 %run %t.exe 2>&1 | \
// RUN: FileCheck %s
-// RUN: %llvm_xray convert --symbolize --output-format=yaml -instr_map=%t \
-// RUN: "`ls fdr-thread-order.* | head -1`"
-// RUN: %llvm_xray convert --symbolize --output-format=yaml -instr_map=%t \
-// RUN: "`ls fdr-thread-order.* | head -1`" | \
-// RUN: FileCheck %s --check-prefix TRACE
-// RUN: rm fdr-thread-order.*
+// RUN: %llvm_xray convert --symbolize --output-format=yaml -instr_map=%t.exe %t/*
+// RUN: %llvm_xray convert --symbolize --output-format=yaml -instr_map=%t.exe %t/* | \
+// RUN: FileCheck %s --check-prefix TRACE
// FIXME: Make llvm-xray work on non-x86_64 as well.
// REQUIRES: x86_64-target-arch
// REQUIRES: built-in-llvm-tree
@@ -19,9 +16,6 @@
#include <cassert>
#include <thread>
-constexpr auto kBufferSize = 16384;
-constexpr auto kBufferMax = 10;
-
std::atomic<uint64_t> var{0};
[[clang::xray_always_instrument]] void __attribute__((noinline)) f1() {
@@ -35,11 +29,8 @@ std::atomic<uint64_t> var{0};
}
int main(int argc, char *argv[]) {
- using namespace __xray;
- FDRLoggingOptions Options;
__xray_patch();
- assert(__xray_log_init(kBufferSize, kBufferMax, &Options,
- sizeof(FDRLoggingOptions)) ==
+ assert(__xray_log_init_mode("xray-fdr", "") ==
XRayLogInitStatus::XRAY_LOG_INITIALIZED);
std::atomic_thread_fence(std::memory_order_acq_rel);
diff --git a/test/xray/TestCases/Posix/profiling-multi-threaded.cc b/test/xray/TestCases/Posix/profiling-multi-threaded.cc
index 45e5e7022..8bd15b7d3 100644
--- a/test/xray/TestCases/Posix/profiling-multi-threaded.cc
+++ b/test/xray/TestCases/Posix/profiling-multi-threaded.cc
@@ -41,7 +41,7 @@ volatile int buffer_counter = 0;
std::string current_mode = __xray_log_get_current_mode();
assert(current_mode == "xray-profiling");
assert(__xray_patch() == XRayPatchingStatus::SUCCESS);
- assert(__xray_log_init(0, 0, nullptr, 0) ==
+ assert(__xray_log_init_mode("xray-profiling", "") ==
XRayLogInitStatus::XRAY_LOG_INITIALIZED);
std::thread t0([] { f0(); });
std::thread t1([] { f0(); });
diff --git a/test/xray/Unit/lit.site.cfg.in b/test/xray/Unit/lit.site.cfg.in
index be860deaf..54fcc1cbd 100644
--- a/test/xray/Unit/lit.site.cfg.in
+++ b/test/xray/Unit/lit.site.cfg.in
@@ -14,3 +14,11 @@ config.test_source_root = config.test_exec_root
# Do not patch the XRay unit tests pre-main, and also make the error logging
# verbose to get a more accurate error logging mechanism.
config.environment['XRAY_OPTIONS'] = 'patch_premain=false'
+
+# Add the LLVM Library directory to the LD_LIBRARY_PATH to allow tests to look
+# up the shared libraries.
+if 'LD_LIBRARY_PATH' in os.environ:
+ libdirs = os.path.pathsep.join((config.llvm_lib_dir, os.environ['LD_LIBRARY_PATH']))
+ config.environment['LD_LIBRARY_PATH'] = libdirs
+else:
+ config.environment['LD_LIBRARY_PATH'] = config.llvm_lib_dir
diff --git a/unittests/lit.common.unit.configured.in b/unittests/lit.common.unit.configured.in
index fafac19b7..9f6fadaff 100644
--- a/unittests/lit.common.unit.configured.in
+++ b/unittests/lit.common.unit.configured.in
@@ -10,6 +10,7 @@ config.compiler_rt_libdir = "@COMPILER_RT_RESOLVED_LIBRARY_OUTPUT_DIR@"
config.llvm_build_mode = "@LLVM_BUILD_MODE@"
config.host_arch = "@HOST_ARCH@"
config.host_os = "@HOST_OS@"
+config.llvm_lib_dir = "@LLVM_LIBRARY_DIR@"
# LLVM tools dir and build mode can be passed in lit parameters,
# so try to apply substitution.