aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-01-19 10:05:08 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-01-19 10:05:08 +0000
commit0646903fc1f75f6e605754621119473ee083f4a4 (patch)
tree57bce79a7423a054cccec23bdf6cd96e2d271b4a
parent005b7ed8f76756d94ef6266ded755ab7863cb936 (diff)
downloadsrc-0646903fc1f75f6e605754621119473ee083f4a4.tar.gz
src-0646903fc1f75f6e605754621119473ee083f4a4.zip
Vendor import of compiler-rt trunk r351319 (just before the release_80vendor/compiler-rt/compiler-rt-trunk-r351319vendor/compiler-rt/compiler-rt-release_80-r351543
Notes
Notes: svn path=/vendor/compiler-rt/dist/; revision=343175 svn path=/vendor/compiler-rt/compiler-rt-release_80-r351543/; revision=343195; tag=vendor/compiler-rt/compiler-rt-release_80-r351543
-rw-r--r--CMakeLists.txt97
-rw-r--r--LICENSE.TXT2
-rw-r--r--cmake/Modules/AddCompilerRT.cmake40
-rw-r--r--cmake/Modules/CompilerRTUtils.cmake141
-rw-r--r--cmake/Modules/HandleCompilerRT.cmake4
-rw-r--r--cmake/Modules/SanitizerUtils.cmake14
-rw-r--r--cmake/base-config-ix.cmake16
-rw-r--r--cmake/builtin-config-ix.cmake14
-rw-r--r--cmake/config-ix.cmake44
-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/sanitizer/netbsd_syscall_hooks.h46
-rw-r--r--include/xray/xray_log_interface.h25
-rw-r--r--lib/asan/CMakeLists.txt40
-rw-r--r--lib/asan/asan_allocator.h41
-rw-r--r--lib/asan/asan_errors.cc5
-rw-r--r--lib/asan/asan_errors.h7
-rw-r--r--lib/asan/asan_flags.inc2
-rw-r--r--lib/asan/asan_fuchsia.cc7
-rw-r--r--lib/asan/asan_globals.cc17
-rw-r--r--lib/asan/asan_globals_win.cc2
-rw-r--r--lib/asan/asan_internal.h5
-rw-r--r--lib/asan/asan_linux.cc7
-rw-r--r--lib/asan/asan_malloc_linux.cc2
-rw-r--r--lib/asan/asan_malloc_mac.cc21
-rw-r--r--lib/asan/asan_malloc_win.cc21
-rw-r--r--lib/asan/asan_new_delete.cc2
-rw-r--r--lib/asan/asan_posix.cc46
-rw-r--r--lib/asan/asan_report.h4
-rw-r--r--lib/asan/asan_rtems.cc6
-rw-r--r--lib/asan/asan_rtl.cc25
-rw-r--r--lib/asan/asan_thread.cc24
-rw-r--r--lib/asan/asan_win.cc15
-rw-r--r--lib/asan/asan_win_dll_thunk.cc1
-rwxr-xr-xlib/asan/scripts/asan_symbolize.py6
-rw-r--r--lib/asan/tests/CMakeLists.txt5
-rw-r--r--lib/asan/tests/asan_interface_test.cc2
-rw-r--r--lib/asan/tests/asan_noinst_test.cc2
-rw-r--r--lib/asan/tests/asan_test.cc2
-rw-r--r--lib/builtins/CMakeLists.txt2
-rw-r--r--lib/builtins/Darwin-excludes/10.4.txt137
-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/cpu_model.c113
-rw-r--r--lib/builtins/ctzdi2.c9
-rw-r--r--lib/builtins/divdc3.c4
-rw-r--r--lib/builtins/divdf3.c46
-rw-r--r--lib/builtins/divsc3.c5
-rw-r--r--lib/builtins/divsf3.c40
-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/gcc_personality_v0.c5
-rw-r--r--lib/builtins/int_lib.h2
-rw-r--r--lib/builtins/int_math.h4
-rw-r--r--lib/builtins/int_types.h23
-rw-r--r--lib/builtins/int_util.c8
-rw-r--r--lib/builtins/int_util.h6
-rw-r--r--lib/builtins/os_version_check.c41
-rw-r--r--lib/builtins/ppc/divtc3.c12
-rw-r--r--lib/builtins/ppc/fixunstfti.c106
-rw-r--r--lib/builtins/ppc/floattitf.c48
-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/dfsan/dfsan.cc2
-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.h1
-rw-r--r--lib/esan/esan_sideline_bsd.cpp35
-rw-r--r--lib/fuzzer/CMakeLists.txt6
-rw-r--r--lib/fuzzer/FuzzerBuiltins.h36
-rw-r--r--lib/fuzzer/FuzzerBuiltinsMsvc.h59
-rw-r--r--lib/fuzzer/FuzzerCommand.h4
-rw-r--r--lib/fuzzer/FuzzerCorpus.h6
-rw-r--r--lib/fuzzer/FuzzerDefs.h19
-rw-r--r--lib/fuzzer/FuzzerDriver.cpp5
-rw-r--r--lib/fuzzer/FuzzerExtFunctionsDlsymWin.cpp62
-rw-r--r--lib/fuzzer/FuzzerExtFunctionsWeak.cpp2
-rw-r--r--lib/fuzzer/FuzzerExtFunctionsWeakAlias.cpp56
-rw-r--r--lib/fuzzer/FuzzerExtFunctionsWindows.cpp83
-rw-r--r--lib/fuzzer/FuzzerFlags.def12
-rw-r--r--lib/fuzzer/FuzzerIO.cpp12
-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/FuzzerInternal.h1
-rw-r--r--lib/fuzzer/FuzzerLoop.cpp47
-rw-r--r--lib/fuzzer/FuzzerMutate.cpp58
-rw-r--r--lib/fuzzer/FuzzerMutate.h8
-rw-r--r--lib/fuzzer/FuzzerOptions.h3
-rw-r--r--lib/fuzzer/FuzzerTracePC.cpp160
-rw-r--r--lib/fuzzer/FuzzerTracePC.h20
-rw-r--r--lib/fuzzer/FuzzerUtil.h6
-rw-r--r--lib/fuzzer/FuzzerUtilFuchsia.cpp32
-rw-r--r--lib/fuzzer/FuzzerUtilWindows.cpp6
-rw-r--r--lib/fuzzer/afl/afl_driver.cpp2
-rw-r--r--lib/fuzzer/tests/CMakeLists.txt8
-rw-r--r--lib/fuzzer/tests/FuzzerUnittest.cpp7
-rw-r--r--lib/hwasan/CMakeLists.txt38
-rw-r--r--lib/hwasan/hwasan.cc271
-rw-r--r--lib/hwasan/hwasan.h36
-rw-r--r--lib/hwasan/hwasan_allocator.cc348
-rw-r--r--lib/hwasan/hwasan_allocator.h77
-rw-r--r--lib/hwasan/hwasan_checks.h80
-rw-r--r--lib/hwasan/hwasan_dynamic_shadow.cc30
-rw-r--r--lib/hwasan/hwasan_flags.h2
-rw-r--r--lib/hwasan/hwasan_flags.inc57
-rw-r--r--lib/hwasan/hwasan_interceptors.cc333
-rw-r--r--lib/hwasan/hwasan_interface_internal.h75
-rw-r--r--lib/hwasan/hwasan_linux.cc231
-rw-r--r--lib/hwasan/hwasan_mapping.h63
-rw-r--r--lib/hwasan/hwasan_memintrinsics.cc45
-rw-r--r--lib/hwasan/hwasan_new_delete.cc2
-rw-r--r--lib/hwasan/hwasan_poisoning.cc6
-rw-r--r--lib/hwasan/hwasan_poisoning.h2
-rw-r--r--lib/hwasan/hwasan_report.cc412
-rw-r--r--lib/hwasan/hwasan_report.h8
-rw-r--r--lib/hwasan/hwasan_thread.cc105
-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.h3
-rw-r--r--lib/interception/interception_linux.h2
-rw-r--r--lib/interception/interception_win.cc8
-rw-r--r--lib/lsan/lsan_allocator.cc3
-rw-r--r--lib/lsan/lsan_allocator.h36
-rw-r--r--lib/lsan/lsan_common.cc9
-rw-r--r--lib/lsan/lsan_common_mac.cc12
-rw-r--r--lib/lsan/lsan_interceptors.cc2
-rw-r--r--lib/msan/msan.cc6
-rw-r--r--lib/msan/msan_allocator.cc8
-rw-r--r--lib/msan/msan_interceptors.cc76
-rw-r--r--lib/msan/msan_linux.cc46
-rw-r--r--lib/msan/tests/msan_test.cc4
-rw-r--r--lib/profile/GCDAProfiling.c49
-rw-r--r--lib/profile/InstrProfData.inc20
-rw-r--r--lib/profile/InstrProfilingPlatformLinux.c2
-rw-r--r--lib/profile/InstrProfilingPlatformOther.c2
-rw-r--r--lib/profile/InstrProfilingValue.c5
-rw-r--r--lib/profile/WindowsMMap.c8
-rw-r--r--lib/profile/WindowsMMap.h10
-rw-r--r--lib/safestack/CMakeLists.txt27
-rw-r--r--lib/safestack/safestack.cc83
-rw-r--r--lib/sanitizer_common/CMakeLists.txt12
-rw-r--r--lib/sanitizer_common/sanitizer_allocator.h6
-rw-r--r--lib/sanitizer_common/sanitizer_allocator_bytemap.h11
-rw-r--r--lib/sanitizer_common/sanitizer_allocator_combined.h16
-rw-r--r--lib/sanitizer_common/sanitizer_allocator_internal.h3
-rw-r--r--lib/sanitizer_common/sanitizer_allocator_primary32.h5
-rw-r--r--lib/sanitizer_common/sanitizer_allocator_primary64.h5
-rw-r--r--lib/sanitizer_common/sanitizer_allocator_secondary.h19
-rw-r--r--lib/sanitizer_common/sanitizer_allocator_size_class_map.h8
-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.h14
-rw-r--r--lib/sanitizer_common/sanitizer_common_interceptors.inc2340
-rw-r--r--lib/sanitizer_common/sanitizer_common_libcdep.cc21
-rw-r--r--lib/sanitizer_common/sanitizer_coverage_fuchsia.cc9
-rw-r--r--lib/sanitizer_common/sanitizer_coverage_win_sections.cc53
-rw-r--r--lib/sanitizer_common/sanitizer_file.h3
-rw-r--r--lib/sanitizer_common/sanitizer_flags.inc5
-rw-r--r--lib/sanitizer_common/sanitizer_fuchsia.cc69
-rw-r--r--lib/sanitizer_common/sanitizer_interceptors_ioctl_netbsd.inc55
-rw-r--r--lib/sanitizer_common/sanitizer_internal_defs.h10
-rw-r--r--lib/sanitizer_common/sanitizer_libc.cc12
-rw-r--r--lib/sanitizer_common/sanitizer_linux.cc304
-rw-r--r--lib/sanitizer_common/sanitizer_linux.h13
-rw-r--r--lib/sanitizer_common/sanitizer_linux_libcdep.cc45
-rw-r--r--lib/sanitizer_common/sanitizer_local_address_space_view.h77
-rw-r--r--lib/sanitizer_common/sanitizer_mac.cc109
-rw-r--r--lib/sanitizer_common/sanitizer_mac.h5
-rw-r--r--lib/sanitizer_common/sanitizer_malloc_mac.inc37
-rw-r--r--lib/sanitizer_common/sanitizer_netbsd.cc335
-rw-r--r--lib/sanitizer_common/sanitizer_openbsd.cc29
-rw-r--r--lib/sanitizer_common/sanitizer_platform.h22
-rw-r--r--lib/sanitizer_common/sanitizer_platform_interceptors.h49
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_freebsd.cc526
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_freebsd.h657
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_netbsd.cc358
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_netbsd.h175
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_posix.cc94
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_posix.h139
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_solaris.h3
-rw-r--r--lib/sanitizer_common/sanitizer_posix.cc40
-rw-r--r--lib/sanitizer_common/sanitizer_posix.h12
-rw-r--r--lib/sanitizer_common/sanitizer_posix_libcdep.cc8
-rw-r--r--lib/sanitizer_common/sanitizer_procmaps.h1
-rw-r--r--lib/sanitizer_common/sanitizer_procmaps_bsd.cc9
-rw-r--r--lib/sanitizer_common/sanitizer_procmaps_common.cc6
-rw-r--r--lib/sanitizer_common/sanitizer_procmaps_linux.cc1
-rw-r--r--lib/sanitizer_common/sanitizer_procmaps_mac.cc12
-rw-r--r--lib/sanitizer_common/sanitizer_procmaps_solaris.cc11
-rw-r--r--lib/sanitizer_common/sanitizer_ring_buffer.h162
-rw-r--r--lib/sanitizer_common/sanitizer_rtems.cc8
-rw-r--r--lib/sanitizer_common/sanitizer_solaris.cc49
-rw-r--r--lib/sanitizer_common/sanitizer_stackdepot.cc2
-rw-r--r--lib/sanitizer_common/sanitizer_stackdepot.h2
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace.h2
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc24
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace_printer.cc2
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace_sparc.cc5
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_fuchsia.h3
-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_syscalls_netbsd.inc38
-rw-r--r--lib/sanitizer_common/sanitizer_termination.cc9
-rw-r--r--lib/sanitizer_common/sanitizer_thread_registry.cc11
-rw-r--r--lib/sanitizer_common/sanitizer_thread_registry.h1
-rw-r--r--lib/sanitizer_common/sanitizer_type_traits.cc21
-rw-r--r--lib/sanitizer_common/sanitizer_type_traits.h44
-rw-r--r--lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cc2
-rw-r--r--lib/sanitizer_common/sanitizer_win.cc21
-rw-r--r--lib/sanitizer_common/sanitizer_win_defs.h20
-rwxr-xr-xlib/sanitizer_common/scripts/gen_dynamic_list.py23
-rw-r--r--lib/sanitizer_common/symbolizer/scripts/global_symbols.txt3
-rw-r--r--lib/sanitizer_common/tests/CMakeLists.txt9
-rw-r--r--lib/sanitizer_common/tests/sanitizer_allocator_test.cc109
-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/sanitizer_common/tests/sanitizer_type_traits_test.cc28
-rw-r--r--lib/scudo/CMakeLists.txt8
-rw-r--r--lib/scudo/scudo_allocator.cpp27
-rw-r--r--lib/scudo/scudo_allocator.h4
-rw-r--r--lib/scudo/scudo_malloc.cpp2
-rw-r--r--lib/tsan/CMakeLists.txt20
-rwxr-xr-xlib/tsan/check_analyze.sh14
-rwxr-xr-xlib/tsan/go/buildgo.sh14
-rw-r--r--lib/tsan/rtl/tsan_debugging.cc50
-rw-r--r--lib/tsan/rtl/tsan_flags.cc3
-rw-r--r--lib/tsan/rtl/tsan_flags.inc2
-rw-r--r--lib/tsan/rtl/tsan_interceptors.cc75
-rw-r--r--lib/tsan/rtl/tsan_interceptors.h4
-rw-r--r--lib/tsan/rtl/tsan_interceptors_mac.cc65
-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_report.cc67
-rw-r--r--lib/tsan/rtl/tsan_rtl.cc4
-rw-r--r--lib/tsan/rtl/tsan_rtl.h9
-rw-r--r--lib/tsan/rtl/tsan_rtl_aarch64.S12
-rw-r--r--lib/tsan/rtl/tsan_rtl_amd64.S3
-rw-r--r--lib/tsan/rtl/tsan_rtl_thread.cc6
-rw-r--r--lib/tsan/rtl/tsan_suppressions.cc56
-rw-r--r--lib/ubsan/ubsan_checks.inc15
-rw-r--r--lib/ubsan/ubsan_flags.inc4
-rw-r--r--lib/ubsan/ubsan_handlers.cc97
-rw-r--r--lib/ubsan/ubsan_handlers.h17
-rw-r--r--lib/ubsan/ubsan_interface.inc2
-rw-r--r--lib/ubsan_minimal/CMakeLists.txt8
-rw-r--r--lib/ubsan_minimal/ubsan_minimal_handlers.cc1
-rw-r--r--lib/xray/CMakeLists.txt6
-rw-r--r--lib/xray/tests/CMakeLists.txt47
-rw-r--r--lib/xray/tests/unit/CMakeLists.txt28
-rw-r--r--lib/xray/tests/unit/allocator_test.cc42
-rw-r--r--lib/xray/tests/unit/buffer_queue_test.cc129
-rw-r--r--lib/xray/tests/unit/fdr_controller_test.cc424
-rw-r--r--lib/xray/tests/unit/fdr_log_writer_test.cc162
-rw-r--r--lib/xray/tests/unit/fdr_logging_test.cc202
-rw-r--r--lib/xray/tests/unit/function_call_trie_test.cc168
-rw-r--r--lib/xray/tests/unit/profile_collector_test.cc65
-rw-r--r--lib/xray/tests/unit/segmented_array_test.cc149
-rw-r--r--lib/xray/tests/unit/test_helpers.cc95
-rw-r--r--lib/xray/tests/unit/test_helpers.h78
-rw-r--r--lib/xray/xray_allocator.h210
-rw-r--r--lib/xray/xray_basic_logging.cc145
-rw-r--r--lib/xray/xray_buffer_queue.cc281
-rw-r--r--lib/xray/xray_buffer_queue.h81
-rw-r--r--lib/xray/xray_defs.h10
-rw-r--r--lib/xray/xray_fdr_controller.h373
-rw-r--r--lib/xray/xray_fdr_log_records.h5
-rw-r--r--lib/xray/xray_fdr_log_writer.h232
-rw-r--r--lib/xray/xray_fdr_logging.cc1096
-rw-r--r--lib/xray/xray_function_call_trie.h387
-rw-r--r--lib/xray/xray_init.cc18
-rw-r--r--lib/xray/xray_interface.cc41
-rw-r--r--lib/xray/xray_profile_collector.cc361
-rw-r--r--lib/xray/xray_profile_collector.h26
-rw-r--r--lib/xray/xray_profiling.cc387
-rw-r--r--lib/xray/xray_profiling_flags.inc5
-rw-r--r--lib/xray/xray_segmented_array.h584
-rw-r--r--lib/xray/xray_trampoline_x86_64.S4
-rw-r--r--lib/xray/xray_tsc.h23
-rw-r--r--lib/xray/xray_utils.cc172
-rw-r--r--lib/xray/xray_utils.h47
-rw-r--r--lib/xray/xray_x86_64.cc22
-rw-r--r--test/.clang-format2
-rw-r--r--test/CMakeLists.txt2
-rw-r--r--test/asan/CMakeLists.txt2
-rw-r--r--test/asan/TestCases/Darwin/init_for_dlopen.cc46
-rw-r--r--test/asan/TestCases/Darwin/odr-lto.cc4
-rw-r--r--test/asan/TestCases/Darwin/segv_read_write.c5
-rw-r--r--test/asan/TestCases/Linux/asan_rt_confict_test-2.cc4
-rw-r--r--test/asan/TestCases/Linux/coverage-missing.cc10
-rw-r--r--test/asan/TestCases/Linux/local_alias.cc11
-rw-r--r--test/asan/TestCases/Linux/new_delete_mismatch.cc1
-rw-r--r--test/asan/TestCases/Linux/new_delete_mismatch_global.cc16
-rw-r--r--test/asan/TestCases/Linux/new_delete_mismatch_stack.cc17
-rw-r--r--test/asan/TestCases/Linux/odr-violation.cc26
-rw-r--r--test/asan/TestCases/Linux/odr-vtable.cc26
-rw-r--r--test/asan/TestCases/Linux/odr_c_test.c8
-rw-r--r--test/asan/TestCases/Linux/odr_indicators.cc26
-rw-r--r--test/asan/TestCases/Linux/preinit_test.cc7
-rw-r--r--test/asan/TestCases/Linux/preinstalled_signal.cc2
-rw-r--r--test/asan/TestCases/Posix/coverage-reset.cc2
-rw-r--r--test/asan/TestCases/Posix/coverage.cc1
-rw-r--r--test/asan/TestCases/Posix/dlclose-test.cc2
-rw-r--r--test/asan/TestCases/Posix/interception-in-shared-lib-test.cc2
-rw-r--r--test/asan/TestCases/Posix/mmap_limit_mb.cc2
-rw-r--r--test/asan/TestCases/Posix/no-fd.cc43
-rw-r--r--test/asan/TestCases/Posix/stack-use-after-return.cc4
-rw-r--r--test/asan/TestCases/Posix/strndup_oob_test.cc2
-rw-r--r--test/asan/TestCases/Posix/strndup_oob_test2.cc44
-rw-r--r--test/asan/TestCases/Posix/tsd_dtor_leak.cc3
-rw-r--r--test/asan/TestCases/asan_and_llvm_coverage_test.cc3
-rw-r--r--test/asan/TestCases/atoll_strict.c2
-rw-r--r--test/asan/TestCases/heavy_uar_test.cc6
-rw-r--r--test/asan/TestCases/initialization-bug.cc2
-rw-r--r--test/asan/TestCases/intercept-rethrow-exception.cc4
-rw-r--r--test/asan/TestCases/interception_failure_test.cc4
-rw-r--r--test/asan/TestCases/intra-object-overflow.cc2
-rw-r--r--test/asan/TestCases/log-path_test.cc2
-rw-r--r--test/asan/TestCases/pass-object-byval.cc2
-rw-r--r--test/asan/TestCases/printf-2.c2
-rw-r--r--test/asan/TestCases/printf-3.c6
-rw-r--r--test/asan/TestCases/printf-4.c2
-rw-r--r--test/asan/TestCases/printf-5.c2
-rw-r--r--test/asan/TestCases/printf-m.c2
-rw-r--r--test/asan/TestCases/set_shadow_test.c2
-rw-r--r--test/asan/TestCases/strcasestr-1.c2
-rw-r--r--test/asan/TestCases/strcasestr-2.c2
-rw-r--r--test/asan/TestCases/strcasestr_strict.c2
-rw-r--r--test/asan/TestCases/strcat-overlap.cc2
-rw-r--r--test/asan/TestCases/strncasecmp_strict.c2
-rw-r--r--test/asan/TestCases/strtoll_strict.c2
-rw-r--r--test/asan/TestCases/suppressions-exec-relative-location.cc2
-rw-r--r--test/asan/TestCases/suppressions-function.cc2
-rw-r--r--test/asan/TestCases/suppressions-library.cc1
-rw-r--r--test/asan/TestCases/throw_catch.cc2
-rw-r--r--test/asan/TestCases/time_interceptor.cc2
-rw-r--r--test/asan/TestCases/verbose-log-path_test.cc2
-rw-r--r--test/asan/lit.cfg11
-rw-r--r--test/builtins/CMakeLists.txt10
-rw-r--r--test/builtins/Unit/absvti2_test.c1
-rw-r--r--test/builtins/Unit/addvti3_test.c1
-rw-r--r--test/builtins/Unit/ashlti3_test.c1
-rw-r--r--test/builtins/Unit/ashrti3_test.c1
-rw-r--r--test/builtins/Unit/clzti2_test.c1
-rw-r--r--test/builtins/Unit/cmpti2_test.c1
-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/builtins/Unit/ctzti2_test.c1
-rw-r--r--test/builtins/Unit/divti3_test.c1
-rw-r--r--test/builtins/Unit/ffsti2_test.c1
-rw-r--r--test/builtins/Unit/fixdfti_test.c1
-rw-r--r--test/builtins/Unit/fixsfti_test.c1
-rw-r--r--test/builtins/Unit/fixunsdfti_test.c21
-rw-r--r--test/builtins/Unit/fixunssfti_test.c9
-rw-r--r--test/builtins/Unit/fixunsxfti_test.c4
-rw-r--r--test/builtins/Unit/fixxfdi_test.c2
-rw-r--r--test/builtins/Unit/fixxfti_test.c10
-rw-r--r--test/builtins/Unit/floattidf_test.c1
-rw-r--r--test/builtins/Unit/floattisf_test.c1
-rw-r--r--test/builtins/Unit/floattixf_test.c10
-rw-r--r--test/builtins/Unit/floatuntidf_test.c1
-rw-r--r--test/builtins/Unit/floatuntisf_test.c10
-rw-r--r--test/builtins/Unit/floatuntixf_test.c10
-rw-r--r--test/builtins/Unit/lit.cfg8
-rw-r--r--test/builtins/Unit/lit.site.cfg.in3
-rw-r--r--test/builtins/Unit/lshrti3_test.c1
-rw-r--r--test/builtins/Unit/modti3_test.c13
-rw-r--r--test/builtins/Unit/muloti4_test.c1
-rw-r--r--test/builtins/Unit/multi3_test.c1
-rw-r--r--test/builtins/Unit/mulvti3_test.c7
-rw-r--r--test/builtins/Unit/negti2_test.c1
-rw-r--r--test/builtins/Unit/negvti2_test.c1
-rw-r--r--test/builtins/Unit/parityti2_test.c1
-rw-r--r--test/builtins/Unit/popcountti2_test.c1
-rw-r--r--test/builtins/Unit/ppc/fixtfdi_test.c6
-rw-r--r--test/builtins/Unit/ppc/fixunstfti_test.c52
-rw-r--r--test/builtins/Unit/ppc/fixunstfti_test.h706
-rw-r--r--test/builtins/Unit/ppc/floatditf_test.c5
-rw-r--r--test/builtins/Unit/ppc/floattitf_test.c59
-rw-r--r--test/builtins/Unit/ppc/floattitf_test.h197
-rw-r--r--test/builtins/Unit/ppc/floatunditf_test.c5
-rw-r--r--test/builtins/Unit/ppc/qadd_test.c4
-rw-r--r--test/builtins/Unit/ppc/qdiv_test.c4
-rw-r--r--test/builtins/Unit/ppc/qmul_test.c4
-rw-r--r--test/builtins/Unit/ppc/qsub_test.c4
-rw-r--r--test/builtins/Unit/subvti3_test.c1
-rw-r--r--test/builtins/Unit/ucmpti2_test.c1
-rw-r--r--test/builtins/Unit/udivmodti4_test.c1
-rw-r--r--test/builtins/Unit/udivti3_test.c1
-rw-r--r--test/builtins/Unit/umodti3_test.c1
-rw-r--r--test/cfi/bad-split.cpp2
-rw-r--r--test/cfi/cross-dso-diagnostic.cpp2
-rw-r--r--test/cfi/cross-dso/lit.local.cfg2
-rw-r--r--test/cfi/mfcall.cpp2
-rw-r--r--test/cfi/target_uninstrumented.cpp2
-rw-r--r--test/cfi/two-vcalls.cpp2
-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/AlignmentAssumptionTest.cpp27
-rw-r--r--test/fuzzer/ImplicitIntegerSignChangeTest.cpp27
-rw-r--r--test/fuzzer/ImplicitSignedIntegerTruncationOrSignChangeTest.cpp27
-rw-r--r--test/fuzzer/ImplicitSignedIntegerTruncationTest.cpp (renamed from test/fuzzer/ImplicitIntegerTruncationTest.cpp)4
-rw-r--r--test/fuzzer/ImplicitUnsignedIntegerTruncationTest.cpp27
-rw-r--r--test/fuzzer/InitializeTest.cpp5
-rw-r--r--test/fuzzer/PrintUnstableStatsTest.cpp69
-rw-r--r--test/fuzzer/ReadBinaryTest.cpp18
-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/counters.test3
-rw-r--r--test/fuzzer/coverage.test3
-rw-r--r--test/fuzzer/dead-stripping.test23
-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-alignment-assumption.test7
-rw-r--r--test/fuzzer/fuzzer-implicit-integer-sign-change.test5
-rw-r--r--test/fuzzer/fuzzer-implicit-integer-truncation.test5
-rw-r--r--test/fuzzer/fuzzer-implicit-signed-integer-truncation-or-sign-change.test5
-rw-r--r--test/fuzzer/fuzzer-implicit-signed-integer-truncation.test5
-rw-r--r--test/fuzzer/fuzzer-implicit-unsigned-integer-truncation.test5
-rw-r--r--test/fuzzer/fuzzer-mutationstats.test5
-rw-r--r--test/fuzzer/fuzzer-oom.test22
-rw-r--r--test/fuzzer/gc-sections.test7
-rw-r--r--test/fuzzer/handle-unstable.test42
-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/only-some-bytes.test2
-rw-r--r--test/fuzzer/print_unstable_stats.test3
-rw-r--r--test/fuzzer/read-binary.test7
-rw-r--r--test/fuzzer/shrink.test2
-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/fuzzer/windows-opt-ref.test9
-rw-r--r--test/hwasan/CMakeLists.txt3
-rw-r--r--test/hwasan/TestCases/Posix/system-allocator-fallback.cc54
-rw-r--r--test/hwasan/TestCases/abort-message-android.cc28
-rw-r--r--test/hwasan/TestCases/cfi.cc18
-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.c60
-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/mem-intrinsics-zero-size.c10
-rw-r--r--test/hwasan/TestCases/mem-intrinsics.c37
-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/random-align-right.c35
-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.c41
-rw-r--r--test/hwasan/TestCases/stack-uar.cc23
-rw-r--r--test/hwasan/TestCases/tail-magic.c28
-rw-r--r--test/hwasan/TestCases/thread-uaf.c58
-rw-r--r--test/hwasan/TestCases/uaf_with_rb_distance.c27
-rw-r--r--test/hwasan/TestCases/use-after-free.c43
-rw-r--r--test/hwasan/lit.cfg6
-rw-r--r--test/hwasan/lit.site.cfg.in1
-rw-r--r--test/lit.common.cfg69
-rw-r--r--test/lit.common.configured.in3
-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/lsan/TestCases/Linux/use_tls_dynamic.cc5
-rw-r--r--test/msan/Linux/reexec_unlimited_stack.cc23
-rw-r--r--test/msan/Linux/sunrpc.cc2
-rw-r--r--test/msan/Linux/sunrpc_bytes.cc2
-rw-r--r--test/msan/Linux/sunrpc_string.cc2
-rw-r--r--test/msan/chained_origin_with_signals.cc3
-rw-r--r--test/msan/dtls_test.c8
-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/pthread_getname_np.cc6
-rw-r--r--test/msan/signal_stress_test.cc3
-rw-r--r--test/msan/strndup.cc2
-rw-r--r--test/msan/tls_reuse.cc1
-rw-r--r--test/msan/vararg.cc60
-rw-r--r--test/profile/Inputs/instrprof-dlopen-dlclose-main.c.gcov2
-rw-r--r--test/profile/Inputs/instrprof-dlopen-dlclose-main_three-libs.c.gcov2
-rw-r--r--test/profile/Inputs/instrprof-gcov-__gcov_flush-multiple.c16
-rw-r--r--test/profile/Inputs/instrprof-gcov-__gcov_flush-multiple.c.gcov21
-rw-r--r--test/profile/Inputs/instrprof-gcov-exceptions.cpp.gcov6
-rw-r--r--test/profile/Inputs/instrprof-gcov-execlp.c15
-rw-r--r--test/profile/Inputs/instrprof-gcov-execlp.c.gcov23
-rw-r--r--test/profile/Inputs/instrprof-gcov-execvp.c17
-rw-r--r--test/profile/Inputs/instrprof-gcov-execvp.c.gcov25
-rw-r--r--test/profile/Inputs/instrprof-gcov-fork.c.gcov4
-rw-r--r--test/profile/Inputs/instrprof-gcov-multiple-bbs-single-line.c.gcov2
-rw-r--r--test/profile/Inputs/instrprof-gcov-one-line-function.c11
-rw-r--r--test/profile/Inputs/instrprof-gcov-one-line-function.c.gcov16
-rw-r--r--test/profile/Inputs/instrprof-gcov-switch1.c.gcov6
-rw-r--r--test/profile/Inputs/instrprof-gcov-switch2.c.gcov6
-rw-r--r--test/profile/Inputs/instrprof-shared-lib.c.gcov2
-rw-r--r--test/profile/Inputs/instrprof-shared-lib_called-twice.c.gcov2
-rw-r--r--test/profile/Inputs/instrprof-shared-lib_in-loop.c.gcov2
-rw-r--r--test/profile/Inputs/instrprof-shared-main-gcov-flush_no-writeout.c.gcov4
-rw-r--r--test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-after.c.gcov4
-rw-r--r--test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-before-after.c.gcov4
-rw-r--r--test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-before.c.gcov4
-rw-r--r--test/profile/Inputs/instrprof-shared-main.c.gcov6
-rw-r--r--test/profile/Posix/instrprof-gcov-execlp.test10
-rw-r--r--test/profile/Posix/instrprof-gcov-execvp.test10
-rw-r--r--test/profile/Posix/instrprof-gcov-fork.test2
-rw-r--r--test/profile/instrprof-darwin-exports.c16
-rw-r--r--test/profile/instrprof-dlopen-dlclose-gcov.test7
-rw-r--r--test/profile/instrprof-gcov-__gcov_flush-multiple.test10
-rw-r--r--test/profile/instrprof-gcov-multiple-bbs-single-line.test2
-rw-r--r--test/profile/instrprof-gcov-one-line-function.test9
-rw-r--r--test/profile/instrprof-gcov-two-objects.test2
-rw-r--r--test/profile/instrprof-merging.cpp53
-rw-r--r--test/profile/instrprof-shared-gcov-flush.test8
-rw-r--r--test/profile/lit.cfg2
-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/CMakeLists.txt7
-rw-r--r--test/sanitizer_common/TestCases/FreeBSD/capsicum.cc68
-rw-r--r--test/sanitizer_common/TestCases/FreeBSD/fdevname.cc44
-rw-r--r--test/sanitizer_common/TestCases/FreeBSD/lit.local.cfg9
-rw-r--r--test/sanitizer_common/TestCases/Linux/allow_user_segv.cc2
-rw-r--r--test/sanitizer_common/TestCases/Linux/assert.cc2
-rw-r--r--test/sanitizer_common/TestCases/Linux/ill.cc2
-rw-r--r--test/sanitizer_common/TestCases/Linux/mallopt.cc10
-rw-r--r--test/sanitizer_common/TestCases/Linux/signal_segv_handler.cc2
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/asysctl.cc44
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/cdb.cc134
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/fparseln.cc25
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/funopen2.cc110
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/getgroupmembership.cc2
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/getvfsstat.cc36
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/md2.cc114
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/md4.cc114
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/md5.cc114
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/mi_vector_hash.cc19
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/rmd160.cc133
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/sha1.cc171
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/sha2.cc206
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/statvfs1.cc58
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/strtoi.cc43
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/sysctlgetmibinfo.cc36
-rw-r--r--test/sanitizer_common/TestCases/Posix/arc4random.cc71
-rw-r--r--test/sanitizer_common/TestCases/Posix/dedup_token_length_test.cc2
-rw-r--r--test/sanitizer_common/TestCases/Posix/devname.cc8
-rw-r--r--test/sanitizer_common/TestCases/Posix/devname_r.cc11
-rw-r--r--test/sanitizer_common/TestCases/Posix/dump_instruction_bytes.cc2
-rw-r--r--test/sanitizer_common/TestCases/Posix/feof_fileno_ferror.cc41
-rw-r--r--test/sanitizer_common/TestCases/Posix/fgetc_ungetc_getc.cc19
-rw-r--r--test/sanitizer_common/TestCases/Posix/fgetln.cc16
-rw-r--r--test/sanitizer_common/TestCases/Posix/fgets.cc18
-rw-r--r--test/sanitizer_common/TestCases/Posix/fputc_putc_putchar.cc13
-rw-r--r--test/sanitizer_common/TestCases/Posix/fputs_puts.cc12
-rw-r--r--test/sanitizer_common/TestCases/Posix/fseek.cc53
-rw-r--r--test/sanitizer_common/TestCases/Posix/fts.cc42
-rw-r--r--test/sanitizer_common/TestCases/Posix/funopen.cc91
-rw-r--r--test/sanitizer_common/TestCases/Posix/getc_unlocked.cc20
-rw-r--r--test/sanitizer_common/TestCases/Posix/getfsent.cc36
-rw-r--r--test/sanitizer_common/TestCases/Posix/getmntinfo.cc35
-rw-r--r--test/sanitizer_common/TestCases/Posix/getusershell.cc23
-rw-r--r--test/sanitizer_common/TestCases/Posix/lstat.cc8
-rw-r--r--test/sanitizer_common/TestCases/Posix/nl_langinfo.cc20
-rw-r--r--test/sanitizer_common/TestCases/Posix/popen.cc23
-rw-r--r--test/sanitizer_common/TestCases/Posix/putc_putchar_unlocked.cc12
-rw-r--r--test/sanitizer_common/TestCases/Posix/readlinkat.c2
-rw-r--r--test/sanitizer_common/TestCases/Posix/regex.cc71
-rw-r--r--test/sanitizer_common/TestCases/Posix/setvbuf.cc81
-rw-r--r--test/sanitizer_common/TestCases/Posix/sl_add.cc26
-rw-r--r--test/sanitizer_common/TestCases/Posix/strtonum.cc54
-rw-r--r--test/sanitizer_common/TestCases/Posix/sysctl.cc64
-rw-r--r--test/sanitizer_common/TestCases/Posix/vis.cc247
-rw-r--r--test/sanitizer_common/TestCases/allocator_returns_null.cc2
-rw-r--r--test/sanitizer_common/TestCases/corelimit.cc2
-rw-r--r--test/sanitizer_common/TestCases/get_module_and_offset_for_pc.cc2
-rw-r--r--test/sanitizer_common/TestCases/hard_rss_limit_mb_test.cc (renamed from test/sanitizer_common/TestCases/Linux/hard_rss_limit_mb_test.cc)3
-rw-r--r--test/sanitizer_common/TestCases/malloc_hook.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/strcasestr.c2
-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/Darwin/ignore-noninstrumented.mm5
-rw-r--r--test/tsan/Darwin/ignored-interceptors.mm55
-rw-r--r--test/tsan/Darwin/objc-synchronize-cycle-tagged.mm42
-rw-r--r--test/tsan/Darwin/objc-synchronize-cycle.mm31
-rw-r--r--test/tsan/Darwin/objc-synchronize-nested-recursive.mm35
-rw-r--r--test/tsan/Linux/thread_timedjoin.c39
-rw-r--r--test/tsan/Linux/thread_tryjoin.c41
-rw-r--r--test/tsan/cxa_guard_acquire.cc25
-rw-r--r--test/tsan/deadlock_detector_stress_test.cc10
-rw-r--r--test/tsan/dtls.c1
-rw-r--r--test/tsan/getline_nohang.cc3
-rw-r--r--test/tsan/ignore_lib5.cc4
-rw-r--r--test/tsan/ignored-interceptors-mmap.cc63
-rw-r--r--test/tsan/large_malloc_meta.cc3
-rw-r--r--test/tsan/mmap_large.cc3
-rw-r--r--test/tsan/mutex_cycle2.c2
-rw-r--r--test/tsan/mutex_lock_destroyed.cc3
-rw-r--r--test/tsan/strerror_r.cc8
-rw-r--r--test/tsan/sunrpc.cc2
-rw-r--r--test/ubsan/CMakeLists.txt63
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/integer-arithmetic-value-change.c345
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/integer-conversion.c351
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/integer-sign-change-blacklist.c28
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/integer-sign-change-summary.cpp (renamed from test/ubsan/TestCases/ImplicitConversion/integer-truncation-summary.cpp)6
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/integer-sign-change.c297
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/integer-truncation-blacklist.c20
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/integer-truncation.c363
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-blacklist.c60
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-or-sign-change-blacklist.c58
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-or-sign-change-summary.cpp13
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-summary.cpp13
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation.c318
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/unsigned-integer-truncation-blacklist.c60
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/unsigned-integer-truncation-summary.cpp13
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/unsigned-integer-truncation.c304
-rw-r--r--test/ubsan/TestCases/Integer/no-recover.cpp4
-rw-r--r--test/ubsan/TestCases/Misc/enum.cpp2
-rw-r--r--test/ubsan/TestCases/Misc/log-path_test.cc4
-rw-r--r--test/ubsan/TestCases/Misc/monitor.cpp2
-rw-r--r--test/ubsan/TestCases/Pointer/alignment-assumption-attribute-align_value-on-lvalue.cpp36
-rw-r--r--test/ubsan/TestCases/Pointer/alignment-assumption-attribute-align_value-on-paramvar.cpp30
-rw-r--r--test/ubsan/TestCases/Pointer/alignment-assumption-attribute-alloc_align-on-function-variable.cpp33
-rw-r--r--test/ubsan/TestCases/Pointer/alignment-assumption-attribute-alloc_align-on-function.cpp29
-rw-r--r--test/ubsan/TestCases/Pointer/alignment-assumption-attribute-assume_aligned-on-function-two-params.cpp28
-rw-r--r--test/ubsan/TestCases/Pointer/alignment-assumption-attribute-assume_aligned-on-function.cpp28
-rw-r--r--test/ubsan/TestCases/Pointer/alignment-assumption-blacklist.cpp20
-rw-r--r--test/ubsan/TestCases/Pointer/alignment-assumption-builtin_assume_aligned-three-params-variable.cpp27
-rw-r--r--test/ubsan/TestCases/Pointer/alignment-assumption-builtin_assume_aligned-three-params.cpp23
-rw-r--r--test/ubsan/TestCases/Pointer/alignment-assumption-builtin_assume_aligned-two-params.cpp23
-rw-r--r--test/ubsan/TestCases/Pointer/alignment-assumption-openmp.cpp28
-rw-r--r--test/ubsan/TestCases/Pointer/alignment-assumption-summary.cpp17
-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/TestCases/TypeCheck/Function/function.cpp2
-rw-r--r--test/ubsan/TestCases/TypeCheck/PR33221.cpp2
-rw-r--r--test/ubsan/TestCases/TypeCheck/vptr-corrupted-vtable-itanium.cpp2
-rw-r--r--test/ubsan/TestCases/TypeCheck/vptr-non-unique-typeinfo.cpp3
-rw-r--r--test/ubsan/TestCases/TypeCheck/vptr-virtual-base-construction.cpp2
-rw-r--r--test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp4
-rw-r--r--test/ubsan/TestCases/TypeCheck/vptr.cpp4
-rw-r--r--test/ubsan/lit.common.cfg5
-rw-r--r--test/ubsan/lit.site.cfg.in1
-rw-r--r--test/ubsan_minimal/TestCases/alignment-assumption.c17
-rw-r--r--test/ubsan_minimal/TestCases/implicit-integer-sign-change.c17
-rw-r--r--test/ubsan_minimal/TestCases/implicit-signed-integer-truncation-or-sign-change.c17
-rw-r--r--test/ubsan_minimal/TestCases/implicit-signed-integer-truncation.c25
-rw-r--r--test/ubsan_minimal/TestCases/implicit-unsigned-integer-truncation.c (renamed from test/ubsan_minimal/TestCases/implicit-integer-truncation.c)5
-rw-r--r--test/xray/TestCases/Posix/argv0-log-file-name.cc2
-rw-r--r--test/xray/TestCases/Posix/basic-filtering.cc4
-rw-r--r--test/xray/TestCases/Posix/clang-no-xray-instrument.cc11
-rw-r--r--test/xray/TestCases/Posix/fdr-mode.cc33
-rw-r--r--test/xray/TestCases/Posix/fdr-reinit.cc73
-rw-r--r--test/xray/TestCases/Posix/fdr-single-thread.cc12
-rw-r--r--test/xray/TestCases/Posix/fdr-thread-order.cc35
-rw-r--r--test/xray/TestCases/Posix/fork_basic_logging.cc21
-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
-rwxr-xr-xutils/generate_netbsd_ioctls.awk154
-rwxr-xr-xutils/generate_netbsd_syscalls.awk12
713 files changed, 23344 insertions, 5691 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 86ca2b3ef74b..aa360a3ef36e 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)
@@ -142,7 +200,7 @@ set(CXXABIS none default libstdc++ libc++)
set_property(CACHE SANITIZER_CXX_ABI PROPERTY STRINGS ;${CXXABIS})
if (SANITIZER_CXX_ABI STREQUAL "default")
- if (APPLE)
+ if (APPLE OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
set(SANITIZER_CXX_ABI_LIBNAME "libc++")
set(SANITIZER_CXX_ABI_SYSTEM 1)
elseif (FUCHSIA)
@@ -310,11 +368,14 @@ 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)
if (COMPILER_RT_USE_BUILTINS_LIBRARY)
list(APPEND SANITIZER_COMMON_LINK_LIBS ${COMPILER_RT_BUILTINS_LIBRARY})
+ string(REPLACE "-Wl,-z,defs" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}")
else()
if (ANDROID)
append_list_if(COMPILER_RT_HAS_GCC_LIB gcc SANITIZER_COMMON_LINK_LIBS)
@@ -325,14 +386,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)
@@ -375,15 +428,19 @@ endif()
add_subdirectory(include)
-foreach(path IN ITEMS ${LLVM_MAIN_SRC_DIR}/projects/libcxx
- ${LLVM_MAIN_SRC_DIR}/runtimes/libcxx
- ${LLVM_MAIN_SRC_DIR}/../libcxx
- ${LLVM_EXTERNAL_LIBCXX_SOURCE_DIR})
- if(IS_DIRECTORY ${path})
- set(COMPILER_RT_LIBCXX_PATH ${path})
- break()
- endif()
-endforeach()
+option(COMPILER_RT_USE_LIBCXX
+ "Enable compiler-rt to use libc++ from the source tree" ON)
+if(COMPILER_RT_USE_LIBCXX)
+ foreach(path IN ITEMS ${LLVM_MAIN_SRC_DIR}/projects/libcxx
+ ${LLVM_MAIN_SRC_DIR}/runtimes/libcxx
+ ${LLVM_MAIN_SRC_DIR}/../libcxx
+ ${LLVM_EXTERNAL_LIBCXX_SOURCE_DIR})
+ if(IS_DIRECTORY ${path})
+ set(COMPILER_RT_LIBCXX_PATH ${path})
+ break()
+ endif()
+ endforeach()
+endif()
set(COMPILER_RT_LLD_PATH ${LLVM_MAIN_SRC_DIR}/tools/lld)
if(EXISTS ${COMPILER_RT_LLD_PATH}/ AND LLVM_TOOL_LLD_BUILD)
diff --git a/LICENSE.TXT b/LICENSE.TXT
index 0134694e4e5f..1c94ad5d8911 100644
--- a/LICENSE.TXT
+++ b/LICENSE.TXT
@@ -14,7 +14,7 @@ Full text of the relevant licenses is included below.
University of Illinois/NCSA
Open Source License
-Copyright (c) 2009-2018 by the contributors listed in CREDITS.TXT
+Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT
All rights reserved.
diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake
index cd4c704fc824..81b110203c27 100644
--- a/cmake/Modules/AddCompilerRT.cmake
+++ b/cmake/Modules/AddCompilerRT.cmake
@@ -77,7 +77,7 @@ function(add_compiler_rt_object_libraries name)
endif()
set_target_compile_flags(${libname}
- ${CMAKE_CXX_FLAGS} ${extra_cflags_${libname}} ${target_flags})
+ ${extra_cflags_${libname}} ${target_flags})
set_property(TARGET ${libname} APPEND PROPERTY
COMPILE_DEFINITIONS ${LIB_DEFS})
set_target_properties(${libname} PROPERTIES FOLDER "Compiler-RT Libraries")
@@ -357,19 +357,20 @@ 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)
if(MSVC)
- # clang doesn't support exceptions on Windows yet.
- list(APPEND COMPILER_RT_UNITTEST_CFLAGS -D_HAS_EXCEPTIONS=0)
-
- # We should teach clang to understand "#pragma intrinsic", see PR19898.
- list(APPEND COMPILER_RT_UNITTEST_CFLAGS -Wno-undefined-inline)
-
- # Clang doesn't support SEH on Windows yet.
- list(APPEND COMPILER_RT_GTEST_CFLAGS -DGTEST_HAS_SEH=0)
-
# gtest use a lot of stuff marked as deprecated on Windows.
list(APPEND COMPILER_RT_GTEST_CFLAGS -Wno-deprecated-declarations)
endif()
@@ -550,6 +551,9 @@ macro(add_custom_libcxx name prefix)
set(PASSTHROUGH_VARIABLES
CMAKE_C_COMPILER_TARGET
CMAKE_CXX_COMPILER_TARGET
+ CMAKE_SHARED_LINKER_FLAGS
+ CMAKE_MODULE_LINKER_FLAGS
+ CMAKE_EXE_LINKER_FLAGS
CMAKE_INSTALL_PREFIX
CMAKE_MAKE_PROGRAM
CMAKE_LINKER
@@ -562,14 +566,20 @@ macro(add_custom_libcxx name prefix)
CMAKE_SYSROOT
CMAKE_SYSTEM_NAME)
foreach(variable ${PASSTHROUGH_VARIABLES})
- if(${variable})
- list(APPEND CMAKE_PASSTHROUGH_VARIABLES -D${variable}=${${variable}})
+ get_property(is_value_set CACHE ${variable} PROPERTY VALUE SET)
+ if(${is_value_set})
+ get_property(value CACHE ${variable} PROPERTY VALUE)
+ list(APPEND CMAKE_PASSTHROUGH_VARIABLES -D${variable}=${value})
endif()
endforeach()
- string(REPLACE ";" " " FLAGS_STRING "${LIBCXX_CFLAGS}")
- set(LIBCXX_C_FLAGS "${FLAGS_STRING}")
- set(LIBCXX_CXX_FLAGS "${FLAGS_STRING}")
+ string(REPLACE ";" " " LIBCXX_C_FLAGS "${LIBCXX_CFLAGS}")
+ get_property(C_FLAGS CACHE CMAKE_C_FLAGS PROPERTY VALUE)
+ set(LIBCXX_C_FLAGS "${LIBCXX_C_FLAGS} ${C_FLAGS}")
+
+ string(REPLACE ";" " " LIBCXX_CXX_FLAGS "${LIBCXX_CFLAGS}")
+ get_property(CXX_FLAGS CACHE CMAKE_CXX_FLAGS PROPERTY VALUE)
+ set(LIBCXX_CXX_FLAGS "${LIBCXX_CXX_FLAGS} ${CXX_FLAGS}")
ExternalProject_Add(${name}
DEPENDS ${name}-clobber ${LIBCXX_DEPS}
diff --git a/cmake/Modules/CompilerRTUtils.cmake b/cmake/Modules/CompilerRTUtils.cmake
index e5651718fa34..5348f2064b67 100644
--- a/cmake/Modules/CompilerRTUtils.cmake
+++ b/cmake/Modules/CompilerRTUtils.cmake
@@ -58,14 +58,6 @@ macro(append_rtti_flag polarity list)
endif()
endmacro()
-macro(append_have_file_definition filename varname list)
- check_include_file("${filename}" "${varname}")
- if (NOT ${varname})
- set("${varname}" 0)
- endif()
- list(APPEND ${list} "${varname}=${${varname}}")
-endmacro()
-
macro(list_intersect output input1 input2)
set(${output})
foreach(it ${${input1}})
@@ -210,47 +202,91 @@ macro(load_llvm_config)
find_program(LLVM_CONFIG_PATH "llvm-config"
DOC "Path to llvm-config binary")
if (NOT LLVM_CONFIG_PATH)
- message(FATAL_ERROR "llvm-config not found: specify LLVM_CONFIG_PATH")
+ message(WARNING "UNSUPPORTED COMPILER-RT CONFIGURATION DETECTED: "
+ "llvm-config not found.\n"
+ "Reconfigure with -DLLVM_CONFIG_PATH=path/to/llvm-config.")
endif()
endif()
- execute_process(
- COMMAND ${LLVM_CONFIG_PATH} "--obj-root" "--bindir" "--libdir" "--src-root"
- RESULT_VARIABLE HAD_ERROR
- OUTPUT_VARIABLE CONFIG_OUTPUT)
- if (HAD_ERROR)
- message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}")
- endif()
- string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" CONFIG_OUTPUT ${CONFIG_OUTPUT})
- list(GET CONFIG_OUTPUT 0 BINARY_DIR)
- list(GET CONFIG_OUTPUT 1 TOOLS_BINARY_DIR)
- list(GET CONFIG_OUTPUT 2 LIBRARY_DIR)
- list(GET CONFIG_OUTPUT 3 MAIN_SRC_DIR)
+ if (LLVM_CONFIG_PATH)
+ execute_process(
+ COMMAND ${LLVM_CONFIG_PATH} "--obj-root" "--bindir" "--libdir" "--src-root" "--includedir"
+ RESULT_VARIABLE HAD_ERROR
+ OUTPUT_VARIABLE CONFIG_OUTPUT)
+ if (HAD_ERROR)
+ message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}")
+ endif()
+ string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" CONFIG_OUTPUT ${CONFIG_OUTPUT})
+ list(GET CONFIG_OUTPUT 0 BINARY_DIR)
+ 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_BINARY_DIR ${BINARY_DIR} CACHE PATH "Path to LLVM build tree")
+ 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")
- # Make use of LLVM CMake modules.
- # --cmakedir is supported since llvm r291218 (4.0 release)
- execute_process(
- COMMAND ${LLVM_CONFIG_PATH} --cmakedir
- RESULT_VARIABLE HAD_ERROR
- OUTPUT_VARIABLE CONFIG_OUTPUT)
- if(NOT HAD_ERROR)
- string(STRIP "${CONFIG_OUTPUT}" LLVM_CMAKE_PATH_FROM_LLVM_CONFIG)
- file(TO_CMAKE_PATH ${LLVM_CMAKE_PATH_FROM_LLVM_CONFIG} LLVM_CMAKE_PATH)
- else()
- file(TO_CMAKE_PATH ${LLVM_BINARY_DIR} LLVM_BINARY_DIR_CMAKE_STYLE)
- set(LLVM_CMAKE_PATH "${LLVM_BINARY_DIR_CMAKE_STYLE}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm")
- endif()
+ # Detect if we have the LLVMXRay and TestingSupport library installed and
+ # available from llvm-config.
+ execute_process(
+ COMMAND ${LLVM_CONFIG_PATH} "--ldflags" "--libs" "xray"
+ 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()
+
+ set(COMPILER_RT_HAS_LLVMTESTINGSUPPORT FALSE)
+ execute_process(
+ COMMAND ${LLVM_CONFIG_PATH} "--ldflags" "--libs" "testingsupport"
+ RESULT_VARIABLE HAD_ERROR
+ OUTPUT_VARIABLE CONFIG_OUTPUT)
+ if (HAD_ERROR)
+ message(WARNING "llvm-config finding testingsupport failed with status ${HAD_ERROR}")
+ else()
+ string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" CONFIG_OUTPUT ${CONFIG_OUTPUT})
+ list(GET CONFIG_OUTPUT 0 LDFLAGS)
+ list(GET CONFIG_OUTPUT 1 LIBLIST)
+ if (LIBLIST STREQUAL "")
+ message(WARNING "testingsupport library not installed, some tests will be skipped")
+ else()
+ set(LLVM_TESTINGSUPPORT_LDFLAGS ${LDFLAGS} CACHE STRING "Linker flags for LLVMTestingSupport library")
+ set(LLVM_TESTINGSUPPORT_LIBLIST ${LIBLIST} CACHE STRING "Library list for LLVMTestingSupport")
+ set(COMPILER_RT_HAS_LLVMTESTINGSUPPORT TRUE)
+ endif()
+ endif()
+
+ # Make use of LLVM CMake modules.
+ # --cmakedir is supported since llvm r291218 (4.0 release)
+ execute_process(
+ COMMAND ${LLVM_CONFIG_PATH} --cmakedir
+ RESULT_VARIABLE HAD_ERROR
+ OUTPUT_VARIABLE CONFIG_OUTPUT)
+ if(NOT HAD_ERROR)
+ string(STRIP "${CONFIG_OUTPUT}" LLVM_CMAKE_PATH_FROM_LLVM_CONFIG)
+ file(TO_CMAKE_PATH ${LLVM_CMAKE_PATH_FROM_LLVM_CONFIG} LLVM_CMAKE_PATH)
+ else()
+ file(TO_CMAKE_PATH ${LLVM_BINARY_DIR} LLVM_BINARY_DIR_CMAKE_STYLE)
+ set(LLVM_CMAKE_PATH "${LLVM_BINARY_DIR_CMAKE_STYLE}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm")
+ endif()
- list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}")
- # Get some LLVM variables from LLVMConfig.
- include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake")
+ list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}")
+ # Get some LLVM variables from LLVMConfig.
+ include("${LLVM_CMAKE_PATH}/LLVMConfig.cmake")
- set(LLVM_LIBRARY_OUTPUT_INTDIR
- ${LLVM_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
+ set(LLVM_LIBRARY_OUTPUT_INTDIR
+ ${LLVM_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX})
+ endif()
endmacro()
macro(construct_compiler_rt_default_triple)
@@ -272,11 +308,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)
@@ -316,13 +347,15 @@ function(filter_builtin_sources output_var exclude_or_include excluded_list)
endfunction()
function(get_compiler_rt_target arch variable)
- if(ANDROID AND ${arch} STREQUAL "i386")
- set(target "i686${COMPILER_RT_OS_SUFFIX}-${COMPILER_RT_DEFAULT_TARGET_OS}")
+ string(FIND ${COMPILER_RT_DEFAULT_TARGET_TRIPLE} "-" dash_index)
+ string(SUBSTRING ${COMPILER_RT_DEFAULT_TARGET_TRIPLE} ${dash_index} -1 triple_suffix)
+ if(COMPILER_RT_DEFAULT_TARGET_ONLY)
+ # Use exact spelling when building only for the target specified to CMake.
+ set(target "${COMPILER_RT_DEFAULT_TARGET_TRIPLE}")
+ elseif(ANDROID AND ${arch} STREQUAL "i386")
+ 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/HandleCompilerRT.cmake b/cmake/Modules/HandleCompilerRT.cmake
index 855d0ff9df1c..61b7792789e7 100644
--- a/cmake/Modules/HandleCompilerRT.cmake
+++ b/cmake/Modules/HandleCompilerRT.cmake
@@ -4,12 +4,16 @@ function(find_compiler_rt_library name variable)
if (CMAKE_CXX_COMPILER_ID MATCHES Clang AND CMAKE_CXX_COMPILER_TARGET)
list(APPEND CLANG_COMMAND "--target=${CMAKE_CXX_COMPILER_TARGET}")
endif()
+ get_property(SANITIZER_CXX_FLAGS CACHE CMAKE_CXX_FLAGS PROPERTY VALUE)
+ string(REPLACE " " ";" SANITIZER_CXX_FLAGS "${SANITIZER_CXX_FLAGS}")
+ list(APPEND CLANG_COMMAND ${SANITIZER_CXX_FLAGS})
execute_process(
COMMAND ${CLANG_COMMAND}
RESULT_VARIABLE HAD_ERROR
OUTPUT_VARIABLE LIBRARY_FILE
)
string(STRIP "${LIBRARY_FILE}" LIBRARY_FILE)
+ file(TO_CMAKE_PATH "${LIBRARY_FILE}" LIBRARY_FILE)
string(REPLACE "builtins" "${name}" LIBRARY_FILE "${LIBRARY_FILE}")
if (NOT HAD_ERROR AND EXISTS "${LIBRARY_FILE}")
message(STATUS "Found compiler-rt ${name} library: ${LIBRARY_FILE}")
diff --git a/cmake/Modules/SanitizerUtils.cmake b/cmake/Modules/SanitizerUtils.cmake
index b6312426cacb..64d3ed92cb6e 100644
--- a/cmake/Modules/SanitizerUtils.cmake
+++ b/cmake/Modules/SanitizerUtils.cmake
@@ -30,7 +30,7 @@ macro(add_sanitizer_rt_symbols name)
add_custom_command(OUTPUT ${stamp}
COMMAND ${PYTHON_EXECUTABLE}
${SANITIZER_GEN_DYNAMIC_LIST} ${extra_args} $<TARGET_FILE:${target_name}>
- > $<TARGET_FILE:${target_name}>.syms
+ -o $<TARGET_FILE:${target_name}>.syms
COMMAND ${CMAKE_COMMAND} -E touch ${stamp}
DEPENDS ${target_name} ${SANITIZER_GEN_DYNAMIC_LIST} ${ARG_EXTRA}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
@@ -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})
@@ -72,7 +80,7 @@ macro(add_sanitizer_rt_version_list name)
add_custom_command(OUTPUT ${vers}
COMMAND ${PYTHON_EXECUTABLE}
${SANITIZER_GEN_DYNAMIC_LIST} --version-list ${args}
- > ${vers}
+ -o ${vers}
DEPENDS ${SANITIZER_GEN_DYNAMIC_LIST} ${ARG_EXTRA} ${ARG_LIBS}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Generating version list for ${name}"
diff --git a/cmake/base-config-ix.cmake b/cmake/base-config-ix.cmake
index 91fe2494b476..6684d7371d68 100644
--- a/cmake/base-config-ix.cmake
+++ b/cmake/base-config-ix.cmake
@@ -8,6 +8,12 @@ include(CheckCXXSourceCompiles)
check_include_file(unwind.h HAVE_UNWIND_H)
+# Used by sanitizer_common and tests.
+check_include_file(rpc/xdr.h HAVE_RPC_XDR_H)
+if (NOT HAVE_RPC_XDR_H)
+ set(HAVE_RPC_XDR_H 0)
+endif()
+
# Top level target used to build all compiler-rt libraries.
add_custom_target(compiler-rt ALL)
add_custom_target(install-compiler-rt)
@@ -122,8 +128,6 @@ macro(test_targets)
# what version of MSVC to pretend to be so that the STL works.
set(MSVC_VERSION_FLAG "")
if (MSVC)
- # Find and run MSVC (not clang-cl) and get its version. This will tell
- # clang-cl what version of MSVC to pretend to be so that the STL works.
execute_process(COMMAND "$ENV{VSINSTALLDIR}/VC/bin/cl.exe"
OUTPUT_QUIET
ERROR_VARIABLE MSVC_COMPAT_VERSION
@@ -191,11 +195,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/builtin-config-ix.cmake b/cmake/builtin-config-ix.cmake
index a5704e5fed53..c4d0940f9f87 100644
--- a/cmake/builtin-config-ix.cmake
+++ b/cmake/builtin-config-ix.cmake
@@ -103,20 +103,6 @@ if(APPLE)
set(CAN_TARGET_${arch} 1)
endforeach()
- # Need to build a 10.4 compatible libclang_rt
- set(DARWIN_10.4_SYSROOT ${DARWIN_osx_SYSROOT})
- set(DARWIN_10.4_BUILTIN_MIN_VER 10.4)
- set(DARWIN_10.4_BUILTIN_MIN_VER_FLAG
- -mmacosx-version-min=${DARWIN_10.4_BUILTIN_MIN_VER})
- set(DARWIN_10.4_SKIP_CC_KEXT On)
- darwin_test_archs(10.4 DARWIN_10.4_ARCHS i386 x86_64)
- message(STATUS "OSX 10.4 supported builtin arches: ${DARWIN_10.4_ARCHS}")
- if(DARWIN_10.4_ARCHS)
- # don't include the Haswell slice in the 10.4 compatibility library
- list(REMOVE_ITEM DARWIN_10.4_ARCHS x86_64h)
- list(APPEND BUILTIN_SUPPORTED_OS 10.4)
- endif()
-
foreach(platform ${DARWIN_EMBEDDED_PLATFORMS})
if(DARWIN_${platform}sim_SYSROOT)
set(DARWIN_${platform}sim_BUILTIN_MIN_VER
diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake
index f3935ffd6fd2..db5c4645dc0a 100644
--- a/cmake/config-ix.cmake
+++ b/cmake/config-ix.cmake
@@ -37,9 +37,23 @@ 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.
+check_c_compiler_flag(-ffreestanding COMPILER_RT_HAS_FFREESTANDING_FLAG)
check_cxx_compiler_flag(-fPIC COMPILER_RT_HAS_FPIC_FLAG)
check_cxx_compiler_flag(-fPIE COMPILER_RT_HAS_FPIE_FLAG)
check_cxx_compiler_flag(-fno-builtin COMPILER_RT_HAS_FNO_BUILTIN_FLAG)
@@ -51,7 +65,6 @@ check_cxx_compiler_flag(-fno-sanitize=safe-stack COMPILER_RT_HAS_FNO_SANITIZE_SA
check_cxx_compiler_flag(-fvisibility=hidden COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG)
check_cxx_compiler_flag(-frtti COMPILER_RT_HAS_FRTTI_FLAG)
check_cxx_compiler_flag(-fno-rtti COMPILER_RT_HAS_FNO_RTTI_FLAG)
-check_cxx_compiler_flag(-ffreestanding COMPILER_RT_HAS_FFREESTANDING_FLAG)
check_cxx_compiler_flag("-Werror -fno-function-sections" COMPILER_RT_HAS_FNO_FUNCTION_SECTIONS_FLAG)
check_cxx_compiler_flag(-std=c++11 COMPILER_RT_HAS_STD_CXX11_FLAG)
check_cxx_compiler_flag(-ftls-model=initial-exec COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC)
@@ -105,6 +118,21 @@ 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 terminfo tinfo curses ncurses ncursesw)
+ string(TOUPPER ${library} library_suffix)
+ check_library_exists(
+ ${library} setupterm "" COMPILER_RT_HAS_TERMINFO_${library_suffix})
+ if(COMPILER_RT_HAS_TERMINFO_${library_suffix})
+ set(COMPILER_RT_HAS_TERMINFO TRUE)
+ 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 +529,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)
@@ -557,7 +586,7 @@ else()
endif()
if (PROFILE_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND
- OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android|Fuchsia|SunOS")
+ OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android|Fuchsia|SunOS|NetBSD")
set(COMPILER_RT_HAS_PROFILE TRUE)
else()
set(COMPILER_RT_HAS_PROFILE FALSE)
@@ -598,7 +627,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)
@@ -612,14 +641,17 @@ else()
endif()
if (COMPILER_RT_HAS_SANITIZER_COMMON AND XRAY_SUPPORTED_ARCH AND
- OS_NAME MATCHES "Darwin|Linux|FreeBSD|NetBSD|OpenBSD")
+ OS_NAME MATCHES "Darwin|Linux|FreeBSD|NetBSD|OpenBSD|Fuchsia")
set(COMPILER_RT_HAS_XRAY TRUE)
else()
set(COMPILER_RT_HAS_XRAY FALSE)
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 89f328301db3..e44c4a13ed67 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 d11cb1addc27..bf015eb237cd 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 0c306cf279e2..1affd875a11b 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/sanitizer/netbsd_syscall_hooks.h b/include/sanitizer/netbsd_syscall_hooks.h
index 4c6c6a88af29..b69f3d2be39f 100644
--- a/include/sanitizer/netbsd_syscall_hooks.h
+++ b/include/sanitizer/netbsd_syscall_hooks.h
@@ -21,8 +21,8 @@
// DO NOT EDIT! THIS FILE HAS BEEN GENERATED!
//
// Generated with: generate_netbsd_syscalls.awk
-// Generated date: 2018-03-03
-// Generated from: syscalls.master,v 1.291 2018/01/06 16:41:23 kamil Exp
+// Generated date: 2018-10-30
+// Generated from: syscalls.master,v 1.293 2018/07/31 13:00:13 rjs Exp
//
//===----------------------------------------------------------------------===//
#ifndef SANITIZER_NETBSD_SYSCALL_HOOKS_H
@@ -986,7 +986,15 @@
#define __sanitizer_syscall_post_fpathconf(res, fd, name) \
__sanitizer_syscall_post_impl_fpathconf(res, (long long)(fd), \
(long long)(name))
-/* syscall 193 has been skipped */
+#define __sanitizer_syscall_pre_getsockopt2(s, level, name, val, avalsize) \
+ __sanitizer_syscall_pre_impl_getsockopt2( \
+ (long long)(s), (long long)(level), (long long)(name), (long long)(val), \
+ (long long)(avalsize))
+#define __sanitizer_syscall_post_getsockopt2(res, s, level, name, val, \
+ avalsize) \
+ __sanitizer_syscall_post_impl_getsockopt2( \
+ res, (long long)(s), (long long)(level), (long long)(name), \
+ (long long)(val), (long long)(avalsize))
#define __sanitizer_syscall_pre_getrlimit(which, rlp) \
__sanitizer_syscall_pre_impl_getrlimit((long long)(which), (long long)(rlp))
#define __sanitizer_syscall_post_getrlimit(res, which, rlp) \
@@ -1752,18 +1760,8 @@
__sanitizer_syscall_post_impl___sigaction_sigtramp( \
res, (long long)(signum), (long long)(nsa), (long long)(osa), \
(long long)(tramp), (long long)(vers))
-#define __sanitizer_syscall_pre_pmc_get_info(ctr, op, args) \
- __sanitizer_syscall_pre_impl_pmc_get_info((long long)(ctr), (long long)(op), \
- (long long)(args))
-#define __sanitizer_syscall_post_pmc_get_info(res, ctr, op, args) \
- __sanitizer_syscall_post_impl_pmc_get_info( \
- res, (long long)(ctr), (long long)(op), (long long)(args))
-#define __sanitizer_syscall_pre_pmc_control(ctr, op, args) \
- __sanitizer_syscall_pre_impl_pmc_control((long long)(ctr), (long long)(op), \
- (long long)(args))
-#define __sanitizer_syscall_post_pmc_control(res, ctr, op, args) \
- __sanitizer_syscall_post_impl_pmc_control( \
- res, (long long)(ctr), (long long)(op), (long long)(args))
+/* syscall 341 has been skipped */
+/* syscall 342 has been skipped */
#define __sanitizer_syscall_pre_rasctl(addr, len, op) \
__sanitizer_syscall_pre_impl_rasctl((long long)(addr), (long long)(len), \
(long long)(op))
@@ -3444,7 +3442,13 @@ void __sanitizer_syscall_post_impl_pathconf(long long res, long long path,
void __sanitizer_syscall_pre_impl_fpathconf(long long fd, long long name);
void __sanitizer_syscall_post_impl_fpathconf(long long res, long long fd,
long long name);
-/* syscall 193 has been skipped */
+void __sanitizer_syscall_pre_impl_getsockopt2(long long s, long long level,
+ long long name, long long val,
+ long long avalsize);
+void __sanitizer_syscall_post_impl_getsockopt2(long long res, long long s,
+ long long level, long long name,
+ long long val,
+ long long avalsize);
void __sanitizer_syscall_pre_impl_getrlimit(long long which, long long rlp);
void __sanitizer_syscall_post_impl_getrlimit(long long res, long long which,
long long rlp);
@@ -4001,14 +4005,8 @@ void __sanitizer_syscall_pre_impl___sigaction_sigtramp(long long signum,
void __sanitizer_syscall_post_impl___sigaction_sigtramp(
long long res, long long signum, long long nsa, long long osa,
long long tramp, long long vers);
-void __sanitizer_syscall_pre_impl_pmc_get_info(long long ctr, long long op,
- long long args);
-void __sanitizer_syscall_post_impl_pmc_get_info(long long res, long long ctr,
- long long op, long long args);
-void __sanitizer_syscall_pre_impl_pmc_control(long long ctr, long long op,
- long long args);
-void __sanitizer_syscall_post_impl_pmc_control(long long res, long long ctr,
- long long op, long long args);
+/* syscall 341 has been skipped */
+/* syscall 342 has been skipped */
void __sanitizer_syscall_pre_impl_rasctl(long long addr, long long len,
long long op);
void __sanitizer_syscall_post_impl_rasctl(long long res, long long addr,
diff --git a/include/xray/xray_log_interface.h b/include/xray/xray_log_interface.h
index 5f8b3a4a06a4..399467860e73 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 2ae5c85ecefb..726da27d052f 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,11 @@ 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)
+
+if (TARGET cxx-headers OR HAVE_LIBCXX)
+ set(ASAN_DEPS cxx-headers)
+endif()
# Compile ASan sources into an object library.
@@ -100,7 +113,8 @@ add_compiler_rt_object_libraries(RTAsan_dynamic
SOURCES ${ASAN_SOURCES} ${ASAN_CXX_SOURCES}
ADDITIONAL_HEADERS ${ASAN_HEADERS}
CFLAGS ${ASAN_DYNAMIC_CFLAGS}
- DEFS ${ASAN_DYNAMIC_DEFINITIONS})
+ DEFS ${ASAN_DYNAMIC_DEFINITIONS}
+ DEPS ${ASAN_DEPS})
if(NOT APPLE)
add_compiler_rt_object_libraries(RTAsan
@@ -108,26 +122,30 @@ if(NOT APPLE)
SOURCES ${ASAN_SOURCES}
ADDITIONAL_HEADERS ${ASAN_HEADERS}
CFLAGS ${ASAN_CFLAGS}
- DEFS ${ASAN_COMMON_DEFINITIONS})
+ DEFS ${ASAN_COMMON_DEFINITIONS}
+ DEPS ${ASAN_DEPS})
add_compiler_rt_object_libraries(RTAsan_cxx
ARCHS ${ASAN_SUPPORTED_ARCH}
SOURCES ${ASAN_CXX_SOURCES}
ADDITIONAL_HEADERS ${ASAN_HEADERS}
CFLAGS ${ASAN_CFLAGS}
- DEFS ${ASAN_COMMON_DEFINITIONS})
+ DEFS ${ASAN_COMMON_DEFINITIONS}
+ DEPS ${ASAN_DEPS})
add_compiler_rt_object_libraries(RTAsan_preinit
ARCHS ${ASAN_SUPPORTED_ARCH}
SOURCES ${ASAN_PREINIT_SOURCES}
ADDITIONAL_HEADERS ${ASAN_HEADERS}
CFLAGS ${ASAN_CFLAGS}
- DEFS ${ASAN_COMMON_DEFINITIONS})
+ DEFS ${ASAN_COMMON_DEFINITIONS}
+ DEPS ${ASAN_DEPS})
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/dummy.cc "")
add_compiler_rt_object_libraries(RTAsan_dynamic_version_script_dummy
ARCHS ${ASAN_SUPPORTED_ARCH}
SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dummy.cc
CFLAGS ${ASAN_DYNAMIC_CFLAGS}
- DEFS ${ASAN_DYNAMIC_DEFINITIONS})
+ DEFS ${ASAN_DYNAMIC_DEFINITIONS}
+ DEPS ${ASAN_DEPS})
endif()
# Build ASan runtimes shipped with Clang.
@@ -138,6 +156,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,13 +234,14 @@ else()
endif()
set(ASAN_DYNAMIC_WEAK_INTERCEPTION)
- if (MSVC)
+ if (WIN32)
add_compiler_rt_object_libraries(AsanWeakInterception
${SANITIZER_COMMON_SUPPORTED_OS}
ARCHS ${arch}
SOURCES asan_win_weak_interception.cc
CFLAGS ${ASAN_CFLAGS} -DSANITIZER_DYNAMIC
- DEFS ${ASAN_COMMON_DEFINITIONS})
+ DEFS ${ASAN_COMMON_DEFINITIONS}
+ DEPS ${ASAN_DEPS})
set(ASAN_DYNAMIC_WEAK_INTERCEPTION
AsanWeakInterception
UbsanWeakInterception
@@ -266,7 +286,8 @@ else()
SOURCES asan_globals_win.cc
asan_win_dll_thunk.cc
CFLAGS ${ASAN_CFLAGS} -DSANITIZER_DLL_THUNK
- DEFS ${ASAN_COMMON_DEFINITIONS})
+ DEFS ${ASAN_COMMON_DEFINITIONS}
+ DEPS ${ASAN_DEPS})
add_compiler_rt_runtime(clang_rt.asan_dll_thunk
STATIC
@@ -291,7 +312,8 @@ else()
SOURCES asan_globals_win.cc
asan_win_dynamic_runtime_thunk.cc
CFLAGS ${ASAN_CFLAGS} ${DYNAMIC_RUNTIME_THUNK_CFLAGS}
- DEFS ${ASAN_COMMON_DEFINITIONS})
+ DEFS ${ASAN_COMMON_DEFINITIONS}
+ DEPS ${ASAN_DEPS})
add_compiler_rt_runtime(clang_rt.asan_dynamic_runtime_thunk
STATIC
diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h
index 93d6f29c5bf5..c9b37dc7a6d4 100644
--- a/lib/asan/asan_allocator.h
+++ b/lib/asan/asan_allocator.h
@@ -148,6 +148,7 @@ const uptr kAllocatorSpace = 0x600000000000ULL;
const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
typedef DefaultSizeClassMap SizeClassMap;
# endif
+template <typename AddressSpaceViewTy>
struct AP64 { // Allocator64 parameters. Deliberately using a short name.
static const uptr kSpaceBeg = kAllocatorSpace;
static const uptr kSpaceSize = kAllocatorSize;
@@ -155,37 +156,57 @@ struct AP64 { // Allocator64 parameters. Deliberately using a short name.
typedef __asan::SizeClassMap SizeClassMap;
typedef AsanMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = AddressSpaceViewTy;
};
-typedef SizeClassAllocator64<AP64> PrimaryAllocator;
+template <typename AddressSpaceView>
+using PrimaryAllocatorASVT = SizeClassAllocator64<AP64<AddressSpaceView>>;
+using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
#else // Fallback to SizeClassAllocator32.
static const uptr kRegionSizeLog = 20;
static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
# if SANITIZER_WORDSIZE == 32
-typedef FlatByteMap<kNumRegions> ByteMap;
+template <typename AddressSpaceView>
+using ByteMapASVT = FlatByteMap<kNumRegions, AddressSpaceView>;
# elif SANITIZER_WORDSIZE == 64
-typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
+template <typename AddressSpaceView>
+using ByteMapASVT =
+ TwoLevelByteMap<(kNumRegions >> 12), 1 << 12, AddressSpaceView>;
# endif
typedef CompactSizeClassMap SizeClassMap;
+template <typename AddressSpaceViewTy>
struct AP32 {
static const uptr kSpaceBeg = 0;
static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
static const uptr kMetadataSize = 16;
typedef __asan::SizeClassMap SizeClassMap;
static const uptr kRegionSizeLog = __asan::kRegionSizeLog;
- typedef __asan::ByteMap ByteMap;
+ using AddressSpaceView = AddressSpaceViewTy;
+ using ByteMap = __asan::ByteMapASVT<AddressSpaceView>;
typedef AsanMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
-typedef SizeClassAllocator32<AP32> PrimaryAllocator;
+template <typename AddressSpaceView>
+using PrimaryAllocatorASVT = SizeClassAllocator32<AP32<AddressSpaceView> >;
+using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
#endif // SANITIZER_CAN_USE_ALLOCATOR64
static const uptr kNumberOfSizeClasses = SizeClassMap::kNumClasses;
-typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
-typedef LargeMmapAllocator<AsanMapUnmapCallback> SecondaryAllocator;
-typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
- SecondaryAllocator> AsanAllocator;
-
+template <typename AddressSpaceView>
+using AllocatorCacheASVT =
+ SizeClassAllocatorLocalCache<PrimaryAllocatorASVT<AddressSpaceView>>;
+using AllocatorCache = AllocatorCacheASVT<LocalAddressSpaceView>;
+
+template <typename AddressSpaceView>
+using SecondaryAllocatorASVT =
+ LargeMmapAllocator<AsanMapUnmapCallback, DefaultLargeMmapAllocatorPtrArray,
+ AddressSpaceView>;
+template <typename AddressSpaceView>
+using AsanAllocatorASVT =
+ CombinedAllocator<PrimaryAllocatorASVT<AddressSpaceView>,
+ AllocatorCacheASVT<AddressSpaceView>,
+ SecondaryAllocatorASVT<AddressSpaceView>>;
+using AsanAllocator = AsanAllocatorASVT<LocalAddressSpaceView>;
struct AsanThreadLocalMallocStorage {
uptr quarantine_cache[16];
diff --git a/lib/asan/asan_errors.cc b/lib/asan/asan_errors.cc
index 33d0613f79f4..0ecd30dcacfd 100644
--- a/lib/asan/asan_errors.cc
+++ b/lib/asan/asan_errors.cc
@@ -125,9 +125,8 @@ void ErrorAllocTypeMismatch::Print() {
Decorator d;
Printf("%s", d.Error());
Report("ERROR: AddressSanitizer: %s (%s vs %s) on %p\n",
- scariness.GetDescription(),
- alloc_names[alloc_type], dealloc_names[dealloc_type],
- addr_description.addr);
+ scariness.GetDescription(), alloc_names[alloc_type],
+ dealloc_names[dealloc_type], addr_description.Address());
Printf("%s", d.Default());
CHECK_GT(dealloc_stack->size, 0);
scariness.Print();
diff --git a/lib/asan/asan_errors.h b/lib/asan/asan_errors.h
index 574197ebff86..7ddd7e94e0fc 100644
--- a/lib/asan/asan_errors.h
+++ b/lib/asan/asan_errors.h
@@ -110,8 +110,8 @@ struct ErrorFreeNotMalloced : ErrorBase {
struct ErrorAllocTypeMismatch : ErrorBase {
const BufferedStackTrace *dealloc_stack;
- HeapAddressDescription addr_description;
AllocType alloc_type, dealloc_type;
+ AddressDescription addr_description;
ErrorAllocTypeMismatch() = default; // (*)
ErrorAllocTypeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr,
@@ -119,9 +119,8 @@ struct ErrorAllocTypeMismatch : ErrorBase {
: ErrorBase(tid, 10, "alloc-dealloc-mismatch"),
dealloc_stack(stack),
alloc_type(alloc_type_),
- dealloc_type(dealloc_type_) {
- GetHeapAddressInformation(addr, 1, &addr_description);
- };
+ dealloc_type(dealloc_type_),
+ addr_description(addr, 1, false) {}
void Print();
};
diff --git a/lib/asan/asan_flags.inc b/lib/asan/asan_flags.inc
index 4af94c56fca0..a9c97d53b31f 100644
--- a/lib/asan/asan_flags.inc
+++ b/lib/asan/asan_flags.inc
@@ -152,8 +152,6 @@ ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
ASAN_FLAG(bool, halt_on_error, true,
"Crash the program after printing the first error report "
"(WARNING: USE AT YOUR OWN RISK!)")
-ASAN_FLAG(bool, use_odr_indicator, false,
- "Use special ODR indicator symbol for ODR violation detection")
ASAN_FLAG(bool, allocator_frees_and_returns_null_on_realloc_zero, true,
"realloc(p, 0) is equivalent to free(p) by default (Same as the "
"POSIX standard). If set to false, realloc(p, 0) will return a "
diff --git a/lib/asan/asan_fuchsia.cc b/lib/asan/asan_fuchsia.cc
index 0b5bff4f565e..34399c92310b 100644
--- a/lib/asan/asan_fuchsia.cc
+++ b/lib/asan/asan_fuchsia.cc
@@ -190,6 +190,13 @@ static void ThreadExitHook(void *hook, uptr os_id) {
AsanThread::TSDDtor(per_thread);
}
+bool HandleDlopenInit() {
+ // Not supported on this platform.
+ static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
+ "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
+ return false;
+}
+
} // namespace __asan
// These are declared (in extern "C") by <zircon/sanitizer.h>.
diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc
index 898f7f40d31b..146234ac6c64 100644
--- a/lib/asan/asan_globals.cc
+++ b/lib/asan/asan_globals.cc
@@ -83,9 +83,11 @@ static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
}
static void ReportGlobal(const Global &g, const char *prefix) {
- Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
- prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
- g.module_name, g.has_dynamic_init);
+ Report(
+ "%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu "
+ "odr_indicator=%p\n",
+ prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
+ g.module_name, g.has_dynamic_init, (void *)g.odr_indicator);
if (g.location) {
Report(" location (%p): name=%s[%p], %d %d\n", g.location,
g.location->filename, g.location->filename, g.location->line_no,
@@ -133,6 +135,9 @@ enum GlobalSymbolState {
// this method in case compiler instruments global variables through their
// local aliases.
static void CheckODRViolationViaIndicator(const Global *g) {
+ // Instrumentation requests to skip ODR check.
+ if (g->odr_indicator == UINTPTR_MAX)
+ return;
u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator);
if (*odr_indicator == UNREGISTERED) {
*odr_indicator = REGISTERED;
@@ -183,9 +188,7 @@ static void CheckODRViolationViaPoisoning(const Global *g) {
// This routine chooses between two different methods of ODR violation
// detection.
static inline bool UseODRIndicator(const Global *g) {
- // Use ODR indicator method iff use_odr_indicator flag is set and
- // indicator symbol address is not 0.
- return flags()->use_odr_indicator && g->odr_indicator > 0;
+ return g->odr_indicator > 0;
}
// Register a global variable.
@@ -248,7 +251,7 @@ static void UnregisterGlobal(const Global *g) {
// implementation. It might not be worth doing anyway.
// Release ODR indicator.
- if (UseODRIndicator(g)) {
+ if (UseODRIndicator(g) && g->odr_indicator != UINTPTR_MAX) {
u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator);
*odr_indicator = UNREGISTERED;
}
diff --git a/lib/asan/asan_globals_win.cc b/lib/asan/asan_globals_win.cc
index 29ab5ebf16d4..0e75992bfc8d 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_internal.h b/lib/asan/asan_internal.h
index 654878cd15f0..57869497c7d3 100644
--- a/lib/asan/asan_internal.h
+++ b/lib/asan/asan_internal.h
@@ -111,6 +111,11 @@ void *AsanDlSymNext(const char *sym);
void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name);
+// Returns `true` iff most of ASan init process should be skipped due to the
+// ASan library being loaded via `dlopen()`. Platforms may perform any
+// `dlopen()` specific initialization inside this function.
+bool HandleDlopenInit();
+
// Add convenient macro for interface functions that may be represented as
// weak hooks.
#define ASAN_MALLOC_HOOK(ptr, size) \
diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc
index 625f32d408df..a150b1955d60 100644
--- a/lib/asan/asan_linux.cc
+++ b/lib/asan/asan_linux.cc
@@ -248,6 +248,13 @@ void *AsanDlSymNext(const char *sym) {
return dlsym(RTLD_NEXT, sym);
}
+bool HandleDlopenInit() {
+ // Not supported on this platform.
+ static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
+ "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
+ return false;
+}
+
} // namespace __asan
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||
diff --git a/lib/asan/asan_malloc_linux.cc b/lib/asan/asan_malloc_linux.cc
index 76bdff999d62..0a534fe21168 100644
--- a/lib/asan/asan_malloc_linux.cc
+++ b/lib/asan/asan_malloc_linux.cc
@@ -209,7 +209,7 @@ INTERCEPTOR(struct fake_mallinfo, mallinfo, void) {
}
INTERCEPTOR(int, mallopt, int cmd, int value) {
- return -1;
+ return 0;
}
#endif // SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
diff --git a/lib/asan/asan_malloc_mac.cc b/lib/asan/asan_malloc_mac.cc
index 733ba2d86e13..27281f1bc834 100644
--- a/lib/asan/asan_malloc_mac.cc
+++ b/lib/asan/asan_malloc_mac.cc
@@ -61,4 +61,25 @@ using namespace __asan;
#include "sanitizer_common/sanitizer_malloc_mac.inc"
+namespace COMMON_MALLOC_NAMESPACE {
+bool HandleDlopenInit() {
+ static_assert(SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
+ "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be true");
+ // We have no reliable way of knowing how we are being loaded
+ // so make it a requirement on Apple platforms to set this environment
+ // variable to indicate that we want to perform initialization via
+ // dlopen().
+ auto init_str = GetEnv("APPLE_ASAN_INIT_FOR_DLOPEN");
+ if (!init_str)
+ return false;
+ if (internal_strncmp(init_str, "1", 1) != 0)
+ return false;
+ // When we are loaded via `dlopen()` path we still initialize the malloc zone
+ // so Symbolication clients (e.g. `leaks`) that load the ASan allocator can
+ // find an initialized malloc zone.
+ InitMallocZoneFields();
+ return true;
+}
+} // namespace COMMON_MALLOC_NAMESPACE
+
#endif
diff --git a/lib/asan/asan_malloc_win.cc b/lib/asan/asan_malloc_win.cc
index efa058243979..887936431931 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,13 +134,18 @@ 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);
}
ALLOCATION_FUNCTION_ATTRIBUTE
+size_t _msize_base(void *ptr) {
+ return _msize(ptr);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
void *_expand(void *memblock, size_t size) {
// _expand is used in realloc-like functions to resize the buffer if possible.
// We don't want memory to stand still while resizing buffers, so return 0.
@@ -226,6 +240,7 @@ void ReplaceSystemMalloc() {
TryToOverrideFunction("_recalloc_base", (uptr)_recalloc);
TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc);
TryToOverrideFunction("_msize", (uptr)_msize);
+ TryToOverrideFunction("_msize_base", (uptr)_msize);
TryToOverrideFunction("_expand", (uptr)_expand);
TryToOverrideFunction("_expand_base", (uptr)_expand);
diff --git a/lib/asan/asan_new_delete.cc b/lib/asan/asan_new_delete.cc
index 30efd61a9680..e6053c1fe87f 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_posix.cc b/lib/asan/asan_posix.cc
index 17c28b0aea2a..ca99c04b3ad8 100644
--- a/lib/asan/asan_posix.cc
+++ b/lib/asan/asan_posix.cc
@@ -40,6 +40,51 @@ void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
// ---------------------- TSD ---------------- {{{1
+#if SANITIZER_NETBSD || SANITIZER_FREEBSD
+// Thread Static Data cannot be used in early init on NetBSD and FreeBSD.
+// Reuse the Asan TSD API for compatibility with existing code
+// with an alternative implementation.
+
+static void (*tsd_destructor)(void *tsd) = nullptr;
+
+struct tsd_key {
+ tsd_key() : key(nullptr) {}
+ ~tsd_key() {
+ CHECK(tsd_destructor);
+ if (key)
+ (*tsd_destructor)(key);
+ }
+ void *key;
+};
+
+static thread_local struct tsd_key key;
+
+void AsanTSDInit(void (*destructor)(void *tsd)) {
+ CHECK(!tsd_destructor);
+ tsd_destructor = destructor;
+}
+
+void *AsanTSDGet() {
+ CHECK(tsd_destructor);
+ return key.key;
+}
+
+void AsanTSDSet(void *tsd) {
+ CHECK(tsd_destructor);
+ CHECK(tsd);
+ CHECK(!key.key);
+ key.key = tsd;
+}
+
+void PlatformTSDDtor(void *tsd) {
+ CHECK(tsd_destructor);
+ CHECK_EQ(key.key, tsd);
+ key.key = nullptr;
+ // Make sure that signal handler can not see a stale current thread pointer.
+ atomic_signal_fence(memory_order_seq_cst);
+ AsanThread::TSDDtor(tsd);
+}
+#else
static pthread_key_t tsd_key;
static bool tsd_key_inited = false;
void AsanTSDInit(void (*destructor)(void *tsd)) {
@@ -67,6 +112,7 @@ void PlatformTSDDtor(void *tsd) {
}
AsanThread::TSDDtor(tsd);
}
+#endif
} // namespace __asan
#endif // SANITIZER_POSIX
diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h
index f7153d4810d0..b0c167dda756 100644
--- a/lib/asan/asan_report.h
+++ b/lib/asan/asan_report.h
@@ -12,6 +12,9 @@
// ASan-private header for error reporting functions.
//===----------------------------------------------------------------------===//
+#ifndef ASAN_REPORT_H
+#define ASAN_REPORT_H
+
#include "asan_allocator.h"
#include "asan_internal.h"
#include "asan_thread.h"
@@ -92,3 +95,4 @@ void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr,
BufferedStackTrace *stack);
} // namespace __asan
+#endif // ASAN_REPORT_H
diff --git a/lib/asan/asan_rtems.cc b/lib/asan/asan_rtems.cc
index a4af940057eb..b48cc6a75d78 100644
--- a/lib/asan/asan_rtems.cc
+++ b/lib/asan/asan_rtems.cc
@@ -213,6 +213,12 @@ static void HandleExit() {
}
}
+bool HandleDlopenInit() {
+ // Not supported on this platform.
+ static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
+ "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
+ return false;
+}
} // namespace __asan
// These are declared (in extern "C") by <some_path/sanitizer.h>.
diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc
index 4cff736f213a..13344f3b8887 100644
--- a/lib/asan/asan_rtl.cc
+++ b/lib/asan/asan_rtl.cc
@@ -383,6 +383,19 @@ void PrintAddressSpaceLayout() {
kHighShadowBeg > kMidMemEnd);
}
+#if defined(__thumb__) && defined(__linux__)
+#define START_BACKGROUND_THREAD_IN_ASAN_INTERNAL
+#endif
+
+#ifndef START_BACKGROUND_THREAD_IN_ASAN_INTERNAL
+static bool UNUSED __local_asan_dyninit = [] {
+ MaybeStartBackgroudThread();
+ SetSoftRssLimitExceededCallback(AsanSoftRssLimitExceededCallback);
+
+ return false;
+}();
+#endif
+
static void AsanInitInternal() {
if (LIKELY(asan_inited)) return;
SanitizerToolName = "AddressSanitizer";
@@ -396,6 +409,14 @@ static void AsanInitInternal() {
// initialization steps look at flags().
InitializeFlags();
+ // Stop performing init at this point if we are being loaded via
+ // dlopen() and the platform supports it.
+ if (SANITIZER_SUPPORTS_INIT_FOR_DLOPEN && UNLIKELY(HandleDlopenInit())) {
+ asan_init_is_running = false;
+ VReport(1, "AddressSanitizer init is being performed for dlopen().\n");
+ return;
+ }
+
AsanCheckIncompatibleRT();
AsanCheckDynamicRTPrereqs();
AvoidCVE_2016_2143();
@@ -420,6 +441,8 @@ static void AsanInitInternal() {
__asan_option_detect_stack_use_after_return =
flags()->detect_stack_use_after_return;
+ __sanitizer::InitializePlatformEarly();
+
// Re-exec ourselves if we need to set additional env or command line args.
MaybeReexec();
@@ -447,8 +470,10 @@ static void AsanInitInternal() {
allocator_options.SetFrom(flags(), common_flags());
InitializeAllocator(allocator_options);
+#ifdef START_BACKGROUND_THREAD_IN_ASAN_INTERNAL
MaybeStartBackgroudThread();
SetSoftRssLimitExceededCallback(AsanSoftRssLimitExceededCallback);
+#endif
// On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
// should be set to 1 prior to initializing the threads.
diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc
index faf423d305b7..0895e4ce0d93 100644
--- a/lib/asan/asan_thread.cc
+++ b/lib/asan/asan_thread.cc
@@ -223,9 +223,11 @@ void AsanThread::Init(const InitOptions *options) {
atomic_store(&stack_switching_, false, memory_order_release);
CHECK_EQ(this->stack_size(), 0U);
SetThreadStackAndTls(options);
- CHECK_GT(this->stack_size(), 0U);
- CHECK(AddrIsInMem(stack_bottom_));
- CHECK(AddrIsInMem(stack_top_ - 1));
+ if (stack_top_ != stack_bottom_) {
+ CHECK_GT(this->stack_size(), 0U);
+ CHECK(AddrIsInMem(stack_bottom_));
+ CHECK(AddrIsInMem(stack_top_ - 1));
+ }
ClearShadowForThreadStackAndTLS();
fake_stack_ = nullptr;
if (__asan_option_detect_stack_use_after_return)
@@ -289,20 +291,23 @@ void AsanThread::SetThreadStackAndTls(const InitOptions *options) {
DCHECK_EQ(options, nullptr);
uptr tls_size = 0;
uptr stack_size = 0;
- GetThreadStackAndTls(tid() == 0, const_cast<uptr *>(&stack_bottom_),
- const_cast<uptr *>(&stack_size), &tls_begin_, &tls_size);
+ GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size, &tls_begin_,
+ &tls_size);
stack_top_ = stack_bottom_ + stack_size;
tls_end_ = tls_begin_ + tls_size;
dtls_ = DTLS_Get();
- int local;
- CHECK(AddrIsInStack((uptr)&local));
+ if (stack_top_ != stack_bottom_) {
+ int local;
+ CHECK(AddrIsInStack((uptr)&local));
+ }
}
#endif // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
void AsanThread::ClearShadowForThreadStackAndTLS() {
- PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0);
+ if (stack_top_ != stack_bottom_)
+ PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0);
if (tls_begin_ != tls_end_) {
uptr tls_begin_aligned = RoundDownTo(tls_begin_, SHADOW_GRANULARITY);
uptr tls_end_aligned = RoundUpTo(tls_end_, SHADOW_GRANULARITY);
@@ -314,6 +319,9 @@ void AsanThread::ClearShadowForThreadStackAndTLS() {
bool AsanThread::GetStackFrameAccessByAddr(uptr addr,
StackFrameAccess *access) {
+ if (stack_top_ == stack_bottom_)
+ return false;
+
uptr bottom = 0;
if (AddrIsInStack(addr)) {
bottom = stack_bottom();
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index 67125d38ad4a..068f4a5d247d 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);
@@ -314,6 +322,13 @@ int __asan_set_seh_filter() {
return 0;
}
+bool HandleDlopenInit() {
+ // Not supported on this platform.
+ static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
+ "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
+ return false;
+}
+
#if !ASAN_DYNAMIC
// The CRT runs initializers in this order:
// - C initializers, from XIA to XIZ
diff --git a/lib/asan/asan_win_dll_thunk.cc b/lib/asan/asan_win_dll_thunk.cc
index c6a313d24026..df593ab92de6 100644
--- a/lib/asan/asan_win_dll_thunk.cc
+++ b/lib/asan/asan_win_dll_thunk.cc
@@ -48,6 +48,7 @@ INTERCEPT_WRAP_W_WWW(_recalloc)
INTERCEPT_WRAP_W_WWW(_recalloc_base)
INTERCEPT_WRAP_W_W(_msize)
+INTERCEPT_WRAP_W_W(_msize_base)
INTERCEPT_WRAP_W_W(_expand)
INTERCEPT_WRAP_W_W(_expand_dbg)
diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py
index 68b6f093b533..2dbb05283c1e 100755
--- a/lib/asan/scripts/asan_symbolize.py
+++ b/lib/asan/scripts/asan_symbolize.py
@@ -231,6 +231,10 @@ class DarwinSymbolizer(Symbolizer):
"""Overrides Symbolizer.symbolize."""
if self.binary != binary:
return None
+ if not os.path.exists(binary):
+ # If the binary doesn't exist atos will exit which will lead to IOError
+ # exceptions being raised later on so just don't try to symbolize.
+ return ['{} ({}:{}+{})'.format(addr, binary, self.arch, offset)]
atos_line = self.atos.convert('0x%x' % int(offset, 16))
while "got symbolicator for" in atos_line:
atos_line = self.atos.readline()
@@ -473,7 +477,7 @@ class SymbolizationLoop(object):
symbolized_line = self.symbolize_address(addr, binary, offset, arch)
if not symbolized_line:
if original_binary != binary:
- symbolized_line = self.symbolize_address(addr, binary, offset, arch)
+ symbolized_line = self.symbolize_address(addr, original_binary, offset, arch)
return self.get_symbolized_lines(symbolized_line)
diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt
index 1b7060591530..9e640d1d8b01 100644
--- a/lib/asan/tests/CMakeLists.txt
+++ b/lib/asan/tests/CMakeLists.txt
@@ -68,11 +68,6 @@ if(APPLE)
list(APPEND ASAN_UNITTEST_COMMON_LINK_FLAGS ${WEAK_SYMBOL_LINK_FLAGS})
endif()
-if(MSVC)
- # Disable exceptions on Windows until they work reliably.
- list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -fno-exceptions -DGTEST_HAS_SEH=0)
-endif()
-
set(ASAN_BLACKLIST_FILE "${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore")
set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS
${ASAN_UNITTEST_COMMON_CFLAGS}
diff --git a/lib/asan/tests/asan_interface_test.cc b/lib/asan/tests/asan_interface_test.cc
index 69c8fe6f4818..b5c8303cb84b 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 65acb2839ba1..3e366842c65f 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/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc
index 25d6a2083450..464e99f614d5 100644
--- a/lib/asan/tests/asan_test.cc
+++ b/lib/asan/tests/asan_test.cc
@@ -1295,6 +1295,7 @@ TEST(AddressSanitizer, DISABLED_DemoTooMuchMemoryTest) {
}
}
+#if !defined(__NetBSD__) && !defined(__i386__)
// https://github.com/google/sanitizers/issues/66
TEST(AddressSanitizer, BufferOverflowAfterManyFrees) {
for (int i = 0; i < 1000000; i++) {
@@ -1304,6 +1305,7 @@ TEST(AddressSanitizer, BufferOverflowAfterManyFrees) {
EXPECT_DEATH(x[Ident(8192)] = 0, "AddressSanitizer: heap-buffer-overflow");
delete [] Ident(x);
}
+#endif
// Test that instrumentation of stack allocations takes into account
diff --git a/lib/builtins/CMakeLists.txt b/lib/builtins/CMakeLists.txt
index 82332967b104..77947417bfed 100644
--- a/lib/builtins/CMakeLists.txt
+++ b/lib/builtins/CMakeLists.txt
@@ -505,7 +505,9 @@ set(mips64el_SOURCES ${GENERIC_TF_SOURCES}
set(powerpc64_SOURCES
ppc/divtc3.c
ppc/fixtfdi.c
+ ppc/fixunstfti.c
ppc/fixunstfdi.c
+ ppc/floattitf.c
ppc/floatditf.c
ppc/floatunditf.c
ppc/gcc_qadd.c
diff --git a/lib/builtins/Darwin-excludes/10.4.txt b/lib/builtins/Darwin-excludes/10.4.txt
deleted file mode 100644
index 603c0b3bf399..000000000000
--- a/lib/builtins/Darwin-excludes/10.4.txt
+++ /dev/null
@@ -1,137 +0,0 @@
-absvdi2
-absvsi2
-absvti2
-adddf3
-addsf3
-addtf3
-addvdi3
-addvsi3
-addvti3
-apple_versioning
-ashldi3
-ashlti3
-ashrdi3
-ashrti3
-atomic_flag_clear
-atomic_flag_clear_explicit
-atomic_flag_test_and_set
-atomic_flag_test_and_set_explicit
-atomic_signal_fence
-atomic_thread_fence
-clear_cache
-clzdi2
-clzsi2
-clzti2
-cmpdi2
-cmpti2
-comparedf2
-comparesf2
-ctzdi2
-ctzsi2
-ctzti2
-divdc3
-divdf3
-divdi3
-divmoddi4
-divmodsi4
-divsc3
-divsf3
-divsi3
-divtf3
-divti3
-divxc3
-enable_execute_stack
-extendhfsf2
-extendsfdf2
-ffsdi2
-ffsti2
-fixdfdi
-fixdfsi
-fixdfti
-fixsfdi
-fixsfsi
-fixsfti
-fixunsdfdi
-fixunsdfsi
-fixunsdfti
-fixunssfdi
-fixunssfsi
-fixunssfti
-fixunsxfdi
-fixunsxfsi
-fixunsxfti
-fixxfdi
-fixxfti
-floatdidf
-floatdisf
-floatdixf
-floatsidf
-floatsisf
-floattidf
-floattisf
-floattixf
-floatunsidf
-floatunsisf
-floatuntidf
-floatuntisf
-floatuntixf
-gcc_personality_v0
-gnu_f2h_ieee
-gnu_h2f_ieee
-lshrdi3
-lshrti3
-moddi3
-modsi3
-modti3
-muldc3
-muldf3
-muldi3
-mulodi4
-mulosi4
-muloti4
-mulsc3
-mulsf3
-multf3
-multi3
-mulvdi3
-mulvsi3
-mulvti3
-mulxc3
-negdf2
-negdi2
-negsf2
-negti2
-negvdi2
-negvsi2
-negvti2
-paritydi2
-paritysi2
-parityti2
-popcountdi2
-popcountsi2
-popcountti2
-powidf2
-powisf2
-powitf2
-powixf2
-subdf3
-subsf3
-subtf3
-subvdi3
-subvsi3
-subvti3
-trampoline_setup
-truncdfhf2
-truncdfsf2
-truncsfhf2
-ucmpdi2
-ucmpti2
-udivdi3
-udivmoddi4
-udivmodsi4
-udivmodti4
-udivsi3
-udivti3
-umoddi3
-umodsi3
-umodti3
diff --git a/lib/builtins/arm/addsf3.S b/lib/builtins/arm/addsf3.S
index 362b5c147ea6..74723cbeff74 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 87dd03dce94d..adc2d55d90f5 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 c5fee6b6a08e..4b1de997687f 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 b56d98f5c01f..1819e6be436b 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/cpu_model.c b/lib/builtins/cpu_model.c
index 43b913390fc4..fb2b899fc70a 100644
--- a/lib/builtins/cpu_model.c
+++ b/lib/builtins/cpu_model.c
@@ -55,6 +55,9 @@ enum ProcessorTypes {
AMD_BTVER2,
AMDFAM17H,
INTEL_KNM,
+ INTEL_GOLDMONT,
+ INTEL_GOLDMONT_PLUS,
+ INTEL_TREMONT,
CPU_TYPE_MAX
};
@@ -76,6 +79,8 @@ enum ProcessorSubtypes {
INTEL_COREI7_SKYLAKE,
INTEL_COREI7_SKYLAKE_AVX512,
INTEL_COREI7_CANNONLAKE,
+ INTEL_COREI7_ICELAKE_CLIENT,
+ INTEL_COREI7_ICELAKE_SERVER,
CPU_SUBTYPE_MAX
};
@@ -110,7 +115,12 @@ enum ProcessorFeatures {
FEATURE_AVX512IFMA,
FEATURE_AVX5124VNNIW,
FEATURE_AVX5124FMAPS,
- FEATURE_AVX512VPOPCNTDQ
+ FEATURE_AVX512VPOPCNTDQ,
+ FEATURE_AVX512VBMI2,
+ FEATURE_GFNI,
+ FEATURE_VPCLMULQDQ,
+ FEATURE_AVX512VNNI,
+ FEATURE_AVX512BITALG
};
// The check below for i386 was copied from clang's cpuid.h (__get_cpuid_max).
@@ -364,6 +374,14 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model,
case 0x4c: // really airmont
*Type = INTEL_SILVERMONT;
break; // "silvermont"
+ // Goldmont:
+ case 0x5c: // Apollo Lake
+ case 0x5f: // Denverton
+ *Type = INTEL_GOLDMONT;
+ break; // "goldmont"
+ case 0x7a:
+ *Type = INTEL_GOLDMONT_PLUS;
+ break;
case 0x57:
*Type = INTEL_KNL; // knl
@@ -438,35 +456,45 @@ static void getAMDProcessorTypeAndSubtype(unsigned Family, unsigned Model,
}
static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf,
- unsigned *FeaturesOut) {
+ unsigned *FeaturesOut,
+ unsigned *Features2Out) {
unsigned Features = 0;
+ unsigned Features2 = 0;
unsigned EAX, EBX;
+#define setFeature(F) \
+ do { \
+ if (F < 32) \
+ Features |= 1U << (F & 0x1f); \
+ else if (F < 64) \
+ Features2 |= 1U << ((F - 32) & 0x1f); \
+ } while (0)
+
if ((EDX >> 15) & 1)
- Features |= 1 << FEATURE_CMOV;
+ setFeature(FEATURE_CMOV);
if ((EDX >> 23) & 1)
- Features |= 1 << FEATURE_MMX;
+ setFeature(FEATURE_MMX);
if ((EDX >> 25) & 1)
- Features |= 1 << FEATURE_SSE;
+ setFeature(FEATURE_SSE);
if ((EDX >> 26) & 1)
- Features |= 1 << FEATURE_SSE2;
+ setFeature(FEATURE_SSE2);
if ((ECX >> 0) & 1)
- Features |= 1 << FEATURE_SSE3;
+ setFeature(FEATURE_SSE3);
if ((ECX >> 1) & 1)
- Features |= 1 << FEATURE_PCLMUL;
+ setFeature(FEATURE_PCLMUL);
if ((ECX >> 9) & 1)
- Features |= 1 << FEATURE_SSSE3;
+ setFeature(FEATURE_SSSE3);
if ((ECX >> 12) & 1)
- Features |= 1 << FEATURE_FMA;
+ setFeature(FEATURE_FMA);
if ((ECX >> 19) & 1)
- Features |= 1 << FEATURE_SSE4_1;
+ setFeature(FEATURE_SSE4_1);
if ((ECX >> 20) & 1)
- Features |= 1 << FEATURE_SSE4_2;
+ setFeature(FEATURE_SSE4_2);
if ((ECX >> 23) & 1)
- Features |= 1 << FEATURE_POPCNT;
+ setFeature(FEATURE_POPCNT);
if ((ECX >> 25) & 1)
- Features |= 1 << FEATURE_AES;
+ setFeature(FEATURE_AES);
// If CPUID indicates support for XSAVE, XRESTORE and AVX, and XGETBV
// indicates that the AVX registers will be saved and restored on context
@@ -477,43 +505,53 @@ static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf,
bool HasAVX512Save = HasAVX && ((EAX & 0xe0) == 0xe0);
if (HasAVX)
- Features |= 1 << FEATURE_AVX;
+ setFeature(FEATURE_AVX);
bool HasLeaf7 =
MaxLeaf >= 0x7 && !getX86CpuIDAndInfoEx(0x7, 0x0, &EAX, &EBX, &ECX, &EDX);
if (HasLeaf7 && ((EBX >> 3) & 1))
- Features |= 1 << FEATURE_BMI;
+ setFeature(FEATURE_BMI);
if (HasLeaf7 && ((EBX >> 5) & 1) && HasAVX)
- Features |= 1 << FEATURE_AVX2;
+ setFeature(FEATURE_AVX2);
if (HasLeaf7 && ((EBX >> 9) & 1))
- Features |= 1 << FEATURE_BMI2;
+ setFeature(FEATURE_BMI2);
if (HasLeaf7 && ((EBX >> 16) & 1) && HasAVX512Save)
- Features |= 1 << FEATURE_AVX512F;
+ setFeature(FEATURE_AVX512F);
if (HasLeaf7 && ((EBX >> 17) & 1) && HasAVX512Save)
- Features |= 1 << FEATURE_AVX512DQ;
+ setFeature(FEATURE_AVX512DQ);
if (HasLeaf7 && ((EBX >> 21) & 1) && HasAVX512Save)
- Features |= 1 << FEATURE_AVX512IFMA;
+ setFeature(FEATURE_AVX512IFMA);
if (HasLeaf7 && ((EBX >> 26) & 1) && HasAVX512Save)
- Features |= 1 << FEATURE_AVX512PF;
+ setFeature(FEATURE_AVX512PF);
if (HasLeaf7 && ((EBX >> 27) & 1) && HasAVX512Save)
- Features |= 1 << FEATURE_AVX512ER;
+ setFeature(FEATURE_AVX512ER);
if (HasLeaf7 && ((EBX >> 28) & 1) && HasAVX512Save)
- Features |= 1 << FEATURE_AVX512CD;
+ setFeature(FEATURE_AVX512CD);
if (HasLeaf7 && ((EBX >> 30) & 1) && HasAVX512Save)
- Features |= 1 << FEATURE_AVX512BW;
+ setFeature(FEATURE_AVX512BW);
if (HasLeaf7 && ((EBX >> 31) & 1) && HasAVX512Save)
- Features |= 1 << FEATURE_AVX512VL;
+ setFeature(FEATURE_AVX512VL);
if (HasLeaf7 && ((ECX >> 1) & 1) && HasAVX512Save)
- Features |= 1 << FEATURE_AVX512VBMI;
+ setFeature(FEATURE_AVX512VBMI);
+ if (HasLeaf7 && ((ECX >> 6) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512VBMI2);
+ if (HasLeaf7 && ((ECX >> 8) & 1))
+ setFeature(FEATURE_GFNI);
+ if (HasLeaf7 && ((ECX >> 10) & 1) && HasAVX)
+ setFeature(FEATURE_VPCLMULQDQ);
+ if (HasLeaf7 && ((ECX >> 11) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512VNNI);
+ if (HasLeaf7 && ((ECX >> 12) & 1) && HasAVX512Save)
+ setFeature(FEATURE_AVX512BITALG);
if (HasLeaf7 && ((ECX >> 14) & 1) && HasAVX512Save)
- Features |= 1 << FEATURE_AVX512VPOPCNTDQ;
+ setFeature(FEATURE_AVX512VPOPCNTDQ);
if (HasLeaf7 && ((EDX >> 2) & 1) && HasAVX512Save)
- Features |= 1 << FEATURE_AVX5124VNNIW;
+ setFeature(FEATURE_AVX5124VNNIW);
if (HasLeaf7 && ((EDX >> 3) & 1) && HasAVX512Save)
- Features |= 1 << FEATURE_AVX5124FMAPS;
+ setFeature(FEATURE_AVX5124FMAPS);
unsigned MaxExtLevel;
getX86CpuIDAndInfo(0x80000000, &MaxExtLevel, &EBX, &ECX, &EDX);
@@ -521,13 +559,15 @@ static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf,
bool HasExtLeaf1 = MaxExtLevel >= 0x80000001 &&
!getX86CpuIDAndInfo(0x80000001, &EAX, &EBX, &ECX, &EDX);
if (HasExtLeaf1 && ((ECX >> 6) & 1))
- Features |= 1 << FEATURE_SSE4_A;
+ setFeature(FEATURE_SSE4_A);
if (HasExtLeaf1 && ((ECX >> 11) & 1))
- Features |= 1 << FEATURE_XOP;
+ setFeature(FEATURE_XOP);
if (HasExtLeaf1 && ((ECX >> 16) & 1))
- Features |= 1 << FEATURE_FMA4;
+ setFeature(FEATURE_FMA4);
*FeaturesOut = Features;
+ *Features2Out = Features2;
+#undef setFeature
}
#if defined(HAVE_INIT_PRIORITY)
@@ -548,8 +588,9 @@ struct __processor_model {
unsigned int __cpu_subtype;
unsigned int __cpu_features[1];
} __cpu_model = {0, 0, 0, {0}};
+unsigned int __cpu_features2;
-/* A constructor function that is sets __cpu_model and __cpu_features with
+/* A constructor function that is sets __cpu_model and __cpu_features2 with
the right values. This needs to run only once. This constructor is
given the highest priority and it should run before constructors without
the priority set. However, it still runs after ifunc initializers and
@@ -562,6 +603,7 @@ __cpu_indicator_init(void) {
unsigned Vendor;
unsigned Model, Family, Brand_id;
unsigned Features = 0;
+ unsigned Features2 = 0;
/* This function needs to run just once. */
if (__cpu_model.__cpu_vendor)
@@ -580,8 +622,9 @@ __cpu_indicator_init(void) {
Brand_id = EBX & 0xff;
/* Find available features. */
- getAvailableFeatures(ECX, EDX, MaxLeaf, &Features);
+ getAvailableFeatures(ECX, EDX, MaxLeaf, &Features, &Features2);
__cpu_model.__cpu_features[0] = Features;
+ __cpu_features2 = Features2;
if (Vendor == SIG_INTEL) {
/* Get CPU type. */
diff --git a/lib/builtins/ctzdi2.c b/lib/builtins/ctzdi2.c
index eecde29718d7..ef6d7fea1365 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 3c88390b5e77..392d6ecacde1 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/divdf3.c b/lib/builtins/divdf3.c
index 04a4dc5571ca..411c82ebb87a 100644
--- a/lib/builtins/divdf3.c
+++ b/lib/builtins/divdf3.c
@@ -21,36 +21,36 @@
COMPILER_RT_ABI fp_t
__divdf3(fp_t a, fp_t b) {
-
+
const unsigned int aExponent = toRep(a) >> significandBits & maxExponent;
const unsigned int bExponent = toRep(b) >> significandBits & maxExponent;
const rep_t quotientSign = (toRep(a) ^ toRep(b)) & signBit;
-
+
rep_t aSignificand = toRep(a) & significandMask;
rep_t bSignificand = toRep(b) & significandMask;
int scale = 0;
-
+
// Detect if a or b is zero, denormal, infinity, or NaN.
if (aExponent-1U >= maxExponent-1U || bExponent-1U >= maxExponent-1U) {
-
+
const rep_t aAbs = toRep(a) & absMask;
const rep_t bAbs = toRep(b) & absMask;
-
+
// NaN / anything = qNaN
if (aAbs > infRep) return fromRep(toRep(a) | quietBit);
// anything / NaN = qNaN
if (bAbs > infRep) return fromRep(toRep(b) | quietBit);
-
+
if (aAbs == infRep) {
// infinity / infinity = NaN
if (bAbs == infRep) return fromRep(qnanRep);
// infinity / anything else = +/- infinity
else return fromRep(aAbs | quotientSign);
}
-
+
// anything else / infinity = +/- 0
if (bAbs == infRep) return fromRep(quotientSign);
-
+
if (!aAbs) {
// zero / zero = NaN
if (!bAbs) return fromRep(qnanRep);
@@ -59,28 +59,28 @@ __divdf3(fp_t a, fp_t b) {
}
// anything else / zero = +/- infinity
if (!bAbs) return fromRep(infRep | quotientSign);
-
+
// one or both of a or b is denormal, the other (if applicable) is a
// normal number. Renormalize one or both of a and b, and set scale to
// include the necessary exponent adjustment.
if (aAbs < implicitBit) scale += normalize(&aSignificand);
if (bAbs < implicitBit) scale -= normalize(&bSignificand);
}
-
+
// Or in the implicit significand bit. (If we fell through from the
// denormal path it was already set by normalize( ), but setting it twice
// won't hurt anything.)
aSignificand |= implicitBit;
bSignificand |= implicitBit;
int quotientExponent = aExponent - bExponent + scale;
-
+
// Align the significand of b as a Q31 fixed-point number in the range
// [1, 2.0) and get a Q32 approximate reciprocal using a small minimax
// polynomial approximation: reciprocal = 3/4 + 1/sqrt(2) - b/2. This
// is accurate to about 3.5 binary digits.
const uint32_t q31b = bSignificand >> 21;
uint32_t recip32 = UINT32_C(0x7504f333) - q31b;
-
+
// Now refine the reciprocal estimate using a Newton-Raphson iteration:
//
// x1 = x0 * (2 - x0 * b)
@@ -95,13 +95,13 @@ __divdf3(fp_t a, fp_t b) {
recip32 = (uint64_t)recip32 * correction32 >> 31;
correction32 = -((uint64_t)recip32 * q31b >> 32);
recip32 = (uint64_t)recip32 * correction32 >> 31;
-
+
// recip32 might have overflowed to exactly zero in the preceding
// computation if the high word of b is exactly 1.0. This would sabotage
// the full-width final stage of the computation that follows, so we adjust
// recip32 downward by one bit.
recip32--;
-
+
// We need to perform one more iteration to get us to 56 binary digits;
// The last iteration needs to happen with extra precision.
const uint32_t q63blo = bSignificand << 11;
@@ -110,14 +110,14 @@ __divdf3(fp_t a, fp_t b) {
uint32_t cHi = correction >> 32;
uint32_t cLo = correction;
reciprocal = (uint64_t)recip32*cHi + ((uint64_t)recip32*cLo >> 32);
-
+
// We already adjusted the 32-bit estimate, now we need to adjust the final
// 64-bit reciprocal estimate downward to ensure that it is strictly smaller
// than the infinitely precise exact reciprocal. Because the computation
// of the Newton-Raphson step is truncating at every step, this adjustment
// is small; most of the work is already done.
reciprocal -= 2;
-
+
// The numerical reciprocal is accurate to within 2^-56, lies in the
// interval [0.5, 1.0), and is strictly smaller than the true reciprocal
// of b. Multiplying a by this reciprocal thus gives a numerical q = a/b
@@ -127,12 +127,12 @@ __divdf3(fp_t a, fp_t b) {
// 2. q is in the interval [0.5, 2.0)
// 3. the error in q is bounded away from 2^-53 (actually, we have a
// couple of bits to spare, but this is all we need).
-
+
// We need a 64 x 64 multiply high to compute q, which isn't a basic
// operation in C, so we need to be a little bit fussy.
rep_t quotient, quotientLo;
wideMultiply(aSignificand << 2, reciprocal, &quotient, &quotientLo);
-
+
// Two cases: quotient is in [0.5, 1.0) or quotient is in [1.0, 2.0).
// In either case, we are going to compute a residual of the form
//
@@ -141,7 +141,7 @@ __divdf3(fp_t a, fp_t b) {
// We know from the construction of q that r satisfies:
//
// 0 <= r < ulp(q)*b
- //
+ //
// if r is greater than 1/2 ulp(q)*b, then q rounds up. Otherwise, we
// already have the correct result. The exact halfway case cannot occur.
// We also take this time to right shift quotient if it falls in the [1,2)
@@ -154,20 +154,20 @@ __divdf3(fp_t a, fp_t b) {
quotient >>= 1;
residual = (aSignificand << 52) - quotient * bSignificand;
}
-
+
const int writtenExponent = quotientExponent + exponentBias;
-
+
if (writtenExponent >= maxExponent) {
// If we have overflowed the exponent, return infinity.
return fromRep(infRep | quotientSign);
}
-
+
else if (writtenExponent < 1) {
// Flush denormals to zero. In the future, it would be nice to add
// code to round them correctly.
return fromRep(quotientSign);
}
-
+
else {
const bool round = (residual << 1) > bSignificand;
// Clear the implicit bit
diff --git a/lib/builtins/divsc3.c b/lib/builtins/divsc3.c
index 42a48315e66d..0d18a256c3de 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/divsf3.c b/lib/builtins/divsf3.c
index 65294d70fc61..a74917fd1de5 100644
--- a/lib/builtins/divsf3.c
+++ b/lib/builtins/divsf3.c
@@ -21,36 +21,36 @@
COMPILER_RT_ABI fp_t
__divsf3(fp_t a, fp_t b) {
-
+
const unsigned int aExponent = toRep(a) >> significandBits & maxExponent;
const unsigned int bExponent = toRep(b) >> significandBits & maxExponent;
const rep_t quotientSign = (toRep(a) ^ toRep(b)) & signBit;
-
+
rep_t aSignificand = toRep(a) & significandMask;
rep_t bSignificand = toRep(b) & significandMask;
int scale = 0;
-
+
// Detect if a or b is zero, denormal, infinity, or NaN.
if (aExponent-1U >= maxExponent-1U || bExponent-1U >= maxExponent-1U) {
-
+
const rep_t aAbs = toRep(a) & absMask;
const rep_t bAbs = toRep(b) & absMask;
-
+
// NaN / anything = qNaN
if (aAbs > infRep) return fromRep(toRep(a) | quietBit);
// anything / NaN = qNaN
if (bAbs > infRep) return fromRep(toRep(b) | quietBit);
-
+
if (aAbs == infRep) {
// infinity / infinity = NaN
if (bAbs == infRep) return fromRep(qnanRep);
// infinity / anything else = +/- infinity
else return fromRep(aAbs | quotientSign);
}
-
+
// anything else / infinity = +/- 0
if (bAbs == infRep) return fromRep(quotientSign);
-
+
if (!aAbs) {
// zero / zero = NaN
if (!bAbs) return fromRep(qnanRep);
@@ -59,28 +59,28 @@ __divsf3(fp_t a, fp_t b) {
}
// anything else / zero = +/- infinity
if (!bAbs) return fromRep(infRep | quotientSign);
-
+
// one or both of a or b is denormal, the other (if applicable) is a
// normal number. Renormalize one or both of a and b, and set scale to
// include the necessary exponent adjustment.
if (aAbs < implicitBit) scale += normalize(&aSignificand);
if (bAbs < implicitBit) scale -= normalize(&bSignificand);
}
-
+
// Or in the implicit significand bit. (If we fell through from the
// denormal path it was already set by normalize( ), but setting it twice
// won't hurt anything.)
aSignificand |= implicitBit;
bSignificand |= implicitBit;
int quotientExponent = aExponent - bExponent + scale;
-
+
// Align the significand of b as a Q31 fixed-point number in the range
// [1, 2.0) and get a Q32 approximate reciprocal using a small minimax
// polynomial approximation: reciprocal = 3/4 + 1/sqrt(2) - b/2. This
// is accurate to about 3.5 binary digits.
uint32_t q31b = bSignificand << 8;
uint32_t reciprocal = UINT32_C(0x7504f333) - q31b;
-
+
// Now refine the reciprocal estimate using a Newton-Raphson iteration:
//
// x1 = x0 * (2 - x0 * b)
@@ -95,7 +95,7 @@ __divsf3(fp_t a, fp_t b) {
reciprocal = (uint64_t)reciprocal * correction >> 31;
correction = -((uint64_t)reciprocal * q31b >> 32);
reciprocal = (uint64_t)reciprocal * correction >> 31;
-
+
// Exhaustive testing shows that the error in reciprocal after three steps
// is in the interval [-0x1.f58108p-31, 0x1.d0e48cp-29], in line with our
// expectations. We bump the reciprocal by a tiny value to force the error
@@ -103,7 +103,7 @@ __divsf3(fp_t a, fp_t b) {
// be specific). This also causes 1/1 to give a sensible approximation
// instead of zero (due to overflow).
reciprocal -= 2;
-
+
// The numerical reciprocal is accurate to within 2^-28, lies in the
// interval [0x1.000000eep-1, 0x1.fffffffcp-1], and is strictly smaller
// than the true reciprocal of b. Multiplying a by this reciprocal thus
@@ -115,9 +115,9 @@ __divsf3(fp_t a, fp_t b) {
// from the fact that we truncate the product, and the 2^27 term
// is the error in the reciprocal of b scaled by the maximum
// possible value of a. As a consequence of this error bound,
- // either q or nextafter(q) is the correctly rounded
+ // either q or nextafter(q) is the correctly rounded
rep_t quotient = (uint64_t)reciprocal*(aSignificand << 1) >> 32;
-
+
// Two cases: quotient is in [0.5, 1.0) or quotient is in [1.0, 2.0).
// In either case, we are going to compute a residual of the form
//
@@ -126,7 +126,7 @@ __divsf3(fp_t a, fp_t b) {
// We know from the construction of q that r satisfies:
//
// 0 <= r < ulp(q)*b
- //
+ //
// if r is greater than 1/2 ulp(q)*b, then q rounds up. Otherwise, we
// already have the correct result. The exact halfway case cannot occur.
// We also take this time to right shift quotient if it falls in the [1,2)
@@ -141,18 +141,18 @@ __divsf3(fp_t a, fp_t b) {
}
const int writtenExponent = quotientExponent + exponentBias;
-
+
if (writtenExponent >= maxExponent) {
// If we have overflowed the exponent, return infinity.
return fromRep(infRep | quotientSign);
}
-
+
else if (writtenExponent < 1) {
// Flush denormals to zero. In the future, it would be nice to add
// code to round them correctly.
return fromRep(quotientSign);
}
-
+
else {
const bool round = (residual << 1) > bSignificand;
// Clear the implicit bit
diff --git a/lib/builtins/divtc3.c b/lib/builtins/divtc3.c
index 16e538ba4a33..e5ea00d841e3 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 07d436e267d8..ef95a1c260c1 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 223fb980aaed..a0e19ab6a8f6 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/gcc_personality_v0.c b/lib/builtins/gcc_personality_v0.c
index 0bc765624564..68581ef16434 100644
--- a/lib/builtins/gcc_personality_v0.c
+++ b/lib/builtins/gcc_personality_v0.c
@@ -206,8 +206,8 @@ __gcc_personality_v0(int version, _Unwind_Action actions,
if ( lsda == (uint8_t*) 0 )
return continueUnwind(exceptionObject, context);
- uintptr_t pc = _Unwind_GetIP(context)-1;
- uintptr_t funcStart = _Unwind_GetRegionStart(context);
+ uintptr_t pc = (uintptr_t)_Unwind_GetIP(context)-1;
+ uintptr_t funcStart = (uintptr_t)_Unwind_GetRegionStart(context);
uintptr_t pcOffset = pc - funcStart;
/* Parse LSDA header. */
@@ -249,4 +249,3 @@ __gcc_personality_v0(int version, _Unwind_Action actions,
/* No landing pad found, continue unwinding. */
return continueUnwind(exceptionObject, context);
}
-
diff --git a/lib/builtins/int_lib.h b/lib/builtins/int_lib.h
index 9d09e2dc915b..fe8a3bdedc0e 100644
--- a/lib/builtins/int_lib.h
+++ b/lib/builtins/int_lib.h
@@ -43,7 +43,7 @@
#define AEABI_RTABI __attribute__((__pcs__("aapcs")))
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && !defined(__clang__)
#define ALWAYS_INLINE __forceinline
#define NOINLINE __declspec(noinline)
#define NORETURN __declspec(noreturn)
diff --git a/lib/builtins/int_math.h b/lib/builtins/int_math.h
index fc81fb7f0220..aa3d0721a8ab 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_types.h b/lib/builtins/int_types.h
index f53f343d35d5..9f8da56cb77b 100644
--- a/lib/builtins/int_types.h
+++ b/lib/builtins/int_types.h
@@ -60,10 +60,19 @@ typedef union
}s;
} udwords;
-#if (defined(__LP64__) || defined(__wasm__) || defined(__mips64)) || defined(__riscv)
+#if defined(__LP64__) || defined(__wasm__) || defined(__mips64) || \
+ defined(__riscv) || defined(_WIN64)
#define CRT_HAS_128BIT
#endif
+/* MSVC doesn't have a working 128bit integer type. Users should really compile
+ * compiler-rt with clang, but if they happen to be doing a standalone build for
+ * asan or something else, disable the 128 bit parts so things sort of work.
+ */
+#if defined(_MSC_VER) && !defined(__clang__)
+#undef CRT_HAS_128BIT
+#endif
+
#ifdef CRT_HAS_128BIT
typedef int ti_int __attribute__ ((mode (TI)));
typedef unsigned tu_int __attribute__ ((mode (TI)));
@@ -137,6 +146,18 @@ typedef struct
#endif /* _YUGA_LITTLE_ENDIAN */
} uqwords;
+/* Check if the target supports 80 bit extended precision long doubles.
+ * Notably, on x86 Windows, MSVC only provides a 64-bit long double, but GCC
+ * still makes it 80 bits. Clang will match whatever compiler it is trying to
+ * be compatible with.
+ */
+#if ((defined(__i386__) || defined(__x86_64__)) && !defined(_MSC_VER)) || \
+ defined(__m68k__) || defined(__ia64__)
+#define HAS_80_BIT_LONG_DOUBLE 1
+#else
+#define HAS_80_BIT_LONG_DOUBLE 0
+#endif
+
typedef union
{
uqwords u;
diff --git a/lib/builtins/int_util.c b/lib/builtins/int_util.c
index de87410dbca2..752f2015580e 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 a7b20ed66244..c3c87381ad8a 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/os_version_check.c b/lib/builtins/os_version_check.c
index 772e33333c0f..e0d40edc7e38 100644
--- a/lib/builtins/os_version_check.c
+++ b/lib/builtins/os_version_check.c
@@ -15,7 +15,6 @@
#ifdef __APPLE__
-#include <CoreFoundation/CoreFoundation.h>
#include <TargetConditionals.h>
#include <dispatch/dispatch.h>
#include <dlfcn.h>
@@ -28,6 +27,33 @@
static int32_t GlobalMajor, GlobalMinor, GlobalSubminor;
static dispatch_once_t DispatchOnceCounter;
+/* We can't include <CoreFoundation/CoreFoundation.h> directly from here, so
+ * just forward declare everything that we need from it. */
+
+typedef const void *CFDataRef, *CFAllocatorRef, *CFPropertyListRef,
+ *CFStringRef, *CFDictionaryRef, *CFTypeRef, *CFErrorRef;
+
+#if __LLP64__
+typedef unsigned long long CFTypeID;
+typedef unsigned long long CFOptionFlags;
+typedef signed long long CFIndex;
+#else
+typedef unsigned long CFTypeID;
+typedef unsigned long CFOptionFlags;
+typedef signed long CFIndex;
+#endif
+
+typedef unsigned char UInt8;
+typedef _Bool Boolean;
+typedef CFIndex CFPropertyListFormat;
+typedef uint32_t CFStringEncoding;
+
+/* kCFStringEncodingASCII analog. */
+#define CF_STRING_ENCODING_ASCII 0x0600
+/* kCFStringEncodingUTF8 analog. */
+#define CF_STRING_ENCODING_UTF8 0x08000100
+#define CF_PROPERTY_LIST_IMMUTABLE 0
+
typedef CFDataRef (*CFDataCreateWithBytesNoCopyFuncTy)(CFAllocatorRef,
const UInt8 *, CFIndex,
CFAllocatorRef);
@@ -55,8 +81,7 @@ static void parseSystemVersionPList(void *Unused) {
const void *NullAllocator = dlsym(RTLD_DEFAULT, "kCFAllocatorNull");
if (!NullAllocator)
return;
- const CFAllocatorRef kCFAllocatorNull =
- *(const CFAllocatorRef *)NullAllocator;
+ const CFAllocatorRef AllocatorNull = *(const CFAllocatorRef *)NullAllocator;
CFDataCreateWithBytesNoCopyFuncTy CFDataCreateWithBytesNoCopyFunc =
(CFDataCreateWithBytesNoCopyFuncTy)dlsym(RTLD_DEFAULT,
"CFDataCreateWithBytesNoCopy");
@@ -140,21 +165,21 @@ static void parseSystemVersionPList(void *Unused) {
/* Get the file buffer into CF's format. We pass in a null allocator here *
* because we free PListBuf ourselves */
FileContentsRef = (*CFDataCreateWithBytesNoCopyFunc)(
- NULL, PListBuf, (CFIndex)NumRead, kCFAllocatorNull);
+ NULL, PListBuf, (CFIndex)NumRead, AllocatorNull);
if (!FileContentsRef)
goto Fail;
if (CFPropertyListCreateWithDataFunc)
PListRef = (*CFPropertyListCreateWithDataFunc)(
- NULL, FileContentsRef, kCFPropertyListImmutable, NULL, NULL);
+ NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL, NULL);
else
PListRef = (*CFPropertyListCreateFromXMLDataFunc)(
- NULL, FileContentsRef, kCFPropertyListImmutable, NULL);
+ NULL, FileContentsRef, CF_PROPERTY_LIST_IMMUTABLE, NULL);
if (!PListRef)
goto Fail;
CFStringRef ProductVersion = (*CFStringCreateWithCStringNoCopyFunc)(
- NULL, "ProductVersion", kCFStringEncodingASCII, kCFAllocatorNull);
+ NULL, "ProductVersion", CF_STRING_ENCODING_ASCII, AllocatorNull);
if (!ProductVersion)
goto Fail;
CFTypeRef OpaqueValue = (*CFDictionaryGetValueFunc)(PListRef, ProductVersion);
@@ -165,7 +190,7 @@ static void parseSystemVersionPList(void *Unused) {
char VersionStr[32];
if (!(*CFStringGetCStringFunc)((CFStringRef)OpaqueValue, VersionStr,
- sizeof(VersionStr), kCFStringEncodingUTF8))
+ sizeof(VersionStr), CF_STRING_ENCODING_UTF8))
goto Fail;
sscanf(VersionStr, "%d.%d.%d", &GlobalMajor, &GlobalMinor, &GlobalSubminor);
diff --git a/lib/builtins/ppc/divtc3.c b/lib/builtins/ppc/divtc3.c
index 8ec41c528ab9..ef532b841145 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/builtins/ppc/fixunstfti.c b/lib/builtins/ppc/fixunstfti.c
new file mode 100644
index 000000000000..fa21084cb576
--- /dev/null
+++ b/lib/builtins/ppc/fixunstfti.c
@@ -0,0 +1,106 @@
+//===-- lib/builtins/ppc/fixunstfti.c - Convert long double->int128 *-C -*-===//
+//
+// 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 implements converting the 128bit IBM/PowerPC long double (double-
+// double) data type to an unsigned 128 bit integer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../int_math.h"
+#define BIAS 1023
+
+/* Convert long double into an unsigned 128-bit integer. */
+__uint128_t __fixunstfti(long double input) {
+
+ /* If we are trying to convert a NaN, return the NaN bit pattern. */
+ if (crt_isnan(input)) {
+ return ((__uint128_t)0x7FF8000000000000ll) << 64 |
+ (__uint128_t)0x0000000000000000ll;
+ }
+
+ __uint128_t result, hiResult, loResult;
+ int hiExponent, loExponent, shift;
+ /* The long double representation, with the high and low portions of
+ * the long double, and the corresponding bit patterns of each double. */
+ union {
+ long double ld;
+ double d[2]; /* [0] is the high double, [1] is the low double. */
+ unsigned long long ull[2]; /* High and low doubles as 64-bit integers. */
+ } ldUnion;
+
+ /* If the long double is less than 1.0 or negative,
+ * return 0.0. */
+ if (input < 1.0)
+ return 0.0;
+
+ /* Retrieve the 64-bit patterns of high and low doubles.
+ * Compute the unbiased exponent of both high and low doubles by
+ * removing the signs, isolating the exponent, and subtracting
+ * the bias from it. */
+ ldUnion.ld = input;
+ hiExponent = ((ldUnion.ull[0] & 0x7FFFFFFFFFFFFFFFll) >> 52) - BIAS;
+ loExponent = ((ldUnion.ull[1] & 0x7FFFFFFFFFFFFFFFll) >> 52) - BIAS;
+
+ /* Convert each double into int64; they will be added to the int128 result.
+ * CASE 1: High or low double fits in int64
+ * - Convert the each double normally into int64.
+ *
+ * CASE 2: High or low double does not fit in int64
+ * - Scale the double to fit within a 64-bit integer
+ * - Calculate the shift (amount to scale the double by in the int128)
+ * - Clear all the bits of the exponent (with 0x800FFFFFFFFFFFFF)
+ * - Add BIAS+53 (0x4350000000000000) to exponent to correct the value
+ * - Scale (move) the double to the correct place in the int128
+ * (Move it by 2^53 places)
+ *
+ * Note: If the high double is assumed to be positive, an unsigned conversion
+ * from long double to 64-bit integer is needed. The low double can be either
+ * positive or negative, so a signed conversion is needed to retain the result
+ * of the low double and to ensure it does not simply get converted to 0. */
+
+ /* CASE 1 - High double fits in int64. */
+ if (hiExponent < 63) {
+ hiResult = (unsigned long long)ldUnion.d[0];
+ } else if (hiExponent < 128) {
+ /* CASE 2 - High double does not fit in int64, scale and convert it. */
+ shift = hiExponent - 54;
+ ldUnion.ull[0] &= 0x800FFFFFFFFFFFFFll;
+ ldUnion.ull[0] |= 0x4350000000000000ll;
+ hiResult = (unsigned long long)ldUnion.d[0];
+ hiResult <<= shift;
+ } else {
+ /* Detect cases for overflow. When the exponent of the high
+ * double is greater than 128 bits and when the long double
+ * input is positive, return the max 128-bit integer.
+ * For negative inputs with exponents > 128, return 1, like gcc. */
+ if (ldUnion.d[0] > 0) {
+ return ((__uint128_t)0xFFFFFFFFFFFFFFFFll) << 64 |
+ (__uint128_t)0xFFFFFFFFFFFFFFFFll;
+ } else {
+ return ((__uint128_t)0x0000000000000000ll) << 64 |
+ (__uint128_t)0x0000000000000001ll;
+ }
+ }
+
+ /* CASE 1 - Low double fits in int64. */
+ if (loExponent < 63) {
+ loResult = (long long)ldUnion.d[1];
+ } else {
+ /* CASE 2 - Low double does not fit in int64, scale and convert it. */
+ shift = loExponent - 54;
+ ldUnion.ull[1] &= 0x800FFFFFFFFFFFFFll;
+ ldUnion.ull[1] |= 0x4350000000000000ll;
+ loResult = (long long)ldUnion.d[1];
+ loResult <<= shift;
+ }
+
+ /* Add the high and low doublewords together to form a 128 bit integer. */
+ result = loResult + hiResult;
+ return result;
+}
diff --git a/lib/builtins/ppc/floattitf.c b/lib/builtins/ppc/floattitf.c
new file mode 100644
index 000000000000..b8e297b6b8d9
--- /dev/null
+++ b/lib/builtins/ppc/floattitf.c
@@ -0,0 +1,48 @@
+//===-- lib/builtins/ppc/floattitf.c - Convert int128->long double -*-C -*-===//
+//
+// 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 implements converting a signed 128 bit integer to a 128bit IBM /
+// PowerPC long double (double-double) value.
+//
+//===----------------------------------------------------------------------===//
+
+#include <stdint.h>
+
+/* Conversions from signed and unsigned 64-bit int to long double. */
+long double __floatditf(int64_t);
+long double __floatunditf(uint64_t);
+
+/* Convert a signed 128-bit integer to long double.
+ * This uses the following property: Let hi and lo be 64-bits each,
+ * and let signed_val_k() and unsigned_val_k() be the value of the
+ * argument interpreted as a signed or unsigned k-bit integer. Then,
+ *
+ * signed_val_128(hi,lo) = signed_val_64(hi) * 2^64 + unsigned_val_64(lo)
+ * = (long double)hi * 2^64 + (long double)lo,
+ *
+ * where (long double)hi and (long double)lo are signed and
+ * unsigned 64-bit integer to long double conversions, respectively.
+ */
+long double __floattitf(__int128_t arg) {
+ /* Split the int128 argument into 64-bit high and low int64 parts. */
+ int64_t ArgHiPart = (int64_t)(arg >> 64);
+ uint64_t ArgLoPart = (uint64_t)arg;
+
+ /* Convert each 64-bit part into long double. The high part
+ * must be a signed conversion and the low part an unsigned conversion
+ * to ensure the correct result. */
+ long double ConvertedHiPart = __floatditf(ArgHiPart);
+ long double ConvertedLoPart = __floatunditf(ArgLoPart);
+
+ /* The low bit of ArgHiPart corresponds to the 2^64 bit in arg.
+ * Multiply the high part by 2^64 to undo the right shift by 64-bits
+ * done in the splitting. Then, add to the low part to obtain the
+ * final result. */
+ return ((ConvertedHiPart * 0x1.0p64) + ConvertedLoPart);
+}
diff --git a/lib/cfi/CMakeLists.txt b/lib/cfi/CMakeLists.txt
index 7ed72bca5d23..463a1fd59915 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 a2f127f93cda..b0a943759678 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 3d73508f5707..4a0f03949ca8 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/dfsan/dfsan.cc b/lib/dfsan/dfsan.cc
index d4dbebc43a78..585bdceac764 100644
--- a/lib/dfsan/dfsan.cc
+++ b/lib/dfsan/dfsan.cc
@@ -423,7 +423,7 @@ static void dfsan_fini() {
static void dfsan_init(int argc, char **argv, char **envp) {
InitializeFlags();
- InitializePlatformEarly();
+ ::InitializePlatformEarly();
if (!MmapFixedNoReserve(ShadowAddr(), UnusedAddr() - ShadowAddr()))
Die();
diff --git a/lib/esan/CMakeLists.txt b/lib/esan/CMakeLists.txt
index 4de5c0205c1d..c880971e3dd9 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 0c596f1cf37e..833faa2cd5bc 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 72a919a190d8..b76a9ccbd9e4 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.h b/lib/esan/esan_sideline.h
index 04aff22f4cf0..74551fbde850 100644
--- a/lib/esan/esan_sideline.h
+++ b/lib/esan/esan_sideline.h
@@ -17,6 +17,7 @@
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_platform_limits_freebsd.h"
#include "sanitizer_common/sanitizer_platform_limits_posix.h"
namespace __esan {
diff --git a/lib/esan/esan_sideline_bsd.cpp b/lib/esan/esan_sideline_bsd.cpp
new file mode 100644
index 000000000000..3134d3776730
--- /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 679318e460b5..caea9734fe53 100644
--- a/lib/fuzzer/CMakeLists.txt
+++ b/lib/fuzzer/CMakeLists.txt
@@ -3,8 +3,8 @@ set(LIBFUZZER_SOURCES
FuzzerDataFlowTrace.cpp
FuzzerDriver.cpp
FuzzerExtFunctionsDlsym.cpp
- FuzzerExtFunctionsDlsymWin.cpp
FuzzerExtFunctionsWeak.cpp
+ FuzzerExtFunctionsWindows.cpp
FuzzerExtraCounters.cpp
FuzzerIO.cpp
FuzzerIOPosix.cpp
@@ -25,6 +25,8 @@ set(LIBFUZZER_SOURCES
FuzzerUtilWindows.cpp)
set(LIBFUZZER_HEADERS
+ FuzzerBuiltins.h
+ FuzzerBuiltinsMsvc.h
FuzzerCommand.h
FuzzerCorpus.h
FuzzerDataFlowTrace.h
@@ -124,12 +126,12 @@ if(OS_NAME MATCHES "Linux|Fuchsia" AND COMPILER_RT_LIBCXX_PATH)
set(LIBCXX_${arch}_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libcxx_fuzzer_${arch})
add_custom_libcxx(libcxx_fuzzer_${arch} ${LIBCXX_${arch}_PREFIX}
CFLAGS ${TARGET_CFLAGS}
- -D_LIBCPP_ABI_VERSION=Fuzzer
-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS=1
-fvisibility=hidden
CMAKE_ARGS -DCMAKE_CXX_COMPILER_WORKS=ON
-DLIBCXX_ENABLE_EXCEPTIONS=OFF
-DLIBCXX_ENABLE_SHARED=OFF
+ -DLIBCXX_ABI_NAMESPACE=Fuzzer
-DLIBCXX_CXX_ABI=none)
target_compile_options(RTfuzzer.${arch} PRIVATE -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1)
add_dependencies(RTfuzzer.${arch} libcxx_fuzzer_${arch}-build)
diff --git a/lib/fuzzer/FuzzerBuiltins.h b/lib/fuzzer/FuzzerBuiltins.h
new file mode 100644
index 000000000000..a80938d9a54d
--- /dev/null
+++ b/lib/fuzzer/FuzzerBuiltins.h
@@ -0,0 +1,36 @@
+//===- FuzzerBuiltins.h - Internal header for builtins ----------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Wrapper functions and marcos around builtin functions.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_BUILTINS_H
+#define LLVM_FUZZER_BUILTINS_H
+
+#include "FuzzerDefs.h"
+
+#if !LIBFUZZER_MSVC
+#include <cstdint>
+
+#define GET_CALLER_PC() __builtin_return_address(0)
+
+namespace fuzzer {
+
+inline uint8_t Bswap(uint8_t x) { return x; }
+inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); }
+inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); }
+inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); }
+
+inline uint32_t Clzll(unsigned long long X) { return __builtin_clzll(X); }
+inline uint32_t Clz(unsigned long long X) { return __builtin_clz(X); }
+inline int Popcountll(unsigned long long X) { return __builtin_popcountll(X); }
+
+} // namespace fuzzer
+
+#endif // !LIBFUZZER_MSVC
+#endif // LLVM_FUZZER_BUILTINS_H
diff --git a/lib/fuzzer/FuzzerBuiltinsMsvc.h b/lib/fuzzer/FuzzerBuiltinsMsvc.h
new file mode 100644
index 000000000000..67dd57ff9a93
--- /dev/null
+++ b/lib/fuzzer/FuzzerBuiltinsMsvc.h
@@ -0,0 +1,59 @@
+//===- FuzzerBuiltinsMSVC.h - Internal header for builtins ------*- C++ -* ===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// Wrapper functions and marcos that use intrinsics instead of builtin functions
+// which cannot be compiled by MSVC.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FUZZER_BUILTINS_MSVC_H
+#define LLVM_FUZZER_BUILTINS_MSVC_H
+
+#include "FuzzerDefs.h"
+
+#if LIBFUZZER_MSVC
+#if !defined(_M_ARM) && !defined(_M_X64)
+#error "_BitScanReverse64 unavailable on this platform so MSVC is unsupported."
+#endif
+#include <intrin.h>
+#include <cstdint>
+#include <cstdlib>
+
+// __builtin_return_address() cannot be compiled with MSVC. Use the equivalent
+// from <intrin.h>
+#define GET_CALLER_PC() reinterpret_cast<uintptr_t>(_ReturnAddress())
+
+namespace fuzzer {
+
+inline uint8_t Bswap(uint8_t x) { return x; }
+// Use alternatives to __builtin functions from <stdlib.h> and <intrin.h> on
+// Windows since the builtins are not supported by MSVC.
+inline uint16_t Bswap(uint16_t x) { return _byteswap_ushort(x); }
+inline uint32_t Bswap(uint32_t x) { return _byteswap_ulong(x); }
+inline uint64_t Bswap(uint64_t x) { return _byteswap_uint64(x); }
+
+// The functions below were mostly copied from
+// compiler-rt/lib/builtins/int_lib.h which defines the __builtin functions used
+// outside of Windows.
+inline uint32_t Clzll(uint64_t X) {
+ unsigned long LeadZeroIdx = 0;
+ if (_BitScanReverse64(&LeadZeroIdx, X)) return 63 - LeadZeroIdx;
+ return 64;
+}
+
+inline uint32_t Clz(uint32_t X) {
+ unsigned long LeadZeroIdx = 0;
+ if (_BitScanReverse(&LeadZeroIdx, X)) return 31 - LeadZeroIdx;
+ return 32;
+}
+
+inline int Popcountll(unsigned long long X) { return __popcnt64(X); }
+
+} // namespace fuzzer
+
+#endif // LIBFUZER_MSVC
+#endif // LLVM_FUZZER_BUILTINS_MSVC_H
diff --git a/lib/fuzzer/FuzzerCommand.h b/lib/fuzzer/FuzzerCommand.h
index 255f571ecf31..9d258a228f89 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/FuzzerCorpus.h b/lib/fuzzer/FuzzerCorpus.h
index 8ad14656cffc..f844c07c7572 100644
--- a/lib/fuzzer/FuzzerCorpus.h
+++ b/lib/fuzzer/FuzzerCorpus.h
@@ -238,12 +238,6 @@ class InputCorpus {
return false;
}
- bool IsFeatureNew(size_t Idx, uint32_t NewSize, bool Shrink) {
- assert(NewSize);
- uint32_t OldSize = GetFeature(Idx % kFeatureSetSize);
- return OldSize == 0 || (Shrink && OldSize > NewSize);
- }
-
size_t NumFeatures() const { return NumAddedFeatures; }
size_t NumFeatureUpdates() const { return NumUpdatedFeatures; }
diff --git a/lib/fuzzer/FuzzerDefs.h b/lib/fuzzer/FuzzerDefs.h
index a35c7a181b74..c3dccbcd86f5 100644
--- a/lib/fuzzer/FuzzerDefs.h
+++ b/lib/fuzzer/FuzzerDefs.h
@@ -82,6 +82,13 @@
#error "Support for your platform has not been implemented"
#endif
+#if defined(_MSC_VER) && !defined(__clang__)
+// MSVC compiler is being used.
+#define LIBFUZZER_MSVC 1
+#else
+#define LIBFUZZER_MSVC 0
+#endif
+
#ifndef __has_attribute
# define __has_attribute(x) 0
#endif
@@ -129,8 +136,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 {
@@ -176,11 +190,6 @@ typedef int (*UserCallback)(const uint8_t *Data, size_t Size);
int FuzzerDriver(int *argc, char ***argv, UserCallback Callback);
-inline uint8_t Bswap(uint8_t x) { return x; }
-inline uint16_t Bswap(uint16_t x) { return __builtin_bswap16(x); }
-inline uint32_t Bswap(uint32_t x) { return __builtin_bswap32(x); }
-inline uint64_t Bswap(uint64_t x) { return __builtin_bswap64(x); }
-
uint8_t *ExtraCountersBegin();
uint8_t *ExtraCountersEnd();
void ClearExtraCounters();
diff --git a/lib/fuzzer/FuzzerDriver.cpp b/lib/fuzzer/FuzzerDriver.cpp
index 783474a39e16..ff2a639ac4a3 100644
--- a/lib/fuzzer/FuzzerDriver.cpp
+++ b/lib/fuzzer/FuzzerDriver.cpp
@@ -615,13 +615,8 @@ 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.PrintCorpusStats = Flags.print_corpus_stats;
Options.PrintCoverage = Flags.print_coverage;
- Options.PrintUnstableStats = Flags.print_unstable_stats;
- if (Flags.handle_unstable == TracePC::MinUnstable ||
- Flags.handle_unstable == TracePC::ZeroUnstable)
- Options.HandleUnstable = Flags.handle_unstable;
Options.DumpCoverage = Flags.dump_coverage;
if (Flags.exit_on_src_pos)
Options.ExitOnSrcPos = Flags.exit_on_src_pos;
diff --git a/lib/fuzzer/FuzzerExtFunctionsDlsymWin.cpp b/lib/fuzzer/FuzzerExtFunctionsDlsymWin.cpp
deleted file mode 100644
index 321b3ec5d414..000000000000
--- 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 a4e56fc27b8d..6a6ef4932f41 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/FuzzerExtFunctionsWeakAlias.cpp b/lib/fuzzer/FuzzerExtFunctionsWeakAlias.cpp
deleted file mode 100644
index e10f7b4dcac2..000000000000
--- a/lib/fuzzer/FuzzerExtFunctionsWeakAlias.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-//===- FuzzerExtFunctionsWeakAlias.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 weak aliases. Works for Windows.
-//===----------------------------------------------------------------------===//
-#include "FuzzerDefs.h"
-#if LIBFUZZER_WINDOWS
-
-#include "FuzzerExtFunctions.h"
-#include "FuzzerIO.h"
-
-using namespace fuzzer;
-
-extern "C" {
-// Declare these symbols as weak to allow them to be optionally defined.
-#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
- RETURN_TYPE NAME##Def FUNC_SIG { \
- Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \
- exit(1); \
- } \
- RETURN_TYPE NAME FUNC_SIG __attribute__((weak, alias(#NAME "Def")));
-
-#include "FuzzerExtFunctions.def"
-
-#undef EXT_FUNC
-}
-
-template <typename T>
-static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) {
- if (Fun == FunDef) {
- if (WarnIfMissing)
- Printf("WARNING: Failed to find function \"%s\".\n", FnName);
- return nullptr;
- }
- return Fun;
-}
-
-namespace fuzzer {
-
-ExternalFunctions::ExternalFunctions() {
-#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
- this->NAME = GetFnPtr<decltype(::NAME)>(::NAME, ::NAME##Def, #NAME, WARN);
-
-#include "FuzzerExtFunctions.def"
-
-#undef EXT_FUNC
-}
-
-} // namespace fuzzer
-
-#endif // LIBFUZZER_WINDOWS
diff --git a/lib/fuzzer/FuzzerExtFunctionsWindows.cpp b/lib/fuzzer/FuzzerExtFunctionsWindows.cpp
new file mode 100644
index 000000000000..b01871439ca0
--- /dev/null
+++ b/lib/fuzzer/FuzzerExtFunctionsWindows.cpp
@@ -0,0 +1,83 @@
+//=== FuzzerExtWindows.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 of FuzzerExtFunctions for Windows. Uses alternatename when
+// compiled with MSVC. Uses weak aliases when compiled with clang. Unfortunately
+// the method each compiler supports is not supported by the other.
+//===----------------------------------------------------------------------===//
+#include "FuzzerDefs.h"
+#if LIBFUZZER_WINDOWS
+
+#include "FuzzerExtFunctions.h"
+#include "FuzzerIO.h"
+
+using namespace fuzzer;
+
+// Intermediate macro to ensure the parameter is expanded before stringified.
+#define STRINGIFY_(A) #A
+#define STRINGIFY(A) STRINGIFY_(A)
+
+#if LIBFUZZER_MSVC
+// Copied from compiler-rt/lib/sanitizer_common/sanitizer_win_defs.h
+#if defined(_M_IX86) || defined(__i386__)
+#define WIN_SYM_PREFIX "_"
+#else
+#define WIN_SYM_PREFIX
+#endif
+
+// Declare external functions as having alternativenames, so that we can
+// determine if they are not defined.
+#define EXTERNAL_FUNC(Name, Default) \
+ __pragma(comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY( \
+ Name) "=" WIN_SYM_PREFIX STRINGIFY(Default)))
+#else
+// Declare external functions as weak to allow them to default to a specified
+// function if not defined explicitly. We must use weak symbols because clang's
+// support for alternatename is not 100%, see
+// https://bugs.llvm.org/show_bug.cgi?id=40218 for more details.
+#define EXTERNAL_FUNC(Name, Default) \
+ __attribute__((weak, alias(STRINGIFY(Default))))
+#endif // LIBFUZZER_MSVC
+
+extern "C" {
+#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
+ RETURN_TYPE NAME##Def FUNC_SIG { \
+ Printf("ERROR: Function \"%s\" not defined.\n", #NAME); \
+ exit(1); \
+ } \
+ EXTERNAL_FUNC(NAME, NAME##Def) RETURN_TYPE NAME FUNC_SIG;
+
+#include "FuzzerExtFunctions.def"
+
+#undef EXT_FUNC
+}
+
+template <typename T>
+static T *GetFnPtr(T *Fun, T *FunDef, const char *FnName, bool WarnIfMissing) {
+ if (Fun == FunDef) {
+ if (WarnIfMissing)
+ Printf("WARNING: Failed to find function \"%s\".\n", FnName);
+ return nullptr;
+ }
+ return Fun;
+}
+
+namespace fuzzer {
+
+ExternalFunctions::ExternalFunctions() {
+#define EXT_FUNC(NAME, RETURN_TYPE, FUNC_SIG, WARN) \
+ this->NAME = GetFnPtr<decltype(::NAME)>(::NAME, ::NAME##Def, #NAME, WARN);
+
+#include "FuzzerExtFunctions.def"
+
+#undef EXT_FUNC
+}
+
+} // namespace fuzzer
+
+#endif // LIBFUZZER_WINDOWS
diff --git a/lib/fuzzer/FuzzerFlags.def b/lib/fuzzer/FuzzerFlags.def
index ba04bc25fd45..91281c979c50 100644
--- a/lib/fuzzer/FuzzerFlags.def
+++ b/lib/fuzzer/FuzzerFlags.def
@@ -17,7 +17,7 @@ FUZZER_FLAG_INT(runs, -1,
FUZZER_FLAG_INT(max_len, 0, "Maximum length of the test input. "
"If 0, libFuzzer tries to guess a good value based on the corpus "
"and reports it. ")
-FUZZER_FLAG_INT(len_control, 1000, "Try generating small inputs first, "
+FUZZER_FLAG_INT(len_control, 100, "Try generating small inputs first, "
"then try larger inputs over time. Specifies the rate at which the length "
"limit is increased (smaller == faster). If 0, immediately try inputs with "
"size up to max_len.")
@@ -110,15 +110,6 @@ FUZZER_FLAG_INT(print_coverage, 0, "If 1, print coverage information as text"
FUZZER_FLAG_INT(dump_coverage, 0, "Deprecated."
" If 1, dump coverage information as a"
" .sancov file at exit.")
-FUZZER_FLAG_INT(handle_unstable, 0, "Experimental."
- " Executes every input 3 times in total if a unique feature"
- " is found during the first execution."
- " If 1, we only use the minimum hit count from the 3 runs"
- " to determine whether an input is interesting."
- " If 2, we disregard edges that are found unstable for"
- " feature collection.")
-FUZZER_FLAG_INT(print_unstable_stats, 0, "Experimental."
- " If 1, print unstable statistics at exit.")
FUZZER_FLAG_INT(handle_segv, 1, "If 1, try to intercept SIGSEGV.")
FUZZER_FLAG_INT(handle_bus, 1, "If 1, try to intercept SIGBUS.")
FUZZER_FLAG_INT(handle_abrt, 1, "If 1, try to intercept SIGABRT.")
@@ -162,4 +153,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")
diff --git a/lib/fuzzer/FuzzerIO.cpp b/lib/fuzzer/FuzzerIO.cpp
index f3ead0ec5357..c4c31e82471f 100644
--- a/lib/fuzzer/FuzzerIO.cpp
+++ b/lib/fuzzer/FuzzerIO.cpp
@@ -31,7 +31,7 @@ long GetEpoch(const std::string &Path) {
}
Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
- std::ifstream T(Path);
+ std::ifstream T(Path, std::ios::binary);
if (ExitOnError && !T) {
Printf("No such directory: %s; exiting\n", Path.c_str());
exit(1);
@@ -51,7 +51,7 @@ Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
}
std::string FileToString(const std::string &Path) {
- std::ifstream T(Path);
+ std::ifstream T(Path, std::ios::binary);
return std::string((std::istreambuf_iterator<char>(T)),
std::istreambuf_iterator<char>());
}
@@ -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 6d7757435b7b..b4a68190e780 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 17e884d3c4c3..401b4cbbf74f 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 74853646b213..75dcaf72a940 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/FuzzerInternal.h b/lib/fuzzer/FuzzerInternal.h
index bfc898248adb..a7fdc89cba5f 100644
--- a/lib/fuzzer/FuzzerInternal.h
+++ b/lib/fuzzer/FuzzerInternal.h
@@ -67,7 +67,6 @@ public:
static void StaticGracefulExitCallback();
void ExecuteCallback(const uint8_t *Data, size_t Size);
- void CheckForUnstableCounters(const uint8_t *Data, size_t Size);
bool RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile = false,
InputInfo *II = nullptr, bool *FoundUniqFeatures = nullptr);
diff --git a/lib/fuzzer/FuzzerLoop.cpp b/lib/fuzzer/FuzzerLoop.cpp
index 4bc88365a0b9..a32a30723004 100644
--- a/lib/fuzzer/FuzzerLoop.cpp
+++ b/lib/fuzzer/FuzzerLoop.cpp
@@ -275,7 +275,8 @@ NO_SANITIZE_MEMORY
void Fuzzer::AlarmCallback() {
assert(Options.UnitTimeoutSec > 0);
// In Windows Alarm callback is executed by a different thread.
-#if !LIBFUZZER_WINDOWS
+ // NetBSD's current behavior needs this change too.
+#if !LIBFUZZER_WINDOWS && !LIBFUZZER_NETBSD
if (!InFuzzingThread())
return;
#endif
@@ -354,13 +355,10 @@ void Fuzzer::PrintStats(const char *Where, const char *End, size_t Units) {
void Fuzzer::PrintFinalStats() {
if (Options.PrintCoverage)
TPC.PrintCoverage();
- if (Options.PrintUnstableStats)
- TPC.PrintUnstableStats();
if (Options.DumpCoverage)
TPC.DumpCoverage();
if (Options.PrintCorpusStats)
Corpus.PrintStats();
- if (Options.PrintMutationStats) MD.PrintMutationStats();
if (!Options.PrintFinalStats)
return;
size_t ExecPerSec = execPerSec();
@@ -449,34 +447,6 @@ void Fuzzer::PrintPulseAndReportSlowInput(const uint8_t *Data, size_t Size) {
}
}
-void Fuzzer::CheckForUnstableCounters(const uint8_t *Data, size_t Size) {
- auto CBSetupAndRun = [&]() {
- ScopedEnableMsanInterceptorChecks S;
- UnitStartTime = system_clock::now();
- TPC.ResetMaps();
- RunningUserCallback = true;
- CB(Data, Size);
- RunningUserCallback = false;
- UnitStopTime = system_clock::now();
- };
-
- // Copy original run counters into our unstable counters
- TPC.InitializeUnstableCounters();
-
- // First Rerun
- CBSetupAndRun();
- TPC.UpdateUnstableCounters(Options.HandleUnstable);
-
- // Second Rerun
- CBSetupAndRun();
- TPC.UpdateUnstableCounters(Options.HandleUnstable);
-
- // Move minimum hit counts back to ModuleInline8bitCounters
- if (Options.HandleUnstable == TracePC::MinUnstable ||
- Options.HandleUnstable == TracePC::ZeroUnstable)
- TPC.ApplyUnstableCounters();
-}
-
bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
InputInfo *II, bool *FoundUniqFeatures) {
if (!Size)
@@ -487,17 +457,6 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
UniqFeatureSetTmp.clear();
size_t FoundUniqFeaturesOfII = 0;
size_t NumUpdatesBefore = Corpus.NumFeatureUpdates();
- bool NewFeaturesUnstable = false;
-
- if (Options.HandleUnstable || Options.PrintUnstableStats) {
- TPC.CollectFeatures([&](size_t Feature) {
- if (Corpus.IsFeatureNew(Feature, Size, Options.Shrink))
- NewFeaturesUnstable = true;
- });
- if (NewFeaturesUnstable)
- CheckForUnstableCounters(Data, Size);
- }
-
TPC.CollectFeatures([&](size_t Feature) {
if (Corpus.AddFeature(Feature, Size, Options.Shrink))
UniqFeatureSetTmp.push_back(Feature);
@@ -506,12 +465,10 @@ bool Fuzzer::RunOne(const uint8_t *Data, size_t Size, bool MayDeleteFile,
II->UniqFeatureSet.end(), Feature))
FoundUniqFeaturesOfII++;
});
-
if (FoundUniqFeatures)
*FoundUniqFeatures = FoundUniqFeaturesOfII;
PrintPulseAndReportSlowInput(Data, Size);
size_t NumNewFeatures = Corpus.NumFeatureUpdates() - NumUpdatesBefore;
-
if (NumNewFeatures) {
TPC.UpdateObservedPCs();
Corpus.AddToCorpus({Data, Data + Size}, NumNewFeatures, MayDeleteFile,
diff --git a/lib/fuzzer/FuzzerMutate.cpp b/lib/fuzzer/FuzzerMutate.cpp
index ff076cca683f..142b2b0b0014 100644
--- a/lib/fuzzer/FuzzerMutate.cpp
+++ b/lib/fuzzer/FuzzerMutate.cpp
@@ -30,36 +30,34 @@ MutationDispatcher::MutationDispatcher(Random &Rand,
DefaultMutators.insert(
DefaultMutators.begin(),
{
- {&MutationDispatcher::Mutate_EraseBytes, "EraseBytes", 0, 0},
- {&MutationDispatcher::Mutate_InsertByte, "InsertByte", 0, 0},
+ {&MutationDispatcher::Mutate_EraseBytes, "EraseBytes"},
+ {&MutationDispatcher::Mutate_InsertByte, "InsertByte"},
{&MutationDispatcher::Mutate_InsertRepeatedBytes,
- "InsertRepeatedBytes", 0, 0},
- {&MutationDispatcher::Mutate_ChangeByte, "ChangeByte", 0, 0},
- {&MutationDispatcher::Mutate_ChangeBit, "ChangeBit", 0, 0},
- {&MutationDispatcher::Mutate_ShuffleBytes, "ShuffleBytes", 0, 0},
- {&MutationDispatcher::Mutate_ChangeASCIIInteger, "ChangeASCIIInt", 0,
- 0},
- {&MutationDispatcher::Mutate_ChangeBinaryInteger, "ChangeBinInt", 0,
- 0},
- {&MutationDispatcher::Mutate_CopyPart, "CopyPart", 0, 0},
- {&MutationDispatcher::Mutate_CrossOver, "CrossOver", 0, 0},
+ "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", 0, 0},
+ "ManualDict"},
{&MutationDispatcher::Mutate_AddWordFromPersistentAutoDictionary,
- "PersAutoDict", 0, 0},
+ "PersAutoDict"},
});
if(Options.UseCmp)
DefaultMutators.push_back(
- {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP", 0, 0});
+ {&MutationDispatcher::Mutate_AddWordFromTORC, "CMP"});
if (EF->LLVMFuzzerCustomMutator)
- Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom", 0, 0});
+ Mutators.push_back({&MutationDispatcher::Mutate_Custom, "Custom"});
else
Mutators = DefaultMutators;
if (EF->LLVMFuzzerCustomCrossOver)
Mutators.push_back(
- {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver", 0, 0});
+ {&MutationDispatcher::Mutate_CustomCrossOver, "CustomCrossOver"});
}
static char RandCh(Random &Rand) {
@@ -466,7 +464,6 @@ void MutationDispatcher::RecordSuccessfulMutationSequence() {
if (!PersistentAutoDictionary.ContainsWord(DE->GetW()))
PersistentAutoDictionary.push_back({DE->GetW(), 1});
}
- RecordUsefulMutations();
}
void MutationDispatcher::PrintRecommendedDictionary() {
@@ -487,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) {
@@ -515,13 +513,12 @@ size_t MutationDispatcher::MutateImpl(uint8_t *Data, size_t Size,
// in which case they will return 0.
// Try several times before returning un-mutated data.
for (int Iter = 0; Iter < 100; Iter++) {
- auto 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;
}
}
@@ -562,21 +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: ");
- for (size_t i = 0; i < Mutators.size(); i++) {
- double UsefulPercentage =
- Mutators[i].TotalCount
- ? (100.0 * Mutators[i].UsefulCount) / Mutators[i].TotalCount
- : 0;
- Printf("%.3f", UsefulPercentage);
- if (i < Mutators.size() - 1) Printf(",");
- }
- Printf("\n");
-}
-
} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerMutate.h b/lib/fuzzer/FuzzerMutate.h
index 828ecc13d866..a51c7fb44d46 100644
--- a/lib/fuzzer/FuzzerMutate.h
+++ b/lib/fuzzer/FuzzerMutate.h
@@ -93,16 +93,10 @@ public:
Random &GetRand() { return Rand; }
- void PrintMutationStats();
-
- void RecordUsefulMutations();
-
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,
@@ -141,7 +135,6 @@ public:
Dictionary PersistentAutoDictionary;
Vector<DictionaryEntry *> CurrentDictionaryEntrySequence;
- Vector<Mutator *> CurrentMutatorSequence;
static const size_t kCmpDictionaryEntriesDequeSize = 16;
DictionaryEntry CmpDictionaryEntriesDeque[kCmpDictionaryEntriesDequeSize];
@@ -156,6 +149,7 @@ public:
Vector<Mutator> Mutators;
Vector<Mutator> DefaultMutators;
+ Vector<Mutator> CurrentMutatorSequence;
};
} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerOptions.h b/lib/fuzzer/FuzzerOptions.h
index ce39c0876cd7..ab90df82a63d 100644
--- a/lib/fuzzer/FuzzerOptions.h
+++ b/lib/fuzzer/FuzzerOptions.h
@@ -52,11 +52,8 @@ struct FuzzingOptions {
bool PrintNewCovPcs = false;
int PrintNewCovFuncs = 0;
bool PrintFinalStats = false;
- bool PrintMutationStats = false;
bool PrintCorpusStats = false;
bool PrintCoverage = false;
- bool PrintUnstableStats = false;
- int HandleUnstable = 0;
bool DumpCoverage = false;
bool DetectLeaks = true;
int PurgeAllocatorIntervalSec = 1;
diff --git a/lib/fuzzer/FuzzerTracePC.cpp b/lib/fuzzer/FuzzerTracePC.cpp
index 29ffc8e34fc0..80b33105bb22 100644
--- a/lib/fuzzer/FuzzerTracePC.cpp
+++ b/lib/fuzzer/FuzzerTracePC.cpp
@@ -13,6 +13,8 @@
//===----------------------------------------------------------------------===//
#include "FuzzerTracePC.h"
+#include "FuzzerBuiltins.h"
+#include "FuzzerBuiltinsMsvc.h"
#include "FuzzerCorpus.h"
#include "FuzzerDefs.h"
#include "FuzzerDictionary.h"
@@ -32,8 +34,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 {
@@ -57,49 +58,6 @@ size_t TracePC::GetTotalPCCoverage() {
return Res;
}
-template<class CallBack>
-void TracePC::IterateInline8bitCounters(CallBack CB) const {
- if (NumInline8bitCounters && NumInline8bitCounters == NumPCsInPCTables) {
- size_t CounterIdx = 0;
- for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
- uint8_t *Beg = ModuleCounters[i].Start;
- size_t Size = ModuleCounters[i].Stop - Beg;
- assert(Size == (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
- for (size_t j = 0; j < Size; j++, CounterIdx++)
- CB(i, j, CounterIdx);
- }
- }
-}
-
-// Initializes unstable counters by copying Inline8bitCounters to unstable
-// counters.
-void TracePC::InitializeUnstableCounters() {
- IterateInline8bitCounters([&](int i, int j, int UnstableIdx) {
- UnstableCounters[UnstableIdx].Counter = ModuleCounters[i].Start[j];
- });
-}
-
-// Compares the current counters with counters from previous runs
-// and records differences as unstable edges.
-void TracePC::UpdateUnstableCounters(int UnstableMode) {
- IterateInline8bitCounters([&](int i, int j, int UnstableIdx) {
- if (ModuleCounters[i].Start[j] != UnstableCounters[UnstableIdx].Counter) {
- UnstableCounters[UnstableIdx].IsUnstable = true;
- if (UnstableMode == ZeroUnstable)
- UnstableCounters[UnstableIdx].Counter = 0;
- else if (UnstableMode == MinUnstable)
- UnstableCounters[UnstableIdx].Counter = std::min(
- ModuleCounters[i].Start[j], UnstableCounters[UnstableIdx].Counter);
- }
- });
-}
-
-// Moves the minimum hit counts to ModuleCounters.
-void TracePC::ApplyUnstableCounters() {
- IterateInline8bitCounters([&](int i, int j, int UnstableIdx) {
- ModuleCounters[i].Start[j] = UnstableCounters[UnstableIdx].Counter;
- });
-}
void TracePC::HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop) {
if (Start == Stop) return;
@@ -185,11 +143,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");
}
};
@@ -203,10 +192,15 @@ void TracePC::UpdateObservedPCs() {
if (NumPCsInPCTables) {
if (NumInline8bitCounters == NumPCsInPCTables) {
- IterateInline8bitCounters([&](int i, int j, int CounterIdx) {
- if (ModuleCounters[i].Start[j])
- Observe(ModulePCTable[i].Start[j]);
- });
+ for (size_t i = 0; i < NumModulesWithInline8bitCounters; i++) {
+ uint8_t *Beg = ModuleCounters[i].Start;
+ size_t Size = ModuleCounters[i].Stop - Beg;
+ assert(Size ==
+ (size_t)(ModulePCTable[i].Stop - ModulePCTable[i].Start));
+ for (size_t j = 0; j < Size; j++)
+ if (Beg[j])
+ Observe(ModulePCTable[i].Start[j]);
+ }
} else if (NumGuards == NumPCsInPCTables) {
size_t GuardIdx = 1;
for (size_t i = 0; i < NumModules; i++) {
@@ -224,22 +218,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++?
@@ -349,15 +332,6 @@ void TracePC::DumpCoverage() {
}
}
-void TracePC::PrintUnstableStats() {
- size_t count = 0;
- for (size_t i = 0; i < NumInline8bitCounters; i++)
- if (UnstableCounters[i].IsUnstable)
- count++;
- Printf("stat::stability_rate: %.2f\n",
- 100 - static_cast<float>(count * 100) / NumInline8bitCounters);
-}
-
// Value profile.
// We keep track of various values that affect control flow.
// These values are inserted into a bit-set-based hash map.
@@ -401,20 +375,14 @@ ATTRIBUTE_TARGET_POPCNT ALWAYS_INLINE
ATTRIBUTE_NO_SANITIZE_ALL
void TracePC::HandleCmp(uintptr_t PC, T Arg1, T Arg2) {
uint64_t ArgXor = Arg1 ^ Arg2;
- uint64_t ArgDistance = __builtin_popcountll(ArgXor) + 1; // [1,65]
- uintptr_t Idx = ((PC & 4095) + 1) * ArgDistance;
if (sizeof(T) == 4)
TORC4.Insert(ArgXor, Arg1, Arg2);
else if (sizeof(T) == 8)
TORC8.Insert(ArgXor, Arg1, Arg2);
- // TODO: remove these flags and instead use all metrics at once.
- if (UseValueProfileMask & 1)
- ValueProfileMap.AddValue(Idx);
- if (UseValueProfileMask & 2)
- ValueProfileMap.AddValue(
- PC * 64 + (Arg1 == Arg2 ? 0 : __builtin_clzll(Arg1 - Arg2) + 1));
- if (UseValueProfileMask & 4) // alternative way to use the hamming distance
- ValueProfileMap.AddValue(PC * 64 + ArgDistance);
+ uint64_t HammingDistance = Popcountll(ArgXor); // [0,64]
+ uint64_t AbsoluteDistance = (Arg1 == Arg2 ? 0 : Clzll(Arg1 - Arg2) + 1);
+ ValueProfileMap.AddValue(PC * 128 + HammingDistance);
+ ValueProfileMap.AddValue(PC * 128 + 64 + AbsoluteDistance);
}
static size_t InternalStrnlen(const char *S, size_t MaxLen) {
@@ -455,7 +423,7 @@ extern "C" {
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
uint32_t Idx = *Guard;
__sancov_trace_pc_pcs[Idx] = PC;
__sancov_trace_pc_guard_8bit_counters[Idx]++;
@@ -466,7 +434,7 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
void __sanitizer_cov_trace_pc() {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
uintptr_t Idx = PC & (((uintptr_t)1 << fuzzer::TracePC::kTracePcBits) - 1);
__sancov_trace_pc_pcs[Idx] = PC;
__sancov_trace_pc_guard_8bit_counters[Idx]++;
@@ -491,7 +459,7 @@ void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,
ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
void __sanitizer_cov_trace_pc_indir(uintptr_t Callee) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCallerCallee(PC, Callee);
}
@@ -499,7 +467,7 @@ ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_cmp8(uint64_t Arg1, uint64_t Arg2) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
@@ -510,7 +478,7 @@ ATTRIBUTE_TARGET_POPCNT
// the behaviour of __sanitizer_cov_trace_cmp[1248] ones. This, however,
// should be changed later to make full use of instrumentation.
void __sanitizer_cov_trace_const_cmp8(uint64_t Arg1, uint64_t Arg2) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
@@ -518,7 +486,7 @@ ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_cmp4(uint32_t Arg1, uint32_t Arg2) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
@@ -526,7 +494,7 @@ ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_const_cmp4(uint32_t Arg1, uint32_t Arg2) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
@@ -534,7 +502,7 @@ ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_cmp2(uint16_t Arg1, uint16_t Arg2) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
@@ -542,7 +510,7 @@ ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_const_cmp2(uint16_t Arg1, uint16_t Arg2) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
@@ -550,7 +518,7 @@ ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_cmp1(uint8_t Arg1, uint8_t Arg2) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
@@ -558,7 +526,7 @@ ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_const_cmp1(uint8_t Arg1, uint8_t Arg2) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Arg1, Arg2);
}
@@ -572,7 +540,7 @@ void __sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases) {
// Skip the most common and the most boring case.
if (Vals[N - 1] < 256 && Val < 256)
return;
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
size_t i;
uint64_t Token = 0;
for (i = 0; i < N; i++) {
@@ -593,7 +561,7 @@ ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_div4(uint32_t Val) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Val, (uint32_t)0);
}
@@ -601,7 +569,7 @@ ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_div8(uint64_t Val) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Val, (uint64_t)0);
}
@@ -609,7 +577,7 @@ ATTRIBUTE_INTERFACE
ATTRIBUTE_NO_SANITIZE_ALL
ATTRIBUTE_TARGET_POPCNT
void __sanitizer_cov_trace_gep(uintptr_t Idx) {
- uintptr_t PC = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
+ uintptr_t PC = reinterpret_cast<uintptr_t>(GET_CALLER_PC());
fuzzer::TPC.HandleCmp(PC, Idx, (uintptr_t)0);
}
diff --git a/lib/fuzzer/FuzzerTracePC.h b/lib/fuzzer/FuzzerTracePC.h
index 097ba69bdc08..46d6c24887fd 100644
--- a/lib/fuzzer/FuzzerTracePC.h
+++ b/lib/fuzzer/FuzzerTracePC.h
@@ -74,11 +74,6 @@ class TracePC {
// How many bits of PC are used from __sanitizer_cov_trace_pc.
static const size_t kTracePcBits = 18;
- enum HandleUnstableOptions {
- MinUnstable = 1,
- ZeroUnstable = 2,
- };
-
void HandleInit(uint32_t *Start, uint32_t *Stop);
void HandleInline8bitCountersInit(uint8_t *Start, uint8_t *Stop);
void HandlePCsInit(const uintptr_t *Start, const uintptr_t *Stop);
@@ -109,7 +104,6 @@ class TracePC {
void PrintCoverage();
void DumpCoverage();
- void PrintUnstableStats();
template<class CallBack>
void IterateCoveredFunctions(CallBack CB);
@@ -142,18 +136,7 @@ class TracePC {
void SetFocusFunction(const std::string &FuncName);
bool ObservedFocusFunction();
- void InitializeUnstableCounters();
- void UpdateUnstableCounters(int UnstableMode);
- void ApplyUnstableCounters();
-
private:
- struct UnstableEdge {
- uint8_t Counter;
- bool IsUnstable;
- };
-
- UnstableEdge UnstableCounters[kNumPCs];
-
bool UseCounters = false;
uint32_t UseValueProfileMask = false;
bool DoPrintNewPCs = false;
@@ -185,9 +168,6 @@ private:
Set<uintptr_t> ObservedPCs;
std::unordered_map<uintptr_t, uintptr_t> ObservedFuncs; // PC => Counter.
- template <class Callback>
- void IterateInline8bitCounters(Callback CB) const;
-
std::pair<size_t, size_t> FocusFunction = {-1, -1}; // Module and PC IDs.
ValueBitMap ValueProfileMap;
diff --git a/lib/fuzzer/FuzzerUtil.h b/lib/fuzzer/FuzzerUtil.h
index 8c5c57c3ab83..d2f1d5de4267 100644
--- a/lib/fuzzer/FuzzerUtil.h
+++ b/lib/fuzzer/FuzzerUtil.h
@@ -12,8 +12,10 @@
#ifndef LLVM_FUZZER_UTIL_H
#define LLVM_FUZZER_UTIL_H
-#include "FuzzerDefs.h"
+#include "FuzzerBuiltins.h"
+#include "FuzzerBuiltinsMsvc.h"
#include "FuzzerCommand.h"
+#include "FuzzerDefs.h"
namespace fuzzer {
@@ -84,7 +86,7 @@ std::string SearchRegexCmd(const std::string &Regex);
size_t SimpleFastHash(const uint8_t *Data, size_t Size);
-inline uint32_t Log(uint32_t X) { return 32 - __builtin_clz(X) - 1; }
+inline uint32_t Log(uint32_t X) { return 32 - Clz(X) - 1; }
} // namespace fuzzer
diff --git a/lib/fuzzer/FuzzerUtilFuchsia.cpp b/lib/fuzzer/FuzzerUtilFuchsia.cpp
index cd2bb7438e9d..cd48fefef352 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 8227e778ea0a..393b4768be7e 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/afl/afl_driver.cpp b/lib/fuzzer/afl/afl_driver.cpp
index fa494c03bde0..5a10c0d27f3a 100644
--- a/lib/fuzzer/afl/afl_driver.cpp
+++ b/lib/fuzzer/afl/afl_driver.cpp
@@ -289,7 +289,7 @@ extern "C" size_t LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize) {
// Execute any files provided as parameters.
int ExecuteFilesOnyByOne(int argc, char **argv) {
for (int i = 1; i < argc; i++) {
- std::ifstream in(argv[i]);
+ std::ifstream in(argv[i], std::ios::binary);
in.seekg(0, in.end);
size_t length = in.tellg();
in.seekg (0, in.beg);
diff --git a/lib/fuzzer/tests/CMakeLists.txt b/lib/fuzzer/tests/CMakeLists.txt
index ed5807168301..6abb72def3e1 100644
--- a/lib/fuzzer/tests/CMakeLists.txt
+++ b/lib/fuzzer/tests/CMakeLists.txt
@@ -1,3 +1,5 @@
+include(CompilerRTCompile)
+
set(LIBFUZZER_UNITTEST_CFLAGS
${COMPILER_RT_UNITTEST_CFLAGS}
${COMPILER_RT_GTEST_CFLAGS}
@@ -17,12 +19,12 @@ 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)
-else()
+elseif(NOT WIN32)
list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lstdc++ -lpthread)
endif()
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND COMPILER_RT_LIBCXX_PATH)
- list(APPEND LIBFUZZER_UNITTEST_CFLAGS -nostdinc++ -D_LIBCPP_ABI_VERSION=Fuzzer)
+ list(APPEND LIBFUZZER_UNITTEST_CFLAGS -nostdinc++)
endif()
if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST FUZZER_SUPPORTED_ARCH)
@@ -45,7 +47,7 @@ if(COMPILER_RT_DEFAULT_TARGET_ARCH IN_LIST FUZZER_SUPPORTED_ARCH)
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux" AND COMPILER_RT_LIBCXX_PATH)
set(LIBFUZZER_TEST_RUNTIME_DEPS libcxx_fuzzer_${arch}-build)
- set(LIBFUZZER_TEST_RUNTIME_CFLAGS -isystem ${COMPILER_RT_LIBCXX_PATH}/include)
+ set(LIBFUZZER_TEST_RUNTIME_CFLAGS -isystem ${LIBCXX_${arch}_PREFIX}/include/c++/v1)
set(LIBFUZZER_TEST_RUNTIME_LINK_FLAGS ${LIBCXX_${arch}_PREFIX}/lib/libc++.a)
endif()
diff --git a/lib/fuzzer/tests/FuzzerUnittest.cpp b/lib/fuzzer/tests/FuzzerUnittest.cpp
index e3b06702603d..7cdd44582329 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 42bf4366f192..20ab94dc0446 100644
--- a/lib/hwasan/CMakeLists.txt
+++ b/lib/hwasan/CMakeLists.txt
@@ -7,9 +7,11 @@ set(HWASAN_RTL_SOURCES
hwasan_dynamic_shadow.cc
hwasan_interceptors.cc
hwasan_linux.cc
+ hwasan_memintrinsics.cc
hwasan_poisoning.cc
hwasan_report.cc
hwasan_thread.cc
+ hwasan_thread_list.cc
)
set(HWASAN_RTL_CXX_SOURCES
@@ -25,8 +27,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 +42,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 +61,10 @@ 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)
+
+if (TARGET cxx-headers OR HAVE_LIBCXX)
+ set(HWASAN_DEPS cxx-headers)
+endif()
# Static runtime library.
add_compiler_rt_component(hwasan)
@@ -56,23 +73,31 @@ 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}
+ DEPS ${HWASAN_DEPS})
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}
+ DEPS ${HWASAN_DEPS})
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}
+ DEPS ${HWASAN_DEPS})
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}
+ DEPS ${HWASAN_DEPS})
foreach(arch ${HWASAN_SUPPORTED_ARCH})
add_compiler_rt_runtime(clang_rt.hwasan
@@ -121,6 +146,7 @@ foreach(arch ${HWASAN_SUPPORTED_ARCH})
RTSanitizerCommonCoverage
RTSanitizerCommonSymbolizer
RTUbsan
+ RTUbsan_cxx
# The only purpose of RTHWAsan_dynamic_version_script_dummy is to
# carry a dependency of the shared runtime on the version script.
# Replacing it with a straightforward
diff --git a/lib/hwasan/hwasan.cc b/lib/hwasan/hwasan.cc
index 7dab8249e3f7..e2bfea5e4226 100644
--- a/lib/hwasan/hwasan.cc
+++ b/lib/hwasan/hwasan.cc
@@ -13,19 +13,20 @@
//===----------------------------------------------------------------------===//
#include "hwasan.h"
-#include "hwasan_mapping.h"
+#include "hwasan_checks.h"
#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"
#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
#include "ubsan/ubsan_flags.h"
#include "ubsan/ubsan_init.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);
@@ -140,11 +159,6 @@ void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp,
request_fast_unwind);
}
-void PrintWarning(uptr pc, uptr bp) {
- GET_FATAL_STACK_TRACE_PC_BP(pc, bp);
- ReportInvalidAccess(&stack, 0);
-}
-
static void HWAsanCheckFailed(const char *file, int line, const char *cond,
u64 v1, u64 v2) {
Report("HWAddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file,
@@ -153,6 +167,84 @@ 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
+
+struct FrameDescription {
+ uptr PC;
+ const char *Descr;
+};
+
+struct FrameDescriptionArray {
+ FrameDescription *beg, *end;
+};
+
+static InternalMmapVectorNoCtor<FrameDescriptionArray> AllFrames;
+
+void InitFrameDescriptors(uptr b, uptr e) {
+ FrameDescription *beg = reinterpret_cast<FrameDescription *>(b);
+ FrameDescription *end = reinterpret_cast<FrameDescription *>(e);
+ if (beg == end)
+ return;
+ AllFrames.push_back({beg, end});
+ if (Verbosity())
+ for (FrameDescription *frame_descr = beg; frame_descr < end; frame_descr++)
+ Printf("Frame: %p %s\n", frame_descr->PC, frame_descr->Descr);
+}
+
+const char *GetStackFrameDescr(uptr pc) {
+ for (uptr i = 0, n = AllFrames.size(); i < n; i++)
+ for (auto p = AllFrames[i].beg; p < AllFrames[i].end; p++)
+ if (p->PC == pc)
+ return p->Descr;
+ return nullptr;
+}
+
} // namespace __hwasan
// Interface.
@@ -161,6 +253,20 @@ 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_frames(uptr beg, uptr end) {
+ InitFrameDescriptors(beg, end);
+}
+
void __hwasan_init() {
CHECK(!hwasan_init_is_running);
if (hwasan_inited) return;
@@ -177,18 +283,20 @@ void __hwasan_init() {
__sanitizer_set_report_path(common_flags()->log_path);
+ AndroidTestTlsSlot();
+
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();
+
+ SetPrintfAndReportCallback(AppendToErrorMessageBuffer);
+ // This may call libc -> needs initialized shadow.
+ AndroidLogInit();
InitializeInterceptors();
InstallDeadlySignalHandlers(HwasanOnDeadlySignal);
@@ -198,14 +306,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 +321,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 +337,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;
}
@@ -255,63 +365,6 @@ void __sanitizer_unaligned_store64(uu64 *p, u64 x) {
*p = x;
}
-template<unsigned X>
-__attribute__((always_inline))
-static void SigTrap(uptr p) {
-#if defined(__aarch64__)
- (void)p;
- // 0x900 is added to do not interfere with the kernel use of lower values of
- // brk immediate.
- // FIXME: Add a constraint to put the pointer into x0, the same as x86 branch.
- asm("brk %0\n\t" ::"n"(0x900 + X));
-#elif defined(__x86_64__)
- // INT3 + NOP DWORD ptr [EAX + X] to pass X to our signal handler, 5 bytes
- // total. The pointer is passed via rdi.
- // 0x40 is added as a safeguard, to help distinguish our trap from others and
- // to avoid 0 offsets in the command (otherwise it'll be reduced to a
- // different nop command, the three bytes one).
- asm volatile(
- "int3\n"
- "nopl %c0(%%rax)\n"
- :: "n"(0x40 + X), "D"(p));
-#else
- // FIXME: not always sigill.
- __builtin_trap();
-#endif
- // __builtin_unreachable();
-}
-
-enum class ErrorAction { Abort, Recover };
-enum class AccessType { Load, Store };
-
-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);
- if (UNLIKELY(ptr_tag != mem_tag)) {
- SigTrap<0x20 * (EA == ErrorAction::Recover) +
- 0x10 * (AT == AccessType::Store) + LogSize>(p);
- if (EA == ErrorAction::Abort) __builtin_unreachable();
- }
-}
-
-template <ErrorAction EA, AccessType AT>
-__attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p,
- uptr sz) {
- 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);
- for (tag_t *t = shadow_first; t <= shadow_last; ++t)
- if (UNLIKELY(ptr_tag != *t)) {
- SigTrap<0x20 * (EA == ErrorAction::Recover) +
- 0x10 * (AT == AccessType::Store) + 0xf>(p);
- if (EA == ErrorAction::Abort) __builtin_unreachable();
- }
-}
-
void __hwasan_loadN(uptr p, uptr sz) {
CheckAddressSized<ErrorAction::Abort, AccessType::Load>(p, sz);
}
@@ -392,10 +445,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 47d1d057a0dd..ce9e904c5c69 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,12 +70,13 @@ extern int hwasan_report_count;
bool ProtectRange(uptr beg, uptr end);
bool InitShadow();
+void InitThreads();
+void MadviseShadow();
char *GetProcSelfMaps();
void InitializeInterceptors();
void HwasanAllocatorInit();
void HwasanAllocatorThreadFinish();
-void HwasanDeallocate(StackTrace *stack, void *ptr);
void *hwasan_malloc(uptr size, StackTrace *stack);
void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack);
@@ -77,11 +87,13 @@ void *hwasan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack);
void *hwasan_memalign(uptr alignment, uptr size, StackTrace *stack);
int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
StackTrace *stack);
+void hwasan_free(void *ptr, StackTrace *stack);
void InstallTrapHandler();
void InstallAtExitHandler();
const char *GetStackOriginDescr(u32 id, uptr *pc);
+const char *GetStackFrameDescr(uptr pc);
void EnterSymbolizer();
void ExitSymbolizer();
@@ -92,8 +104,6 @@ struct SymbolizerScope {
~SymbolizerScope() { ExitSymbolizer(); }
};
-void PrintWarning(uptr pc, uptr bp);
-
void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp,
void *context, bool request_fast_unwind);
@@ -135,13 +145,17 @@ 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();
+
+void AppendToErrorMessageBuffer(const char *buffer);
+
+void AndroidTestTlsSlot();
+
} // namespace __hwasan
#define HWASAN_MALLOC_HOOK(ptr, size) \
diff --git a/lib/hwasan/hwasan_allocator.cc b/lib/hwasan/hwasan_allocator.cc
index c2b9b0b69589..8487ed7e1e55 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,30 +19,53 @@
#include "hwasan_allocator.h"
#include "hwasan_mapping.h"
#include "hwasan_thread.h"
-#include "hwasan_poisoning.h"
+#include "hwasan_report.h"
+
+#if HWASAN_WITH_INTERCEPTORS
+DEFINE_REAL(void *, realloc, void *ptr, uptr size)
+DEFINE_REAL(void, free, void *ptr)
+#endif
namespace __hwasan {
-enum {
- CHUNK_INVALID = 0,
- CHUNK_FREE = 1,
- CHUNK_ALLOCATED = 2
-};
+static Allocator allocator;
+static AllocatorCache fallback_allocator_cache;
+static SpinMutex fallback_mutex;
+static atomic_uint8_t hwasan_allocator_tagging_enabled;
+
+static const tag_t kFallbackAllocTag = 0xBB;
+static const tag_t kFallbackFreeTag = 0xBC;
-struct Metadata {
- u64 state : 2;
- u64 requested_size : 62;
- u32 alloc_context_id;
- u32 free_context_id;
+enum RightAlignMode {
+ kRightAlignNever,
+ kRightAlignSometimes,
+ kRightAlignAlways
};
-bool HwasanChunkView::IsValid() const {
- return metadata_ && metadata_->state != CHUNK_INVALID;
-}
+// These two variables are initialized from flags()->malloc_align_right
+// in HwasanAllocatorInit and are never changed afterwards.
+static RightAlignMode right_align_mode = kRightAlignNever;
+static bool right_align_8 = false;
+
+// Initialized in HwasanAllocatorInit, an never changed.
+static ALIGNED(16) u8 tail_magic[kShadowAlignment];
+
bool HwasanChunkView::IsAllocated() const {
- return metadata_ && metadata_->state == CHUNK_ALLOCATED;
+ return metadata_ && metadata_->alloc_context_id && metadata_->requested_size;
+}
+
+// Aligns the 'addr' right to the granule boundary.
+static uptr AlignRight(uptr addr, uptr requested_size) {
+ uptr tail_size = requested_size % kShadowAlignment;
+ if (!tail_size) return addr;
+ if (right_align_8)
+ return tail_size > 8 ? addr : addr + 8;
+ return addr + kShadowAlignment - tail_size;
}
+
uptr HwasanChunkView::Beg() const {
+ if (metadata_ && metadata_->right_aligned)
+ return AlignRight(block_, metadata_->requested_size);
return block_;
}
uptr HwasanChunkView::End() const {
@@ -58,88 +77,79 @@ 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;
+uptr HwasanChunkView::ActualSize() const {
+ return allocator.GetActuallyAllocatedSize(reinterpret_cast<void *>(block_));
+}
-static Allocator allocator;
-static AllocatorCache fallback_allocator_cache;
-static SpinMutex fallback_mutex;
-static atomic_uint8_t hwasan_allocator_tagging_enabled;
+bool HwasanChunkView::FromSmallHeap() const {
+ return allocator.FromPrimary(reinterpret_cast<void *>(block_));
+}
-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);
SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
allocator.Init(common_flags()->allocator_release_to_os_interval_ms);
+ switch (flags()->malloc_align_right) {
+ case 0: break;
+ case 1:
+ right_align_mode = kRightAlignSometimes;
+ right_align_8 = false;
+ break;
+ case 2:
+ right_align_mode = kRightAlignAlways;
+ right_align_8 = false;
+ break;
+ case 8:
+ right_align_mode = kRightAlignSometimes;
+ right_align_8 = true;
+ break;
+ case 9:
+ right_align_mode = kRightAlignAlways;
+ right_align_8 = true;
+ break;
+ default:
+ Report("ERROR: unsupported value of malloc_align_right flag: %d\n",
+ flags()->malloc_align_right);
+ Die();
+ }
+ for (uptr i = 0; i < kShadowAlignment; i++)
+ tail_magic[i] = GetCurrentThread()->GenerateRandomTag();
}
-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 +163,18 @@ 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)
+ meta->right_aligned = false;
+ 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);
+ }
+ if (!right_align_mode)
+ internal_memcpy(reinterpret_cast<u8 *>(allocated) + orig_size, tail_magic,
+ size - orig_size);
void *user_ptr = allocated;
if (flags()->tag_in_malloc &&
@@ -165,74 +182,101 @@ static void *HwasanAllocate(StackTrace *stack, uptr size, uptr alignment,
user_ptr = (void *)TagMemoryAligned(
(uptr)user_ptr, size, t ? t->GenerateRandomTag() : kFallbackAllocTag);
+ if ((orig_size % kShadowAlignment) && (alignment <= kShadowAlignment) &&
+ right_align_mode) {
+ uptr as_uptr = reinterpret_cast<uptr>(user_ptr);
+ if (right_align_mode == kRightAlignAlways ||
+ GetTagFromPointer(as_uptr) & 1) { // use a tag bit as a random bit.
+ user_ptr = reinterpret_cast<void *>(AlignRight(as_uptr, orig_size));
+ meta->right_aligned = 1;
+ }
+ }
+
HWASAN_MALLOC_HOOK(user_ptr, size);
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;
+}
+
+static 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 *untagged_ptr = UntagPtr(tagged_ptr);
+ void *aligned_ptr = reinterpret_cast<void *>(
+ RoundDownTo(reinterpret_cast<uptr>(untagged_ptr), kShadowAlignment));
+ Metadata *meta =
+ reinterpret_cast<Metadata *>(allocator.GetMetaData(aligned_ptr));
+ uptr orig_size = meta->requested_size;
+ u32 free_context_id = StackDepotPut(*stack);
+ u32 alloc_context_id = meta->alloc_context_id;
+
+ // Check tail magic.
+ uptr tagged_size = TaggedSize(orig_size);
+ if (flags()->free_checks_tail_magic && !right_align_mode && orig_size) {
+ uptr tail_size = tagged_size - orig_size;
+ CHECK_LT(tail_size, kShadowAlignment);
+ void *tail_beg = reinterpret_cast<void *>(
+ reinterpret_cast<uptr>(aligned_ptr) + orig_size);
+ if (tail_size && internal_memcmp(tail_beg, tail_magic, tail_size))
+ ReportTailOverwritten(stack, reinterpret_cast<uptr>(tagged_ptr),
+ orig_size, tail_size, tail_magic);
+ }
- void *p = GetAddressFromPointer(user_ptr);
- Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(p));
- uptr size = meta->requested_size;
- meta->state = CHUNK_FREE;
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(TaggedSize(orig_size), (uptr)flags()->max_free_fill_size);
+ internal_memset(aligned_ptr, flags()->free_fill_byte, fill_size);
+ }
if (flags()->tag_in_free &&
atomic_load_relaxed(&hwasan_allocator_tagging_enabled))
- TagMemoryAligned((uptr)p, size,
+ TagMemoryAligned(reinterpret_cast<uptr>(aligned_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(), aligned_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, aligned_ptr);
}
}
-void *HwasanReallocate(StackTrace *stack, void *user_old_p, 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;
+static void *HwasanReallocate(StackTrace *stack, void *tagged_ptr_old,
+ uptr new_size, uptr alignment) {
+ 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);
}
- 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);
- }
- return new_p;
+ return tagged_ptr_new;
}
-void *HwasanCalloc(StackTrace *stack, uptr nmemb, uptr size) {
+static void *HwasanCalloc(StackTrace *stack, uptr nmemb, uptr size) {
if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
if (AllocatorMayReturnNull())
return nullptr;
@@ -250,12 +294,18 @@ 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);
+ Metadata *b = (Metadata *)allocator.GetMetaData(untagged_ptr);
+ if (b->right_aligned) {
+ if (beg != reinterpret_cast<void *>(RoundDownTo(
+ reinterpret_cast<uptr>(untagged_ptr), kShadowAlignment)))
+ return 0;
+ } else {
+ if (beg != untagged_ptr) return 0;
+ }
return b->requested_size;
}
@@ -270,6 +320,14 @@ void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
void *hwasan_realloc(void *ptr, uptr size, StackTrace *stack) {
if (!ptr)
return SetErrnoOnNull(HwasanAllocate(stack, size, sizeof(u64), false));
+
+#if HWASAN_WITH_INTERCEPTORS
+ // A tag of 0 means that this is a system allocator allocation, so we must use
+ // the system allocator to realloc it.
+ if (!flags()->disable_allocator_tagging && GetTagFromPointer((uptr)ptr) == 0)
+ return REAL(realloc)(ptr, size);
+#endif
+
if (size == 0) {
HwasanDeallocate(stack, ptr);
return nullptr;
@@ -331,6 +389,17 @@ int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
return 0;
}
+void hwasan_free(void *ptr, StackTrace *stack) {
+#if HWASAN_WITH_INTERCEPTORS
+ // A tag of 0 means that this is a system allocator allocation, so we must use
+ // the system allocator to free it.
+ if (!flags()->disable_allocator_tagging && GetTagFromPointer((uptr)ptr) == 0)
+ return REAL(free)(ptr);
+#endif
+
+ return HwasanDeallocate(stack, ptr);
+}
+
} // namespace __hwasan
using namespace __hwasan;
@@ -340,6 +409,15 @@ void __hwasan_enable_allocator_tagging() {
}
void __hwasan_disable_allocator_tagging() {
+#if HWASAN_WITH_INTERCEPTORS
+ // Allocator tagging must be enabled for the system allocator fallback to work
+ // correctly. This means that we can't disable it at runtime if it was enabled
+ // at startup since that might result in our deallocations going to the system
+ // allocator. If tagging was disabled at startup we avoid this problem by
+ // disabling the fallback altogether.
+ CHECK(flags()->disable_allocator_tagging);
+#endif
+
atomic_store_relaxed(&hwasan_allocator_tagging_enabled, 0);
}
diff --git a/lib/hwasan/hwasan_allocator.h b/lib/hwasan/hwasan_allocator.h
index d025112e9773..6ab722fa6bef 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,73 @@
#ifndef HWASAN_ALLOCATOR_H
#define HWASAN_ALLOCATOR_H
+#include "interception/interception.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
+
+#if HWASAN_WITH_INTERCEPTORS
+DECLARE_REAL(void *, realloc, void *ptr, uptr size)
+DECLARE_REAL(void, free, void *ptr)
+#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 : 31; // sizes are < 2G.
+ u32 right_aligned : 1;
+ 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);
+ }
};
-struct Metadata;
+static const uptr kMaxAllowedMallocSize = 2UL << 30; // 2G
+
+struct AP64 {
+ static const uptr kSpaceBeg = ~0ULL;
+ static const uptr kSpaceSize = 0x2000000000ULL;
+ static const uptr kMetadataSize = sizeof(Metadata);
+ typedef __sanitizer::VeryDenseSizeClassMap SizeClassMap;
+ using AddressSpaceView = LocalAddressSpaceView;
+ typedef HwasanMapUnmapCallback MapUnmapCallback;
+ static const uptr kFlags = 0;
+};
+typedef SizeClassAllocator64<AP64> PrimaryAllocator;
+typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
+typedef LargeMmapAllocator<HwasanMapUnmapCallback> SecondaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
+ SecondaryAllocator> Allocator;
+
+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
+ uptr ActualSize() const; // Size allocated by the allocator.
u32 GetAllocStackId() const;
- u32 GetFreeStackId() const;
+ bool FromSmallHeap() const;
private:
uptr block_;
Metadata *const metadata_;
@@ -50,6 +88,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_checks.h b/lib/hwasan/hwasan_checks.h
new file mode 100644
index 000000000000..688b5e2bed82
--- /dev/null
+++ b/lib/hwasan/hwasan_checks.h
@@ -0,0 +1,80 @@
+//===-- hwasan_checks.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.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef HWASAN_CHECKS_H
+#define HWASAN_CHECKS_H
+
+#include "hwasan_mapping.h"
+
+namespace __hwasan {
+template <unsigned X>
+__attribute__((always_inline)) static void SigTrap(uptr p) {
+#if defined(__aarch64__)
+ (void)p;
+ // 0x900 is added to do not interfere with the kernel use of lower values of
+ // brk immediate.
+ // FIXME: Add a constraint to put the pointer into x0, the same as x86 branch.
+ asm("brk %0\n\t" ::"n"(0x900 + X));
+#elif defined(__x86_64__)
+ // INT3 + NOP DWORD ptr [EAX + X] to pass X to our signal handler, 5 bytes
+ // total. The pointer is passed via rdi.
+ // 0x40 is added as a safeguard, to help distinguish our trap from others and
+ // to avoid 0 offsets in the command (otherwise it'll be reduced to a
+ // different nop command, the three bytes one).
+ asm volatile(
+ "int3\n"
+ "nopl %c0(%%rax)\n" ::"n"(0x40 + X),
+ "D"(p));
+#else
+ // FIXME: not always sigill.
+ __builtin_trap();
+#endif
+ // __builtin_unreachable();
+}
+
+enum class ErrorAction { Abort, Recover };
+enum class AccessType { Load, Store };
+
+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 *)MemToShadow(ptr_raw);
+ if (UNLIKELY(ptr_tag != mem_tag)) {
+ SigTrap<0x20 * (EA == ErrorAction::Recover) +
+ 0x10 * (AT == AccessType::Store) + LogSize>(p);
+ if (EA == ErrorAction::Abort)
+ __builtin_unreachable();
+ }
+}
+
+template <ErrorAction EA, AccessType AT>
+__attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p,
+ uptr sz) {
+ if (sz == 0)
+ return;
+ tag_t ptr_tag = GetTagFromPointer(p);
+ uptr ptr_raw = p & ~kAddressTagMask;
+ 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) +
+ 0x10 * (AT == AccessType::Store) + 0xf>(p);
+ if (EA == ErrorAction::Abort)
+ __builtin_unreachable();
+ }
+}
+} // end namespace __hwasan
+
+#endif // HWASAN_CHECKS_H
diff --git a/lib/hwasan/hwasan_dynamic_shadow.cc b/lib/hwasan/hwasan_dynamic_shadow.cc
index 17338003aa65..87670f508283 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 16d60c4d8ba5..492d5bb98c27 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 c45781168d6c..b450ab9503f9 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,57 @@ 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.")
+
+// Rules for malloc alignment on aarch64:
+// * If the size is 16-aligned, then malloc should return 16-aligned memory.
+// * Otherwise, malloc should return 8-alignment memory.
+// So,
+// * If the size is 16-aligned, we don't need to do anything.
+// * Otherwise we don't have to obey 16-alignment, just the 8-alignment.
+// * We may want to break the 8-alignment rule to catch more buffer overflows
+// but this will break valid code in some rare cases, like this:
+// struct Foo {
+// // accessed via atomic instructions that require 8-alignment.
+// std::atomic<int64_t> atomic_stuff;
+// ...
+// char vla[1]; // the actual size of vla could be anything.
+// }
+// Which means that the safe values for malloc_align_right are 0, 8, 9,
+// and the values 1 and 2 may require changes in otherwise valid code.
+
+HWASAN_FLAG(
+ int, malloc_align_right, 0, // off by default
+ "HWASan allocator flag. "
+ "0 (default): allocations are always aligned left to 16-byte boundary; "
+ "1: allocations are sometimes aligned right to 1-byte boundary (risky); "
+ "2: allocations are always aligned right to 1-byte boundary (risky); "
+ "8: allocations are sometimes aligned right to 8-byte boundary; "
+ "9: allocations are always aligned right to 8-byte boundary."
+ )
+HWASAN_FLAG(bool, free_checks_tail_magic, 1,
+ "If set, free() will check the magic values "
+ "to the right of the allocated object "
+ "if the allocation size is not a divident of the granule size")
+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 66aab95db56f..fb0dcb8905c0 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
//
@@ -17,6 +17,7 @@
#include "interception/interception.h"
#include "hwasan.h"
+#include "hwasan_allocator.h"
#include "hwasan_mapping.h"
#include "hwasan_thread.h"
#include "hwasan_poisoning.h"
@@ -44,24 +45,19 @@ using __sanitizer::atomic_load;
using __sanitizer::atomic_store;
using __sanitizer::atomic_uintptr_t;
-DECLARE_REAL(SIZE_T, strlen, const char *s)
-DECLARE_REAL(SIZE_T, strnlen, const char *s, SIZE_T maxlen)
-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();
}
@@ -92,66 +88,24 @@ static void *AllocateFromLocalPool(uptr size_in_bytes) {
} while (0)
-
-#define HWASAN_READ_RANGE(ctx, offset, size) \
- CHECK_UNPOISONED(offset, size)
-#define HWASAN_WRITE_RANGE(ctx, offset, size) \
- CHECK_UNPOISONED(offset, size)
-
-
-
-// Check that [x, x+n) range is unpoisoned.
-#define CHECK_UNPOISONED_0(x, n) \
- do { \
- sptr __offset = __hwasan_test_shadow(x, n); \
- if (__hwasan::IsInSymbolizer()) break; \
- if (__offset >= 0) { \
- GET_CALLER_PC_BP_SP; \
- (void)sp; \
- ReportInvalidAccessInsideAddressRange(__func__, x, n, __offset); \
- __hwasan::PrintWarning(pc, bp); \
- if (__hwasan::flags()->halt_on_error) { \
- Printf("Exiting\n"); \
- Die(); \
- } \
- } \
- } while (0)
-
-// Check that [x, x+n) range is unpoisoned unless we are in a nested
-// interceptor.
-#define CHECK_UNPOISONED(x, n) \
- do { \
- if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \
- } while (0)
-
-#define CHECK_UNPOISONED_STRING_OF_LEN(x, len, n) \
- 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 +113,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);
+ hwasan_free(ptr, &stack);
}
-#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);
+ hwasan_free(ptr, &stack);
}
-#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) {
- return -1;
+int __sanitizer_mallopt(int cmd, int value) {
+ return 0;
}
-#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 +161,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 +179,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 +189,44 @@ 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);
-}
-
-extern "C" int pthread_attr_init(void *attr);
-extern "C" int pthread_attr_destroy(void *attr);
-
-static void *HwasanThreadStartFunc(void *arg) {
- HwasanThread *t = (HwasanThread *)arg;
- SetCurrentThread(t);
- return t->ThreadStart();
-}
-
-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;
- }
+#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);
- AdjustStackSize(attr);
-
- HwasanThread *t = HwasanThread::Create(callback, param);
+#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
- int res = REAL(pthread_create)(th, attr, HwasanThreadStartFunc, t);
- if (attr == &myattr)
- pthread_attr_destroy(&myattr);
+#if HWASAN_WITH_INTERCEPTORS && !defined(__aarch64__)
+INTERCEPTOR(int, pthread_create, void *th, void *attr,
+ void *(*callback)(void *), void *param) {
+ ScopedTaggingDisabler disabler;
+ int res = REAL(pthread_create)(UntagPtr(th), UntagPtr(attr),
+ callback, param);
return res;
}
+#endif
static void BeforeFork() {
StackDepotLockAll();
@@ -341,137 +258,21 @@ 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(fork);
+
+#if HWASAN_WITH_INTERCEPTORS
+#if !defined(__aarch64__)
+ INTERCEPT_FUNCTION(pthread_create);
+#endif
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);
+#endif
inited = 1;
}
diff --git a/lib/hwasan/hwasan_interface_internal.h b/lib/hwasan/hwasan_interface_internal.h
index b4e5c80904df..d3b2087d03c6 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;
@@ -33,6 +37,9 @@ using __sanitizer::u16;
using __sanitizer::u8;
SANITIZER_INTERFACE_ATTRIBUTE
+void __hwasan_init_frames(uptr, uptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
extern uptr __hwasan_shadow_memory_dynamic_address;
SANITIZER_INTERFACE_ATTRIBUTE
@@ -91,6 +98,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 +115,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 +141,66 @@ 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);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__hwasan_memcpy(void *dst, const void *src, uptr size);
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__hwasan_memset(void *s, int c, uptr n);
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__hwasan_memmove(void *dest, const void *src, uptr n);
} // extern "C"
#endif // HWASAN_INTERFACE_INTERNAL_H
diff --git a/lib/hwasan/hwasan_linux.cc b/lib/hwasan/hwasan_linux.cc
index 5ab98dca594d..5b0a8b4ac98f 100644
--- a/lib/hwasan/hwasan_linux.cc
+++ b/lib/hwasan/hwasan_linux.cc
@@ -22,7 +22,9 @@
#include "hwasan_mapping.h"
#include "hwasan_report.h"
#include "hwasan_thread.h"
+#include "hwasan_thread_list.h"
+#include <dlfcn.h>
#include <elf.h>
#include <link.h>
#include <pthread.h>
@@ -37,6 +39,11 @@
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_procmaps.h"
+#if HWASAN_WITH_INTERCEPTORS && !SANITIZER_ANDROID
+SANITIZER_INTERFACE_ATTRIBUTE
+THREADLOCAL uptr __hwasan_tls;
+#endif
+
namespace __hwasan {
static void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
@@ -51,8 +58,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 +108,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 +140,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 +165,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 +176,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 +234,81 @@ 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;
+#if SANITIZER_ANDROID
+void AndroidTestTlsSlot() {
+ uptr kMagicValue = 0x010203040A0B0C0D;
+ *(uptr *)get_android_tls_ptr() = kMagicValue;
+ dlerror();
+ if (*(uptr *)get_android_tls_ptr() != kMagicValue) {
+ Printf(
+ "ERROR: Incompatible version of Android: TLS_SLOT_SANITIZER(6) is used "
+ "for dlerror().\n");
+ Die();
}
- // Make sure that signal handler can not see a stale current thread pointer.
- atomic_signal_fence(memory_order_seq_cst);
- HwasanThread::TSDDtor(tsd);
+}
+#else
+void AndroidTestTlsSlot() {}
+#endif
+
+Thread *GetCurrentThread() {
+ uptr *ThreadLong = GetCurrentThreadLongPtr();
+#if HWASAN_WITH_INTERCEPTORS
+ if (!*ThreadLong)
+ __hwasan_thread_enter();
+#endif
+ auto *R = (StackAllocationsRingBuffer *)ThreadLong;
+ return hwasanThreadList().GetThreadByBufferAddress((uptr)(R->Next()));
}
struct AccessInfo {
@@ -340,14 +378,13 @@ 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);
-
- ReportTagMismatch(stack, ai.addr, ai.size, ai.is_store);
+ GetStackTrace(stack, kStackTraceMax, StackTrace::GetNextInstructionPc(sig.pc),
+ sig.bp, uc, common_flags()->fast_unwind_on_fatal);
++hwasan_report_count;
- if (flags()->halt_on_error || !ai.recover)
- Die();
+
+ bool fatal = flags()->halt_on_error || !ai.recover;
+ ReportTagMismatch(stack, ai.addr, ai.size, ai.is_store, fatal);
#if defined(__aarch64__)
uc->uc_mcontext.pc += 4;
@@ -360,8 +397,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 650a5aefcb2d..e5e23dc60337 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_memintrinsics.cc b/lib/hwasan/hwasan_memintrinsics.cc
new file mode 100644
index 000000000000..9cb844e45c40
--- /dev/null
+++ b/lib/hwasan/hwasan_memintrinsics.cc
@@ -0,0 +1,45 @@
+//===-- hwasan_memintrinsics.cc ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is a part of HWAddressSanitizer and contains HWASAN versions of
+/// memset, memcpy and memmove
+///
+//===----------------------------------------------------------------------===//
+
+#include <string.h>
+#include "hwasan.h"
+#include "hwasan_checks.h"
+#include "hwasan_flags.h"
+#include "hwasan_interface_internal.h"
+#include "sanitizer_common/sanitizer_libc.h"
+
+using namespace __hwasan;
+
+void *__hwasan_memset(void *block, int c, uptr size) {
+ CheckAddressSized<ErrorAction::Recover, AccessType::Store>(
+ reinterpret_cast<uptr>(block), size);
+ return memset(UntagPtr(block), c, size);
+}
+
+void *__hwasan_memcpy(void *to, const void *from, uptr size) {
+ CheckAddressSized<ErrorAction::Recover, AccessType::Store>(
+ reinterpret_cast<uptr>(to), size);
+ CheckAddressSized<ErrorAction::Recover, AccessType::Load>(
+ reinterpret_cast<uptr>(from), size);
+ return memcpy(UntagPtr(to), UntagPtr(from), size);
+}
+
+void *__hwasan_memmove(void *to, const void *from, uptr size) {
+ CheckAddressSized<ErrorAction::Recover, AccessType::Store>(
+ reinterpret_cast<uptr>(to), size);
+ CheckAddressSized<ErrorAction::Recover, AccessType::Load>(
+ reinterpret_cast<uptr>(from), size);
+ return memmove(UntagPtr(to), UntagPtr(from), size);
+}
diff --git a/lib/hwasan/hwasan_new_delete.cc b/lib/hwasan/hwasan_new_delete.cc
index 63ca74edd481..f2e8faf5da73 100644
--- a/lib/hwasan/hwasan_new_delete.cc
+++ b/lib/hwasan/hwasan_new_delete.cc
@@ -51,7 +51,7 @@ void *operator new[](size_t size, std::nothrow_t const&) {
#define OPERATOR_DELETE_BODY \
GET_MALLOC_STACK_TRACE; \
- if (ptr) HwasanDeallocate(&stack, ptr)
+ if (ptr) hwasan_free(ptr, &stack)
INTERCEPTOR_ATTRIBUTE
void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
diff --git a/lib/hwasan/hwasan_poisoning.cc b/lib/hwasan/hwasan_poisoning.cc
index b99d8ed0be79..9c8e16b12ad5 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 b44a91f975f5..0dbf9d8ed4ef 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 16e9016ea35b..ea3e4096daef 100644
--- a/lib/hwasan/hwasan_report.cc
+++ b/lib/hwasan/hwasan_report.cc
@@ -15,18 +15,64 @@
#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"
#include "sanitizer_common/sanitizer_mutex.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace_printer.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
using namespace __sanitizer;
namespace __hwasan {
+class ScopedReport {
+ public:
+ ScopedReport(bool fatal = false) : error_message_(1), fatal(fatal) {
+ BlockingMutexLock lock(&error_message_lock_);
+ error_message_ptr_ = fatal ? &error_message_ : nullptr;
+ }
+
+ ~ScopedReport() {
+ BlockingMutexLock lock(&error_message_lock_);
+ if (fatal) {
+ SetAbortMessage(error_message_.data());
+ Die();
+ }
+ error_message_ptr_ = nullptr;
+ }
+
+ static void MaybeAppendToErrorMessage(const char *msg) {
+ BlockingMutexLock lock(&error_message_lock_);
+ if (!error_message_ptr_)
+ return;
+ uptr len = internal_strlen(msg);
+ uptr old_size = error_message_ptr_->size();
+ error_message_ptr_->resize(old_size + len);
+ // overwrite old trailing '\0', keep new trailing '\0' untouched.
+ internal_memcpy(&(*error_message_ptr_)[old_size - 1], msg, len);
+ }
+ private:
+ ScopedErrorReportLock error_report_lock_;
+ InternalMmapVector<char> error_message_;
+ bool fatal;
+
+ static InternalMmapVector<char> *error_message_ptr_;
+ static BlockingMutex error_message_lock_;
+};
+
+InternalMmapVector<char> *ScopedReport::error_message_ptr_;
+BlockingMutex ScopedReport::error_message_lock_;
+
+// If there is an active ScopedReport, append to its error message.
+void AppendToErrorMessageBuffer(const char *buffer) {
+ ScopedReport::MaybeAppendToErrorMessage(buffer);
+}
+
static StackTrace GetStackTraceFromId(u32 id) {
CHECK(id);
StackTrace res = StackDepotGet(id);
@@ -34,100 +80,344 @@ 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();
}
-};
+ return 0;
+}
+
+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);
+
+ // Print some very basic information about the address, if it's a heap.
+ HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
+ if (uptr beg = chunk.Beg()) {
+ uptr size = chunk.ActualSize();
+ Printf("%s[%p,%p) is a %s %s heap chunk; "
+ "size: %zd offset: %zd\n%s",
+ d.Location(),
+ beg, beg + size,
+ chunk.FromSmallHeap() ? "small" : "large",
+ chunk.IsAllocated() ? "allocated" : "unallocated",
+ size, untagged_addr - beg,
+ d.Default());
+ }
+
+ // 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++;
+ }
+ }
+ }
+
+ 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();
+
+ // 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);
+
+ t->Announce();
+ 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();
-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;
+ // Temporary report section, needs to be improved.
+ Printf("Previously 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());
+ InternalScopedString frame_desc(GetPageSizeCached() * 2);
+ 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;
+ if (SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc)) {
+ frame_desc.append(" sp: 0x%zx pc: %p ", sp, pc);
+ RenderFrame(&frame_desc, "in %f %s:%l\n", 0, frame->info,
+ common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
+ frame->ClearAll();
+ if (auto Descr = GetStackFrameDescr(pc))
+ frame_desc.append(" %s\n", Descr);
+ }
+ Printf("%s", frame_desc.data());
+ frame_desc.clear();
+ }
+
+ num_descriptions_printed++;
+ }
+ });
+
+ // Print the remaining threads, as an extra information, 1 line per thread.
+ hwasanThreadList().VisitAllLiveThreads([&](Thread *t) { t->Announce(); });
+
+ if (!num_descriptions_printed)
+ // We exhausted our possibilities. Bail out.
+ Printf("HWAddressSanitizer can not describe address in more detail.\n");
}
-void PrintAddressDescription(uptr addr, uptr access_size) {
- HeapAddressDescription heap_description;
- if (GetHeapAddressInformation(addr, access_size, &heap_description)) {
- heap_description.Print();
- return;
+void ReportStats() {}
+
+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 = 17;
+ 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() * 8);
+ 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 ? "<=" : " ");
}
- // We exhausted our possibilities. Bail out.
- Printf("HWAddressSanitizer can not describe address in more detail.\n");
+ Printf("%s", s.data());
}
-void ReportInvalidAccess(StackTrace *stack, u32 origin) {
- ScopedErrorReportLock l;
+void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
+ ScopedReport R(flags()->halt_on_error);
+ 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.Warning());
- Report("WARNING: HWAddressSanitizer: invalid access\n");
+ 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();
- ReportErrorSummary("invalid-access", stack);
-}
-void ReportStats() {}
+ PrintAddressDescription(tagged_addr, 0, nullptr);
-void ReportInvalidAccessInsideAddressRange(const char *what, const void *start,
- uptr size, uptr offset) {
- ScopedErrorReportLock l;
+ PrintTagsAroundAddr(tag_ptr);
+ ReportErrorSummary(bug_type, stack);
+}
+
+void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
+ uptr tail_size, const u8 *expected) {
+ ScopedReport R(flags()->halt_on_error);
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);
- // if (__sanitizer::Verbosity())
- // DescribeMemoryRange(start, size);
+ uptr untagged_addr = UntagAddr(tagged_addr);
+ Printf("%s", d.Error());
+ const char *bug_type = "alocation-tail-overwritten";
+ Report("ERROR: %s: %s; heap object [%p,%p) of size %zd\n", SanitizerToolName,
+ bug_type, untagged_addr, untagged_addr + orig_size, orig_size);
+ Printf("\n%s", d.Default());
+ stack->Print();
+ HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
+ if (chunk.Beg()) {
+ Printf("%s", d.Allocation());
+ Printf("allocated here:\n");
+ Printf("%s", d.Default());
+ GetStackTraceFromId(chunk.GetAllocStackId()).Print();
+ }
+
+ InternalScopedString s(GetPageSizeCached() * 8);
+ CHECK_GT(tail_size, 0U);
+ CHECK_LT(tail_size, kShadowAlignment);
+ u8 *tail = reinterpret_cast<u8*>(untagged_addr + orig_size);
+ s.append("Tail contains: ");
+ for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
+ s.append(".. ");
+ for (uptr i = 0; i < tail_size; i++)
+ s.append("%02x ", tail[i]);
+ s.append("\n");
+ s.append("Expected: ");
+ for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
+ s.append(".. ");
+ for (uptr i = 0; i < tail_size; i++)
+ s.append("%02x ", expected[i]);
+ s.append("\n");
+ s.append(" ");
+ for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
+ s.append(" ");
+ for (uptr i = 0; i < tail_size; i++)
+ s.append("%s ", expected[i] != tail[i] ? "^^" : " ");
+
+ s.append("\nThis error occurs when a buffer overflow overwrites memory\n"
+ "to the right of a heap object, but within the %zd-byte granule, e.g.\n"
+ " char *x = new char[20];\n"
+ " x[25] = 42;\n"
+ "By default %s does not detect such bugs at the time of write,\n"
+ "but can detect them at the time of free/delete.\n"
+ "To disable this feature set HWASAN_OPTIONS=free_checks_tail_magic=0;\n"
+ "To enable checking at the time of access, set "
+ "HWASAN_OPTIONS=malloc_align_right to non-zero\n\n",
+ kShadowAlignment, SanitizerToolName);
+ Printf("%s", s.data());
+ GetCurrentThread()->Announce();
+
+ tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
+ PrintTagsAroundAddr(tag_ptr);
+
+ ReportErrorSummary(bug_type, stack);
}
-void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
- bool is_store) {
- ScopedErrorReportLock l;
+void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
+ bool is_store, bool fatal) {
+ ScopedReport R(fatal);
+ 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);
-
- 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);
+ 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);
+
+ 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 bb33f1a87308..10fb20cc5be1 100644
--- a/lib/hwasan/hwasan_report.h
+++ b/lib/hwasan/hwasan_report.h
@@ -21,12 +21,12 @@
namespace __hwasan {
-void ReportInvalidAccess(StackTrace *stack, u32 origin);
void ReportStats();
-void ReportInvalidAccessInsideAddressRange(const char *what, const void *start,
- uptr size, uptr offset);
void ReportTagMismatch(StackTrace *stack, uptr addr, uptr access_size,
- bool is_store);
+ bool is_store, bool fatal);
+void ReportInvalidFree(StackTrace *stack, uptr addr);
+void ReportTailOverwritten(StackTrace *stack, uptr addr, uptr orig_size,
+ uptr tail_size, const u8 *expected);
void ReportAtExitStatistics();
diff --git a/lib/hwasan/hwasan_thread.cc b/lib/hwasan/hwasan_thread.cc
index b50c0fc76be4..631c2813eb06 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,70 @@ 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);
+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;
+
+ 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));
-}
-
-void HwasanThread::Init() {
- SetThreadStackAndTls();
- CHECK(MEM_IS_APP(stack_bottom_));
- CHECK(MEM_IS_APP(stack_top_ - 1));
-}
+ if (stack_bottom_) {
+ int local;
+ CHECK(AddrIsInStack((uptr)&local));
+ 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 +99,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 1e482adeac84..4830473f4aab 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 000000000000..a31eee84ed93
--- /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 000000000000..53747b51fd65
--- /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 ddd6ec20979b..87b2365fd767 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;
@@ -169,7 +170,7 @@ const interpose_substitution substitution_##func_name[] \
#elif !SANITIZER_MAC
# define PTR_TO_REAL(x) real_##x
# define REAL(x) __interception::PTR_TO_REAL(x)
-# define FUNC_TYPE(x) x##_f
+# define FUNC_TYPE(x) x##_type
# define DECLARE_REAL(ret_type, func, ...) \
typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
diff --git a/lib/interception/interception_linux.h b/lib/interception/interception_linux.h
index 942c25609ccb..765a186e5823 100644
--- a/lib/interception/interception_linux.h
+++ b/lib/interception/interception_linux.h
@@ -38,7 +38,7 @@ void *GetFuncAddrVer(const char *func_name, const char *ver);
// Android, Solaris and OpenBSD do not have dlvsym
#if !SANITIZER_ANDROID && !SANITIZER_SOLARIS && !SANITIZER_OPENBSD
#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \
- (::__interception::real_##func = (func##_f)( \
+ (::__interception::real_##func = (func##_type)( \
unsigned long)::__interception::GetFuncAddrVer(#func, symver))
#else
#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \
diff --git a/lib/interception/interception_win.cc b/lib/interception/interception_win.cc
index bd4ad7274dde..cd13827e5857 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_allocator.cc b/lib/lsan/lsan_allocator.cc
index c58c3548002f..1b338bd5973e 100644
--- a/lib/lsan/lsan_allocator.cc
+++ b/lib/lsan/lsan_allocator.cc
@@ -34,9 +34,6 @@ static const uptr kMaxAllowedMallocSize = 4UL << 30;
#else
static const uptr kMaxAllowedMallocSize = 8UL << 30;
#endif
-typedef LargeMmapAllocator<> SecondaryAllocator;
-typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
- SecondaryAllocator> Allocator;
static Allocator allocator;
diff --git a/lib/lsan/lsan_allocator.h b/lib/lsan/lsan_allocator.h
index 7c70bb6d9766..4c4e02fc0902 100644
--- a/lib/lsan/lsan_allocator.h
+++ b/lib/lsan/lsan_allocator.h
@@ -54,19 +54,25 @@ struct ChunkMetadata {
defined(__arm__)
static const uptr kRegionSizeLog = 20;
static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
-typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
+template <typename AddressSpaceView>
+using ByteMapASVT =
+ TwoLevelByteMap<(kNumRegions >> 12), 1 << 12, AddressSpaceView>;
+template <typename AddressSpaceViewTy>
struct AP32 {
static const uptr kSpaceBeg = 0;
static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
static const uptr kMetadataSize = sizeof(ChunkMetadata);
typedef __sanitizer::CompactSizeClassMap SizeClassMap;
static const uptr kRegionSizeLog = __lsan::kRegionSizeLog;
- typedef __lsan::ByteMap ByteMap;
+ using AddressSpaceView = AddressSpaceViewTy;
+ using ByteMap = __lsan::ByteMapASVT<AddressSpaceView>;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
-typedef SizeClassAllocator32<AP32> PrimaryAllocator;
+template <typename AddressSpaceView>
+using PrimaryAllocatorASVT = SizeClassAllocator32<AP32<AddressSpaceView>>;
+using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
#elif defined(__x86_64__) || defined(__powerpc64__)
# if defined(__powerpc64__)
const uptr kAllocatorSpace = 0xa0000000000ULL;
@@ -75,6 +81,7 @@ const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
const uptr kAllocatorSpace = 0x600000000000ULL;
const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
# endif
+template <typename AddressSpaceViewTy>
struct AP64 { // Allocator64 parameters. Deliberately using a short name.
static const uptr kSpaceBeg = kAllocatorSpace;
static const uptr kSpaceSize = kAllocatorSize;
@@ -82,11 +89,30 @@ struct AP64 { // Allocator64 parameters. Deliberately using a short name.
typedef DefaultSizeClassMap SizeClassMap;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = AddressSpaceViewTy;
};
-typedef SizeClassAllocator64<AP64> PrimaryAllocator;
+template <typename AddressSpaceView>
+using PrimaryAllocatorASVT = SizeClassAllocator64<AP64<AddressSpaceView>>;
+using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
#endif
-typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
+
+template <typename AddressSpaceView>
+using AllocatorCacheASVT =
+ SizeClassAllocatorLocalCache<PrimaryAllocatorASVT<AddressSpaceView>>;
+using AllocatorCache = AllocatorCacheASVT<LocalAddressSpaceView>;
+
+template <typename AddressSpaceView>
+using SecondaryAllocatorASVT =
+ LargeMmapAllocator<NoOpMapUnmapCallback, DefaultLargeMmapAllocatorPtrArray,
+ AddressSpaceView>;
+
+template <typename AddressSpaceView>
+using AllocatorASVT =
+ CombinedAllocator<PrimaryAllocatorASVT<AddressSpaceView>,
+ AllocatorCacheASVT<AddressSpaceView>,
+ SecondaryAllocatorASVT<AddressSpaceView>>;
+using Allocator = AllocatorASVT<LocalAddressSpaceView>;
AllocatorCache *GetAllocatorCache();
diff --git a/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc
index 012a673c3b25..eaa5cadc8ffb 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 2508c1dbd873..a355cea96c43 100644
--- a/lib/lsan/lsan_common_mac.cc
+++ b/lib/lsan/lsan_common_mac.cc
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_platform.h"
+#include "sanitizer_common/sanitizer_libc.h"
#include "lsan_common.h"
#if CAN_SANITIZE_LEAKS && SANITIZER_MAC
@@ -116,7 +117,8 @@ static const char *kSkippedSecNames[] = {
// Scans global variables for heap pointers.
void ProcessGlobalRegions(Frontier *frontier) {
- for (auto name : kSkippedSecNames) CHECK(ARRAY_SIZE(name) < kMaxSegName);
+ for (auto name : kSkippedSecNames)
+ CHECK(internal_strnlen(name, kMaxSegName + 1) <= kMaxSegName);
MemoryMappingLayout memory_mapping(false);
InternalMmapVector<LoadedModule> modules;
@@ -142,12 +144,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 +154,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/lsan/lsan_interceptors.cc b/lib/lsan/lsan_interceptors.cc
index fde52e496164..a9bd2ba42319 100644
--- a/lib/lsan/lsan_interceptors.cc
+++ b/lib/lsan/lsan_interceptors.cc
@@ -153,7 +153,7 @@ INTERCEPTOR(struct fake_mallinfo, mallinfo, void) {
#define LSAN_MAYBE_INTERCEPT_MALLINFO INTERCEPT_FUNCTION(mallinfo)
INTERCEPTOR(int, mallopt, int cmd, int value) {
- return -1;
+ return 0;
}
#define LSAN_MAYBE_INTERCEPT_MALLOPT INTERCEPT_FUNCTION(mallopt)
#else
diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc
index 06bcbdf88691..ba2d5d593303 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_allocator.cc b/lib/msan/msan_allocator.cc
index 36f0497a9d83..053ab0280397 100644
--- a/lib/msan/msan_allocator.cc
+++ b/lib/msan/msan_allocator.cc
@@ -57,7 +57,8 @@ struct MsanMapUnmapCallback {
static const uptr kMetadataSize = sizeof(Metadata);
typedef __sanitizer::CompactSizeClassMap SizeClassMap;
static const uptr kRegionSizeLog = __msan::kRegionSizeLog;
- typedef __msan::ByteMap ByteMap;
+ using AddressSpaceView = LocalAddressSpaceView;
+ using ByteMap = __msan::ByteMap;
typedef MsanMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
@@ -78,6 +79,7 @@ struct MsanMapUnmapCallback {
typedef DefaultSizeClassMap SizeClassMap;
typedef MsanMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = LocalAddressSpaceView;
};
typedef SizeClassAllocator64<AP64> PrimaryAllocator;
@@ -92,6 +94,7 @@ struct MsanMapUnmapCallback {
typedef DefaultSizeClassMap SizeClassMap;
typedef MsanMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = LocalAddressSpaceView;
};
typedef SizeClassAllocator64<AP64> PrimaryAllocator;
@@ -107,7 +110,8 @@ struct MsanMapUnmapCallback {
static const uptr kMetadataSize = sizeof(Metadata);
typedef __sanitizer::CompactSizeClassMap SizeClassMap;
static const uptr kRegionSizeLog = __msan::kRegionSizeLog;
- typedef __msan::ByteMap ByteMap;
+ using AddressSpaceView = LocalAddressSpaceView;
+ using ByteMap = __msan::ByteMap;
typedef MsanMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
diff --git a/lib/msan/msan_interceptors.cc b/lib/msan/msan_interceptors.cc
index b3429bcf06b5..497f943a8a0e 100644
--- a/lib/msan/msan_interceptors.cc
+++ b/lib/msan/msan_interceptors.cc
@@ -34,11 +34,13 @@
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_linux.h"
#include "sanitizer_common/sanitizer_tls_get_addr.h"
+#include "sanitizer_common/sanitizer_vector.h"
#if SANITIZER_NETBSD
#define fstat __fstat50
#define gettimeofday __gettimeofday50
#define getrusage __getrusage50
+#define tzset __tzset50
#endif
#include <stdarg.h>
@@ -249,11 +251,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));
@@ -265,7 +267,7 @@ INTERCEPTOR(void, mallinfo, __sanitizer_mallinfo *sret) {
#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD
INTERCEPTOR(int, mallopt, int cmd, int value) {
- return -1;
+ return 0;
}
#define MSAN_MAYBE_INTERCEPT_MALLOPT INTERCEPT_FUNCTION(mallopt)
#else
@@ -1071,6 +1073,7 @@ extern char *tzname[2];
INTERCEPTOR(void, tzset, int fake) {
ENSURE_MSAN_INITED();
+ InterceptorScope interceptor_scope;
REAL(tzset)(fake);
if (tzname[0])
__msan_unpoison(tzname[0], REAL(strlen)(tzname[0]) + 1);
@@ -1084,23 +1087,80 @@ struct MSanAtExitRecord {
void *arg;
};
-void MSanAtExitWrapper(void *arg) {
+struct InterceptorContext {
+ BlockingMutex atexit_mu;
+ Vector<struct MSanAtExitRecord *> AtExitStack;
+
+ InterceptorContext()
+ : AtExitStack() {
+ }
+};
+
+static ALIGNED(64) char interceptor_placeholder[sizeof(InterceptorContext)];
+InterceptorContext *interceptor_ctx() {
+ return reinterpret_cast<InterceptorContext*>(&interceptor_placeholder[0]);
+}
+
+void MSanAtExitWrapper() {
+ MSanAtExitRecord *r;
+ {
+ BlockingMutexLock l(&interceptor_ctx()->atexit_mu);
+
+ uptr element = interceptor_ctx()->AtExitStack.Size() - 1;
+ r = interceptor_ctx()->AtExitStack[element];
+ interceptor_ctx()->AtExitStack.PopBack();
+ }
+
+ UnpoisonParam(1);
+ ((void(*)())r->func)();
+ InternalFree(r);
+}
+
+void MSanCxaAtExitWrapper(void *arg) {
UnpoisonParam(1);
MSanAtExitRecord *r = (MSanAtExitRecord *)arg;
r->func(r->arg);
InternalFree(r);
}
+static int setup_at_exit_wrapper(void(*f)(), void *arg, void *dso);
+
// Unpoison argument shadow for C++ module destructors.
INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg,
void *dso_handle) {
if (msan_init_is_running) return REAL(__cxa_atexit)(func, arg, dso_handle);
+ return setup_at_exit_wrapper((void(*)())func, arg, dso_handle);
+}
+
+// Unpoison argument shadow for C++ module destructors.
+INTERCEPTOR(int, atexit, void (*func)()) {
+ // Avoid calling real atexit as it is unrechable on at least on Linux.
+ if (msan_init_is_running)
+ return REAL(__cxa_atexit)((void (*)(void *a))func, 0, 0);
+ return setup_at_exit_wrapper((void(*)())func, 0, 0);
+}
+
+static int setup_at_exit_wrapper(void(*f)(), void *arg, void *dso) {
ENSURE_MSAN_INITED();
MSanAtExitRecord *r =
(MSanAtExitRecord *)InternalAlloc(sizeof(MSanAtExitRecord));
- r->func = func;
+ r->func = (void(*)(void *a))f;
r->arg = arg;
- return REAL(__cxa_atexit)(MSanAtExitWrapper, r, dso_handle);
+ int res;
+ if (!dso) {
+ // NetBSD does not preserve the 2nd argument if dso is equal to 0
+ // Store ctx in a local stack-like structure
+
+ BlockingMutexLock l(&interceptor_ctx()->atexit_mu);
+
+ res = REAL(__cxa_atexit)((void (*)(void *a))MSanAtExitWrapper, 0, 0);
+ if (!res) {
+ interceptor_ctx()->AtExitStack.PushBack(r);
+ }
+ } else {
+ res = REAL(__cxa_atexit)(MSanCxaAtExitWrapper, r, dso);
+ }
+ return res;
}
static void BeforeFork() {
@@ -1520,6 +1580,9 @@ namespace __msan {
void InitializeInterceptors() {
static int inited = 0;
CHECK_EQ(inited, 0);
+
+ new(interceptor_ctx()) InterceptorContext();
+
InitializeCommonInterceptors();
InitializeSignalInterceptors();
@@ -1629,6 +1692,7 @@ void InitializeInterceptors() {
INTERCEPT_FUNCTION(pthread_join);
INTERCEPT_FUNCTION(tzset);
+ INTERCEPT_FUNCTION(atexit);
INTERCEPT_FUNCTION(__cxa_atexit);
INTERCEPT_FUNCTION(shmat);
INTERCEPT_FUNCTION(fork);
diff --git a/lib/msan/msan_linux.cc b/lib/msan/msan_linux.cc
index 385a650c4afc..0b0208884d2f 100644
--- a/lib/msan/msan_linux.cc
+++ b/lib/msan/msan_linux.cc
@@ -175,6 +175,51 @@ void InstallAtExitHandler() {
// ---------------------- TSD ---------------- {{{1
+#if SANITIZER_NETBSD || SANITIZER_FREEBSD
+// Thread Static Data cannot be used in early init on NetBSD and FreeBSD.
+// Reuse the MSan TSD API for compatibility with existing code
+// with an alternative implementation.
+
+static void (*tsd_destructor)(void *tsd) = nullptr;
+
+struct tsd_key {
+ tsd_key() : key(nullptr) {}
+ ~tsd_key() {
+ CHECK(tsd_destructor);
+ if (key)
+ (*tsd_destructor)(key);
+ }
+ MsanThread *key;
+};
+
+static thread_local struct tsd_key key;
+
+void MsanTSDInit(void (*destructor)(void *tsd)) {
+ CHECK(!tsd_destructor);
+ tsd_destructor = destructor;
+}
+
+MsanThread *GetCurrentThread() {
+ CHECK(tsd_destructor);
+ return key.key;
+}
+
+void SetCurrentThread(MsanThread *tsd) {
+ CHECK(tsd_destructor);
+ CHECK(tsd);
+ CHECK(!key.key);
+ key.key = tsd;
+}
+
+void MsanTSDDtor(void *tsd) {
+ CHECK(tsd_destructor);
+ CHECK_EQ(key.key, tsd);
+ key.key = nullptr;
+ // Make sure that signal handler can not see a stale current thread pointer.
+ atomic_signal_fence(memory_order_seq_cst);
+ MsanThread::TSDDtor(tsd);
+}
+#else
static pthread_key_t tsd_key;
static bool tsd_key_inited = false;
@@ -211,6 +256,7 @@ void MsanTSDDtor(void *tsd) {
atomic_signal_fence(memory_order_seq_cst);
MsanThread::TSDDtor(tsd);
}
+#endif
} // namespace __msan
diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc
index 29260f16e704..19f46abdc574 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/GCDAProfiling.c b/lib/profile/GCDAProfiling.c
index cbca365510b5..0665a680cf01 100644
--- a/lib/profile/GCDAProfiling.c
+++ b/lib/profile/GCDAProfiling.c
@@ -29,6 +29,8 @@
#include <string.h>
#if defined(_WIN32)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
#include "WindowsMMap.h"
#else
#include <sys/mman.h>
@@ -86,6 +88,9 @@ static uint64_t cur_buffer_size = 0;
static uint64_t cur_pos = 0;
static uint64_t file_size = 0;
static int new_file = 0;
+#if defined(_WIN32)
+static HANDLE mmap_handle = NULL;
+#endif
static int fd = -1;
typedef void (*fn_ptr)();
@@ -255,6 +260,28 @@ static int map_file() {
if (file_size == 0)
return -1;
+#if defined(_WIN32)
+ HANDLE mmap_fd;
+ if (fd == -1)
+ mmap_fd = INVALID_HANDLE_VALUE;
+ else
+ mmap_fd = (HANDLE)_get_osfhandle(fd);
+
+ mmap_handle = CreateFileMapping(mmap_fd, NULL, PAGE_READWRITE, DWORD_HI(file_size), DWORD_LO(file_size), NULL);
+ if (mmap_handle == NULL) {
+ fprintf(stderr, "profiling: %s: cannot create file mapping: %d\n", filename,
+ GetLastError());
+ return -1;
+ }
+
+ write_buffer = MapViewOfFile(mmap_handle, FILE_MAP_WRITE, 0, 0, file_size);
+ if (write_buffer == NULL) {
+ fprintf(stderr, "profiling: %s: cannot map: %d\n", filename,
+ GetLastError());
+ CloseHandle(mmap_handle);
+ return -1;
+ }
+#else
write_buffer = mmap(0, file_size, PROT_READ | PROT_WRITE,
MAP_FILE | MAP_SHARED, fd, 0);
if (write_buffer == (void *)-1) {
@@ -263,10 +290,30 @@ static int map_file() {
strerror(errnum));
return -1;
}
+#endif
+
return 0;
}
static void unmap_file() {
+#if defined(_WIN32)
+ if (!FlushViewOfFile(write_buffer, file_size)) {
+ fprintf(stderr, "profiling: %s: cannot flush mapped view: %d\n", filename,
+ GetLastError());
+ }
+
+ if (!UnmapViewOfFile(write_buffer)) {
+ fprintf(stderr, "profiling: %s: cannot unmap mapped view: %d\n", filename,
+ GetLastError());
+ }
+
+ if (!CloseHandle(mmap_handle)) {
+ fprintf(stderr, "profiling: %s: cannot close file mapping handle: %d\n", filename,
+ GetLastError());
+ }
+
+ mmap_handle = NULL;
+#else
if (msync(write_buffer, file_size, MS_SYNC) == -1) {
int errnum = errno;
fprintf(stderr, "profiling: %s: cannot msync: %s\n", filename,
@@ -277,6 +324,8 @@ static void unmap_file() {
* is written and we don't care.
*/
(void)munmap(write_buffer, file_size);
+#endif
+
write_buffer = NULL;
file_size = 0;
}
diff --git a/lib/profile/InstrProfData.inc b/lib/profile/InstrProfData.inc
index eb4a792ce82d..454620ed997a 100644
--- a/lib/profile/InstrProfData.inc
+++ b/lib/profile/InstrProfData.inc
@@ -308,14 +308,14 @@ typedef struct ValueProfRecord {
#ifdef __cplusplus
/*!
- * \brief Return the number of value sites.
+ * Return the number of value sites.
*/
uint32_t getNumValueSites() const { return NumValueSites; }
/*!
- * \brief Read data from this record and save it to Record.
+ * Read data from this record and save it to Record.
*/
void deserializeTo(InstrProfRecord &Record,
- InstrProfRecord::ValueMapType *VMap);
+ InstrProfSymtab *SymTab);
/*
* In-place byte swap:
* Do byte swap for this instance. \c Old is the original order before
@@ -393,7 +393,7 @@ typedef struct ValueProfData {
* Read data from this data and save it to \c Record.
*/
void deserializeTo(InstrProfRecord &Record,
- InstrProfRecord::ValueMapType *VMap);
+ InstrProfSymtab *SymTab);
void operator delete(void *ptr) { ::operator delete(ptr); }
#endif
} ValueProfData;
@@ -458,7 +458,7 @@ getValueProfRecordHeaderSize(uint32_t NumValueSites);
#endif
/*!
- * \brief Return the \c ValueProfRecord header size including the
+ * Return the \c ValueProfRecord header size including the
* padding bytes.
*/
INSTR_PROF_VISIBILITY INSTR_PROF_INLINE
@@ -471,7 +471,7 @@ uint32_t getValueProfRecordHeaderSize(uint32_t NumValueSites) {
}
/*!
- * \brief Return the total size of the value profile record including the
+ * Return the total size of the value profile record including the
* header and the value data.
*/
INSTR_PROF_VISIBILITY INSTR_PROF_INLINE
@@ -482,7 +482,7 @@ uint32_t getValueProfRecordSize(uint32_t NumValueSites,
}
/*!
- * \brief Return the pointer to the start of value data array.
+ * Return the pointer to the start of value data array.
*/
INSTR_PROF_VISIBILITY INSTR_PROF_INLINE
InstrProfValueData *getValueProfRecordValueData(ValueProfRecord *This) {
@@ -491,7 +491,7 @@ InstrProfValueData *getValueProfRecordValueData(ValueProfRecord *This) {
}
/*!
- * \brief Return the total number of value data for \c This record.
+ * Return the total number of value data for \c This record.
*/
INSTR_PROF_VISIBILITY INSTR_PROF_INLINE
uint32_t getValueProfRecordNumValueData(ValueProfRecord *This) {
@@ -503,7 +503,7 @@ uint32_t getValueProfRecordNumValueData(ValueProfRecord *This) {
}
/*!
- * \brief Use this method to advance to the next \c This \c ValueProfRecord.
+ * Use this method to advance to the next \c This \c ValueProfRecord.
*/
INSTR_PROF_VISIBILITY INSTR_PROF_INLINE
ValueProfRecord *getValueProfRecordNext(ValueProfRecord *This) {
@@ -514,7 +514,7 @@ ValueProfRecord *getValueProfRecordNext(ValueProfRecord *This) {
}
/*!
- * \brief Return the first \c ValueProfRecord instance.
+ * Return the first \c ValueProfRecord instance.
*/
INSTR_PROF_VISIBILITY INSTR_PROF_INLINE
ValueProfRecord *getFirstValueProfRecord(ValueProfData *This) {
diff --git a/lib/profile/InstrProfilingPlatformLinux.c b/lib/profile/InstrProfilingPlatformLinux.c
index a517821a2fbd..3764df1d8079 100644
--- a/lib/profile/InstrProfilingPlatformLinux.c
+++ b/lib/profile/InstrProfilingPlatformLinux.c
@@ -8,7 +8,7 @@
\*===----------------------------------------------------------------------===*/
#if defined(__linux__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \
- (defined(__sun__) && defined(__svr4__))
+ (defined(__sun__) && defined(__svr4__)) || defined(__NetBSD__)
#include <stdlib.h>
diff --git a/lib/profile/InstrProfilingPlatformOther.c b/lib/profile/InstrProfilingPlatformOther.c
index a339abc7f883..7c2f14cfce16 100644
--- a/lib/profile/InstrProfilingPlatformOther.c
+++ b/lib/profile/InstrProfilingPlatformOther.c
@@ -8,7 +8,7 @@
\*===----------------------------------------------------------------------===*/
#if !defined(__APPLE__) && !defined(__linux__) && !defined(__FreeBSD__) && \
- !(defined(__sun__) && defined(__svr4__))
+ !(defined(__sun__) && defined(__svr4__)) && !defined(__NetBSD__)
#include <stdlib.h>
diff --git a/lib/profile/InstrProfilingValue.c b/lib/profile/InstrProfilingValue.c
index 674a48609878..c7b01a570649 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.c b/lib/profile/WindowsMMap.c
index dc87a888ae7b..41cc67f41f1f 100644
--- a/lib/profile/WindowsMMap.c
+++ b/lib/profile/WindowsMMap.c
@@ -24,14 +24,6 @@
#include "InstrProfiling.h"
-#ifdef __USE_FILE_OFFSET64
-# define DWORD_HI(x) (x >> 32)
-# define DWORD_LO(x) ((x) & 0xffffffff)
-#else
-# define DWORD_HI(x) (0)
-# define DWORD_LO(x) (x)
-#endif
-
COMPILER_RT_VISIBILITY
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
{
diff --git a/lib/profile/WindowsMMap.h b/lib/profile/WindowsMMap.h
index 271619aea09a..51a130b31977 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>
@@ -45,6 +45,14 @@
#define LOCK_NB 4 /* don't block when locking */
#define LOCK_UN 8 /* unlock */
+#ifdef __USE_FILE_OFFSET64
+# define DWORD_HI(x) (x >> 32)
+# define DWORD_LO(x) ((x) & 0xffffffff)
+#else
+# define DWORD_HI(x) (0)
+# define DWORD_LO(x) (x)
+#endif
+
void *mmap(void *start, size_t length, int prot, int flags, int fd,
off_t offset);
diff --git a/lib/safestack/CMakeLists.txt b/lib/safestack/CMakeLists.txt
index 5a1bac2912b7..cc874a3fe8f1 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 8af93624b991..e68208015fb9 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 1be99616e232..f7bf4b009720 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -16,8 +16,10 @@ 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_freebsd.cc
sanitizer_platform_limits_linux.cc
sanitizer_platform_limits_netbsd.cc
sanitizer_platform_limits_openbsd.cc
@@ -36,6 +38,7 @@ set(SANITIZER_SOURCES_NOTERMINATION
sanitizer_suppressions.cc
sanitizer_tls_get_addr.cc
sanitizer_thread_registry.cc
+ sanitizer_type_traits.cc
sanitizer_win.cc)
if(UNIX AND NOT APPLE AND NOT OS_NAME MATCHES "SunOS")
@@ -143,6 +146,7 @@ set(SANITIZER_IMPL_HEADERS
sanitizer_libignore.h
sanitizer_linux.h
sanitizer_list.h
+ sanitizer_local_address_space_view.h
sanitizer_mac.h
sanitizer_malloc_mac.inc
sanitizer_mutex.h
@@ -158,6 +162,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
@@ -187,11 +192,8 @@ set(SANITIZER_IMPL_HEADERS
include_directories(..)
-set(SANITIZER_COMMON_DEFINITIONS)
-
-include(CheckIncludeFile)
-append_have_file_definition(rpc/xdr.h HAVE_RPC_XDR_H SANITIZER_COMMON_DEFINITIONS)
-append_have_file_definition(tirpc/rpc/xdr.h HAVE_TIRPC_RPC_XDR_H SANITIZER_COMMON_DEFINITIONS)
+set(SANITIZER_COMMON_DEFINITIONS
+ HAVE_RPC_XDR_H=${HAVE_RPC_XDR_H})
set(SANITIZER_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_rtti_flag(OFF SANITIZER_CFLAGS)
diff --git a/lib/sanitizer_common/sanitizer_allocator.h b/lib/sanitizer_common/sanitizer_allocator.h
index 9655a2264f34..88017160acf9 100644
--- a/lib/sanitizer_common/sanitizer_allocator.h
+++ b/lib/sanitizer_common/sanitizer_allocator.h
@@ -14,13 +14,15 @@
#ifndef SANITIZER_ALLOCATOR_H
#define SANITIZER_ALLOCATOR_H
-#include "sanitizer_internal_defs.h"
#include "sanitizer_common.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_lfstack.h"
#include "sanitizer_libc.h"
#include "sanitizer_list.h"
+#include "sanitizer_local_address_space_view.h"
#include "sanitizer_mutex.h"
-#include "sanitizer_lfstack.h"
#include "sanitizer_procmaps.h"
+#include "sanitizer_type_traits.h"
namespace __sanitizer {
diff --git a/lib/sanitizer_common/sanitizer_allocator_bytemap.h b/lib/sanitizer_common/sanitizer_allocator_bytemap.h
index 7df3e4097bf2..ef26941fe609 100644
--- a/lib/sanitizer_common/sanitizer_allocator_bytemap.h
+++ b/lib/sanitizer_common/sanitizer_allocator_bytemap.h
@@ -15,9 +15,10 @@
#endif
// Maps integers in rage [0, kSize) to u8 values.
-template<u64 kSize>
+template <u64 kSize, typename AddressSpaceViewTy = LocalAddressSpaceView>
class FlatByteMap {
public:
+ using AddressSpaceView = AddressSpaceViewTy;
void Init() {
internal_memset(map_, 0, sizeof(map_));
}
@@ -41,9 +42,12 @@ class FlatByteMap {
// to kSize2-byte arrays. The secondary arrays are mmaped on demand.
// Each value is initially zero and can be set to something else only once.
// Setting and getting values from multiple threads is safe w/o extra locking.
-template <u64 kSize1, u64 kSize2, class MapUnmapCallback = NoOpMapUnmapCallback>
+template <u64 kSize1, u64 kSize2,
+ typename AddressSpaceViewTy = LocalAddressSpaceView,
+ class MapUnmapCallback = NoOpMapUnmapCallback>
class TwoLevelByteMap {
public:
+ using AddressSpaceView = AddressSpaceViewTy;
void Init() {
internal_memset(map1_, 0, sizeof(map1_));
mu_.Init();
@@ -73,7 +77,8 @@ class TwoLevelByteMap {
CHECK_LT(idx, kSize1 * kSize2);
u8 *map2 = Get(idx / kSize2);
if (!map2) return 0;
- return map2[idx % kSize2];
+ auto value_ptr = AddressSpaceView::Load(&map2[idx % kSize2]);
+ return *value_ptr;
}
private:
diff --git a/lib/sanitizer_common/sanitizer_allocator_combined.h b/lib/sanitizer_common/sanitizer_allocator_combined.h
index 1f874d60b92b..fcc4469c98cf 100644
--- a/lib/sanitizer_common/sanitizer_allocator_combined.h
+++ b/lib/sanitizer_common/sanitizer_allocator_combined.h
@@ -21,19 +21,28 @@
// PrimaryAllocator is used via a local AllocatorCache.
// SecondaryAllocator can allocate anything, but is not efficient.
template <class PrimaryAllocator, class AllocatorCache,
- class SecondaryAllocator> // NOLINT
+ class SecondaryAllocator,
+ typename AddressSpaceViewTy = LocalAddressSpaceView> // NOLINT
class CombinedAllocator {
public:
+ using AddressSpaceView = AddressSpaceViewTy;
+ static_assert(is_same<AddressSpaceView,
+ typename PrimaryAllocator::AddressSpaceView>::value,
+ "PrimaryAllocator is using wrong AddressSpaceView");
+ static_assert(is_same<AddressSpaceView,
+ typename SecondaryAllocator::AddressSpaceView>::value,
+ "SecondaryAllocator is using wrong AddressSpaceView");
+
void InitLinkerInitialized(s32 release_to_os_interval_ms) {
+ stats_.InitLinkerInitialized();
primary_.Init(release_to_os_interval_ms);
secondary_.InitLinkerInitialized();
- stats_.InitLinkerInitialized();
}
void Init(s32 release_to_os_interval_ms) {
+ stats_.Init();
primary_.Init(release_to_os_interval_ms);
secondary_.Init();
- stats_.Init();
}
void *Allocate(AllocatorCache *cache, uptr size, uptr alignment) {
@@ -194,4 +203,3 @@ class CombinedAllocator {
SecondaryAllocator secondary_;
AllocatorGlobalStats stats_;
};
-
diff --git a/lib/sanitizer_common/sanitizer_allocator_internal.h b/lib/sanitizer_common/sanitizer_allocator_internal.h
index c0c03d3f4345..30fc7042b6b5 100644
--- a/lib/sanitizer_common/sanitizer_allocator_internal.h
+++ b/lib/sanitizer_common/sanitizer_allocator_internal.h
@@ -37,7 +37,8 @@ struct AP32 {
static const uptr kMetadataSize = 0;
typedef InternalSizeClassMap SizeClassMap;
static const uptr kRegionSizeLog = kInternalAllocatorRegionSizeLog;
- typedef __sanitizer::ByteMap ByteMap;
+ using AddressSpaceView = LocalAddressSpaceView;
+ using ByteMap = __sanitizer::ByteMap;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
diff --git a/lib/sanitizer_common/sanitizer_allocator_primary32.h b/lib/sanitizer_common/sanitizer_allocator_primary32.h
index 67970e95b31e..abaac3d1a70b 100644
--- a/lib/sanitizer_common/sanitizer_allocator_primary32.h
+++ b/lib/sanitizer_common/sanitizer_allocator_primary32.h
@@ -48,6 +48,7 @@ struct SizeClassAllocator32FlagMasks { // Bit masks.
template <class Params>
class SizeClassAllocator32 {
public:
+ using AddressSpaceView = typename Params::AddressSpaceView;
static const uptr kSpaceBeg = Params::kSpaceBeg;
static const u64 kSpaceSize = Params::kSpaceSize;
static const uptr kMetadataSize = Params::kMetadataSize;
@@ -56,6 +57,10 @@ class SizeClassAllocator32 {
typedef typename Params::ByteMap ByteMap;
typedef typename Params::MapUnmapCallback MapUnmapCallback;
+ static_assert(
+ is_same<typename ByteMap::AddressSpaceView, AddressSpaceView>::value,
+ "AddressSpaceView type mismatch");
+
static const bool kRandomShuffleChunks = Params::kFlags &
SizeClassAllocator32FlagMasks::kRandomShuffleChunks;
static const bool kUseSeparateSizeClassForBatch = Params::kFlags &
diff --git a/lib/sanitizer_common/sanitizer_allocator_primary64.h b/lib/sanitizer_common/sanitizer_allocator_primary64.h
index 6acb4f8bc56d..b063bf0d302f 100644
--- a/lib/sanitizer_common/sanitizer_allocator_primary64.h
+++ b/lib/sanitizer_common/sanitizer_allocator_primary64.h
@@ -46,6 +46,7 @@ struct SizeClassAllocator64FlagMasks { // Bit masks.
template <class Params>
class SizeClassAllocator64 {
public:
+ using AddressSpaceView = typename Params::AddressSpaceView;
static const uptr kSpaceBeg = Params::kSpaceBeg;
static const uptr kSpaceSize = Params::kSpaceSize;
static const uptr kMetadataSize = Params::kMetadataSize;
@@ -294,8 +295,10 @@ class SizeClassAllocator64 {
RegionInfo *region = GetRegionInfo(class_id);
uptr chunk_size = ClassIdToSize(class_id);
uptr region_beg = SpaceBeg() + class_id * kRegionSize;
+ uptr region_allocated_user_size =
+ AddressSpaceView::Load(region)->allocated_user;
for (uptr chunk = region_beg;
- chunk < region_beg + region->allocated_user;
+ chunk < region_beg + region_allocated_user_size;
chunk += chunk_size) {
// Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk));
callback(chunk, arg);
diff --git a/lib/sanitizer_common/sanitizer_allocator_secondary.h b/lib/sanitizer_common/sanitizer_allocator_secondary.h
index ab680b5e2d11..0c8505c34c87 100644
--- a/lib/sanitizer_common/sanitizer_allocator_secondary.h
+++ b/lib/sanitizer_common/sanitizer_allocator_secondary.h
@@ -68,9 +68,11 @@ typedef LargeMmapAllocatorPtrArrayDynamic DefaultLargeMmapAllocatorPtrArray;
// The main purpose of this allocator is to cover large and rare allocation
// sizes not covered by more efficient allocators (e.g. SizeClassAllocator64).
template <class MapUnmapCallback = NoOpMapUnmapCallback,
- class PtrArrayT = DefaultLargeMmapAllocatorPtrArray>
+ class PtrArrayT = DefaultLargeMmapAllocatorPtrArray,
+ class AddressSpaceViewTy = LocalAddressSpaceView>
class LargeMmapAllocator {
public:
+ using AddressSpaceView = AddressSpaceViewTy;
void InitLinkerInitialized() {
page_size_ = GetPageSizeCached();
chunks_ = reinterpret_cast<Header**>(ptr_array_.Init());
@@ -202,9 +204,10 @@ class LargeMmapAllocator {
void EnsureSortedChunks() {
if (chunks_sorted_) return;
- Sort(reinterpret_cast<uptr *>(chunks_), n_chunks_);
+ Header **chunks = AddressSpaceView::LoadWritable(chunks_, n_chunks_);
+ Sort(reinterpret_cast<uptr *>(chunks), n_chunks_);
for (uptr i = 0; i < n_chunks_; i++)
- chunks_[i]->chunk_idx = i;
+ AddressSpaceView::LoadWritable(chunks[i])->chunk_idx = i;
chunks_sorted_ = true;
}
@@ -272,12 +275,13 @@ class LargeMmapAllocator {
// The allocator must be locked when calling this function.
void ForEachChunk(ForEachChunkCallback callback, void *arg) {
EnsureSortedChunks(); // Avoid doing the sort while iterating.
+ const Header *const *chunks = AddressSpaceView::Load(chunks_, n_chunks_);
for (uptr i = 0; i < n_chunks_; i++) {
- auto t = chunks_[i];
+ const Header *t = chunks[i];
callback(reinterpret_cast<uptr>(GetUser(t)), arg);
// Consistency check: verify that the array did not change.
- CHECK_EQ(chunks_[i], t);
- CHECK_EQ(chunks_[i]->chunk_idx, i);
+ CHECK_EQ(chunks[i], t);
+ CHECK_EQ(AddressSpaceView::Load(chunks[i])->chunk_idx, i);
}
}
@@ -297,7 +301,7 @@ class LargeMmapAllocator {
return GetHeader(reinterpret_cast<uptr>(p));
}
- void *GetUser(Header *h) {
+ void *GetUser(const Header *h) {
CHECK(IsAligned((uptr)h, page_size_));
return reinterpret_cast<void*>(reinterpret_cast<uptr>(h) + page_size_);
}
@@ -316,4 +320,3 @@ class LargeMmapAllocator {
} stats;
StaticSpinMutex mutex_;
};
-
diff --git a/lib/sanitizer_common/sanitizer_allocator_size_class_map.h b/lib/sanitizer_common/sanitizer_allocator_size_class_map.h
index 77ab4fb544a2..1c05fb8ff06b 100644
--- a/lib/sanitizer_common/sanitizer_allocator_size_class_map.h
+++ b/lib/sanitizer_common/sanitizer_allocator_size_class_map.h
@@ -232,3 +232,11 @@ 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;
+// Similar to VeryCompact map above, this one has a small number of different
+// size classes, and also reduced thread-local caches.
+typedef SizeClassMap<2, 5, 9, 16, 8, 10> VeryDenseSizeClassMap;
diff --git a/lib/sanitizer_common/sanitizer_atomic_clang_x86.h b/lib/sanitizer_common/sanitizer_atomic_clang_x86.h
index 38feb29287f1..195533ea2320 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 7d72b0cfe4ce..6868961902ce 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 3b999edfbe58..d0aebd994129 100644
--- a/lib/sanitizer_common/sanitizer_common.h
+++ b/lib/sanitizer_common/sanitizer_common.h
@@ -61,6 +61,15 @@ INLINE int Verbosity() {
return atomic_load(&current_verbosity, memory_order_relaxed);
}
+#if SANITIZER_ANDROID
+INLINE uptr GetPageSize() {
+// Android post-M sysconf(_SC_PAGESIZE) crashes if called from .preinit_array.
+ return 4096;
+}
+INLINE uptr GetPageSizeCached() {
+ return 4096;
+}
+#else
uptr GetPageSize();
extern uptr PageSizeCached;
INLINE uptr GetPageSizeCached() {
@@ -68,11 +77,13 @@ INLINE uptr GetPageSizeCached() {
PageSizeCached = GetPageSize();
return PageSizeCached;
}
+#endif
uptr GetMmapGranularity();
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);
@@ -222,7 +233,9 @@ bool SetEnv(const char *name, const char *value);
u32 GetUid();
void ReExec();
void CheckASLR();
+void CheckMPROTECT();
char **GetArgv();
+char **GetEnviron();
void PrintCmdline();
bool StackSizeIsUnlimited();
uptr GetStackSizeLimitInBytes();
@@ -896,6 +909,7 @@ struct SignalContext {
bool IsMemoryAccess() const;
};
+void InitializePlatformEarly();
void MaybeReexec();
template <typename Fn>
diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc
index ff18bc801749..50f7837747e6 100644
--- a/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -77,7 +77,15 @@
#define ctime __ctime50
#define ctime_r __ctime_r50
#define devname __devname50
+#define fgetpos __fgetpos50
+#define fsetpos __fsetpos50
+#define fts_children __fts_children60
+#define fts_close __fts_close60
+#define fts_open __fts_open60
+#define fts_read __fts_read60
+#define fts_set __fts_set60
#define getitimer __getitimer50
+#define getmntinfo __getmntinfo13
#define getpwent __getpwent50
#define getpwnam __getpwnam50
#define getpwnam_r __getpwnam_r50
@@ -87,6 +95,7 @@
#define getutxent __getutxent50
#define getutxid __getutxid50
#define getutxline __getutxline50
+#define pututxline __pututxline50
#define glob __glob30
#define gmtime __gmtime50
#define gmtime_r __gmtime_r50
@@ -109,6 +118,7 @@
#define stat __stat50
#define time __time50
#define times __times13
+#define unvis __unvis50
#define wait3 __wait350
#define wait4 __wait450
extern const unsigned short *_ctype_tab_;
@@ -1810,7 +1820,10 @@ INTERCEPTOR(int, ioctl, int d, unsigned long request, ...) {
#if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS || \
SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT || \
- SANITIZER_INTERCEPT_GETPWENT_R || SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
+ SANITIZER_INTERCEPT_GETPWENT_R || \
+ SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS || \
+ SANITIZER_INTERCEPT_FGETPWENT_R || \
+ SANITIZER_INTERCEPT_FGETGRENT_R
static void unpoison_passwd(void *ctx, __sanitizer_passwd *pwd) {
if (pwd) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, sizeof(*pwd));
@@ -2034,36 +2047,51 @@ INTERCEPTOR(int, getpwent_r, __sanitizer_passwd *pwbuf, char *buf,
if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
return res;
}
-INTERCEPTOR(int, fgetpwent_r, void *fp, __sanitizer_passwd *pwbuf, char *buf,
- SIZE_T buflen, __sanitizer_passwd **pwbufp) {
+INTERCEPTOR(int, getgrent_r, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen,
+ __sanitizer_group **pwbufp) {
void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent_r, fp, pwbuf, buf, buflen, pwbufp);
+ COMMON_INTERCEPTOR_ENTER(ctx, getgrent_r, pwbuf, buf, buflen, pwbufp);
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- int res = REAL(fgetpwent_r)(fp, pwbuf, buf, buflen, pwbufp);
+ int res = REAL(getgrent_r)(pwbuf, buf, buflen, pwbufp);
if (!res) {
- if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp);
+ if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp);
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
}
if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
return res;
}
-INTERCEPTOR(int, getgrent_r, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen,
- __sanitizer_group **pwbufp) {
+#define INIT_GETPWENT_R \
+ COMMON_INTERCEPT_FUNCTION(getpwent_r); \
+ COMMON_INTERCEPT_FUNCTION(getgrent_r);
+#else
+#define INIT_GETPWENT_R
+#endif
+
+#if SANITIZER_INTERCEPT_FGETPWENT_R
+INTERCEPTOR(int, fgetpwent_r, void *fp, __sanitizer_passwd *pwbuf, char *buf,
+ SIZE_T buflen, __sanitizer_passwd **pwbufp) {
void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, getgrent_r, pwbuf, buf, buflen, pwbufp);
+ COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent_r, fp, pwbuf, buf, buflen, pwbufp);
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- int res = REAL(getgrent_r)(pwbuf, buf, buflen, pwbufp);
+ int res = REAL(fgetpwent_r)(fp, pwbuf, buf, buflen, pwbufp);
if (!res) {
- if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp);
+ if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp);
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
}
if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
return res;
}
+#define INIT_FGETPWENT_R \
+ COMMON_INTERCEPT_FUNCTION(fgetpwent_r);
+#else
+#define INIT_FGETPWENT_R
+#endif
+
+#if SANITIZER_INTERCEPT_FGETGRENT_R
INTERCEPTOR(int, fgetgrent_r, void *fp, __sanitizer_group *pwbuf, char *buf,
SIZE_T buflen, __sanitizer_group **pwbufp) {
void *ctx;
@@ -2079,13 +2107,10 @@ INTERCEPTOR(int, fgetgrent_r, void *fp, __sanitizer_group *pwbuf, char *buf,
if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
return res;
}
-#define INIT_GETPWENT_R \
- COMMON_INTERCEPT_FUNCTION(getpwent_r); \
- COMMON_INTERCEPT_FUNCTION(fgetpwent_r); \
- COMMON_INTERCEPT_FUNCTION(getgrent_r); \
+#define INIT_FGETGRENT_R \
COMMON_INTERCEPT_FUNCTION(fgetgrent_r);
#else
-#define INIT_GETPWENT_R
+#define INIT_FGETGRENT_R
#endif
#if SANITIZER_INTERCEPT_SETPWENT
@@ -2149,6 +2174,8 @@ INTERCEPTOR(int, clock_gettime, u32 clk_id, void *tp) {
namespace __sanitizer {
extern "C" {
int real_clock_gettime(u32 clk_id, void *tp) {
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_clock_gettime(clk_id, tp);
return REAL(clock_gettime)(clk_id, tp);
}
} // extern "C"
@@ -3345,14 +3372,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;
}
@@ -4254,11 +4281,16 @@ INTERCEPTOR(int, statvfs, char *path, void *buf) {
INTERCEPTOR(int, fstatvfs, int fd, void *buf) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs, fd, buf);
+ COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
int res = REAL(fstatvfs)(fd, buf);
- if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
+ if (!res) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
+ if (fd >= 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ }
return res;
}
#define INIT_STATVFS \
@@ -4460,6 +4492,7 @@ INTERCEPTOR(int, random_r, void *buf, u32 *result) {
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET || \
+ SANITIZER_INTERCEPT_PTHREAD_ATTR_GET_SCHED || \
SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSSCHED || \
SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GET || \
SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GET || \
@@ -4818,6 +4851,14 @@ INTERCEPTOR(float, remquof, float x, float y, int *quo) {
if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo));
return res;
}
+#define INIT_REMQUO \
+ COMMON_INTERCEPT_FUNCTION(remquo); \
+ COMMON_INTERCEPT_FUNCTION(remquof);
+#else
+#define INIT_REMQUO
+#endif
+
+#if SANITIZER_INTERCEPT_REMQUOL
INTERCEPTOR(long double, remquol, long double x, long double y, int *quo) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, remquol, x, y, quo);
@@ -4828,12 +4869,10 @@ INTERCEPTOR(long double, remquol, long double x, long double y, int *quo) {
if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo));
return res;
}
-#define INIT_REMQUO \
- COMMON_INTERCEPT_FUNCTION(remquo); \
- COMMON_INTERCEPT_FUNCTION(remquof); \
+#define INIT_REMQUOL \
COMMON_INTERCEPT_FUNCTION_LDBL(remquol);
#else
-#define INIT_REMQUO
+#define INIT_REMQUOL
#endif
#if SANITIZER_INTERCEPT_LGAMMA
@@ -4852,6 +4891,14 @@ INTERCEPTOR(float, lgammaf, float x) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam));
return res;
}
+#define INIT_LGAMMA \
+ COMMON_INTERCEPT_FUNCTION(lgamma); \
+ COMMON_INTERCEPT_FUNCTION(lgammaf);
+#else
+#define INIT_LGAMMA
+#endif
+
+#if SANITIZER_INTERCEPT_LGAMMAL
INTERCEPTOR(long double, lgammal, long double x) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, lgammal, x);
@@ -4859,12 +4906,10 @@ INTERCEPTOR(long double, lgammal, long double x) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam));
return res;
}
-#define INIT_LGAMMA \
- COMMON_INTERCEPT_FUNCTION(lgamma); \
- COMMON_INTERCEPT_FUNCTION(lgammaf); \
+#define INIT_LGAMMAL \
COMMON_INTERCEPT_FUNCTION_LDBL(lgammal);
#else
-#define INIT_LGAMMA
+#define INIT_LGAMMAL
#endif
#if SANITIZER_INTERCEPT_LGAMMA_R
@@ -5650,9 +5695,15 @@ INTERCEPTOR(void *, tsearch, void *key, void **rootp,
void unpoison_file(__sanitizer_FILE *fp) {
#if SANITIZER_HAS_STRUCT_FILE
COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp, sizeof(*fp));
+#if SANITIZER_NETBSD
+ if (fp->_bf._base && fp->_bf._size > 0)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp->_bf._base,
+ fp->_bf._size);
+#else
if (fp->_IO_read_base && fp->_IO_read_base < fp->_IO_read_end)
COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp->_IO_read_base,
fp->_IO_read_end - fp->_IO_read_base);
+#endif
#endif // SANITIZER_HAS_STRUCT_FILE
}
#endif
@@ -6522,10 +6573,21 @@ INTERCEPTOR(void *, getutxline, void *ut) {
COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmpx_sz);
return res;
}
+INTERCEPTOR(void *, pututxline, const void *ut) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pututxline, ut);
+ if (ut)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ut, __sanitizer::struct_utmpx_sz);
+ void *res = REAL(pututxline)(ut);
+ if (res)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, __sanitizer::struct_utmpx_sz);
+ return res;
+}
#define INIT_UTMPX \
COMMON_INTERCEPT_FUNCTION(getutxent); \
COMMON_INTERCEPT_FUNCTION(getutxid); \
- COMMON_INTERCEPT_FUNCTION(getutxline);
+ COMMON_INTERCEPT_FUNCTION(getutxline); \
+ COMMON_INTERCEPT_FUNCTION(pututxline);
#else
#define INIT_UTMPX
#endif
@@ -7017,12 +7079,19 @@ INTERCEPTOR(char *, devname, u64 dev, u32 type) {
#endif
#if SANITIZER_INTERCEPT_DEVNAME_R
-INTERCEPTOR(int, devname_r, u64 dev, u32 type, char *path, uptr len) {
+#if SANITIZER_NETBSD
+#define DEVNAME_R_RETTYPE int
+#define DEVNAME_R_SUCCESS(x) (!(x))
+#else
+#define DEVNAME_R_RETTYPE char*
+#define DEVNAME_R_SUCCESS(x) (x)
+#endif
+INTERCEPTOR(DEVNAME_R_RETTYPE, devname_r, u64 dev, u32 type, char *path,
+ uptr len) {
void *ctx;
- int res;
COMMON_INTERCEPTOR_ENTER(ctx, devname_r, dev, type, path, len);
- res = REAL(devname_r)(dev, type, path, len);
- if (!res)
+ DEVNAME_R_RETTYPE res = REAL(devname_r)(dev, type, path, len);
+ if (DEVNAME_R_SUCCESS(res))
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, path, REAL(strlen)(path) + 1);
return res;
}
@@ -7234,9 +7303,2174 @@ INTERCEPTOR(struct __sanitizer_netent *, getnetbyaddr, u32 net, int type) {
#define INIT_NETENT
#endif
+#if SANITIZER_INTERCEPT_GETMNTINFO
+INTERCEPTOR(int, getmntinfo, void **mntbufp, int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getmntinfo, mntbufp, flags);
+ int cnt = REAL(getmntinfo)(mntbufp, flags);
+ if (cnt > 0 && mntbufp) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mntbufp, sizeof(void *));
+ if (*mntbufp)
+#if SANITIZER_NETBSD
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *mntbufp, cnt * struct_statvfs_sz);
+#else
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *mntbufp, cnt * struct_statfs_sz);
+#endif
+ }
+ return cnt;
+}
+#define INIT_GETMNTINFO COMMON_INTERCEPT_FUNCTION(getmntinfo)
+#else
+#define INIT_GETMNTINFO
+#endif
+
+#if SANITIZER_INTERCEPT_MI_VECTOR_HASH
+INTERCEPTOR(void, mi_vector_hash, const void *key, SIZE_T len, u32 seed,
+ u32 hashes[3]) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, mi_vector_hash, key, len, seed, hashes);
+ if (key)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, key, len);
+ REAL(mi_vector_hash)(key, len, seed, hashes);
+ if (hashes)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hashes, sizeof(hashes[0]) * 3);
+}
+#define INIT_MI_VECTOR_HASH COMMON_INTERCEPT_FUNCTION(mi_vector_hash)
+#else
+#define INIT_MI_VECTOR_HASH
+#endif
+
+#if SANITIZER_INTERCEPT_SETVBUF
+INTERCEPTOR(int, setvbuf, __sanitizer_FILE *stream, char *buf, int mode,
+ SIZE_T size) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, setvbuf, stream, buf, mode, size);
+ int ret = REAL(setvbuf)(stream, buf, mode, size);
+ if (buf)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, size);
+ if (stream)
+ unpoison_file(stream);
+ return ret;
+}
+
+INTERCEPTOR(void, setbuf, __sanitizer_FILE *stream, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, setbuf, stream, buf);
+ REAL(setbuf)(stream, buf);
+ if (buf) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer_bufsiz);
+ }
+ if (stream)
+ unpoison_file(stream);
+}
+
+INTERCEPTOR(void, setbuffer, __sanitizer_FILE *stream, char *buf, int mode) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, setbuffer, stream, buf, mode);
+ REAL(setbuffer)(stream, buf, mode);
+ if (buf) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer_bufsiz);
+ }
+ if (stream)
+ unpoison_file(stream);
+}
+
+INTERCEPTOR(void, setlinebuf, __sanitizer_FILE *stream) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, setlinebuf, stream);
+ REAL(setlinebuf)(stream);
+ if (stream)
+ unpoison_file(stream);
+}
+#define INIT_SETVBUF COMMON_INTERCEPT_FUNCTION(setvbuf); \
+ COMMON_INTERCEPT_FUNCTION(setbuf); \
+ COMMON_INTERCEPT_FUNCTION(setbuffer); \
+ COMMON_INTERCEPT_FUNCTION(setlinebuf)
+#else
+#define INIT_SETVBUF
+#endif
+
+#if SANITIZER_INTERCEPT_GETVFSSTAT
+INTERCEPTOR(int, getvfsstat, void *buf, SIZE_T bufsize, int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getvfsstat, buf, bufsize, flags);
+ int ret = REAL(getvfsstat)(buf, bufsize, flags);
+ if (buf && ret > 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, ret * struct_statvfs_sz);
+ return ret;
+}
+#define INIT_GETVFSSTAT COMMON_INTERCEPT_FUNCTION(getvfsstat)
+#else
+#define INIT_GETVFSSTAT
+#endif
+
+#if SANITIZER_INTERCEPT_REGEX
+INTERCEPTOR(int, regcomp, void *preg, const char *pattern, int cflags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, regcomp, preg, pattern, cflags);
+ if (pattern)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, pattern, REAL(strlen)(pattern) + 1);
+ int res = REAL(regcomp)(preg, pattern, cflags);
+ if (!res)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, preg, struct_regex_sz);
+ return res;
+}
+INTERCEPTOR(int, regexec, const void *preg, const char *string, SIZE_T nmatch,
+ struct __sanitizer_regmatch *pmatch[], int eflags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, regexec, preg, string, nmatch, pmatch, eflags);
+ if (preg)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, preg, struct_regex_sz);
+ if (string)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, string, REAL(strlen)(string) + 1);
+ int res = REAL(regexec)(preg, string, nmatch, pmatch, eflags);
+ if (!res && pmatch)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pmatch, nmatch * struct_regmatch_sz);
+ return res;
+}
+INTERCEPTOR(SIZE_T, regerror, int errcode, const void *preg, char *errbuf,
+ SIZE_T errbuf_size) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, regerror, errcode, preg, errbuf, errbuf_size);
+ if (preg)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, preg, struct_regex_sz);
+ SIZE_T res = REAL(regerror)(errcode, preg, errbuf, errbuf_size);
+ if (errbuf)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, errbuf, REAL(strlen)(errbuf) + 1);
+ return res;
+}
+INTERCEPTOR(void, regfree, const void *preg) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, regfree, preg);
+ if (preg)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, preg, struct_regex_sz);
+ REAL(regfree)(preg);
+}
+#define INIT_REGEX \
+ COMMON_INTERCEPT_FUNCTION(regcomp); \
+ COMMON_INTERCEPT_FUNCTION(regexec); \
+ COMMON_INTERCEPT_FUNCTION(regerror); \
+ COMMON_INTERCEPT_FUNCTION(regfree);
+#else
+#define INIT_REGEX
+#endif
+
+#if SANITIZER_INTERCEPT_REGEXSUB
+INTERCEPTOR(SSIZE_T, regnsub, char *buf, SIZE_T bufsiz, const char *sub,
+ const struct __sanitizer_regmatch *rm, const char *str) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, regnsub, buf, bufsiz, sub, rm, str);
+ if (sub)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sub, REAL(strlen)(sub) + 1);
+ // The implementation demands and hardcodes 10 elements
+ if (rm)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rm, 10 * struct_regmatch_sz);
+ if (str)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, str, REAL(strlen)(str) + 1);
+ SSIZE_T res = REAL(regnsub)(buf, bufsiz, sub, rm, str);
+ if (res > 0 && buf)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, REAL(strlen)(buf) + 1);
+ return res;
+}
+INTERCEPTOR(SSIZE_T, regasub, char **buf, const char *sub,
+ const struct __sanitizer_regmatch *rm, const char *sstr) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, regasub, buf, sub, rm, sstr);
+ if (sub)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sub, REAL(strlen)(sub) + 1);
+ // Hardcode 10 elements as this is hardcoded size
+ if (rm)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rm, 10 * struct_regmatch_sz);
+ if (sstr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sstr, REAL(strlen)(sstr) + 1);
+ SSIZE_T res = REAL(regasub)(buf, sub, rm, sstr);
+ if (res > 0 && buf) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sizeof(char *));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *buf, REAL(strlen)(*buf) + 1);
+ }
+ return res;
+}
+
+#define INIT_REGEXSUB \
+ COMMON_INTERCEPT_FUNCTION(regnsub); \
+ COMMON_INTERCEPT_FUNCTION(regasub);
+#else
+#define INIT_REGEXSUB
+#endif
+
+#if SANITIZER_INTERCEPT_FTS
+INTERCEPTOR(void *, fts_open, char *const *path_argv, int options,
+ int (*compar)(void **, void **)) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fts_open, path_argv, options, compar);
+ if (path_argv) {
+ for (char *const *pa = path_argv; ; ++pa) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, pa, sizeof(char **));
+ if (!*pa)
+ break;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, *pa, REAL(strlen)(*pa) + 1);
+ }
+ }
+ // TODO(kamil): handle compar callback
+ void *fts = REAL(fts_open)(path_argv, options, compar);
+ if (fts)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, fts, struct_FTS_sz);
+ return fts;
+}
+
+INTERCEPTOR(void *, fts_read, void *ftsp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fts_read, ftsp);
+ if (ftsp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ftsp, struct_FTS_sz);
+ void *ftsent = REAL(fts_read)(ftsp);
+ if (ftsent)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ftsent, struct_FTSENT_sz);
+ return ftsent;
+}
+
+INTERCEPTOR(void *, fts_children, void *ftsp, int options) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fts_children, ftsp, options);
+ if (ftsp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ftsp, struct_FTS_sz);
+ void *ftsent = REAL(fts_children)(ftsp, options);
+ if (ftsent)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ftsent, struct_FTSENT_sz);
+ return ftsent;
+}
+
+INTERCEPTOR(int, fts_set, void *ftsp, void *f, int options) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fts_set, ftsp, f, options);
+ if (ftsp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ftsp, struct_FTS_sz);
+ if (f)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, f, struct_FTSENT_sz);
+ return REAL(fts_set)(ftsp, f, options);
+}
+
+INTERCEPTOR(int, fts_close, void *ftsp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fts_close, ftsp);
+ if (ftsp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ftsp, struct_FTS_sz);
+ return REAL(fts_close)(ftsp);
+}
+#define INIT_FTS \
+ COMMON_INTERCEPT_FUNCTION(fts_open); \
+ COMMON_INTERCEPT_FUNCTION(fts_read); \
+ COMMON_INTERCEPT_FUNCTION(fts_children); \
+ COMMON_INTERCEPT_FUNCTION(fts_set); \
+ COMMON_INTERCEPT_FUNCTION(fts_close);
+#else
+#define INIT_FTS
+#endif
+
+#if SANITIZER_INTERCEPT_SYSCTL
+INTERCEPTOR(int, sysctl, int *name, unsigned int namelen, void *oldp,
+ SIZE_T *oldlenp, void *newp, SIZE_T newlen) {
+ void *ctx;
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_sysctl(name, namelen, oldp, oldlenp, newp, newlen);
+ COMMON_INTERCEPTOR_ENTER(ctx, sysctl, name, namelen, oldp, oldlenp, newp,
+ newlen);
+ if (name)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, name, namelen * sizeof(*name));
+ if (oldlenp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, oldlenp, sizeof(*oldlenp));
+ if (newp && newlen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, newp, newlen);
+ int res = REAL(sysctl)(name, namelen, oldp, oldlenp, newp, newlen);
+ if (!res) {
+ if (oldlenp) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldlenp, sizeof(*oldlenp));
+ if (oldp)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldp, *oldlenp);
+ }
+ }
+ return res;
+}
+
+INTERCEPTOR(int, sysctlbyname, char *sname, void *oldp, SIZE_T *oldlenp,
+ void *newp, SIZE_T newlen) {
+ void *ctx;
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_sysctlbyname(sname, oldp, oldlenp, newp, newlen);
+ COMMON_INTERCEPTOR_ENTER(ctx, sysctlbyname, sname, oldp, oldlenp, newp,
+ newlen);
+ if (sname)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sname, REAL(strlen)(sname) + 1);
+ if (oldlenp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, oldlenp, sizeof(*oldlenp));
+ if (newp && newlen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, newp, newlen);
+ int res = REAL(sysctlbyname)(sname, oldp, oldlenp, newp, newlen);
+ if (!res) {
+ if (oldlenp) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldlenp, sizeof(*oldlenp));
+ if (oldp)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldp, *oldlenp);
+ }
+ }
+ return res;
+}
+
+INTERCEPTOR(int, sysctlnametomib, const char *sname, int *name,
+ SIZE_T *namelenp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sysctlnametomib, sname, name, namelenp);
+ if (sname)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sname, REAL(strlen)(sname) + 1);
+ if (namelenp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, namelenp, sizeof(*namelenp));
+ int res = REAL(sysctlnametomib)(sname, name, namelenp);
+ if (!res) {
+ if (namelenp) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelenp, sizeof(*namelenp));
+ if (name)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, name, *namelenp * sizeof(*name));
+ }
+ }
+ return res;
+}
+
+#define INIT_SYSCTL \
+ COMMON_INTERCEPT_FUNCTION(sysctl); \
+ COMMON_INTERCEPT_FUNCTION(sysctlbyname); \
+ COMMON_INTERCEPT_FUNCTION(sysctlnametomib);
+#else
+#define INIT_SYSCTL
+#endif
+
+#if SANITIZER_INTERCEPT_ASYSCTL
+INTERCEPTOR(void *, asysctl, const int *name, SIZE_T namelen, SIZE_T *len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, asysctl, name, namelen, len);
+ if (name)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, name, sizeof(*name) * namelen);
+ void *res = REAL(asysctl)(name, namelen, len);
+ if (res && len) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, len, sizeof(*len));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, *len);
+ }
+ return res;
+}
+
+INTERCEPTOR(void *, asysctlbyname, const char *sname, SIZE_T *len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, asysctlbyname, sname, len);
+ if (sname)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sname, REAL(strlen)(sname) + 1);
+ void *res = REAL(asysctlbyname)(sname, len);
+ if (res && len) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, len, sizeof(*len));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, *len);
+ }
+ return res;
+}
+#define INIT_ASYSCTL \
+ COMMON_INTERCEPT_FUNCTION(asysctl); \
+ COMMON_INTERCEPT_FUNCTION(asysctlbyname);
+#else
+#define INIT_ASYSCTL
+#endif
+
+#if SANITIZER_INTERCEPT_SYSCTLGETMIBINFO
+INTERCEPTOR(int, sysctlgetmibinfo, char *sname, int *name,
+ unsigned int *namelenp, char *cname, SIZE_T *csz, void **rnode,
+ int v) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sysctlgetmibinfo, sname, name, namelenp, cname,
+ csz, rnode, v);
+ if (sname)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sname, REAL(strlen)(sname) + 1);
+ if (namelenp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, namelenp, sizeof(*namelenp));
+ if (csz)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, csz, sizeof(*csz));
+ // Skip rnode, it's rarely used and not trivial to sanitize
+ // It's also used mostly internally
+ int res = REAL(sysctlgetmibinfo)(sname, name, namelenp, cname, csz, rnode, v);
+ if (!res) {
+ if (namelenp) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelenp, sizeof(*namelenp));
+ if (name)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, name, *namelenp * sizeof(*name));
+ }
+ if (csz) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, csz, sizeof(*csz));
+ if (cname)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cname, *csz);
+ }
+ }
+ return res;
+}
+#define INIT_SYSCTLGETMIBINFO \
+ COMMON_INTERCEPT_FUNCTION(sysctlgetmibinfo);
+#else
+#define INIT_SYSCTLGETMIBINFO
+#endif
+
+#if SANITIZER_INTERCEPT_NL_LANGINFO
+INTERCEPTOR(char *, nl_langinfo, long item) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, nl_langinfo, item);
+ char *ret = REAL(nl_langinfo)(item);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, REAL(strlen)(ret) + 1);
+ return ret;
+}
+#define INIT_NL_LANGINFO COMMON_INTERCEPT_FUNCTION(nl_langinfo)
+#else
+#define INIT_NL_LANGINFO
+#endif
+
+#if SANITIZER_INTERCEPT_MODCTL
+INTERCEPTOR(int, modctl, int operation, void *argp) {
+ void *ctx;
+ int ret;
+ COMMON_INTERCEPTOR_ENTER(ctx, modctl, operation, argp);
+
+ if (operation == modctl_load) {
+ if (argp) {
+ __sanitizer_modctl_load_t *ml = (__sanitizer_modctl_load_t *)argp;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ml, sizeof(*ml));
+ if (ml->ml_filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ml->ml_filename,
+ REAL(strlen)(ml->ml_filename) + 1);
+ if (ml->ml_props)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ml->ml_props, ml->ml_propslen);
+ }
+ ret = REAL(modctl)(operation, argp);
+ } else if (operation == modctl_unload) {
+ if (argp) {
+ const char *name = (const char *)argp;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+ }
+ ret = REAL(modctl)(operation, argp);
+ } else if (operation == modctl_stat) {
+ uptr iov_len;
+ struct __sanitizer_iovec *iov = (struct __sanitizer_iovec *)argp;
+ if (iov) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, iov, sizeof(*iov));
+ iov_len = iov->iov_len;
+ }
+ ret = REAL(modctl)(operation, argp);
+ if (iov)
+ COMMON_INTERCEPTOR_WRITE_RANGE(
+ ctx, iov->iov_base, Min(iov_len, iov->iov_len));
+ } else if (operation == modctl_exists)
+ ret = REAL(modctl)(operation, argp);
+ else
+ ret = REAL(modctl)(operation, argp);
+
+ return ret;
+}
+#define INIT_MODCTL COMMON_INTERCEPT_FUNCTION(modctl)
+#else
+#define INIT_MODCTL
+#endif
+
+#if SANITIZER_INTERCEPT_STRTONUM
+INTERCEPTOR(long long, strtonum, const char *nptr, long long minval,
+ long long maxval, const char **errstr) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strtonum, nptr, minval, maxval, errstr);
+
+ // TODO(kamil): Implement strtoll as a common inteceptor
+ char *real_endptr;
+ long long ret = (long long)REAL(strtoimax)(nptr, &real_endptr, 10);
+ StrtolFixAndCheck(ctx, nptr, nullptr, real_endptr, 10);
+
+ ret = REAL(strtonum)(nptr, minval, maxval, errstr);
+ if (errstr) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, errstr, sizeof(const char *));
+ if (*errstr)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *errstr, REAL(strlen)(*errstr) + 1);
+ }
+ return ret;
+}
+#define INIT_STRTONUM COMMON_INTERCEPT_FUNCTION(strtonum)
+#else
+#define INIT_STRTONUM
+#endif
+
+#if SANITIZER_INTERCEPT_FPARSELN
+INTERCEPTOR(char *, fparseln, __sanitizer_FILE *stream, SIZE_T *len,
+ SIZE_T *lineno, const char delim[3], int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fparseln, stream, len, lineno, delim, flags);
+ if (lineno)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, lineno, sizeof(*lineno));
+ if (delim)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, delim, sizeof(delim[0]) * 3);
+ char *ret = REAL(fparseln)(stream, len, lineno, delim, flags);
+ if (ret) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, REAL(strlen)(ret) + 1);
+ if (len)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, len, sizeof(*len));
+ if (lineno)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineno, sizeof(*lineno));
+ }
+ return ret;
+}
+#define INIT_FPARSELN COMMON_INTERCEPT_FUNCTION(fparseln)
+#else
+#define INIT_FPARSELN
+#endif
+
+#if SANITIZER_INTERCEPT_STATVFS1
+INTERCEPTOR(int, statvfs1, const char *path, void *buf, int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, statvfs1, path, buf, flags);
+ if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ int res = REAL(statvfs1)(path, buf, flags);
+ if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
+ return res;
+}
+INTERCEPTOR(int, fstatvfs1, int fd, void *buf, int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs1, fd, buf, flags);
+ COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+ int res = REAL(fstatvfs1)(fd, buf, flags);
+ if (!res) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
+ if (fd >= 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ }
+ return res;
+}
+#define INIT_STATVFS1 \
+ COMMON_INTERCEPT_FUNCTION(statvfs1); \
+ COMMON_INTERCEPT_FUNCTION(fstatvfs1);
+#else
+#define INIT_STATVFS1
+#endif
+
+#if SANITIZER_INTERCEPT_STRTOI
+INTERCEPTOR(INTMAX_T, strtoi, const char *nptr, char **endptr, int base,
+ INTMAX_T low, INTMAX_T high, int *rstatus) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strtoi, nptr, endptr, base, low, high, rstatus);
+ char *real_endptr;
+ INTMAX_T ret = REAL(strtoi)(nptr, &real_endptr, base, low, high, rstatus);
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
+ if (rstatus)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rstatus, sizeof(*rstatus));
+ return ret;
+}
+
+INTERCEPTOR(UINTMAX_T, strtou, const char *nptr, char **endptr, int base,
+ UINTMAX_T low, UINTMAX_T high, int *rstatus) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strtou, nptr, endptr, base, low, high, rstatus);
+ char *real_endptr;
+ UINTMAX_T ret = REAL(strtou)(nptr, &real_endptr, base, low, high, rstatus);
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
+ if (rstatus)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rstatus, sizeof(*rstatus));
+ return ret;
+}
+#define INIT_STRTOI \
+ COMMON_INTERCEPT_FUNCTION(strtoi); \
+ COMMON_INTERCEPT_FUNCTION(strtou)
+#else
+#define INIT_STRTOI
+#endif
+
+#if SANITIZER_INTERCEPT_CAPSICUM
+#define CAP_RIGHTS_INIT_INTERCEPTOR(cap_rights_init, rights, ...) \
+ { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_init, rights, ##__VA_ARGS__); \
+ if (rights) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rights, sizeof(*rights)); \
+ __sanitizer_cap_rights_t *ret = \
+ REAL(cap_rights_init)(rights, ##__VA_ARGS__); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, sizeof(*ret)); \
+ return ret; \
+ }
+
+#define CAP_RIGHTS_SET_INTERCEPTOR(cap_rights_set, rights, ...) \
+ { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_set, rights, ##__VA_ARGS__); \
+ if (rights) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rights, sizeof(*rights)); \
+ __sanitizer_cap_rights_t *ret = \
+ REAL(cap_rights_set)(rights, ##__VA_ARGS__); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, sizeof(*ret)); \
+ return ret; \
+ }
+
+#define CAP_RIGHTS_CLEAR_INTERCEPTOR(cap_rights_clear, rights, ...) \
+ { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_clear, rights, ##__VA_ARGS__); \
+ if (rights) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rights, sizeof(*rights)); \
+ __sanitizer_cap_rights_t *ret = \
+ REAL(cap_rights_clear)(rights, ##__VA_ARGS__); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, sizeof(*ret)); \
+ return ret; \
+ }
+
+#define CAP_RIGHTS_IS_SET_INTERCEPTOR(cap_rights_is_set, rights, ...) \
+ { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_is_set, rights, ##__VA_ARGS__); \
+ if (rights) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rights, sizeof(*rights)); \
+ return REAL(cap_rights_is_set)(rights, ##__VA_ARGS__); \
+ }
+
+INTERCEPTOR(__sanitizer_cap_rights_t *, cap_rights_init,
+ __sanitizer_cap_rights_t *rights) {
+ CAP_RIGHTS_INIT_INTERCEPTOR(cap_rights_init, rights);
+}
+
+INTERCEPTOR(__sanitizer_cap_rights_t *, cap_rights_set,
+ __sanitizer_cap_rights_t *rights) {
+ CAP_RIGHTS_SET_INTERCEPTOR(cap_rights_set, rights);
+}
+
+INTERCEPTOR(__sanitizer_cap_rights_t *, cap_rights_clear,
+ __sanitizer_cap_rights_t *rights) {
+ CAP_RIGHTS_CLEAR_INTERCEPTOR(cap_rights_clear, rights);
+}
+
+INTERCEPTOR(bool, cap_rights_is_set,
+ __sanitizer_cap_rights_t *rights) {
+ CAP_RIGHTS_IS_SET_INTERCEPTOR(cap_rights_is_set, rights);
+}
+
+INTERCEPTOR(int, cap_rights_limit, int fd,
+ const __sanitizer_cap_rights_t *rights) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_limit, fd, rights);
+ if (rights)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rights, sizeof(*rights));
+
+ return REAL(cap_rights_limit)(fd, rights);
+}
+
+INTERCEPTOR(int, cap_rights_get, int fd, __sanitizer_cap_rights_t *rights) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_get, fd, rights);
+ int ret = REAL(cap_rights_get)(fd, rights);
+ if (!ret && rights)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rights, sizeof(*rights));
+
+ return ret;
+}
+
+INTERCEPTOR(bool, cap_rights_is_valid, const __sanitizer_cap_rights_t *rights) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_is_valid, rights);
+ if (rights)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rights, sizeof(*rights));
+
+ return REAL(cap_rights_is_valid(rights));
+}
+
+INTERCEPTOR(__sanitizer_cap_rights *, cap_rights_merge,
+ __sanitizer_cap_rights *dst, const __sanitizer_cap_rights *src) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_merge, dst, src);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src));
+
+ __sanitizer_cap_rights *ret = REAL(cap_rights_merge)(dst, src);
+ if (dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sizeof(*dst));
+
+ return ret;
+}
+
+INTERCEPTOR(__sanitizer_cap_rights *, cap_rights_remove,
+ __sanitizer_cap_rights *dst, const __sanitizer_cap_rights *src) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_remove, dst, src);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src));
+
+ __sanitizer_cap_rights *ret = REAL(cap_rights_remove)(dst, src);
+ if (dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sizeof(*dst));
+
+ return ret;
+}
+
+INTERCEPTOR(bool, cap_rights_contains, const __sanitizer_cap_rights *big,
+ const __sanitizer_cap_rights *little) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_contains, big, little);
+ if (little)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, little, sizeof(*little));
+ if (big)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, big, sizeof(*big));
+
+ return REAL(cap_rights_contains)(big, little);
+}
+
+INTERCEPTOR(int, cap_ioctls_limit, int fd, const uptr *cmds, SIZE_T ncmds) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_ioctls_limit, fd, cmds, ncmds);
+ if (cmds)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cmds, sizeof(*cmds) * ncmds);
+
+ return REAL(cap_ioctls_limit)(fd, cmds, ncmds);
+}
+
+INTERCEPTOR(int, cap_ioctls_get, int fd, uptr *cmds, SIZE_T maxcmds) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_ioctls_get, fd, cmds, maxcmds);
+ int ret = REAL(cap_ioctls_get)(fd, cmds, maxcmds);
+ if (!ret && cmds)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cmds, sizeof(*cmds) * maxcmds);
+
+ return ret;
+}
+#define INIT_CAPSICUM \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_init); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_set); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_clear); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_is_set); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_get); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_limit); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_contains); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_remove); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_merge); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_is_valid); \
+ COMMON_INTERCEPT_FUNCTION(cap_ioctls_get); \
+ COMMON_INTERCEPT_FUNCTION(cap_ioctls_limit)
+#else
+#define INIT_CAPSICUM
+#endif
+
+#if SANITIZER_INTERCEPT_SHA1
+INTERCEPTOR(void, SHA1Init, void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1Init, context);
+ REAL(SHA1Init)(context);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA1_CTX_sz);
+}
+INTERCEPTOR(void, SHA1Update, void *context, const u8 *data, unsigned len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1Update, context, data, len);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA1_CTX_sz);
+ REAL(SHA1Update)(context, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA1_CTX_sz);
+}
+INTERCEPTOR(void, SHA1Final, u8 digest[20], void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1Final, digest, context);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA1_CTX_sz);
+ REAL(SHA1Final)(digest, context);
+ if (digest)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, sizeof(u8) * 20);
+}
+INTERCEPTOR(void, SHA1Transform, u32 state[5], u8 buffer[64]) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1Transform, state, buffer);
+ if (state)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, state, sizeof(u32) * 5);
+ if (buffer)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, sizeof(u8) * 64);
+ REAL(SHA1Transform)(state, buffer);
+ if (state)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, state, sizeof(u32) * 5);
+}
+INTERCEPTOR(char *, SHA1End, void *context, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1End, context, buf);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA1_CTX_sz);
+ char *ret = REAL(SHA1End)(context, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA1_return_length);
+ return ret;
+}
+INTERCEPTOR(char *, SHA1File, char *filename, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1File, filename, buf);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(SHA1File)(filename, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA1_return_length);
+ return ret;
+}
+INTERCEPTOR(char *, SHA1FileChunk, char *filename, char *buf, OFF_T offset,
+ OFF_T length) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1FileChunk, filename, buf, offset, length);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(SHA1FileChunk)(filename, buf, offset, length);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA1_return_length);
+ return ret;
+}
+INTERCEPTOR(char *, SHA1Data, u8 *data, SIZE_T len, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1Data, data, len, buf);
+ if (data)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ char *ret = REAL(SHA1Data)(data, len, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA1_return_length);
+ return ret;
+}
+#define INIT_SHA1 \
+ COMMON_INTERCEPT_FUNCTION(SHA1Init); \
+ COMMON_INTERCEPT_FUNCTION(SHA1Update); \
+ COMMON_INTERCEPT_FUNCTION(SHA1Final); \
+ COMMON_INTERCEPT_FUNCTION(SHA1Transform); \
+ COMMON_INTERCEPT_FUNCTION(SHA1End); \
+ COMMON_INTERCEPT_FUNCTION(SHA1File); \
+ COMMON_INTERCEPT_FUNCTION(SHA1FileChunk); \
+ COMMON_INTERCEPT_FUNCTION(SHA1Data)
+#else
+#define INIT_SHA1
+#endif
+
+#if SANITIZER_INTERCEPT_MD4
+INTERCEPTOR(void, MD4Init, void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD4Init, context);
+ REAL(MD4Init)(context);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD4_CTX_sz);
+}
+
+INTERCEPTOR(void, MD4Update, void *context, const unsigned char *data,
+ unsigned int len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD4Update, context, data, len);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD4_CTX_sz);
+ REAL(MD4Update)(context, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD4_CTX_sz);
+}
+
+INTERCEPTOR(void, MD4Final, unsigned char digest[16], void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD4Final, digest, context);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD4_CTX_sz);
+ REAL(MD4Final)(digest, context);
+ if (digest)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, sizeof(unsigned char) * 16);
+}
+
+INTERCEPTOR(char *, MD4End, void *context, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD4End, context, buf);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD4_CTX_sz);
+ char *ret = REAL(MD4End)(context, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD4_return_length);
+ return ret;
+}
+
+INTERCEPTOR(char *, MD4File, const char *filename, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD4File, filename, buf);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(MD4File)(filename, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD4_return_length);
+ return ret;
+}
+
+INTERCEPTOR(char *, MD4Data, const unsigned char *data, unsigned int len,
+ char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD4Data, data, len, buf);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ char *ret = REAL(MD4Data)(data, len, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD4_return_length);
+ return ret;
+}
+
+#define INIT_MD4 \
+ COMMON_INTERCEPT_FUNCTION(MD4Init); \
+ COMMON_INTERCEPT_FUNCTION(MD4Update); \
+ COMMON_INTERCEPT_FUNCTION(MD4Final); \
+ COMMON_INTERCEPT_FUNCTION(MD4End); \
+ COMMON_INTERCEPT_FUNCTION(MD4File); \
+ COMMON_INTERCEPT_FUNCTION(MD4Data)
+#else
+#define INIT_MD4
+#endif
+
+#if SANITIZER_INTERCEPT_RMD160
+INTERCEPTOR(void, RMD160Init, void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160Init, context);
+ REAL(RMD160Init)(context);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, RMD160_CTX_sz);
+}
+INTERCEPTOR(void, RMD160Update, void *context, const u8 *data, unsigned len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160Update, context, data, len);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, RMD160_CTX_sz);
+ REAL(RMD160Update)(context, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, RMD160_CTX_sz);
+}
+INTERCEPTOR(void, RMD160Final, u8 digest[20], void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160Final, digest, context);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, RMD160_CTX_sz);
+ REAL(RMD160Final)(digest, context);
+ if (digest)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, sizeof(u8) * 20);
+}
+INTERCEPTOR(void, RMD160Transform, u32 state[5], u16 buffer[16]) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160Transform, state, buffer);
+ if (state)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, state, sizeof(u32) * 5);
+ if (buffer)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, sizeof(u32) * 16);
+ REAL(RMD160Transform)(state, buffer);
+ if (state)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, state, sizeof(u32) * 5);
+}
+INTERCEPTOR(char *, RMD160End, void *context, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160End, context, buf);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, RMD160_CTX_sz);
+ char *ret = REAL(RMD160End)(context, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, RMD160_return_length);
+ return ret;
+}
+INTERCEPTOR(char *, RMD160File, char *filename, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160File, filename, buf);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(RMD160File)(filename, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, RMD160_return_length);
+ return ret;
+}
+INTERCEPTOR(char *, RMD160FileChunk, char *filename, char *buf, OFF_T offset,
+ OFF_T length) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160FileChunk, filename, buf, offset, length);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(RMD160FileChunk)(filename, buf, offset, length);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, RMD160_return_length);
+ return ret;
+}
+INTERCEPTOR(char *, RMD160Data, u8 *data, SIZE_T len, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160Data, data, len, buf);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ char *ret = REAL(RMD160Data)(data, len, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, RMD160_return_length);
+ return ret;
+}
+#define INIT_RMD160 \
+ COMMON_INTERCEPT_FUNCTION(RMD160Init); \
+ COMMON_INTERCEPT_FUNCTION(RMD160Update); \
+ COMMON_INTERCEPT_FUNCTION(RMD160Final); \
+ COMMON_INTERCEPT_FUNCTION(RMD160Transform); \
+ COMMON_INTERCEPT_FUNCTION(RMD160End); \
+ COMMON_INTERCEPT_FUNCTION(RMD160File); \
+ COMMON_INTERCEPT_FUNCTION(RMD160FileChunk); \
+ COMMON_INTERCEPT_FUNCTION(RMD160Data)
+#else
+#define INIT_RMD160
+#endif
+
+#if SANITIZER_INTERCEPT_MD5
+INTERCEPTOR(void, MD5Init, void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD5Init, context);
+ REAL(MD5Init)(context);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD5_CTX_sz);
+}
+
+INTERCEPTOR(void, MD5Update, void *context, const unsigned char *data,
+ unsigned int len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD5Update, context, data, len);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz);
+ REAL(MD5Update)(context, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD5_CTX_sz);
+}
+
+INTERCEPTOR(void, MD5Final, unsigned char digest[16], void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD5Final, digest, context);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz);
+ REAL(MD5Final)(digest, context);
+ if (digest)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, sizeof(unsigned char) * 16);
+}
+
+INTERCEPTOR(char *, MD5End, void *context, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD5End, context, buf);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz);
+ char *ret = REAL(MD5End)(context, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length);
+ return ret;
+}
+
+INTERCEPTOR(char *, MD5File, const char *filename, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD5File, filename, buf);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(MD5File)(filename, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length);
+ return ret;
+}
+
+INTERCEPTOR(char *, MD5Data, const unsigned char *data, unsigned int len,
+ char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD5Data, data, len, buf);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ char *ret = REAL(MD5Data)(data, len, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length);
+ return ret;
+}
+
+#define INIT_MD5 \
+ COMMON_INTERCEPT_FUNCTION(MD5Init); \
+ COMMON_INTERCEPT_FUNCTION(MD5Update); \
+ COMMON_INTERCEPT_FUNCTION(MD5Final); \
+ COMMON_INTERCEPT_FUNCTION(MD5End); \
+ COMMON_INTERCEPT_FUNCTION(MD5File); \
+ COMMON_INTERCEPT_FUNCTION(MD5Data)
+#else
+#define INIT_MD5
+#endif
+
+#if SANITIZER_INTERCEPT_FSEEK
+INTERCEPTOR(int, fseek, __sanitizer_FILE *stream, long int offset, int whence) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fseek, stream, offset, whence);
+ return REAL(fseek)(stream, offset, whence);
+}
+INTERCEPTOR(int, fseeko, __sanitizer_FILE *stream, OFF_T offset, int whence) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fseeko, stream, offset, whence);
+ return REAL(fseeko)(stream, offset, whence);
+}
+INTERCEPTOR(long int, ftell, __sanitizer_FILE *stream) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, ftell, stream);
+ return REAL(ftell)(stream);
+}
+INTERCEPTOR(OFF_T, ftello, __sanitizer_FILE *stream) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, ftello, stream);
+ return REAL(ftello)(stream);
+}
+INTERCEPTOR(void, rewind, __sanitizer_FILE *stream) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, rewind, stream);
+ return REAL(rewind)(stream);
+}
+INTERCEPTOR(int, fgetpos, __sanitizer_FILE *stream, void *pos) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fgetpos, stream, pos);
+ int ret = REAL(fgetpos)(stream, pos);
+ if (pos && !ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pos, fpos_t_sz);
+ return ret;
+}
+INTERCEPTOR(int, fsetpos, __sanitizer_FILE *stream, const void *pos) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fsetpos, stream, pos);
+ if (pos)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, pos, fpos_t_sz);
+ return REAL(fsetpos)(stream, pos);
+}
+#define INIT_FSEEK \
+ COMMON_INTERCEPT_FUNCTION(fseek); \
+ COMMON_INTERCEPT_FUNCTION(fseeko); \
+ COMMON_INTERCEPT_FUNCTION(ftell); \
+ COMMON_INTERCEPT_FUNCTION(ftello); \
+ COMMON_INTERCEPT_FUNCTION(rewind); \
+ COMMON_INTERCEPT_FUNCTION(fgetpos); \
+ COMMON_INTERCEPT_FUNCTION(fsetpos)
+#else
+#define INIT_FSEEK
+#endif
+
+#if SANITIZER_INTERCEPT_MD2
+INTERCEPTOR(void, MD2Init, void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD2Init, context);
+ REAL(MD2Init)(context);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD2_CTX_sz);
+}
+
+INTERCEPTOR(void, MD2Update, void *context, const unsigned char *data,
+ unsigned int len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD2Update, context, data, len);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD2_CTX_sz);
+ REAL(MD2Update)(context, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD2_CTX_sz);
+}
+
+INTERCEPTOR(void, MD2Final, unsigned char digest[16], void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD2Final, digest, context);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD2_CTX_sz);
+ REAL(MD2Final)(digest, context);
+ if (digest)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, sizeof(unsigned char) * 16);
+}
+
+INTERCEPTOR(char *, MD2End, void *context, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD2End, context, buf);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD2_CTX_sz);
+ char *ret = REAL(MD2End)(context, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD2_return_length);
+ return ret;
+}
+
+INTERCEPTOR(char *, MD2File, const char *filename, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD2File, filename, buf);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(MD2File)(filename, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD2_return_length);
+ return ret;
+}
+
+INTERCEPTOR(char *, MD2Data, const unsigned char *data, unsigned int len,
+ char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD2Data, data, len, buf);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ char *ret = REAL(MD2Data)(data, len, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD2_return_length);
+ return ret;
+}
+
+#define INIT_MD2 \
+ COMMON_INTERCEPT_FUNCTION(MD2Init); \
+ COMMON_INTERCEPT_FUNCTION(MD2Update); \
+ COMMON_INTERCEPT_FUNCTION(MD2Final); \
+ COMMON_INTERCEPT_FUNCTION(MD2End); \
+ COMMON_INTERCEPT_FUNCTION(MD2File); \
+ COMMON_INTERCEPT_FUNCTION(MD2Data)
+#else
+#define INIT_MD2
+#endif
+
+#if SANITIZER_INTERCEPT_SHA2
+#define SHA2_INTERCEPTORS(LEN, SHA2_STATE_T) \
+ INTERCEPTOR(void, SHA##LEN##_Init, void *context) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Init, context); \
+ REAL(SHA##LEN##_Init)(context); \
+ if (context) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
+ } \
+ INTERCEPTOR(void, SHA##LEN##_Update, void *context, \
+ const u8 *data, SIZE_T len) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Update, context, data, len); \
+ if (data && len > 0) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len); \
+ if (context) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
+ REAL(SHA##LEN##_Update)(context, data, len); \
+ if (context) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
+ } \
+ INTERCEPTOR(void, SHA##LEN##_Final, u8 digest[LEN/8], \
+ void *context) { \
+ void *ctx; \
+ CHECK_EQ(SHA##LEN##_digest_length, LEN/8); \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Final, digest, context); \
+ if (context) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
+ REAL(SHA##LEN##_Final)(digest, context); \
+ if (digest) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, \
+ sizeof(digest[0]) * \
+ SHA##LEN##_digest_length); \
+ } \
+ INTERCEPTOR(char *, SHA##LEN##_End, void *context, char *buf) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_End, context, buf); \
+ if (context) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
+ char *ret = REAL(SHA##LEN##_End)(context, buf); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
+ return ret; \
+ } \
+ INTERCEPTOR(char *, SHA##LEN##_File, const char *filename, char *buf) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_File, filename, buf); \
+ if (filename) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);\
+ char *ret = REAL(SHA##LEN##_File)(filename, buf); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
+ return ret; \
+ } \
+ INTERCEPTOR(char *, SHA##LEN##_FileChunk, const char *filename, char *buf, \
+ OFF_T offset, OFF_T length) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_FileChunk, filename, buf, offset, \
+ length); \
+ if (filename) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);\
+ char *ret = REAL(SHA##LEN##_FileChunk)(filename, buf, offset, length); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
+ return ret; \
+ } \
+ INTERCEPTOR(char *, SHA##LEN##_Data, u8 *data, SIZE_T len, char *buf) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Data, data, len, buf); \
+ if (data && len > 0) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len); \
+ char *ret = REAL(SHA##LEN##_Data)(data, len, buf); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
+ return ret; \
+ }
+
+SHA2_INTERCEPTORS(224, u32);
+SHA2_INTERCEPTORS(256, u32);
+SHA2_INTERCEPTORS(384, u64);
+SHA2_INTERCEPTORS(512, u64);
+
+#define INIT_SHA2_INTECEPTORS(LEN) \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Init); \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Update); \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Final); \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_End); \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_File); \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_FileChunk); \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Data)
+
+#define INIT_SHA2 \
+ INIT_SHA2_INTECEPTORS(224); \
+ INIT_SHA2_INTECEPTORS(256); \
+ INIT_SHA2_INTECEPTORS(384); \
+ INIT_SHA2_INTECEPTORS(512)
+#undef SHA2_INTERCEPTORS
+#else
+#define INIT_SHA2
+#endif
+
+#if SANITIZER_INTERCEPT_VIS
+INTERCEPTOR(char *, vis, char *dst, int c, int flag, int nextc) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, vis, dst, c, flag, nextc);
+ char *end = REAL(vis)(dst, c, flag, nextc);
+ // dst is NULL terminated and end points to the NULL char
+ if (dst && end)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, end - dst + 1);
+ return end;
+}
+INTERCEPTOR(char *, nvis, char *dst, SIZE_T dlen, int c, int flag, int nextc) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, nvis, dst, dlen, c, flag, nextc);
+ char *end = REAL(nvis)(dst, dlen, c, flag, nextc);
+ // nvis cannot make sure the dst is NULL terminated
+ if (dst && end)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, end - dst + 1);
+ return end;
+}
+INTERCEPTOR(int, strvis, char *dst, const char *src, int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strvis, dst, src, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int len = REAL(strvis)(dst, src, flag);
+ if (dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, len + 1);
+ return len;
+}
+INTERCEPTOR(int, stravis, char **dst, const char *src, int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, stravis, dst, src, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int len = REAL(stravis)(dst, src, flag);
+ if (dst) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sizeof(char *));
+ if (*dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *dst, len + 1);
+ }
+ return len;
+}
+INTERCEPTOR(int, strnvis, char *dst, SIZE_T dlen, const char *src, int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strnvis, dst, dlen, src, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int len = REAL(strnvis)(dst, dlen, src, flag);
+ // The interface will be valid even if there is no space for NULL char
+ if (dst && len > 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, len + 1);
+ return len;
+}
+INTERCEPTOR(int, strvisx, char *dst, const char *src, SIZE_T len, int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strvisx, dst, src, len, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, len);
+ int ret = REAL(strvisx)(dst, src, len, flag);
+ if (dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strnvisx, char *dst, SIZE_T dlen, const char *src, SIZE_T len,
+ int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strnvisx, dst, dlen, src, len, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, len);
+ int ret = REAL(strnvisx)(dst, dlen, src, len, flag);
+ if (dst && ret >= 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strenvisx, char *dst, SIZE_T dlen, const char *src, SIZE_T len,
+ int flag, int *cerr_ptr) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strenvisx, dst, dlen, src, len, flag, cerr_ptr);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, len);
+ // FIXME: only need to be checked when "flag | VIS_NOLOCALE" doesn't hold
+ // according to the implementation
+ if (cerr_ptr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cerr_ptr, sizeof(int));
+ int ret = REAL(strenvisx)(dst, dlen, src, len, flag, cerr_ptr);
+ if (dst && ret >= 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ if (cerr_ptr)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cerr_ptr, sizeof(int));
+ return ret;
+}
+INTERCEPTOR(char *, svis, char *dst, int c, int flag, int nextc,
+ const char *extra) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, svis, dst, c, flag, nextc, extra);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ char *end = REAL(svis)(dst, c, flag, nextc, extra);
+ if (dst && end)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, end - dst + 1);
+ return end;
+}
+INTERCEPTOR(char *, snvis, char *dst, SIZE_T dlen, int c, int flag, int nextc,
+ const char *extra) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, snvis, dst, dlen, c, flag, nextc, extra);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ char *end = REAL(snvis)(dst, dlen, c, flag, nextc, extra);
+ if (dst && end)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst,
+ Min((SIZE_T)(end - dst + 1), dlen));
+ return end;
+}
+INTERCEPTOR(int, strsvis, char *dst, const char *src, int flag,
+ const char *extra) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strsvis, dst, src, flag, extra);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ int len = REAL(strsvis)(dst, src, flag, extra);
+ if (dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, len + 1);
+ return len;
+}
+INTERCEPTOR(int, strsnvis, char *dst, SIZE_T dlen, const char *src, int flag,
+ const char *extra) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strsnvis, dst, dlen, src, flag, extra);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ int len = REAL(strsnvis)(dst, dlen, src, flag, extra);
+ // The interface will be valid even if there is no space for NULL char
+ if (dst && len >= 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, len + 1);
+ return len;
+}
+INTERCEPTOR(int, strsvisx, char *dst, const char *src, SIZE_T len, int flag,
+ const char *extra) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strsvisx, dst, src, len, flag, extra);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, len);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ int ret = REAL(strsvisx)(dst, src, len, flag, extra);
+ if (dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strsnvisx, char *dst, SIZE_T dlen, const char *src, SIZE_T len,
+ int flag, const char *extra) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strsnvisx, dst, dlen, src, len, flag, extra);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, len);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ int ret = REAL(strsnvisx)(dst, dlen, src, len, flag, extra);
+ if (dst && ret >= 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strsenvisx, char *dst, SIZE_T dlen, const char *src,
+ SIZE_T len, int flag, const char *extra, int *cerr_ptr) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strsenvisx, dst, dlen, src, len, flag, extra,
+ cerr_ptr);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, len);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ // FIXME: only need to be checked when "flag | VIS_NOLOCALE" doesn't hold
+ // according to the implementation
+ if (cerr_ptr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cerr_ptr, sizeof(int));
+ int ret = REAL(strsenvisx)(dst, dlen, src, len, flag, extra, cerr_ptr);
+ if (dst && ret >= 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ if (cerr_ptr)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cerr_ptr, sizeof(int));
+ return ret;
+}
+INTERCEPTOR(int, unvis, char *cp, int c, int *astate, int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, unvis, cp, c, astate, flag);
+ if (astate)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, astate, sizeof(*astate));
+ int ret = REAL(unvis)(cp, c, astate, flag);
+ if (ret == unvis_valid || ret == unvis_validpush) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cp, sizeof(*cp));
+ }
+ return ret;
+}
+INTERCEPTOR(int, strunvis, char *dst, const char *src) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strunvis, dst, src);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int ret = REAL(strunvis)(dst, src);
+ if (ret != -1)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strnunvis, char *dst, SIZE_T dlen, const char *src) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strnunvis, dst, dlen, src);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int ret = REAL(strnunvis)(dst, dlen, src);
+ if (ret != -1)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strunvisx, char *dst, const char *src, int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strunvisx, dst, src, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int ret = REAL(strunvisx)(dst, src, flag);
+ if (ret != -1)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strnunvisx, char *dst, SIZE_T dlen, const char *src,
+ int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strnunvisx, dst, dlen, src, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int ret = REAL(strnunvisx)(dst, dlen, src, flag);
+ if (ret != -1)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+#define INIT_VIS \
+ COMMON_INTERCEPT_FUNCTION(vis); \
+ COMMON_INTERCEPT_FUNCTION(nvis); \
+ COMMON_INTERCEPT_FUNCTION(strvis); \
+ COMMON_INTERCEPT_FUNCTION(stravis); \
+ COMMON_INTERCEPT_FUNCTION(strnvis); \
+ COMMON_INTERCEPT_FUNCTION(strvisx); \
+ COMMON_INTERCEPT_FUNCTION(strnvisx); \
+ COMMON_INTERCEPT_FUNCTION(strenvisx); \
+ COMMON_INTERCEPT_FUNCTION(svis); \
+ COMMON_INTERCEPT_FUNCTION(snvis); \
+ COMMON_INTERCEPT_FUNCTION(strsvis); \
+ COMMON_INTERCEPT_FUNCTION(strsnvis); \
+ COMMON_INTERCEPT_FUNCTION(strsvisx); \
+ COMMON_INTERCEPT_FUNCTION(strsnvisx); \
+ COMMON_INTERCEPT_FUNCTION(strsenvisx); \
+ COMMON_INTERCEPT_FUNCTION(unvis); \
+ COMMON_INTERCEPT_FUNCTION(strunvis); \
+ COMMON_INTERCEPT_FUNCTION(strnunvis); \
+ COMMON_INTERCEPT_FUNCTION(strunvisx); \
+ COMMON_INTERCEPT_FUNCTION(strnunvisx)
+#else
+#define INIT_VIS
+#endif
+
+#if SANITIZER_INTERCEPT_CDB
+INTERCEPTOR(struct __sanitizer_cdbr *, cdbr_open, const char *path, int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbr_open, path, flags);
+ if (path)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ struct __sanitizer_cdbr *cdbr = REAL(cdbr_open)(path, flags);
+ if (cdbr)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cdbr, sizeof(*cdbr));
+ return cdbr;
+}
+
+INTERCEPTOR(struct __sanitizer_cdbr *, cdbr_open_mem, void *base, SIZE_T size,
+ int flags, void (*unmap)(void *, void *, SIZE_T), void *cookie) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbr_open_mem, base, size, flags, unmap,
+ cookie);
+ if (base && size)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, base, size);
+ struct __sanitizer_cdbr *cdbr =
+ REAL(cdbr_open_mem)(base, size, flags, unmap, cookie);
+ if (cdbr)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cdbr, sizeof(*cdbr));
+ return cdbr;
+}
+
+INTERCEPTOR(u32, cdbr_entries, struct __sanitizer_cdbr *cdbr) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbr_entries, cdbr);
+ if (cdbr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbr, sizeof(*cdbr));
+ return REAL(cdbr_entries)(cdbr);
+}
+
+INTERCEPTOR(int, cdbr_get, struct __sanitizer_cdbr *cdbr, u32 index,
+ const void **data, SIZE_T *datalen) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbr_get, cdbr, index, data, datalen);
+ if (cdbr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbr, sizeof(*cdbr));
+ int ret = REAL(cdbr_get)(cdbr, index, data, datalen);
+ if (!ret) {
+ if (data)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(*data));
+ if (datalen)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, datalen, sizeof(*datalen));
+ if (data && datalen)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *data, *datalen);
+ }
+ return ret;
+}
+
+INTERCEPTOR(int, cdbr_find, struct __sanitizer_cdbr *cdbr, const void *key,
+ SIZE_T keylen, const void **data, SIZE_T *datalen) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbr_find, cdbr, key, keylen, data, datalen);
+ if (cdbr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbr, sizeof(*cdbr));
+ if (key)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, key, keylen);
+ int ret = REAL(cdbr_find)(cdbr, key, keylen, data, datalen);
+ if (!ret) {
+ if (data)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(*data));
+ if (datalen)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, datalen, sizeof(*datalen));
+ if (data && datalen)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *data, *datalen);
+ }
+ return ret;
+}
+
+INTERCEPTOR(void, cdbr_close, struct __sanitizer_cdbr *cdbr) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbr_close, cdbr);
+ if (cdbr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbr, sizeof(*cdbr));
+ REAL(cdbr_close)(cdbr);
+}
+
+INTERCEPTOR(struct __sanitizer_cdbw *, cdbw_open) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbw_open);
+ struct __sanitizer_cdbw *ret = REAL(cdbw_open)();
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, sizeof(*ret));
+ return ret;
+}
+
+INTERCEPTOR(int, cdbw_put, struct __sanitizer_cdbw *cdbw, const void *key,
+ SIZE_T keylen, const void *data, SIZE_T datalen) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbw_put, cdbw, key, keylen, data, datalen);
+ if (cdbw)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbw, sizeof(*cdbw));
+ if (data && datalen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, datalen);
+ if (key && keylen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, key, keylen);
+ int ret = REAL(cdbw_put)(cdbw, key, keylen, data, datalen);
+ if (!ret && cdbw)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cdbw, sizeof(*cdbw));
+ return ret;
+}
+
+INTERCEPTOR(int, cdbw_put_data, struct __sanitizer_cdbw *cdbw, const void *data,
+ SIZE_T datalen, u32 *index) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbw_put_data, cdbw, data, datalen, index);
+ if (cdbw)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbw, sizeof(*cdbw));
+ if (data && datalen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, datalen);
+ int ret = REAL(cdbw_put_data)(cdbw, data, datalen, index);
+ if (!ret) {
+ if (index)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, index, sizeof(*index));
+ if (cdbw)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cdbw, sizeof(*cdbw));
+ }
+ return ret;
+}
+
+INTERCEPTOR(int, cdbw_put_key, struct __sanitizer_cdbw *cdbw, const void *key,
+ SIZE_T keylen, u32 index) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbw_put_key, cdbw, key, keylen, index);
+ if (cdbw)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbw, sizeof(*cdbw));
+ if (key && keylen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, key, keylen);
+ int ret = REAL(cdbw_put_key)(cdbw, key, keylen, index);
+ if (!ret && cdbw)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cdbw, sizeof(*cdbw));
+ return ret;
+}
+
+INTERCEPTOR(int, cdbw_output, struct __sanitizer_cdbw *cdbw, int output,
+ const char descr[16], u32 (*seedgen)(void)) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbw_output, cdbw, output, descr, seedgen);
+ COMMON_INTERCEPTOR_FD_ACCESS(ctx, output);
+ if (cdbw)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbw, sizeof(*cdbw));
+ if (descr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, descr, internal_strnlen(descr, 16));
+ if (seedgen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, (void *)seedgen, sizeof(seedgen));
+ int ret = REAL(cdbw_output)(cdbw, output, descr, seedgen);
+ if (!ret) {
+ if (cdbw)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cdbw, sizeof(*cdbw));
+ if (output >= 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, output);
+ }
+ return ret;
+}
+
+INTERCEPTOR(void, cdbw_close, struct __sanitizer_cdbw *cdbw) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbw_close, cdbw);
+ if (cdbw)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbw, sizeof(*cdbw));
+ REAL(cdbw_close)(cdbw);
+}
+
+#define INIT_CDB \
+ COMMON_INTERCEPT_FUNCTION(cdbr_open); \
+ COMMON_INTERCEPT_FUNCTION(cdbr_open_mem); \
+ COMMON_INTERCEPT_FUNCTION(cdbr_entries); \
+ COMMON_INTERCEPT_FUNCTION(cdbr_get); \
+ COMMON_INTERCEPT_FUNCTION(cdbr_find); \
+ COMMON_INTERCEPT_FUNCTION(cdbr_close); \
+ COMMON_INTERCEPT_FUNCTION(cdbw_open); \
+ COMMON_INTERCEPT_FUNCTION(cdbw_put); \
+ COMMON_INTERCEPT_FUNCTION(cdbw_put_data); \
+ COMMON_INTERCEPT_FUNCTION(cdbw_put_key); \
+ COMMON_INTERCEPT_FUNCTION(cdbw_output); \
+ COMMON_INTERCEPT_FUNCTION(cdbw_close)
+#else
+#define INIT_CDB
+#endif
+
+#if SANITIZER_INTERCEPT_GETFSENT
+INTERCEPTOR(void *, getfsent) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getfsent);
+ void *ret = REAL(getfsent)();
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, struct_fstab_sz);
+ return ret;
+}
+
+INTERCEPTOR(void *, getfsspec, const char *spec) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getfsspec, spec);
+ if (spec)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, spec, REAL(strlen)(spec) + 1);
+ void *ret = REAL(getfsspec)(spec);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, struct_fstab_sz);
+ return ret;
+}
+
+INTERCEPTOR(void *, getfsfile, const char *file) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getfsfile, file);
+ if (file)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, file, REAL(strlen)(file) + 1);
+ void *ret = REAL(getfsfile)(file);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, struct_fstab_sz);
+ return ret;
+}
+
+#define INIT_GETFSENT \
+ COMMON_INTERCEPT_FUNCTION(getfsent); \
+ COMMON_INTERCEPT_FUNCTION(getfsspec); \
+ COMMON_INTERCEPT_FUNCTION(getfsfile);
+#else
+#define INIT_GETFSENT
+#endif
+
+#if SANITIZER_INTERCEPT_ARC4RANDOM
+INTERCEPTOR(void, arc4random_buf, void *buf, SIZE_T len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, arc4random_buf, buf, len);
+ REAL(arc4random_buf)(buf, len);
+ if (buf && len)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, len);
+}
+
+INTERCEPTOR(void, arc4random_addrandom, u8 *dat, int datlen) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, arc4random_addrandom, dat, datlen);
+ if (dat && datlen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, dat, datlen);
+ REAL(arc4random_addrandom)(dat, datlen);
+}
+
+#define INIT_ARC4RANDOM \
+ COMMON_INTERCEPT_FUNCTION(arc4random_buf); \
+ COMMON_INTERCEPT_FUNCTION(arc4random_addrandom);
+#else
+#define INIT_ARC4RANDOM
+#endif
+
+#if SANITIZER_INTERCEPT_POPEN
+INTERCEPTOR(__sanitizer_FILE *, popen, const char *command, const char *type) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, popen, command, type);
+ if (command)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, command, REAL(strlen)(command) + 1);
+ if (type)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, type, REAL(strlen)(type) + 1);
+ __sanitizer_FILE *res = REAL(popen)(command, type);
+ COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, nullptr);
+ if (res) unpoison_file(res);
+ return res;
+}
+#define INIT_POPEN COMMON_INTERCEPT_FUNCTION(popen)
+#else
+#define INIT_POPEN
+#endif
+
+#if SANITIZER_INTERCEPT_POPENVE
+INTERCEPTOR(__sanitizer_FILE *, popenve, const char *path,
+ char *const *argv, char *const *envp, const char *type) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, popenve, path, argv, envp, type);
+ if (path)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ if (argv) {
+ for (char *const *pa = argv; ; ++pa) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, pa, sizeof(char **));
+ if (!*pa)
+ break;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, *pa, REAL(strlen)(*pa) + 1);
+ }
+ }
+ if (envp) {
+ for (char *const *pa = envp; ; ++pa) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, pa, sizeof(char **));
+ if (!*pa)
+ break;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, *pa, REAL(strlen)(*pa) + 1);
+ }
+ }
+ if (type)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, type, REAL(strlen)(type) + 1);
+ __sanitizer_FILE *res = REAL(popenve)(path, argv, envp, type);
+ COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, nullptr);
+ if (res) unpoison_file(res);
+ return res;
+}
+#define INIT_POPENVE COMMON_INTERCEPT_FUNCTION(popenve)
+#else
+#define INIT_POPENVE
+#endif
+
+#if SANITIZER_INTERCEPT_PCLOSE
+INTERCEPTOR(int, pclose, __sanitizer_FILE *fp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pclose, fp);
+ COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp);
+ const FileMetadata *m = GetInterceptorMetadata(fp);
+ int res = REAL(pclose)(fp);
+ if (m) {
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size);
+ DeleteInterceptorMetadata(fp);
+ }
+ return res;
+}
+#define INIT_PCLOSE COMMON_INTERCEPT_FUNCTION(pclose);
+#else
+#define INIT_PCLOSE
+#endif
+
+#if SANITIZER_INTERCEPT_FUNOPEN
+typedef int (*funopen_readfn)(void *cookie, char *buf, int len);
+typedef int (*funopen_writefn)(void *cookie, const char *buf, int len);
+typedef OFF_T (*funopen_seekfn)(void *cookie, OFF_T offset, int whence);
+typedef int (*funopen_closefn)(void *cookie);
+
+struct WrappedFunopenCookie {
+ void *real_cookie;
+ funopen_readfn real_read;
+ funopen_writefn real_write;
+ funopen_seekfn real_seek;
+ funopen_closefn real_close;
+};
+
+static int wrapped_funopen_read(void *cookie, char *buf, int len) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedFunopenCookie *wrapped_cookie = (WrappedFunopenCookie *)cookie;
+ funopen_readfn real_read = wrapped_cookie->real_read;
+ return real_read(wrapped_cookie->real_cookie, buf, len);
+}
+
+static int wrapped_funopen_write(void *cookie, const char *buf, int len) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedFunopenCookie *wrapped_cookie = (WrappedFunopenCookie *)cookie;
+ funopen_writefn real_write = wrapped_cookie->real_write;
+ return real_write(wrapped_cookie->real_cookie, buf, len);
+}
+
+static OFF_T wrapped_funopen_seek(void *cookie, OFF_T offset, int whence) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedFunopenCookie *wrapped_cookie = (WrappedFunopenCookie *)cookie;
+ funopen_seekfn real_seek = wrapped_cookie->real_seek;
+ return real_seek(wrapped_cookie->real_cookie, offset, whence);
+}
+
+static int wrapped_funopen_close(void *cookie) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ WrappedFunopenCookie *wrapped_cookie = (WrappedFunopenCookie *)cookie;
+ funopen_closefn real_close = wrapped_cookie->real_close;
+ int res = real_close(wrapped_cookie->real_cookie);
+ InternalFree(wrapped_cookie);
+ return res;
+}
+
+INTERCEPTOR(__sanitizer_FILE *, funopen, void *cookie, funopen_readfn readfn,
+ funopen_writefn writefn, funopen_seekfn seekfn,
+ funopen_closefn closefn) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, funopen, cookie, readfn, writefn, seekfn,
+ closefn);
+
+ WrappedFunopenCookie *wrapped_cookie =
+ (WrappedFunopenCookie *)InternalAlloc(sizeof(WrappedFunopenCookie));
+ wrapped_cookie->real_cookie = cookie;
+ wrapped_cookie->real_read = readfn;
+ wrapped_cookie->real_write = writefn;
+ wrapped_cookie->real_seek = seekfn;
+ wrapped_cookie->real_close = closefn;
+
+ __sanitizer_FILE *res =
+ REAL(funopen)(wrapped_cookie,
+ readfn ? wrapped_funopen_read : nullptr,
+ writefn ? wrapped_funopen_write : nullptr,
+ seekfn ? wrapped_funopen_seek : nullptr,
+ closefn ? wrapped_funopen_close : nullptr);
+ if (res)
+ unpoison_file(res);
+ return res;
+}
+#define INIT_FUNOPEN COMMON_INTERCEPT_FUNCTION(funopen)
+#else
+#define INIT_FUNOPEN
+#endif
+
+#if SANITIZER_INTERCEPT_FUNOPEN2
+typedef SSIZE_T (*funopen2_readfn)(void *cookie, void *buf, SIZE_T len);
+typedef SSIZE_T (*funopen2_writefn)(void *cookie, const void *buf, SIZE_T len);
+typedef OFF_T (*funopen2_seekfn)(void *cookie, OFF_T offset, int whence);
+typedef int (*funopen2_flushfn)(void *cookie);
+typedef int (*funopen2_closefn)(void *cookie);
+
+struct WrappedFunopen2Cookie {
+ void *real_cookie;
+ funopen2_readfn real_read;
+ funopen2_writefn real_write;
+ funopen2_seekfn real_seek;
+ funopen2_flushfn real_flush;
+ funopen2_closefn real_close;
+};
+
+static SSIZE_T wrapped_funopen2_read(void *cookie, void *buf, SIZE_T len) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedFunopen2Cookie *wrapped_cookie = (WrappedFunopen2Cookie *)cookie;
+ funopen2_readfn real_read = wrapped_cookie->real_read;
+ return real_read(wrapped_cookie->real_cookie, buf, len);
+}
+
+static SSIZE_T wrapped_funopen2_write(void *cookie, const void *buf,
+ SIZE_T len) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedFunopen2Cookie *wrapped_cookie = (WrappedFunopen2Cookie *)cookie;
+ funopen2_writefn real_write = wrapped_cookie->real_write;
+ return real_write(wrapped_cookie->real_cookie, buf, len);
+}
+
+static OFF_T wrapped_funopen2_seek(void *cookie, OFF_T offset, int whence) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedFunopen2Cookie *wrapped_cookie = (WrappedFunopen2Cookie *)cookie;
+ funopen2_seekfn real_seek = wrapped_cookie->real_seek;
+ return real_seek(wrapped_cookie->real_cookie, offset, whence);
+}
+
+static int wrapped_funopen2_flush(void *cookie) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ WrappedFunopen2Cookie *wrapped_cookie = (WrappedFunopen2Cookie *)cookie;
+ funopen2_flushfn real_flush = wrapped_cookie->real_flush;
+ return real_flush(wrapped_cookie->real_cookie);
+}
+
+static int wrapped_funopen2_close(void *cookie) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ WrappedFunopen2Cookie *wrapped_cookie = (WrappedFunopen2Cookie *)cookie;
+ funopen2_closefn real_close = wrapped_cookie->real_close;
+ int res = real_close(wrapped_cookie->real_cookie);
+ InternalFree(wrapped_cookie);
+ return res;
+}
+
+INTERCEPTOR(__sanitizer_FILE *, funopen2, void *cookie, funopen2_readfn readfn,
+ funopen2_writefn writefn, funopen2_seekfn seekfn,
+ funopen2_flushfn flushfn, funopen2_closefn closefn) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, funopen2, cookie, readfn, writefn, seekfn,
+ flushfn, closefn);
+
+ WrappedFunopen2Cookie *wrapped_cookie =
+ (WrappedFunopen2Cookie *)InternalAlloc(sizeof(WrappedFunopen2Cookie));
+ wrapped_cookie->real_cookie = cookie;
+ wrapped_cookie->real_read = readfn;
+ wrapped_cookie->real_write = writefn;
+ wrapped_cookie->real_seek = seekfn;
+ wrapped_cookie->real_flush = flushfn;
+ wrapped_cookie->real_close = closefn;
+
+ __sanitizer_FILE *res =
+ REAL(funopen2)(wrapped_cookie,
+ readfn ? wrapped_funopen2_read : nullptr,
+ writefn ? wrapped_funopen2_write : nullptr,
+ seekfn ? wrapped_funopen2_seek : nullptr,
+ flushfn ? wrapped_funopen2_flush : nullptr,
+ closefn ? wrapped_funopen2_close : nullptr);
+ if (res)
+ unpoison_file(res);
+ return res;
+}
+#define INIT_FUNOPEN2 COMMON_INTERCEPT_FUNCTION(funopen2)
+#else
+#define INIT_FUNOPEN2
+#endif
+
+#if SANITIZER_INTERCEPT_FDEVNAME
+INTERCEPTOR(char *, fdevname, int fd) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fdevname, fd);
+ COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+ char *name = REAL(fdevname)(fd);
+ if (name) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, name, REAL(strlen)(name) + 1);
+ if (fd > 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ }
+ return name;
+}
+
+INTERCEPTOR(char *, fdevname_r, int fd, char *buf, SIZE_T len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fdevname_r, fd, buf, len);
+ COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+ char *name = REAL(fdevname_r)(fd, buf, len);
+ if (name && buf && len > 0) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, REAL(strlen)(buf) + 1);
+ if (fd > 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ }
+ return name;
+}
+
+#define INIT_FDEVNAME \
+ COMMON_INTERCEPT_FUNCTION(fdevname); \
+ COMMON_INTERCEPT_FUNCTION(fdevname_r);
+#else
+#define INIT_FDEVNAME
+#endif
+
+#if SANITIZER_INTERCEPT_GETUSERSHELL
+INTERCEPTOR(char *, getusershell) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getusershell);
+ char *res = REAL(getusershell)();
+ if (res)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+ return res;
+}
+
+#define INIT_GETUSERSHELL COMMON_INTERCEPT_FUNCTION(getusershell);
+#else
+#define INIT_GETUSERSHELL
+#endif
+
+#if SANITIZER_INTERCEPT_SL_INIT
+INTERCEPTOR(void *, sl_init) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sl_init);
+ void *res = REAL(sl_init)();
+ if (res)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, __sanitizer::struct_StringList_sz);
+ return res;
+}
+
+INTERCEPTOR(int, sl_add, void *sl, char *item) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sl_add, sl, item);
+ if (sl)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sl, __sanitizer::struct_StringList_sz);
+ if (item)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, item, REAL(strlen)(item) + 1);
+ int res = REAL(sl_add)(sl, item);
+ if (!res)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sl, __sanitizer::struct_StringList_sz);
+ return res;
+}
+
+INTERCEPTOR(char *, sl_find, void *sl, const char *item) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sl_find, sl, item);
+ if (sl)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sl, __sanitizer::struct_StringList_sz);
+ if (item)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, item, REAL(strlen)(item) + 1);
+ char *res = REAL(sl_find)(sl, item);
+ if (res)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+ return res;
+}
+
+INTERCEPTOR(void, sl_free, void *sl, int freeall) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sl_free, sl, freeall);
+ if (sl)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sl, __sanitizer::struct_StringList_sz);
+ REAL(sl_free)(sl, freeall);
+}
+
+#define INIT_SL_INIT \
+ COMMON_INTERCEPT_FUNCTION(sl_init); \
+ COMMON_INTERCEPT_FUNCTION(sl_add); \
+ COMMON_INTERCEPT_FUNCTION(sl_find); \
+ COMMON_INTERCEPT_FUNCTION(sl_free);
+#else
+#define INIT_SL_INIT
+#endif
+
static void InitializeCommonInterceptors() {
static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1];
- interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap();
+ interceptor_metadata_map =
+ new ((void *)&metadata_mem) MetadataHashMap(); // NOLINT
INIT_MMAP;
INIT_MMAP64;
@@ -7298,6 +9532,8 @@ static void InitializeCommonInterceptors() {
INIT_GETPWENT;
INIT_FGETPWENT;
INIT_GETPWENT_R;
+ INIT_FGETPWENT_R;
+ INIT_FGETGRENT_R;
INIT_SETPWENT;
INIT_CLOCK_GETTIME;
INIT_GETITIMER;
@@ -7407,7 +9643,9 @@ static void InitializeCommonInterceptors() {
INIT_PTHREAD_GETNAME_NP;
INIT_SINCOS;
INIT_REMQUO;
+ INIT_REMQUOL;
INIT_LGAMMA;
+ INIT_LGAMMAL;
INIT_LGAMMA_R;
INIT_LGAMMAL_R;
INIT_DRAND48_R;
@@ -7483,6 +9721,42 @@ static void InitializeCommonInterceptors() {
INIT_TTYENT;
INIT_PROTOENT;
INIT_NETENT;
+ INIT_GETMNTINFO;
+ INIT_MI_VECTOR_HASH;
+ INIT_SETVBUF;
+ INIT_GETVFSSTAT;
+ INIT_REGEX;
+ INIT_REGEXSUB;
+ INIT_FTS;
+ INIT_SYSCTL;
+ INIT_ASYSCTL;
+ INIT_SYSCTLGETMIBINFO;
+ INIT_NL_LANGINFO;
+ INIT_MODCTL;
+ INIT_STRTONUM;
+ INIT_FPARSELN;
+ INIT_STATVFS1;
+ INIT_STRTOI;
+ INIT_CAPSICUM;
+ INIT_SHA1;
+ INIT_MD4;
+ INIT_RMD160;
+ INIT_MD5;
+ INIT_FSEEK;
+ INIT_MD2;
+ INIT_SHA2;
+ INIT_VIS;
+ INIT_CDB;
+ INIT_GETFSENT;
+ INIT_ARC4RANDOM;
+ INIT_POPEN;
+ INIT_POPENVE;
+ INIT_PCLOSE;
+ INIT_FUNOPEN;
+ INIT_FUNOPEN2;
+ INIT_FDEVNAME;
+ INIT_GETUSERSHELL;
+ INIT_SL_INIT;
INIT___PRINTF_CHK;
}
diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc
index 796a054858bc..1c0995b2d27b 100644
--- a/lib/sanitizer_common/sanitizer_common_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc
@@ -25,7 +25,7 @@ void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) {
SoftRssLimitExceededCallback = Callback;
}
-#if SANITIZER_LINUX && !SANITIZER_GO
+#if (SANITIZER_LINUX || SANITIZER_NETBSD) && !SANITIZER_GO
// Weak default implementation for when sanitizer_stackdepot is not linked in.
SANITIZER_WEAK_ATTRIBUTE StackDepotStats *StackDepotGetStats() {
return nullptr;
@@ -100,18 +100,21 @@ void WriteToSyslog(const char *msg) {
// Print one line at a time.
// syslog, at least on Android, has an implicit message length limit.
- do {
- q = internal_strchr(p, '\n');
- if (q)
- *q = '\0';
+ while ((q = internal_strchr(p, '\n'))) {
+ *q = '\0';
+ WriteOneLineToSyslog(p);
+ p = q + 1;
+ }
+ // Print remaining characters, if there are any.
+ // Note that this will add an extra newline at the end.
+ // FIXME: buffer extra output. This would need a thread-local buffer, which
+ // on Android requires plugging into the tools (ex. ASan's) Thread class.
+ if (*p)
WriteOneLineToSyslog(p);
- if (q)
- p = q + 1;
- } while (q);
}
void MaybeStartBackgroudThread() {
-#if SANITIZER_LINUX && \
+#if (SANITIZER_LINUX || SANITIZER_NETBSD) && \
!SANITIZER_GO // Need to implement/test on other platforms.
// Start the background thread if one of the rss limits is given.
if (!common_flags()->hard_rss_limit_mb &&
diff --git a/lib/sanitizer_common/sanitizer_coverage_fuchsia.cc b/lib/sanitizer_common/sanitizer_coverage_fuchsia.cc
index b4ffcca5c30b..9ff8798a5731 100644
--- a/lib/sanitizer_common/sanitizer_coverage_fuchsia.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_fuchsia.cc
@@ -31,6 +31,7 @@
#include "sanitizer_atomic.h"
#include "sanitizer_common.h"
#include "sanitizer_internal_defs.h"
+#include "sanitizer_symbolizer_fuchsia.h"
#include <zircon/process.h>
#include <zircon/sanitizer.h>
@@ -101,7 +102,7 @@ class TracePcGuardController final {
// uses the `dumpfile` symbolizer markup element to highlight the
// dump. See the explanation for this in:
// https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md
- Printf("SanitizerCoverage: {{{dumpfile:%s:%s}}} with up to %u PCs\n",
+ Printf("SanitizerCoverage: " FORMAT_DUMPFILE " with up to %u PCs\n",
kSancovSinkName, vmo_name_, next_index_ - 1);
}
}
@@ -146,9 +147,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 4b0bbf1ed1e5..108f76effeeb 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_file.h b/lib/sanitizer_common/sanitizer_file.h
index 9a12ab7342c7..52e6ef8fb1ac 100644
--- a/lib/sanitizer_common/sanitizer_file.h
+++ b/lib/sanitizer_common/sanitizer_file.h
@@ -66,9 +66,6 @@ bool ReadFromFile(fd_t fd, void *buff, uptr buff_size,
bool WriteToFile(fd_t fd, const void *buff, uptr buff_size,
uptr *bytes_written = nullptr, error_t *error_p = nullptr);
-bool RenameFile(const char *oldpath, const char *newpath,
- error_t *error_p = nullptr);
-
// Scoped file handle closer.
struct FileCloser {
explicit FileCloser(fd_t fd) : fd(fd) {}
diff --git a/lib/sanitizer_common/sanitizer_flags.inc b/lib/sanitizer_common/sanitizer_flags.inc
index cfe8af893423..b047741b9083 100644
--- a/lib/sanitizer_common/sanitizer_flags.inc
+++ b/lib/sanitizer_common/sanitizer_flags.inc
@@ -103,7 +103,7 @@ COMMON_FLAG(bool, allow_user_segv_handler, true,
"handle_*=1 will be upgraded to handle_*=2.")
COMMON_FLAG(bool, use_sigaltstack, true,
"If set, uses alternate stack for signal handling.")
-COMMON_FLAG(bool, detect_deadlocks, false,
+COMMON_FLAG(bool, detect_deadlocks, true,
"If set, deadlock detection is enabled.")
COMMON_FLAG(
uptr, clear_shadow_mmap_threshold, 64 * 1024,
@@ -243,3 +243,6 @@ COMMON_FLAG(bool, dump_registers, true,
COMMON_FLAG(bool, detect_write_exec, false,
"If true, triggers warning when writable-executable pages requests "
"are being made")
+COMMON_FLAG(bool, test_only_emulate_no_memorymap, false,
+ "TEST ONLY fail to read memory mappings to emulate sanitized "
+ "\"init\"")
diff --git a/lib/sanitizer_common/sanitizer_fuchsia.cc b/lib/sanitizer_common/sanitizer_fuchsia.cc
index 391620690f30..2c259b6cd7da 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() {
@@ -89,8 +86,10 @@ void GetThreadStackTopAndBottom(bool, uptr *stack_top, uptr *stack_bottom) {
*stack_top = *stack_bottom + size;
}
+void InitializePlatformEarly() {}
void MaybeReexec() {}
void CheckASLR() {}
+void CheckMPROTECT() {}
void PlatformPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {}
void DisableCoreDumperIfNecessary() {}
void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
@@ -122,8 +121,9 @@ void BlockingMutex::Lock() {
if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
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);
+ zx_status_t status =
+ _zx_futex_wait(reinterpret_cast<zx_futex_t *>(m), MtxSleeping,
+ ZX_HANDLE_INVALID, ZX_TIME_INFINITE);
if (status != ZX_ERR_BAD_STATE) // Normal race.
CHECK_EQ(status, ZX_OK);
}
@@ -175,8 +175,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) {
@@ -210,10 +210,10 @@ uptr ReservedAddressRange::Init(uptr init_size, const char *name,
uintptr_t base;
zx_handle_t vmar;
zx_status_t status =
- _zx_vmar_allocate_old(_zx_vmar_root_self(), 0, init_size,
- ZX_VM_FLAG_CAN_MAP_READ | ZX_VM_FLAG_CAN_MAP_WRITE |
- ZX_VM_FLAG_CAN_MAP_SPECIFIC,
- &vmar, &base);
+ _zx_vmar_allocate(
+ _zx_vmar_root_self(),
+ ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC,
+ 0, init_size, &vmar, &base);
if (status != ZX_OK)
ReportMmapFailureAndDie(init_size, name, "zx_vmar_allocate", status);
base_ = reinterpret_cast<void *>(base);
@@ -239,10 +239,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 +280,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 +328,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 +341,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 +404,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;
@@ -448,6 +454,7 @@ char **StoredArgv;
char **StoredEnviron;
char **GetArgv() { return StoredArgv; }
+char **GetEnviron() { return StoredEnviron; }
const char *GetEnv(const char *name) {
if (StoredEnviron) {
diff --git a/lib/sanitizer_common/sanitizer_interceptors_ioctl_netbsd.inc b/lib/sanitizer_common/sanitizer_interceptors_ioctl_netbsd.inc
index 9a2a9f120684..86cb44047606 100644
--- a/lib/sanitizer_common/sanitizer_interceptors_ioctl_netbsd.inc
+++ b/lib/sanitizer_common/sanitizer_interceptors_ioctl_netbsd.inc
@@ -25,7 +25,7 @@ struct ioctl_desc {
const char *name;
};
-const unsigned ioctl_table_max = 1198;
+const unsigned ioctl_table_max = 1202;
static ioctl_desc ioctl_table[ioctl_table_max];
static unsigned ioctl_table_size = 0;
@@ -288,6 +288,8 @@ static void ioctl_table_fill() {
_(MLX_GET_CINFO, WRITE, struct_mlx_cinfo_sz);
/* Entries from file: dev/ic/nvmeio.h */
_(NVME_PASSTHROUGH_CMD, READWRITE, struct_nvme_pt_command_sz);
+ /* Entries from file: dev/ic/qemufwcfgio.h */
+ _(FWCFGIO_SET_INDEX, READ, sizeof(u16));
/* Entries from file: dev/ir/irdaio.h */
_(IRDA_RESET_PARAMS, NONE, 0);
_(IRDA_SET_PARAMS, READ, struct_irda_params_sz);
@@ -647,6 +649,26 @@ static void ioctl_table_fill() {
_(SPKRTUNE, NONE, 0);
_(SPKRGETVOL, WRITE, sizeof(unsigned int));
_(SPKRSETVOL, READ, sizeof(unsigned int));
+ /* Entries from file: dev/nvmm/nvmm_ioctl.h */
+#if 0 /* WIP */
+ _(NVMM_IOC_CAPABILITY, WRITE, struct_nvmm_ioc_capability_sz);
+ _(NVMM_IOC_MACHINE_CREATE, READWRITE, struct_nvmm_ioc_machine_create_sz);
+ _(NVMM_IOC_MACHINE_DESTROY, READ, struct_nvmm_ioc_machine_destroy_sz);
+ _(NVMM_IOC_MACHINE_CONFIGURE, READ, struct_nvmm_ioc_machine_configure_sz);
+ _(NVMM_IOC_VCPU_CREATE, READ, struct_nvmm_ioc_vcpu_create_sz);
+ _(NVMM_IOC_VCPU_DESTROY, READ, struct_nvmm_ioc_vcpu_destroy_sz);
+ _(NVMM_IOC_VCPU_SETSTATE, READ, struct_nvmm_ioc_vcpu_setstate_sz);
+ _(NVMM_IOC_VCPU_GETSTATE, READ, struct_nvmm_ioc_vcpu_getstate_sz);
+ _(NVMM_IOC_VCPU_INJECT, READWRITE, struct_nvmm_ioc_vcpu_inject_sz);
+ _(NVMM_IOC_VCPU_RUN, READWRITE, struct_nvmm_ioc_vcpu_run_sz);
+ _(NVMM_IOC_GPA_MAP, READ, struct_nvmm_ioc_gpa_map_sz);
+ _(NVMM_IOC_GPA_UNMAP, READ, struct_nvmm_ioc_gpa_unmap_sz);
+ _(NVMM_IOC_HVA_MAP, READ, struct_nvmm_ioc_hva_map_sz);
+ _(NVMM_IOC_HVA_UNMAP, READ, struct_nvmm_ioc_hva_unmap_sz);
+#endif
+ /* Entries from file: fs/autofs/autofs_ioctl.h */
+ _(AUTOFSREQUEST, WRITE, struct_autofs_daemon_request_sz);
+ _(AUTOFSDONE, READ, struct_autofs_daemon_done_sz);
/* Entries from file: net/bpf.h */
_(BIOCGBLEN, WRITE, sizeof(unsigned int));
_(BIOCSBLEN, READWRITE, sizeof(unsigned int));
@@ -666,20 +688,12 @@ static void ioctl_table_fill() {
_(BIOCSHDRCMPLT, READ, sizeof(unsigned int));
_(BIOCSDLT, READ, sizeof(unsigned int));
_(BIOCGDLTLIST, READWRITE, struct_bpf_dltlist_sz);
- _(BIOCGSEESENT, WRITE, sizeof(unsigned int));
- _(BIOCSSEESENT, READ, sizeof(unsigned int));
+ _(BIOCGDIRECTION, WRITE, sizeof(unsigned int));
+ _(BIOCSDIRECTION, READ, sizeof(unsigned int));
_(BIOCSRTIMEOUT, READ, struct_timeval_sz);
_(BIOCGRTIMEOUT, WRITE, struct_timeval_sz);
_(BIOCGFEEDBACK, WRITE, sizeof(unsigned int));
_(BIOCSFEEDBACK, READ, sizeof(unsigned int));
- /* Entries from file: net/if_atm.h */
- _(SIOCRAWATM, READWRITE, sizeof(int));
- _(SIOCATMENA, READWRITE, struct_atm_pseudoioctl_sz);
- _(SIOCATMDIS, READWRITE, struct_atm_pseudoioctl_sz);
- _(SIOCSPVCTX, READWRITE, struct_pvctxreq_sz);
- _(SIOCGPVCTX, READWRITE, struct_pvctxreq_sz);
- _(SIOCSPVCSIF, READWRITE, struct_ifreq_sz);
- _(SIOCGPVCSIF, READWRITE, struct_ifreq_sz);
/* Entries from file: net/if_gre.h */
_(GRESADDRS, READ, struct_ifreq_sz);
_(GRESADDRD, READ, struct_ifreq_sz);
@@ -715,12 +729,12 @@ static void ioctl_table_fill() {
/* Entries from file: net/npf.h */
_(IOC_NPF_VERSION, WRITE, sizeof(int));
_(IOC_NPF_SWITCH, READ, sizeof(int));
- _(IOC_NPF_LOAD, READWRITE, struct_plistref_sz);
+ _(IOC_NPF_LOAD, READWRITE, struct_nvlist_ref_sz);
_(IOC_NPF_TABLE, READ, struct_npf_ioctl_table_sz);
_(IOC_NPF_STATS, READ, sizeof(uptr));
- _(IOC_NPF_SAVE, WRITE, struct_plistref_sz);
- _(IOC_NPF_RULE, READWRITE, struct_plistref_sz);
- _(IOC_NPF_CONN_LOOKUP, READWRITE, struct_plistref_sz);
+ _(IOC_NPF_SAVE, WRITE, struct_nvlist_ref_sz);
+ _(IOC_NPF_RULE, READWRITE, struct_nvlist_ref_sz);
+ _(IOC_NPF_CONN_LOOKUP, READWRITE, struct_nvlist_ref_sz);
/* Entries from file: net/if_pppoe.h */
_(PPPOESETPARMS, READ, struct_pppoediscparms_sz);
_(PPPOEGETPARMS, READWRITE, struct_pppoediscparms_sz);
@@ -843,6 +857,9 @@ static void ioctl_table_fill() {
_(SIOCGNATS, READWRITE, struct_ipfobj_sz);
_(SIOCGNATL, READWRITE, struct_ipfobj_sz);
_(SIOCPURGENAT, READWRITE, struct_ipfobj_sz);
+ /* Entries from file: netinet/sctp_uio.h */
+ _(SIOCCONNECTX, READWRITE, struct_sctp_connectx_addrs_sz);
+ _(SIOCCONNECTXDEL, READWRITE, struct_sctp_connectx_addrs_sz);
/* Entries from file: netinet6/in6_var.h */
_(SIOCSIFINFO_FLAGS, READWRITE, struct_in6_ndireq_sz);
_(SIOCAADDRCTL_POLICY, READ, struct_in6_addrpolicy_sz);
@@ -1002,6 +1019,8 @@ static void ioctl_table_fill() {
/* Entries from file: sys/filio.h */
_(FIOCLEX, NONE, 0);
_(FIONCLEX, NONE, 0);
+ _(FIOSEEKDATA, READWRITE, sizeof(uptr));
+ _(FIOSEEKHOLE, READWRITE, sizeof(uptr));
_(FIONREAD, WRITE, sizeof(int));
_(FIONBIO, READ, sizeof(int));
_(FIOASYNC, READ, sizeof(int));
@@ -1095,7 +1114,6 @@ static void ioctl_table_fill() {
/* Entries from file: sys/power.h */
_(POWER_EVENT_RECVDICT, READWRITE, struct_plistref_sz);
_(POWER_IOC_GET_TYPE, WRITE, struct_power_type_sz);
- _(POWER_IOC_GET_TYPE_WITH_LOSSAGE, WRITE, sizeof(uptr));
/* Entries from file: sys/radioio.h */
_(RIOCGINFO, WRITE, struct_radio_info_sz);
_(RIOCSINFO, READWRITE, struct_radio_info_sz);
@@ -1133,6 +1151,7 @@ static void ioctl_table_fill() {
_(SIOCATMARK, WRITE, sizeof(int));
_(SIOCSPGRP, READ, sizeof(int));
_(SIOCGPGRP, WRITE, sizeof(int));
+ _(SIOCPEELOFF, READWRITE, sizeof(int));
_(SIOCADDRT, READ, struct_ortentry_sz);
_(SIOCDELRT, READ, struct_ortentry_sz);
_(SIOCSIFADDR, READ, struct_ifreq_sz);
@@ -1190,6 +1209,10 @@ static void ioctl_table_fill() {
_(SIOCSLINKSTR, READ, struct_ifdrv_sz);
_(SIOCGETHERCAP, READWRITE, struct_eccapreq_sz);
_(SIOCGIFINDEX, READWRITE, struct_ifreq_sz);
+ _(SIOCSETHERCAP, READ, struct_eccapreq_sz);
+ _(SIOCGUMBINFO, READWRITE, struct_ifreq_sz);
+ _(SIOCSUMBPARAM, READ, struct_ifreq_sz);
+ _(SIOCGUMBPARAM, READWRITE, struct_ifreq_sz);
_(SIOCSETPFSYNC, READ, struct_ifreq_sz);
_(SIOCGETPFSYNC, READWRITE, struct_ifreq_sz);
/* Entries from file: sys/timepps.h */
diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h
index f8a405ba6e4d..14258d617e62 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.
@@ -172,6 +172,7 @@ typedef int pid_t;
#if SANITIZER_FREEBSD || SANITIZER_NETBSD || \
SANITIZER_OPENBSD || SANITIZER_MAC || \
+ (SANITIZER_SOLARIS && (defined(_LP64) || _FILE_OFFSET_BITS == 64)) || \
(SANITIZER_LINUX && defined(__x86_64__))
typedef u64 OFF_T;
#else
@@ -196,7 +197,9 @@ typedef u64 tid_t;
// This header should NOT include any other headers to avoid portability issues.
// Common defs.
+#ifndef INLINE
#define INLINE inline
+#endif
#define INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
#define SANITIZER_WEAK_DEFAULT_IMPL \
extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE
@@ -275,8 +278,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 +432,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 4b462bfe9728..4032cb10461f 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 96d6c1eff42c..48795674c54c 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
@@ -55,6 +51,7 @@
#include <sched.h>
#include <signal.h>
#include <sys/mman.h>
+#include <sys/param.h>
#if !SANITIZER_SOLARIS
#include <sys/ptrace.h>
#endif
@@ -68,6 +65,7 @@
#endif
#if SANITIZER_OPENBSD
#include <sys/futex.h>
+#include <sys/sysctl.h>
#endif
#include <unistd.h>
@@ -116,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?
@@ -149,7 +150,11 @@ extern void internal_sigreturn();
#if SANITIZER_OPENBSD
# define SANITIZER_USE_GETENTROPY 1
#else
-# define SANITIZER_USE_GETENTROPY 0
+# if SANITIZER_FREEBSD && __FreeBSD_version >= 1200000
+# define SANITIZER_USE_GETENTROPY 1
+# else
+# define SANITIZER_USE_GETENTROPY 0
+# endif
#endif // SANITIZER_USE_GETENTROPY
namespace __sanitizer {
@@ -165,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
@@ -186,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
@@ -202,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
}
@@ -211,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;
}
@@ -308,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);
@@ -333,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
@@ -360,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;
@@ -386,6 +381,10 @@ uptr internal_filesize(fd_t fd) {
return (uptr)st.st_size;
}
+uptr internal_dup(int oldfd) {
+ return internal_syscall(SYSCALL(dup), oldfd);
+}
+
uptr internal_dup2(int oldfd, int newfd) {
#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
return internal_syscall(SYSCALL(dup3), oldfd, newfd, 0);
@@ -399,10 +398,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
}
@@ -410,7 +409,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
}
@@ -419,7 +418,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
}
@@ -428,7 +427,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);
@@ -438,22 +437,24 @@ void internal__exit(int exitcode) {
unsigned int internal_sleep(unsigned int seconds) {
struct timespec ts;
- ts.tv_sec = 1;
+ ts.tv_sec = seconds;
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) {
+ if (ShouldMockFailureToOpen(filename))
+ return false;
struct stat st;
#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
if (internal_syscall(SYSCALL(newfstatat), AT_FDCWD, filename, &st, 0))
@@ -465,6 +466,7 @@ bool FileExists(const char *filename) {
return S_ISREG(st.st_mode);
}
+#if !SANITIZER_NETBSD
tid_t GetTid() {
#if SANITIZER_FREEBSD
long Tid;
@@ -472,8 +474,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
@@ -481,28 +481,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);
@@ -580,8 +595,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();
}
@@ -614,33 +629,13 @@ char **GetArgv() {
return argv;
}
-void ReExec() {
+char **GetEnviron() {
char **argv, **envp;
- const char *pathname = "/proc/self/exe";
-
-#if SANITIZER_NETBSD
- static const int name[] = {
- CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME,
- };
- char path[400];
- size_t len;
-
- len = sizeof(path);
- if (sysctl(name, ARRAY_SIZE(name), path, &len, NULL, 0) != -1)
- pathname = path;
-#elif SANITIZER_SOLARIS
- pathname = getexecname();
- CHECK_NE(pathname, NULL);
-#endif
-
GetArgsAndEnv(&argv, &envp);
- uptr rv = internal_execve(pathname, argv, envp);
- int rverrno;
- CHECK_EQ(internal_iserror(rv, &rverrno), true);
- Printf("execve failed, errno %d\n", rverrno);
- Die();
+ return envp;
}
-#endif
+
+#endif // !SANITIZER_OPENBSD
#if !SANITIZER_SOLARIS
enum MutexState {
@@ -664,7 +659,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
}
}
@@ -679,7 +675,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
}
}
@@ -694,7 +690,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 {
@@ -721,27 +719,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 */);
}
@@ -759,16 +745,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
@@ -778,7 +760,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() {
@@ -789,6 +771,26 @@ 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 internal_syscall(SYSCALL(__sysctl), name, namelen, oldp,
+ (size_t *)oldlenp, newp, (size_t)newlen);
+#endif
+}
+
+#if SANITIZER_FREEBSD
+int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
+ const void *newp, uptr newlen) {
+ 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
@@ -858,8 +860,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;
@@ -897,9 +899,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];
@@ -986,6 +999,7 @@ ThreadLister::~ThreadLister() {
if (!internal_iserror(descriptor_))
internal_close(descriptor_);
}
+#endif
#if SANITIZER_WORDSIZE == 32
// Take care of unusable kernel area in top gigabyte.
@@ -996,6 +1010,8 @@ static uptr GetKernelAreaSize() {
// Firstly check if there are writable segments
// mapped to top gigabyte (e.g. stack).
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ if (proc_maps.Error())
+ return 0;
MemoryMappedSegment segment;
while (proc_maps.Next(&segment)) {
if ((segment.end >= 3 * gbyte) && segment.IsWritable()) return 0;
@@ -1061,18 +1077,25 @@ uptr GetMaxUserVirtualAddress() {
return addr;
}
+#if !SANITIZER_ANDROID
uptr GetPageSize() {
-// Android post-M sysconf(_SC_PAGESIZE) crashes if called from .preinit_array.
-#if SANITIZER_ANDROID
- return 4096;
-#elif SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__))
+#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__))
return EXEC_PAGESIZE;
#elif SANITIZER_USE_GETAUXVAL
return getauxval(AT_PAGESZ);
+#elif SANITIZER_FREEBSD || SANITIZER_NETBSD
+// Use sysctl as sysconf can trigger interceptors internally.
+ int pz = 0;
+ uptr pzl = sizeof(pz);
+ int mib[2] = {CTL_HW, HW_PAGESIZE};
+ int rv = internal_sysctl(mib, 2, &pz, &pzl, nullptr, 0);
+ CHECK_EQ(rv, 0);
+ return (uptr)pz;
#else
return sysconf(_SC_PAGESIZE); // EXEC_PAGESIZE may not be trustworthy.
#endif
}
+#endif // !SANITIZER_ANDROID
#if !SANITIZER_OPENBSD
uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
@@ -1088,8 +1111,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
@@ -1239,7 +1263,7 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
"d"(parent_tidptr),
"r"(r8),
"r"(r10)
- : "rsp", "memory", "r11", "rcx");
+ : "memory", "r11", "rcx");
return res;
}
#elif defined(__mips__)
@@ -1631,6 +1655,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
@@ -1643,11 +1677,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;
}
@@ -1905,14 +1942,14 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
#elif defined(__sparc__)
ucontext_t *ucontext = (ucontext_t*)context;
uptr *stk_ptr;
-# if defined (__sparcv9)
+# if defined(__sparcv9) || defined (__arch64__)
# ifndef MC_PC
# define MC_PC REG_PC
# endif
# ifndef MC_O6
# define MC_O6 REG_O6
# endif
-# ifdef SANITIZER_SOLARIS
+# if SANITIZER_SOLARIS
# define mc_gregs gregs
# endif
*pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
@@ -1946,6 +1983,10 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
void SignalContext::InitPcSpBp() { GetPcSpBp(context, &pc, &sp, &bp); }
+void InitializePlatformEarly() {
+ // Do nothing.
+}
+
void MaybeReexec() {
// No need to re-exec on Linux.
}
@@ -1954,13 +1995,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();
}
@@ -1969,6 +2010,41 @@ void CheckASLR() {
Printf("This sanitizer is not compatible with enabled ASLR\n");
Die();
}
+#elif SANITIZER_PPC64V2
+ // Disable ASLR for Linux PPC64LE.
+ int old_personality = personality(0xffffffff);
+ if (old_personality != -1 && (old_personality & ADDR_NO_RANDOMIZE) == 0) {
+ VReport(1, "WARNING: Program is being run with address space layout "
+ "randomization (ASLR) enabled which prevents the thread and "
+ "memory sanitizers from working on powerpc64le.\n"
+ "ASLR will be disabled and the program re-executed.\n");
+ CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);
+ ReExec();
+ }
+#else
+ // Do nothing
+#endif
+}
+
+void CheckMPROTECT() {
+#if SANITIZER_NETBSD
+ int mib[3];
+ int paxflags;
+ uptr len = sizeof(paxflags);
+
+ mib[0] = CTL_PROC;
+ mib[1] = internal_getpid();
+ mib[2] = PROC_PID_PAXFLAGS;
+
+ if (UNLIKELY(internal_sysctl(mib, 3, &paxflags, &len, NULL, 0) == -1)) {
+ Printf("sysctl failed\n");
+ Die();
+ }
+
+ if (UNLIKELY(paxflags & CTL_PROC_PAXFLAGS_MPROTECT)) {
+ Printf("This sanitizer is not compatible with enabled MPROTECT\n");
+ Die();
+ }
#else
// Do nothing
#endif
diff --git a/lib/sanitizer_common/sanitizer_linux.h b/lib/sanitizer_common/sanitizer_linux.h
index 975d6541d88a..c309e33f81b1 100644
--- a/lib/sanitizer_common/sanitizer_linux.h
+++ b/lib/sanitizer_common/sanitizer_linux.h
@@ -18,6 +18,7 @@
SANITIZER_OPENBSD || SANITIZER_SOLARIS
#include "sanitizer_common.h"
#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_freebsd.h"
#include "sanitizer_platform_limits_netbsd.h"
#include "sanitizer_platform_limits_openbsd.h"
#include "sanitizer_platform_limits_posix.h"
@@ -69,6 +70,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.
@@ -131,13 +134,13 @@ void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr));
#error "Unsupported architecture."
#endif
-// The Android Bionic team has allocated a TLS slot for TSan starting with N,
-// given that Android currently doesn't support ELF TLS. It is used to store
-// Sanitizers thread specific data.
-static const int TLS_SLOT_TSAN = 8;
+// The Android Bionic team has allocated a TLS slot for sanitizers starting
+// with Q, given that Android currently doesn't support ELF TLS. It is used to
+// store sanitizer thread specific data.
+static const int TLS_SLOT_SANITIZER = 6;
ALWAYS_INLINE uptr *get_android_tls_ptr() {
- return reinterpret_cast<uptr *>(&__get_tls()[TLS_SLOT_TSAN]);
+ return reinterpret_cast<uptr *>(&__get_tls()[TLS_SLOT_SANITIZER]);
}
#endif // SANITIZER_ANDROID
diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
index 1c7fb7db8844..6ce47ec620dc 100644
--- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
@@ -23,6 +23,7 @@
#include "sanitizer_file.h"
#include "sanitizer_flags.h"
#include "sanitizer_freebsd.h"
+#include "sanitizer_getauxval.h"
#include "sanitizer_linux.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_procmaps.h"
@@ -99,6 +100,10 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
// Find the mapping that contains a stack variable.
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ if (proc_maps.Error()) {
+ *stack_top = *stack_bottom = 0;
+ return;
+ }
MemoryMappedSegment segment;
uptr prev_end = 0;
while (proc_maps.Next(&segment)) {
@@ -292,7 +297,7 @@ uptr ThreadDescriptorSize() {
val = FIRST_32_SECOND_64(1168, 1776);
else if (minor == 11 || (minor == 12 && patch == 1))
val = FIRST_32_SECOND_64(1168, 2288);
- else if (minor <= 13)
+ else if (minor <= 14)
val = FIRST_32_SECOND_64(1168, 2304);
else
val = FIRST_32_SECOND_64(1216, 2304);
@@ -652,10 +657,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
@@ -806,6 +811,40 @@ u64 MonotonicNanoTime() {
}
#endif // SANITIZER_LINUX && !SANITIZER_GO
+#if !SANITIZER_OPENBSD
+void ReExec() {
+ const char *pathname = "/proc/self/exe";
+
+#if SANITIZER_NETBSD
+ static const int name[] = {
+ CTL_KERN,
+ KERN_PROC_ARGS,
+ -1,
+ KERN_PROC_PATHNAME,
+ };
+ char path[400];
+ uptr len;
+
+ len = sizeof(path);
+ if (internal_sysctl(name, ARRAY_SIZE(name), path, &len, NULL, 0) != -1)
+ pathname = path;
+#elif SANITIZER_SOLARIS
+ pathname = getexecname();
+ CHECK_NE(pathname, NULL);
+#elif SANITIZER_USE_GETAUXVAL
+ // Calling execve with /proc/self/exe sets that as $EXEC_ORIGIN. Binaries that
+ // rely on that will fail to load shared libraries. Query AT_EXECFN instead.
+ pathname = reinterpret_cast<const char *>(getauxval(AT_EXECFN));
+#endif
+
+ uptr rv = internal_execve(pathname, GetArgv(), GetEnviron());
+ int rverrno;
+ CHECK_EQ(internal_iserror(rv, &rverrno), true);
+ Printf("execve failed, errno %d\n", rverrno);
+ Die();
+}
+#endif // !SANITIZER_OPENBSD
+
} // namespace __sanitizer
#endif
diff --git a/lib/sanitizer_common/sanitizer_local_address_space_view.h b/lib/sanitizer_common/sanitizer_local_address_space_view.h
new file mode 100644
index 000000000000..ec1847abc53d
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_local_address_space_view.h
@@ -0,0 +1,77 @@
+//===-- sanitizer_local_address_space_view.h --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// `LocalAddressSpaceView` provides the local (i.e. target and current address
+// space are the same) implementation of the `AddressSpaveView` interface which
+// provides a simple interface to load memory from another process (i.e.
+// out-of-process)
+//
+// The `AddressSpaceView` interface requires that the type can be used as a
+// template parameter to objects that wish to be able to operate in an
+// out-of-process manner. In normal usage, objects are in-process and are thus
+// instantiated with the `LocalAddressSpaceView` type. This type is used to
+// load any pointers in instance methods. This implementation is effectively
+// a no-op. When an object is to be used in an out-of-process manner it is
+// instansiated with the `RemoteAddressSpaceView` type.
+//
+// By making `AddressSpaceView` a template parameter of an object, it can
+// change its implementation at compile time which has no run time overhead.
+// This also allows unifying in-process and out-of-process code which avoids
+// code duplication.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_LOCAL_ADDRES_SPACE_VIEW_H
+#define SANITIZER_LOCAL_ADDRES_SPACE_VIEW_H
+
+namespace __sanitizer {
+struct LocalAddressSpaceView {
+ // Load memory `sizeof(T) * num_elements` bytes of memory from the target
+ // process (always local for this implementation) starting at address
+ // `target_address`. The local copy of this memory is returned as a pointer.
+ // The caller should not write to this memory. The behaviour when doing so is
+ // undefined. Callers should use `LoadWritable()` to get access to memory
+ // that is writable.
+ //
+ // The lifetime of loaded memory is implementation defined.
+ template <typename T>
+ static const T *Load(const T *target_address, uptr num_elements = 1) {
+ // The target address space is the local address space so
+ // nothing needs to be copied. Just return the pointer.
+ return target_address;
+ }
+
+ // Load memory `sizeof(T) * num_elements` bytes of memory from the target
+ // process (always local for this implementation) starting at address
+ // `target_address`. The local copy of this memory is returned as a pointer.
+ // The memory returned may be written to.
+ //
+ // Writes made to the returned memory will be visible in the memory returned
+ // by subsequent `Load()` or `LoadWritable()` calls provided the
+ // `target_address` parameter is the same. It is not guaranteed that the
+ // memory returned by previous calls to `Load()` will contain any performed
+ // writes. If two or more overlapping regions of memory are loaded via
+ // separate calls to `LoadWritable()`, it is implementation defined whether
+ // writes made to the region returned by one call are visible in the regions
+ // returned by other calls.
+ //
+ // Given the above it is recommended to load the largest possible object
+ // that requires modification (e.g. a class) rather than individual fields
+ // from a class to avoid issues with overlapping writable regions.
+ //
+ // The lifetime of loaded memory is implementation defined.
+ template <typename T>
+ static T *LoadWritable(T *target_address, uptr num_elements = 1) {
+ // The target address space is the local address space so
+ // nothing needs to be copied. Just return the pointer.
+ return target_address;
+ }
+};
+} // namespace __sanitizer
+
+#endif
diff --git a/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc
index 180d7c199aef..8954a7a88073 100644
--- a/lib/sanitizer_common/sanitizer_mac.cc
+++ b/lib/sanitizer_common/sanitizer_mac.cc
@@ -108,9 +108,20 @@ extern "C" int __munmap(void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
#define VM_MEMORY_SANITIZER 99
#endif
+// XNU on Darwin provides a mmap flag that optimizes allocation/deallocation of
+// giant memory regions (i.e. shadow memory regions).
+#define kXnuFastMmapFd 0x4
+static size_t kXnuFastMmapThreshold = 2 << 30; // 2 GB
+static bool use_xnu_fast_mmap = false;
+
uptr internal_mmap(void *addr, size_t length, int prot, int flags,
int fd, u64 offset) {
- if (fd == -1) fd = VM_MAKE_TAG(VM_MEMORY_SANITIZER);
+ if (fd == -1) {
+ fd = VM_MAKE_TAG(VM_MEMORY_SANITIZER);
+ if (length >= kXnuFastMmapThreshold) {
+ if (use_xnu_fast_mmap) fd |= kXnuFastMmapFd;
+ }
+ }
if (&__mmap) return (uptr)__mmap(addr, length, prot, flags, fd, offset);
return (uptr)mmap(addr, length, prot, flags, fd, offset);
}
@@ -163,6 +174,10 @@ uptr internal_filesize(fd_t fd) {
return (uptr)st.st_size;
}
+uptr internal_dup(int oldfd) {
+ return dup(oldfd);
+}
+
uptr internal_dup2(int oldfd, int newfd) {
return dup2(oldfd, newfd);
}
@@ -213,6 +228,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;
@@ -255,6 +282,8 @@ uptr internal_waitpid(int pid, int *status, int options) {
// ----------------- sanitizer_common.h
bool FileExists(const char *filename) {
+ if (ShouldMockFailureToOpen(filename))
+ return false;
struct stat st;
if (stat(filename, &st))
return false;
@@ -350,6 +379,10 @@ void CheckASLR() {
// Do nothing
}
+void CheckMPROTECT() {
+ // Do nothing
+}
+
uptr GetPageSize() {
return sysconf(_SC_PAGESIZE);
}
@@ -499,26 +532,38 @@ 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);
- switch (version[0]) {
- case '9': return MACOS_VERSION_LEOPARD;
- case '1': {
- switch (version[1]) {
- case '0': return MACOS_VERSION_SNOW_LEOPARD;
- case '1': return MACOS_VERSION_LION;
- case '2': return MACOS_VERSION_MOUNTAIN_LION;
- case '3': return MACOS_VERSION_MAVERICKS;
- case '4': return MACOS_VERSION_YOSEMITE;
- default:
- if (IsDigit(version[1]))
- return MACOS_VERSION_UNKNOWN_NEWER;
- else
- return MACOS_VERSION_UNKNOWN;
- }
- }
- default: return MACOS_VERSION_UNKNOWN;
+ CHECK_NE(internal_sysctl(mib, 2, version, &len, 0, 0), -1);
+
+ // Expect <major>.<minor>(.<patch>)
+ CHECK_GE(len, 3);
+ const char *p = version;
+ int major = internal_simple_strtoll(p, &p, /*base=*/10);
+ if (*p != '.') return MACOS_VERSION_UNKNOWN;
+ p += 1;
+ int minor = internal_simple_strtoll(p, &p, /*base=*/10);
+ if (*p != '.') return MACOS_VERSION_UNKNOWN;
+
+ switch (major) {
+ case 9: return MACOS_VERSION_LEOPARD;
+ case 10: return MACOS_VERSION_SNOW_LEOPARD;
+ case 11: return MACOS_VERSION_LION;
+ case 12: return MACOS_VERSION_MOUNTAIN_LION;
+ case 13: return MACOS_VERSION_MAVERICKS;
+ case 14: return MACOS_VERSION_YOSEMITE;
+ case 15: return MACOS_VERSION_EL_CAPITAN;
+ case 16: return MACOS_VERSION_SIERRA;
+ case 17:
+ // Not a typo, 17.5 Darwin Kernel Version maps to High Sierra 10.13.4.
+ if (minor >= 5)
+ return MACOS_VERSION_HIGH_SIERRA_DOT_RELEASE_4;
+ return MACOS_VERSION_HIGH_SIERRA;
+ case 18:
+ return MACOS_VERSION_MOJAVE;
+ default:
+ if (major < 9) return MACOS_VERSION_UNKNOWN;
+ return MACOS_VERSION_UNKNOWN_NEWER;
}
}
@@ -661,6 +706,16 @@ static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
void SignalContext::InitPcSpBp() { GetPcSpBp(context, &pc, &sp, &bp); }
+void InitializePlatformEarly() {
+ // Only use xnu_fast_mmap when on x86_64 and the OS supports it.
+ use_xnu_fast_mmap =
+#if defined(__x86_64__)
+ GetMacosVersion() >= MACOS_VERSION_HIGH_SIERRA_DOT_RELEASE_4;
+#else
+ false;
+#endif
+}
+
#if !SANITIZER_GO
static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
LowLevelAllocator allocator_for_env;
@@ -890,10 +945,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 +1115,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 e022a2c0363c..52825f8cf819 100644
--- a/lib/sanitizer_common/sanitizer_mac.h
+++ b/lib/sanitizer_common/sanitizer_mac.h
@@ -40,6 +40,11 @@ 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_HIGH_SIERRA_DOT_RELEASE_4,
+ MACOS_VERSION_MOJAVE,
MACOS_VERSION_UNKNOWN_NEWER
};
diff --git a/lib/sanitizer_common/sanitizer_malloc_mac.inc b/lib/sanitizer_common/sanitizer_malloc_mac.inc
index e69d6f94b0e1..44c914cea9f9 100644
--- a/lib/sanitizer_common/sanitizer_malloc_mac.inc
+++ b/lib/sanitizer_common/sanitizer_malloc_mac.inc
@@ -30,9 +30,27 @@
// https://github.com/gperftools/gperftools.
namespace __sanitizer {
+
extern malloc_zone_t sanitizer_zone;
+
+struct sanitizer_malloc_introspection_t : public malloc_introspection_t {
+ // IMPORTANT: Do not change the order, alignment, or types of these fields to
+ // maintain binary compatibility. You should only add fields to this struct.
+
+ // Used to track changes to the allocator that will affect
+ // zone enumeration.
+ u64 allocator_enumeration_version;
+};
+
+u64 GetMallocZoneAllocatorEnumerationVersion() {
+ // This represents the current allocator ABI version.
+ // This field should be incremented every time the Allocator
+ // ABI changes in a way that breaks allocator enumeration.
+ return 0;
}
+} // namespace __sanitizer
+
INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
vm_size_t start_size, unsigned zone_flags) {
COMMON_MALLOC_ENTER();
@@ -247,6 +265,13 @@ void *__sanitizer_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
return p;
}
+// This public API exists purely for testing purposes.
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+malloc_zone_t* __sanitizer_mz_default_zone() {
+ return &sanitizer_zone;
+}
+
// This function is currently unused, and we build with -Werror.
#if 0
void __sanitizer_mz_free_definite_size(
@@ -302,8 +327,8 @@ boolean_t mi_zone_locked(malloc_zone_t *zone) {
namespace COMMON_MALLOC_NAMESPACE {
-void ReplaceSystemMalloc() {
- static malloc_introspection_t sanitizer_zone_introspection;
+void InitMallocZoneFields() {
+ static sanitizer_malloc_introspection_t sanitizer_zone_introspection;
// Ok to use internal_memset, these places are not performance-critical.
internal_memset(&sanitizer_zone_introspection, 0,
sizeof(sanitizer_zone_introspection));
@@ -318,6 +343,10 @@ void ReplaceSystemMalloc() {
sanitizer_zone_introspection.statistics = &mi_statistics;
sanitizer_zone_introspection.zone_locked = &mi_zone_locked;
+ // Set current allocator enumeration version.
+ sanitizer_zone_introspection.allocator_enumeration_version =
+ GetMallocZoneAllocatorEnumerationVersion();
+
internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
// Use version 6 for OSX >= 10.6.
@@ -335,6 +364,10 @@ void ReplaceSystemMalloc() {
sanitizer_zone.free_definite_size = 0;
sanitizer_zone.memalign = &__sanitizer_mz_memalign;
sanitizer_zone.introspect = &sanitizer_zone_introspection;
+}
+
+void ReplaceSystemMalloc() {
+ InitMallocZoneFields();
// Register the zone.
malloc_zone_register(&sanitizer_zone);
diff --git a/lib/sanitizer_common/sanitizer_netbsd.cc b/lib/sanitizer_common/sanitizer_netbsd.cc
new file mode 100644
index 000000000000..80d0855efab0
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_netbsd.cc
@@ -0,0 +1,335 @@
+//===-- 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_dup(int oldfd) {
+ DEFINE__REAL(int, dup, int a);
+ return _REAL(dup, oldfd);
+}
+
+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 = seconds;
+ 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 2aea7cb14875..f0d071e51f02 100644
--- a/lib/sanitizer_common/sanitizer_openbsd.cc
+++ b/lib/sanitizer_common/sanitizer_openbsd.cc
@@ -51,12 +51,19 @@ int internal_mprotect(void *addr, uptr length, int prot) {
return mprotect(addr, length, prot);
}
+int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
+ const void *newp, uptr newlen) {
+ Printf("internal_sysctlbyname not implemented for OpenBSD");
+ Die();
+ return 0;
+}
+
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 +71,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();
}
@@ -92,6 +99,12 @@ char **GetArgv() {
return argv;
}
+char **GetEnviron() {
+ char **argv, **envp;
+ GetArgsAndEnv(&argv, &envp);
+ return envp;
+}
+
void ReExec() {
UNIMPLEMENTED();
}
diff --git a/lib/sanitizer_common/sanitizer_platform.h b/lib/sanitizer_common/sanitizer_platform.h
index d81e25580d9e..82bb1af7746b 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
@@ -270,12 +275,6 @@
# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12)
#endif
-// Assume obsolete RPC headers are available by default
-#if !defined(HAVE_RPC_XDR_H) && !defined(HAVE_TIRPC_RPC_XDR_H)
-# define HAVE_RPC_XDR_H (SANITIZER_LINUX && !SANITIZER_ANDROID)
-# define HAVE_TIRPC_RPC_XDR_H 0
-#endif
-
/// \macro MSC_PREREQ
/// \brief Is the compiler MSVC of at least the specified version?
/// The common \param version values to check for are:
@@ -337,4 +336,13 @@
#define SANITIZER_SYMBOLIZER_MARKUP 0
#endif
+// Enable ability to support sanitizer initialization that is
+// compatible with the sanitizer library being loaded via
+// `dlopen()`.
+#if SANITIZER_MAC
+#define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 1
+#else
+#define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 0
+#endif
+
#endif // SANITIZER_PLATFORM_H
diff --git a/lib/sanitizer_common/sanitizer_platform_interceptors.h b/lib/sanitizer_common/sanitizer_platform_interceptors.h
index f95539a73c69..4d146651b78d 100644
--- a/lib/sanitizer_common/sanitizer_platform_interceptors.h
+++ b/lib/sanitizer_common/sanitizer_platform_interceptors.h
@@ -33,8 +33,9 @@
#endif
#if SI_POSIX
+# include "sanitizer_platform_limits_freebsd.h"
# include "sanitizer_platform_limits_netbsd.h"
-#include "sanitizer_platform_limits_openbsd.h"
+# include "sanitizer_platform_limits_openbsd.h"
# include "sanitizer_platform_limits_posix.h"
# include "sanitizer_platform_limits_solaris.h"
#endif
@@ -207,9 +208,13 @@
#define SANITIZER_INTERCEPT_GETPWENT \
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_LINUX_NOT_ANDROID || \
SI_SOLARIS)
+#define SANITIZER_INTERCEPT_FGETGRENT_R \
+ (SI_FREEBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
#define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID || SI_SOLARIS
#define SANITIZER_INTERCEPT_GETPWENT_R \
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
+#define SANITIZER_INTERCEPT_FGETPWENT_R \
+ (SI_FREEBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
#define SANITIZER_INTERCEPT_SETPWENT \
(SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
#define SANITIZER_INTERCEPT_CLOCK_GETTIME \
@@ -356,7 +361,9 @@
#define SANITIZER_INTERCEPT_TEMPNAM SI_POSIX
#define SANITIZER_INTERCEPT_SINCOS SI_LINUX || SI_SOLARIS
#define SANITIZER_INTERCEPT_REMQUO SI_POSIX
+#define SANITIZER_INTERCEPT_REMQUOL (SI_POSIX && !SI_NETBSD)
#define SANITIZER_INTERCEPT_LGAMMA SI_POSIX
+#define SANITIZER_INTERCEPT_LGAMMAL (SI_POSIX && !SI_NETBSD)
#define SANITIZER_INTERCEPT_LGAMMA_R (SI_FREEBSD || SI_LINUX || SI_SOLARIS)
#define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID || SI_SOLARIS
#define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID
@@ -380,7 +387,7 @@
#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
#define SANITIZER_INTERCEPT_PTHREAD_GETNAME_NP \
- (SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
+ (SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
#define SANITIZER_INTERCEPT_TLS_GET_ADDR \
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
@@ -510,5 +517,43 @@
#define SANITIZER_INTERCEPT_TTYENT SI_NETBSD
#define SANITIZER_INTERCEPT_PROTOENT SI_NETBSD
#define SANITIZER_INTERCEPT_NETENT SI_NETBSD
+#define SANITIZER_INTERCEPT_SETVBUF (SI_NETBSD || SI_FREEBSD || \
+ SI_LINUX || SI_MAC)
+#define SANITIZER_INTERCEPT_GETMNTINFO (SI_NETBSD || SI_FREEBSD || SI_MAC)
+#define SANITIZER_INTERCEPT_MI_VECTOR_HASH SI_NETBSD
+#define SANITIZER_INTERCEPT_GETVFSSTAT SI_NETBSD
+#define SANITIZER_INTERCEPT_REGEX (SI_NETBSD || SI_FREEBSD || SI_LINUX)
+#define SANITIZER_INTERCEPT_REGEXSUB SI_NETBSD
+#define SANITIZER_INTERCEPT_FTS (SI_NETBSD || SI_FREEBSD)
+#define SANITIZER_INTERCEPT_SYSCTL (SI_NETBSD || SI_FREEBSD || SI_MAC)
+#define SANITIZER_INTERCEPT_ASYSCTL SI_NETBSD
+#define SANITIZER_INTERCEPT_SYSCTLGETMIBINFO SI_NETBSD
+#define SANITIZER_INTERCEPT_NL_LANGINFO (SI_NETBSD || SI_FREEBSD || SI_MAC)
+#define SANITIZER_INTERCEPT_MODCTL SI_NETBSD
+#define SANITIZER_INTERCEPT_CAPSICUM SI_FREEBSD
+#define SANITIZER_INTERCEPT_STRTONUM (SI_NETBSD || SI_FREEBSD)
+#define SANITIZER_INTERCEPT_FPARSELN SI_NETBSD
+#define SANITIZER_INTERCEPT_STATVFS1 SI_NETBSD
+#define SANITIZER_INTERCEPT_STRTOI SI_NETBSD
+#define SANITIZER_INTERCEPT_CAPSICUM SI_FREEBSD
+#define SANITIZER_INTERCEPT_SHA1 SI_NETBSD
+#define SANITIZER_INTERCEPT_MD4 SI_NETBSD
+#define SANITIZER_INTERCEPT_RMD160 SI_NETBSD
+#define SANITIZER_INTERCEPT_MD5 SI_NETBSD
+#define SANITIZER_INTERCEPT_FSEEK (SI_NETBSD || SI_FREEBSD)
+#define SANITIZER_INTERCEPT_MD2 SI_NETBSD
+#define SANITIZER_INTERCEPT_SHA2 SI_NETBSD
+#define SANITIZER_INTERCEPT_CDB SI_NETBSD
+#define SANITIZER_INTERCEPT_VIS (SI_NETBSD || SI_FREEBSD)
+#define SANITIZER_INTERCEPT_POPEN SI_POSIX
+#define SANITIZER_INTERCEPT_POPENVE SI_NETBSD
+#define SANITIZER_INTERCEPT_PCLOSE SI_POSIX
+#define SANITIZER_INTERCEPT_FUNOPEN (SI_NETBSD || SI_FREEBSD)
+#define SANITIZER_INTERCEPT_FUNOPEN2 SI_NETBSD
+#define SANITIZER_INTERCEPT_GETFSENT (SI_FREEBSD || SI_NETBSD || SI_MAC)
+#define SANITIZER_INTERCEPT_ARC4RANDOM (SI_FREEBSD || SI_NETBSD)
+#define SANITIZER_INTERCEPT_FDEVNAME SI_FREEBSD
+#define SANITIZER_INTERCEPT_GETUSERSHELL (SI_POSIX && !SI_POSIX)
+#define SANITIZER_INTERCEPT_SL_INIT (SI_FREEBSD || SI_NETBSD)
#endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_freebsd.cc b/lib/sanitizer_common/sanitizer_platform_limits_freebsd.cc
new file mode 100644
index 000000000000..377a62cab6c4
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_platform_limits_freebsd.cc
@@ -0,0 +1,526 @@
+//===-- sanitizer_platform_limits_freebsd.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 common code.
+//
+// Sizes and layouts of platform-specific FreeBSD data structures.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_FREEBSD
+
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <fts.h>
+#include <fstab.h>
+#include <grp.h>
+#include <limits.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <poll.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <regex.h>
+#include <signal.h>
+#include <stddef.h>
+#include <sys/mman.h>
+#include <sys/capsicum.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <termios.h>
+#include <time.h>
+
+#include <net/route.h>
+#include <sys/mount.h>
+#include <sys/sockio.h>
+#include <sys/socket.h>
+#include <sys/filio.h>
+#include <sys/signal.h>
+#include <sys/timespec.h>
+#include <sys/timeb.h>
+#include <sys/mqueue.h>
+#include <sys/msg.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/statvfs.h>
+#include <sys/soundcard.h>
+#include <sys/mtio.h>
+#include <sys/consio.h>
+#include <sys/kbio.h>
+#include <sys/link_elf.h>
+#include <netinet/ip_mroute.h>
+#include <netinet/in.h>
+#include <net/ethernet.h>
+#include <net/ppp_defs.h>
+#include <glob.h>
+#include <stdio.h>
+#include <stringlist.h>
+#include <term.h>
+#include <utmpx.h>
+#include <wchar.h>
+#include <vis.h>
+
+#define _KERNEL // to declare 'shminfo' structure
+# include <sys/shm.h>
+#undef _KERNEL
+
+#undef INLINE // to avoid clashes with sanitizers' definitions
+
+#undef IOC_DIRMASK
+
+# include <utime.h>
+# include <sys/ptrace.h>
+# include <semaphore.h>
+
+#include <ifaddrs.h>
+#include <sys/ucontext.h>
+#include <wordexp.h>
+
+// Include these after system headers to avoid name clashes and ambiguities.
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_freebsd.h"
+
+namespace __sanitizer {
+ unsigned struct_cap_rights_sz = sizeof(cap_rights_t);
+ unsigned struct_utsname_sz = sizeof(struct utsname);
+ unsigned struct_stat_sz = sizeof(struct stat);
+ unsigned struct_rusage_sz = sizeof(struct rusage);
+ unsigned struct_tm_sz = sizeof(struct tm);
+ unsigned struct_passwd_sz = sizeof(struct passwd);
+ unsigned struct_group_sz = sizeof(struct group);
+ unsigned siginfo_t_sz = sizeof(siginfo_t);
+ unsigned struct_sigaction_sz = sizeof(struct sigaction);
+ unsigned struct_itimerval_sz = sizeof(struct itimerval);
+ unsigned pthread_t_sz = sizeof(pthread_t);
+ unsigned pthread_mutex_t_sz = sizeof(pthread_mutex_t);
+ unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
+ unsigned pid_t_sz = sizeof(pid_t);
+ unsigned timeval_sz = sizeof(timeval);
+ unsigned uid_t_sz = sizeof(uid_t);
+ unsigned gid_t_sz = sizeof(gid_t);
+ unsigned fpos_t_sz = sizeof(fpos_t);
+ unsigned mbstate_t_sz = sizeof(mbstate_t);
+ unsigned sigset_t_sz = sizeof(sigset_t);
+ unsigned struct_timezone_sz = sizeof(struct timezone);
+ unsigned struct_tms_sz = sizeof(struct tms);
+ unsigned struct_sigevent_sz = sizeof(struct sigevent);
+ unsigned struct_sched_param_sz = sizeof(struct sched_param);
+ unsigned struct_statfs_sz = sizeof(struct statfs);
+ unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
+ unsigned ucontext_t_sz = sizeof(ucontext_t);
+ unsigned struct_rlimit_sz = sizeof(struct rlimit);
+ unsigned struct_timespec_sz = sizeof(struct timespec);
+ unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
+ unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
+ unsigned struct_timeb_sz = sizeof(struct timeb);
+ unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
+ unsigned struct_mq_attr_sz = sizeof(struct mq_attr);
+ unsigned struct_statvfs_sz = sizeof(struct statvfs);
+ unsigned struct_shminfo_sz = sizeof(struct shminfo);
+ unsigned struct_shm_info_sz = sizeof(struct shm_info);
+ unsigned struct_regmatch_sz = sizeof(regmatch_t);
+ unsigned struct_regex_sz = sizeof(regex_t);
+ unsigned struct_fstab_sz = sizeof(struct fstab);
+ unsigned struct_FTS_sz = sizeof(FTS);
+ unsigned struct_FTSENT_sz = sizeof(FTSENT);
+ unsigned struct_StringList_sz = sizeof(StringList);
+
+ const uptr sig_ign = (uptr)SIG_IGN;
+ const uptr sig_dfl = (uptr)SIG_DFL;
+ const uptr sig_err = (uptr)SIG_ERR;
+ const uptr sa_siginfo = (uptr)SA_SIGINFO;
+
+ int shmctl_ipc_stat = (int)IPC_STAT;
+ int shmctl_ipc_info = (int)IPC_INFO;
+ int shmctl_shm_info = (int)SHM_INFO;
+ int shmctl_shm_stat = (int)SHM_STAT;
+ unsigned struct_utmpx_sz = sizeof(struct utmpx);
+
+ int map_fixed = MAP_FIXED;
+
+ int af_inet = (int)AF_INET;
+ int af_inet6 = (int)AF_INET6;
+
+ uptr __sanitizer_in_addr_sz(int af) {
+ if (af == AF_INET)
+ return sizeof(struct in_addr);
+ else if (af == AF_INET6)
+ return sizeof(struct in6_addr);
+ else
+ return 0;
+ }
+
+ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
+ int glob_nomatch = GLOB_NOMATCH;
+ int glob_altdirfunc = GLOB_ALTDIRFUNC;
+
+ unsigned path_max = PATH_MAX;
+
+ // ioctl arguments
+ unsigned struct_ifreq_sz = sizeof(struct ifreq);
+ unsigned struct_termios_sz = sizeof(struct termios);
+ unsigned struct_winsize_sz = sizeof(struct winsize);
+#if SOUND_VERSION >= 0x040000
+ unsigned struct_copr_buffer_sz = 0;
+ unsigned struct_copr_debug_buf_sz = 0;
+ unsigned struct_copr_msg_sz = 0;
+#else
+ unsigned struct_copr_buffer_sz = sizeof(struct copr_buffer);
+ unsigned struct_copr_debug_buf_sz = sizeof(struct copr_debug_buf);
+ unsigned struct_copr_msg_sz = sizeof(struct copr_msg);
+#endif
+ unsigned struct_midi_info_sz = sizeof(struct midi_info);
+ unsigned struct_mtget_sz = sizeof(struct mtget);
+ unsigned struct_mtop_sz = sizeof(struct mtop);
+ unsigned struct_sbi_instrument_sz = sizeof(struct sbi_instrument);
+ unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec);
+ unsigned struct_synth_info_sz = sizeof(struct synth_info);
+ unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info);
+ unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats);
+ unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
+ unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
+ const unsigned long __sanitizer_bufsiz = BUFSIZ;
+
+ const unsigned IOCTL_NOT_PRESENT = 0;
+
+ unsigned IOCTL_FIOASYNC = FIOASYNC;
+ unsigned IOCTL_FIOCLEX = FIOCLEX;
+ unsigned IOCTL_FIOGETOWN = FIOGETOWN;
+ unsigned IOCTL_FIONBIO = FIONBIO;
+ unsigned IOCTL_FIONCLEX = FIONCLEX;
+ unsigned IOCTL_FIOSETOWN = FIOSETOWN;
+ unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI;
+ unsigned IOCTL_SIOCATMARK = SIOCATMARK;
+ unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI;
+ unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR;
+ unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR;
+ unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF;
+ unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR;
+ unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS;
+ unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC;
+ unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU;
+ unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK;
+ unsigned IOCTL_SIOCGPGRP = SIOCGPGRP;
+ unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR;
+ unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR;
+ unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR;
+ unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS;
+ unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC;
+ unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU;
+ unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK;
+ unsigned IOCTL_SIOCSPGRP = SIOCSPGRP;
+ unsigned IOCTL_TIOCCONS = TIOCCONS;
+ unsigned IOCTL_TIOCEXCL = TIOCEXCL;
+ unsigned IOCTL_TIOCGETD = TIOCGETD;
+ unsigned IOCTL_TIOCGPGRP = TIOCGPGRP;
+ unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ;
+ unsigned IOCTL_TIOCMBIC = TIOCMBIC;
+ unsigned IOCTL_TIOCMBIS = TIOCMBIS;
+ unsigned IOCTL_TIOCMGET = TIOCMGET;
+ unsigned IOCTL_TIOCMSET = TIOCMSET;
+ unsigned IOCTL_TIOCNOTTY = TIOCNOTTY;
+ unsigned IOCTL_TIOCNXCL = TIOCNXCL;
+ unsigned IOCTL_TIOCOUTQ = TIOCOUTQ;
+ unsigned IOCTL_TIOCPKT = TIOCPKT;
+ unsigned IOCTL_TIOCSCTTY = TIOCSCTTY;
+ unsigned IOCTL_TIOCSETD = TIOCSETD;
+ unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
+ unsigned IOCTL_TIOCSTI = TIOCSTI;
+ unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
+ unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT;
+ unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT;
+ unsigned IOCTL_MTIOCGET = MTIOCGET;
+ unsigned IOCTL_MTIOCTOP = MTIOCTOP;
+ unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE;
+ unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS;
+ unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK;
+ unsigned IOCTL_SNDCTL_DSP_POST = SNDCTL_DSP_POST;
+ unsigned IOCTL_SNDCTL_DSP_RESET = SNDCTL_DSP_RESET;
+ unsigned IOCTL_SNDCTL_DSP_SETFMT = SNDCTL_DSP_SETFMT;
+ unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT = SNDCTL_DSP_SETFRAGMENT;
+ unsigned IOCTL_SNDCTL_DSP_SPEED = SNDCTL_DSP_SPEED;
+ unsigned IOCTL_SNDCTL_DSP_STEREO = SNDCTL_DSP_STEREO;
+ unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE = SNDCTL_DSP_SUBDIVIDE;
+ unsigned IOCTL_SNDCTL_DSP_SYNC = SNDCTL_DSP_SYNC;
+ unsigned IOCTL_SNDCTL_FM_4OP_ENABLE = SNDCTL_FM_4OP_ENABLE;
+ unsigned IOCTL_SNDCTL_FM_LOAD_INSTR = SNDCTL_FM_LOAD_INSTR;
+ unsigned IOCTL_SNDCTL_MIDI_INFO = SNDCTL_MIDI_INFO;
+ unsigned IOCTL_SNDCTL_MIDI_PRETIME = SNDCTL_MIDI_PRETIME;
+ unsigned IOCTL_SNDCTL_SEQ_CTRLRATE = SNDCTL_SEQ_CTRLRATE;
+ unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT = SNDCTL_SEQ_GETINCOUNT;
+ unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT = SNDCTL_SEQ_GETOUTCOUNT;
+ unsigned IOCTL_SNDCTL_SEQ_NRMIDIS = SNDCTL_SEQ_NRMIDIS;
+ unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS = SNDCTL_SEQ_NRSYNTHS;
+ unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND = SNDCTL_SEQ_OUTOFBAND;
+ unsigned IOCTL_SNDCTL_SEQ_PANIC = SNDCTL_SEQ_PANIC;
+ unsigned IOCTL_SNDCTL_SEQ_PERCMODE = SNDCTL_SEQ_PERCMODE;
+ unsigned IOCTL_SNDCTL_SEQ_RESET = SNDCTL_SEQ_RESET;
+ unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES = SNDCTL_SEQ_RESETSAMPLES;
+ unsigned IOCTL_SNDCTL_SEQ_SYNC = SNDCTL_SEQ_SYNC;
+ unsigned IOCTL_SNDCTL_SEQ_TESTMIDI = SNDCTL_SEQ_TESTMIDI;
+ unsigned IOCTL_SNDCTL_SEQ_THRESHOLD = SNDCTL_SEQ_THRESHOLD;
+ unsigned IOCTL_SNDCTL_SYNTH_INFO = SNDCTL_SYNTH_INFO;
+ unsigned IOCTL_SNDCTL_SYNTH_MEMAVL = SNDCTL_SYNTH_MEMAVL;
+ unsigned IOCTL_SNDCTL_TMR_CONTINUE = SNDCTL_TMR_CONTINUE;
+ unsigned IOCTL_SNDCTL_TMR_METRONOME = SNDCTL_TMR_METRONOME;
+ unsigned IOCTL_SNDCTL_TMR_SELECT = SNDCTL_TMR_SELECT;
+ unsigned IOCTL_SNDCTL_TMR_SOURCE = SNDCTL_TMR_SOURCE;
+ unsigned IOCTL_SNDCTL_TMR_START = SNDCTL_TMR_START;
+ unsigned IOCTL_SNDCTL_TMR_STOP = SNDCTL_TMR_STOP;
+ unsigned IOCTL_SNDCTL_TMR_TEMPO = SNDCTL_TMR_TEMPO;
+ unsigned IOCTL_SNDCTL_TMR_TIMEBASE = SNDCTL_TMR_TIMEBASE;
+ unsigned IOCTL_SOUND_MIXER_READ_ALTPCM = SOUND_MIXER_READ_ALTPCM;
+ unsigned IOCTL_SOUND_MIXER_READ_BASS = SOUND_MIXER_READ_BASS;
+ unsigned IOCTL_SOUND_MIXER_READ_CAPS = SOUND_MIXER_READ_CAPS;
+ unsigned IOCTL_SOUND_MIXER_READ_CD = SOUND_MIXER_READ_CD;
+ unsigned IOCTL_SOUND_MIXER_READ_DEVMASK = SOUND_MIXER_READ_DEVMASK;
+ unsigned IOCTL_SOUND_MIXER_READ_ENHANCE = SOUND_MIXER_READ_ENHANCE;
+ unsigned IOCTL_SOUND_MIXER_READ_IGAIN = SOUND_MIXER_READ_IGAIN;
+ unsigned IOCTL_SOUND_MIXER_READ_IMIX = SOUND_MIXER_READ_IMIX;
+ unsigned IOCTL_SOUND_MIXER_READ_LINE = SOUND_MIXER_READ_LINE;
+ unsigned IOCTL_SOUND_MIXER_READ_LINE1 = SOUND_MIXER_READ_LINE1;
+ unsigned IOCTL_SOUND_MIXER_READ_LINE2 = SOUND_MIXER_READ_LINE2;
+ unsigned IOCTL_SOUND_MIXER_READ_LINE3 = SOUND_MIXER_READ_LINE3;
+ unsigned IOCTL_SOUND_MIXER_READ_LOUD = SOUND_MIXER_READ_LOUD;
+ unsigned IOCTL_SOUND_MIXER_READ_MIC = SOUND_MIXER_READ_MIC;
+ unsigned IOCTL_SOUND_MIXER_READ_MUTE = SOUND_MIXER_READ_MUTE;
+ unsigned IOCTL_SOUND_MIXER_READ_OGAIN = SOUND_MIXER_READ_OGAIN;
+ unsigned IOCTL_SOUND_MIXER_READ_PCM = SOUND_MIXER_READ_PCM;
+ unsigned IOCTL_SOUND_MIXER_READ_RECLEV = SOUND_MIXER_READ_RECLEV;
+ unsigned IOCTL_SOUND_MIXER_READ_RECMASK = SOUND_MIXER_READ_RECMASK;
+ unsigned IOCTL_SOUND_MIXER_READ_RECSRC = SOUND_MIXER_READ_RECSRC;
+ unsigned IOCTL_SOUND_MIXER_READ_SPEAKER = SOUND_MIXER_READ_SPEAKER;
+ unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS = SOUND_MIXER_READ_STEREODEVS;
+ unsigned IOCTL_SOUND_MIXER_READ_SYNTH = SOUND_MIXER_READ_SYNTH;
+ unsigned IOCTL_SOUND_MIXER_READ_TREBLE = SOUND_MIXER_READ_TREBLE;
+ unsigned IOCTL_SOUND_MIXER_READ_VOLUME = SOUND_MIXER_READ_VOLUME;
+ unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM = SOUND_MIXER_WRITE_ALTPCM;
+ unsigned IOCTL_SOUND_MIXER_WRITE_BASS = SOUND_MIXER_WRITE_BASS;
+ unsigned IOCTL_SOUND_MIXER_WRITE_CD = SOUND_MIXER_WRITE_CD;
+ unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE = SOUND_MIXER_WRITE_ENHANCE;
+ unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN = SOUND_MIXER_WRITE_IGAIN;
+ unsigned IOCTL_SOUND_MIXER_WRITE_IMIX = SOUND_MIXER_WRITE_IMIX;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LINE = SOUND_MIXER_WRITE_LINE;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LINE1 = SOUND_MIXER_WRITE_LINE1;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LINE2 = SOUND_MIXER_WRITE_LINE2;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LINE3 = SOUND_MIXER_WRITE_LINE3;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LOUD = SOUND_MIXER_WRITE_LOUD;
+ unsigned IOCTL_SOUND_MIXER_WRITE_MIC = SOUND_MIXER_WRITE_MIC;
+ unsigned IOCTL_SOUND_MIXER_WRITE_MUTE = SOUND_MIXER_WRITE_MUTE;
+ unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN = SOUND_MIXER_WRITE_OGAIN;
+ unsigned IOCTL_SOUND_MIXER_WRITE_PCM = SOUND_MIXER_WRITE_PCM;
+ unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV = SOUND_MIXER_WRITE_RECLEV;
+ unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC = SOUND_MIXER_WRITE_RECSRC;
+ unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER = SOUND_MIXER_WRITE_SPEAKER;
+ unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH = SOUND_MIXER_WRITE_SYNTH;
+ unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE = SOUND_MIXER_WRITE_TREBLE;
+ unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME = SOUND_MIXER_WRITE_VOLUME;
+ unsigned IOCTL_VT_ACTIVATE = VT_ACTIVATE;
+ unsigned IOCTL_VT_GETMODE = VT_GETMODE;
+ unsigned IOCTL_VT_OPENQRY = VT_OPENQRY;
+ unsigned IOCTL_VT_RELDISP = VT_RELDISP;
+ unsigned IOCTL_VT_SETMODE = VT_SETMODE;
+ unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE;
+ unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP;
+ unsigned IOCTL_KDDISABIO = KDDISABIO;
+ unsigned IOCTL_KDENABIO = KDENABIO;
+ unsigned IOCTL_KDGETLED = KDGETLED;
+ unsigned IOCTL_KDGETMODE = KDGETMODE;
+ unsigned IOCTL_KDGKBMODE = KDGKBMODE;
+ unsigned IOCTL_KDGKBTYPE = KDGKBTYPE;
+ unsigned IOCTL_KDMKTONE = KDMKTONE;
+ unsigned IOCTL_KDSETLED = KDSETLED;
+ unsigned IOCTL_KDSETMODE = KDSETMODE;
+ unsigned IOCTL_KDSKBMODE = KDSKBMODE;
+ unsigned IOCTL_KIOCSOUND = KIOCSOUND;
+ unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP;
+ unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE;
+
+ const int si_SEGV_MAPERR = SEGV_MAPERR;
+ const int si_SEGV_ACCERR = SEGV_ACCERR;
+ const int unvis_valid = UNVIS_VALID;
+ const int unvis_validpush = UNVIS_VALIDPUSH;
+} // namespace __sanitizer
+
+using namespace __sanitizer;
+
+COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
+
+COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
+CHECK_TYPE_SIZE(pthread_key_t);
+
+// There are more undocumented fields in dl_phdr_info that we are not interested
+// in.
+COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info));
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
+
+CHECK_TYPE_SIZE(glob_t);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_flags);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_stat);
+
+CHECK_TYPE_SIZE(addrinfo);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
+
+CHECK_TYPE_SIZE(hostent);
+CHECK_SIZE_AND_OFFSET(hostent, h_name);
+CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
+CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
+CHECK_SIZE_AND_OFFSET(hostent, h_length);
+CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
+
+CHECK_TYPE_SIZE(iovec);
+CHECK_SIZE_AND_OFFSET(iovec, iov_base);
+CHECK_SIZE_AND_OFFSET(iovec, iov_len);
+
+CHECK_TYPE_SIZE(msghdr);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
+
+CHECK_TYPE_SIZE(cmsghdr);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
+
+COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
+CHECK_SIZE_AND_OFFSET(dirent, d_ino);
+CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
+
+CHECK_TYPE_SIZE(ifconf);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
+
+CHECK_TYPE_SIZE(pollfd);
+CHECK_SIZE_AND_OFFSET(pollfd, fd);
+CHECK_SIZE_AND_OFFSET(pollfd, events);
+CHECK_SIZE_AND_OFFSET(pollfd, revents);
+
+CHECK_TYPE_SIZE(nfds_t);
+
+CHECK_TYPE_SIZE(sigset_t);
+
+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);
+
+CHECK_TYPE_SIZE(wordexp_t);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs);
+
+CHECK_TYPE_SIZE(tm);
+CHECK_SIZE_AND_OFFSET(tm, tm_sec);
+CHECK_SIZE_AND_OFFSET(tm, tm_min);
+CHECK_SIZE_AND_OFFSET(tm, tm_hour);
+CHECK_SIZE_AND_OFFSET(tm, tm_mday);
+CHECK_SIZE_AND_OFFSET(tm, tm_mon);
+CHECK_SIZE_AND_OFFSET(tm, tm_year);
+CHECK_SIZE_AND_OFFSET(tm, tm_wday);
+CHECK_SIZE_AND_OFFSET(tm, tm_yday);
+CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
+CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff);
+CHECK_SIZE_AND_OFFSET(tm, tm_zone);
+
+CHECK_TYPE_SIZE(ether_addr);
+
+CHECK_TYPE_SIZE(ipc_perm);
+CHECK_SIZE_AND_OFFSET(ipc_perm, key);
+CHECK_SIZE_AND_OFFSET(ipc_perm, seq);
+CHECK_SIZE_AND_OFFSET(ipc_perm, uid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, gid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cuid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cgid);
+
+CHECK_TYPE_SIZE(shmid_ds);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
+
+CHECK_TYPE_SIZE(clock_t);
+
+CHECK_TYPE_SIZE(ifaddrs);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
+#undef ifa_dstaddr
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
+
+CHECK_TYPE_SIZE(timeb);
+CHECK_SIZE_AND_OFFSET(timeb, time);
+CHECK_SIZE_AND_OFFSET(timeb, millitm);
+CHECK_SIZE_AND_OFFSET(timeb, timezone);
+CHECK_SIZE_AND_OFFSET(timeb, dstflag);
+
+CHECK_TYPE_SIZE(passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_name);
+CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
+CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
+
+CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
+
+CHECK_TYPE_SIZE(group);
+CHECK_SIZE_AND_OFFSET(group, gr_name);
+CHECK_SIZE_AND_OFFSET(group, gr_passwd);
+CHECK_SIZE_AND_OFFSET(group, gr_gid);
+CHECK_SIZE_AND_OFFSET(group, gr_mem);
+
+#if HAVE_RPC_XDR_H
+CHECK_TYPE_SIZE(XDR);
+CHECK_SIZE_AND_OFFSET(XDR, x_op);
+CHECK_SIZE_AND_OFFSET(XDR, x_ops);
+CHECK_SIZE_AND_OFFSET(XDR, x_public);
+CHECK_SIZE_AND_OFFSET(XDR, x_private);
+CHECK_SIZE_AND_OFFSET(XDR, x_base);
+CHECK_SIZE_AND_OFFSET(XDR, x_handy);
+COMPILER_CHECK(__sanitizer_XDR_ENCODE == XDR_ENCODE);
+COMPILER_CHECK(__sanitizer_XDR_DECODE == XDR_DECODE);
+COMPILER_CHECK(__sanitizer_XDR_FREE == XDR_FREE);
+#endif
+
+CHECK_TYPE_SIZE(sem_t);
+
+COMPILER_CHECK(sizeof(__sanitizer_cap_rights_t) >= sizeof(cap_rights_t));
+#endif // SANITIZER_FREEBSD
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_freebsd.h b/lib/sanitizer_common/sanitizer_platform_limits_freebsd.h
new file mode 100644
index 000000000000..588bead2ac12
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_platform_limits_freebsd.h
@@ -0,0 +1,657 @@
+//===-- sanitizer_platform_limits_freebsd.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 Sanitizer common code.
+//
+// Sizes and layouts of platform-specific FreeBSD data structures.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_PLATFORM_LIMITS_FREEBSD_H
+#define SANITIZER_PLATFORM_LIMITS_FREEBSD_H
+
+#if SANITIZER_FREEBSD
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform.h"
+
+#include "sanitizer_platform_limits_posix.h"
+
+// FreeBSD's dlopen() returns a pointer to an Obj_Entry structure that
+// incorporates the map structure.
+# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
+ ((link_map*)((handle) == nullptr ? nullptr : ((char*)(handle) + 560)))
+// Get sys/_types.h, because that tells us whether 64-bit inodes are
+// used in struct dirent below.
+#include <sys/_types.h>
+
+namespace __sanitizer {
+ extern unsigned struct_utsname_sz;
+ extern unsigned struct_stat_sz;
+#if defined(__powerpc64__)
+ const unsigned struct___old_kernel_stat_sz = 0;
+#else
+ const unsigned struct___old_kernel_stat_sz = 32;
+#endif
+ extern unsigned struct_rusage_sz;
+ extern unsigned siginfo_t_sz;
+ extern unsigned struct_itimerval_sz;
+ extern unsigned pthread_t_sz;
+ extern unsigned pthread_mutex_t_sz;
+ extern unsigned pthread_cond_t_sz;
+ extern unsigned pid_t_sz;
+ extern unsigned timeval_sz;
+ extern unsigned uid_t_sz;
+ extern unsigned gid_t_sz;
+ extern unsigned fpos_t_sz;
+ extern unsigned mbstate_t_sz;
+ extern unsigned struct_timezone_sz;
+ extern unsigned struct_tms_sz;
+ extern unsigned struct_itimerspec_sz;
+ extern unsigned struct_sigevent_sz;
+ extern unsigned struct_sched_param_sz;
+ extern unsigned struct_statfs64_sz;
+ extern unsigned struct_statfs_sz;
+ extern unsigned struct_sockaddr_sz;
+ extern unsigned ucontext_t_sz;
+ extern unsigned struct_rlimit_sz;
+ extern unsigned struct_utimbuf_sz;
+ extern unsigned struct_timespec_sz;
+ extern unsigned struct_regmatch_sz;
+ extern unsigned struct_regex_sz;
+ extern unsigned struct_FTS_sz;
+ extern unsigned struct_FTSENT_sz;
+ extern const int unvis_valid;
+ extern const int unvis_validpush;
+
+ struct __sanitizer_iocb {
+ u64 aio_data;
+ u32 aio_key_or_aio_reserved1; // Simply crazy.
+ u32 aio_reserved1_or_aio_key; // Luckily, we don't need these.
+ u16 aio_lio_opcode;
+ s16 aio_reqprio;
+ u32 aio_fildes;
+ u64 aio_buf;
+ u64 aio_nbytes;
+ s64 aio_offset;
+ u64 aio_reserved2;
+ u64 aio_reserved3;
+ };
+
+ struct __sanitizer_io_event {
+ u64 data;
+ u64 obj;
+ u64 res;
+ u64 res2;
+ };
+
+ const unsigned iocb_cmd_pread = 0;
+ const unsigned iocb_cmd_pwrite = 1;
+ const unsigned iocb_cmd_preadv = 7;
+ const unsigned iocb_cmd_pwritev = 8;
+
+ struct __sanitizer___sysctl_args {
+ int *name;
+ int nlen;
+ void *oldval;
+ uptr *oldlenp;
+ void *newval;
+ uptr newlen;
+ unsigned long ___unused[4];
+ };
+
+ struct __sanitizer_ipc_perm {
+ unsigned int cuid;
+ unsigned int cgid;
+ unsigned int uid;
+ unsigned int gid;
+ unsigned short mode;
+ unsigned short seq;
+ long key;
+ };
+
+ struct __sanitizer_shmid_ds {
+ __sanitizer_ipc_perm shm_perm;
+ unsigned long shm_segsz;
+ unsigned int shm_lpid;
+ unsigned int shm_cpid;
+ int shm_nattch;
+ unsigned long shm_atime;
+ unsigned long shm_dtime;
+ unsigned long shm_ctime;
+ };
+
+ extern unsigned struct_msqid_ds_sz;
+ extern unsigned struct_mq_attr_sz;
+ extern unsigned struct_timeb_sz;
+ extern unsigned struct_statvfs_sz;
+
+ struct __sanitizer_iovec {
+ void *iov_base;
+ uptr iov_len;
+ };
+
+ struct __sanitizer_ifaddrs {
+ struct __sanitizer_ifaddrs *ifa_next;
+ char *ifa_name;
+ unsigned int ifa_flags;
+ void *ifa_addr; // (struct sockaddr *)
+ void *ifa_netmask; // (struct sockaddr *)
+# undef ifa_dstaddr
+ void *ifa_dstaddr; // (struct sockaddr *)
+ void *ifa_data;
+ };
+
+ typedef unsigned __sanitizer_pthread_key_t;
+
+ struct __sanitizer_passwd {
+ char *pw_name;
+ char *pw_passwd;
+ int pw_uid;
+ int pw_gid;
+ long pw_change;
+ char *pw_class;
+ char *pw_gecos;
+ char *pw_dir;
+ char *pw_shell;
+ long pw_expire;
+ int pw_fields;
+ };
+
+ struct __sanitizer_group {
+ char *gr_name;
+ char *gr_passwd;
+ int gr_gid;
+ char **gr_mem;
+ };
+
+#if defined(__LP64___)
+ typedef long long __sanitizer_time_t;
+#else
+ typedef long __sanitizer_time_t;
+#endif
+
+ typedef long __sanitizer_suseconds_t;
+
+ struct __sanitizer_timeval {
+ __sanitizer_time_t tv_sec;
+ __sanitizer_suseconds_t tv_usec;
+ };
+
+ struct __sanitizer_itimerval {
+ struct __sanitizer_timeval it_interval;
+ struct __sanitizer_timeval it_value;
+ };
+
+ struct __sanitizer_timeb {
+ __sanitizer_time_t time;
+ unsigned short millitm;
+ short timezone;
+ short dstflag;
+ };
+
+ struct __sanitizer_ether_addr {
+ u8 octet[6];
+ };
+
+ struct __sanitizer_tm {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+ long int tm_gmtoff;
+ const char *tm_zone;
+ };
+
+ struct __sanitizer_msghdr {
+ void *msg_name;
+ unsigned msg_namelen;
+ struct __sanitizer_iovec *msg_iov;
+ unsigned msg_iovlen;
+ void *msg_control;
+ unsigned msg_controllen;
+ int msg_flags;
+ };
+
+ struct __sanitizer_cmsghdr {
+ unsigned cmsg_len;
+ int cmsg_level;
+ int cmsg_type;
+ };
+
+ struct __sanitizer_dirent {
+#if defined(__INO64)
+ unsigned long long d_fileno;
+ unsigned long long d_off;
+#else
+ unsigned int d_fileno;
+#endif
+ unsigned short d_reclen;
+ // more fields that we don't care about
+ };
+
+// 'clock_t' is 32 bits wide on x64 FreeBSD
+ typedef int __sanitizer_clock_t;
+ typedef int __sanitizer_clockid_t;
+
+#if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__)\
+ || defined(__mips__)
+ typedef unsigned __sanitizer___kernel_uid_t;
+ typedef unsigned __sanitizer___kernel_gid_t;
+#else
+ typedef unsigned short __sanitizer___kernel_uid_t;
+ typedef unsigned short __sanitizer___kernel_gid_t;
+#endif
+ typedef long long __sanitizer___kernel_off_t;
+
+#if defined(__powerpc__) || defined(__mips__)
+ typedef unsigned int __sanitizer___kernel_old_uid_t;
+ typedef unsigned int __sanitizer___kernel_old_gid_t;
+#else
+ typedef unsigned short __sanitizer___kernel_old_uid_t;
+ typedef unsigned short __sanitizer___kernel_old_gid_t;
+#endif
+
+ typedef long long __sanitizer___kernel_loff_t;
+ typedef struct {
+ unsigned long fds_bits[1024 / (8 * sizeof(long))];
+ } __sanitizer___kernel_fd_set;
+
+ // This thing depends on the platform. We are only interested in the upper
+ // limit. Verified with a compiler assert in .cc.
+ const int pthread_attr_t_max_sz = 128;
+ union __sanitizer_pthread_attr_t {
+ char size[pthread_attr_t_max_sz]; // NOLINT
+ void *align;
+ };
+
+ const unsigned old_sigset_t_sz = sizeof(unsigned long);
+
+ struct __sanitizer_sigset_t {
+ // uint32_t * 4
+ unsigned int __bits[4];
+ };
+
+ typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;
+
+ struct __sanitizer_siginfo {
+ // The size is determined by looking at sizeof of real siginfo_t on linux.
+ u64 opaque[128 / sizeof(u64)];
+ };
+
+ using __sanitizer_sighandler_ptr = void (*)(int sig);
+ using __sanitizer_sigactionhandler_ptr =
+ void (*)(int sig, __sanitizer_siginfo *siginfo, void *uctx);
+
+ struct __sanitizer_sigaction {
+ union {
+ __sanitizer_sigactionhandler_ptr sigaction;
+ __sanitizer_sighandler_ptr handler;
+ };
+ int sa_flags;
+ __sanitizer_sigset_t sa_mask;
+ };
+
+ struct __sanitizer_sem_t {
+ u32 data[4];
+ };
+
+ extern const uptr sig_ign;
+ extern const uptr sig_dfl;
+ extern const uptr sig_err;
+ extern const uptr sa_siginfo;
+
+ extern int af_inet;
+ extern int af_inet6;
+ uptr __sanitizer_in_addr_sz(int af);
+
+ struct __sanitizer_dl_phdr_info {
+ uptr dlpi_addr;
+ const char *dlpi_name;
+ const void *dlpi_phdr;
+ short dlpi_phnum;
+ };
+
+ extern unsigned struct_ElfW_Phdr_sz;
+
+ struct __sanitizer_addrinfo {
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ unsigned ai_addrlen;
+ char *ai_canonname;
+ void *ai_addr;
+ struct __sanitizer_addrinfo *ai_next;
+ };
+
+ struct __sanitizer_hostent {
+ char *h_name;
+ char **h_aliases;
+ int h_addrtype;
+ int h_length;
+ char **h_addr_list;
+ };
+
+ struct __sanitizer_pollfd {
+ int fd;
+ short events;
+ short revents;
+ };
+
+ typedef unsigned __sanitizer_nfds_t;
+
+ struct __sanitizer_glob_t {
+ uptr gl_pathc;
+ uptr gl_matchc;
+ uptr gl_offs;
+ int gl_flags;
+ char **gl_pathv;
+ int (*gl_errfunc)(const char*, int);
+ void (*gl_closedir)(void *dirp);
+ struct dirent *(*gl_readdir)(void *dirp);
+ void *(*gl_opendir)(const char*);
+ int (*gl_lstat)(const char*, void* /* struct stat* */);
+ int (*gl_stat)(const char*, void* /* struct stat* */);
+ };
+
+ extern int glob_nomatch;
+ extern int glob_altdirfunc;
+
+ extern unsigned path_max;
+
+ struct __sanitizer_wordexp_t {
+ uptr we_wordc;
+ char **we_wordv;
+ uptr we_offs;
+ char *we_strings;
+ uptr we_nbytes;
+ };
+
+ typedef void __sanitizer_FILE;
+
+ extern unsigned struct_shminfo_sz;
+ extern unsigned struct_shm_info_sz;
+ extern int shmctl_ipc_stat;
+ extern int shmctl_ipc_info;
+ extern int shmctl_shm_info;
+ extern int shmctl_shm_stat;
+
+ extern unsigned struct_utmpx_sz;
+
+ extern int map_fixed;
+
+ // ioctl arguments
+ struct __sanitizer_ifconf {
+ int ifc_len;
+ union {
+ void *ifcu_req;
+ } ifc_ifcu;
+ };
+
+#define IOC_NRBITS 8
+#define IOC_TYPEBITS 8
+#if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__)
+#define IOC_SIZEBITS 13
+#define IOC_DIRBITS 3
+#define IOC_NONE 1U
+#define IOC_WRITE 4U
+#define IOC_READ 2U
+#else
+#define IOC_SIZEBITS 14
+#define IOC_DIRBITS 2
+#define IOC_NONE 0U
+#define IOC_WRITE 1U
+#define IOC_READ 2U
+#endif
+#define IOC_NRMASK ((1 << IOC_NRBITS) - 1)
+#define IOC_TYPEMASK ((1 << IOC_TYPEBITS) - 1)
+#define IOC_SIZEMASK ((1 << IOC_SIZEBITS) - 1)
+#if defined(IOC_DIRMASK)
+#undef IOC_DIRMASK
+#endif
+#define IOC_DIRMASK ((1 << IOC_DIRBITS) - 1)
+#define IOC_NRSHIFT 0
+#define IOC_TYPESHIFT (IOC_NRSHIFT + IOC_NRBITS)
+#define IOC_SIZESHIFT (IOC_TYPESHIFT + IOC_TYPEBITS)
+#define IOC_DIRSHIFT (IOC_SIZESHIFT + IOC_SIZEBITS)
+#define EVIOC_EV_MAX 0x1f
+#define EVIOC_ABS_MAX 0x3f
+
+#define IOC_DIR(nr) (((nr) >> IOC_DIRSHIFT) & IOC_DIRMASK)
+#define IOC_TYPE(nr) (((nr) >> IOC_TYPESHIFT) & IOC_TYPEMASK)
+#define IOC_NR(nr) (((nr) >> IOC_NRSHIFT) & IOC_NRMASK)
+#define IOC_SIZE(nr) (((nr) >> IOC_SIZESHIFT) & IOC_SIZEMASK)
+
+ extern unsigned struct_ifreq_sz;
+ extern unsigned struct_termios_sz;
+ extern unsigned struct_winsize_sz;
+
+ extern unsigned struct_copr_buffer_sz;
+ extern unsigned struct_copr_debug_buf_sz;
+ extern unsigned struct_copr_msg_sz;
+ extern unsigned struct_midi_info_sz;
+ extern unsigned struct_mtget_sz;
+ extern unsigned struct_mtop_sz;
+ extern unsigned struct_rtentry_sz;
+ extern unsigned struct_sbi_instrument_sz;
+ extern unsigned struct_seq_event_rec_sz;
+ extern unsigned struct_synth_info_sz;
+ extern unsigned struct_vt_mode_sz;
+
+ extern const unsigned long __sanitizer_bufsiz;
+ extern unsigned struct_audio_buf_info_sz;
+ extern unsigned struct_ppp_stats_sz;
+ extern unsigned struct_sioc_sg_req_sz;
+ extern unsigned struct_sioc_vif_req_sz;
+
+ // ioctl request identifiers
+
+ // A special value to mark ioctls that are not present on the target platform,
+ // when it can not be determined without including any system headers.
+ extern const unsigned IOCTL_NOT_PRESENT;
+
+ extern unsigned IOCTL_FIOASYNC;
+ extern unsigned IOCTL_FIOCLEX;
+ extern unsigned IOCTL_FIOGETOWN;
+ extern unsigned IOCTL_FIONBIO;
+ extern unsigned IOCTL_FIONCLEX;
+ extern unsigned IOCTL_FIOSETOWN;
+ extern unsigned IOCTL_SIOCADDMULTI;
+ extern unsigned IOCTL_SIOCATMARK;
+ extern unsigned IOCTL_SIOCDELMULTI;
+ extern unsigned IOCTL_SIOCGIFADDR;
+ extern unsigned IOCTL_SIOCGIFBRDADDR;
+ extern unsigned IOCTL_SIOCGIFCONF;
+ extern unsigned IOCTL_SIOCGIFDSTADDR;
+ extern unsigned IOCTL_SIOCGIFFLAGS;
+ extern unsigned IOCTL_SIOCGIFMETRIC;
+ extern unsigned IOCTL_SIOCGIFMTU;
+ extern unsigned IOCTL_SIOCGIFNETMASK;
+ extern unsigned IOCTL_SIOCGPGRP;
+ extern unsigned IOCTL_SIOCSIFADDR;
+ extern unsigned IOCTL_SIOCSIFBRDADDR;
+ extern unsigned IOCTL_SIOCSIFDSTADDR;
+ extern unsigned IOCTL_SIOCSIFFLAGS;
+ extern unsigned IOCTL_SIOCSIFMETRIC;
+ extern unsigned IOCTL_SIOCSIFMTU;
+ extern unsigned IOCTL_SIOCSIFNETMASK;
+ extern unsigned IOCTL_SIOCSPGRP;
+ extern unsigned IOCTL_TIOCCONS;
+ extern unsigned IOCTL_TIOCEXCL;
+ extern unsigned IOCTL_TIOCGETD;
+ extern unsigned IOCTL_TIOCGPGRP;
+ extern unsigned IOCTL_TIOCGWINSZ;
+ extern unsigned IOCTL_TIOCMBIC;
+ extern unsigned IOCTL_TIOCMBIS;
+ extern unsigned IOCTL_TIOCMGET;
+ extern unsigned IOCTL_TIOCMSET;
+ extern unsigned IOCTL_TIOCNOTTY;
+ extern unsigned IOCTL_TIOCNXCL;
+ extern unsigned IOCTL_TIOCOUTQ;
+ extern unsigned IOCTL_TIOCPKT;
+ extern unsigned IOCTL_TIOCSCTTY;
+ extern unsigned IOCTL_TIOCSETD;
+ extern unsigned IOCTL_TIOCSPGRP;
+ extern unsigned IOCTL_TIOCSTI;
+ extern unsigned IOCTL_TIOCSWINSZ;
+ extern unsigned IOCTL_SIOCGETSGCNT;
+ extern unsigned IOCTL_SIOCGETVIFCNT;
+ extern unsigned IOCTL_MTIOCGET;
+ extern unsigned IOCTL_MTIOCTOP;
+ extern unsigned IOCTL_SIOCADDRT;
+ extern unsigned IOCTL_SIOCDELRT;
+ extern unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE;
+ extern unsigned IOCTL_SNDCTL_DSP_GETFMTS;
+ extern unsigned IOCTL_SNDCTL_DSP_NONBLOCK;
+ extern unsigned IOCTL_SNDCTL_DSP_POST;
+ extern unsigned IOCTL_SNDCTL_DSP_RESET;
+ extern unsigned IOCTL_SNDCTL_DSP_SETFMT;
+ extern unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT;
+ extern unsigned IOCTL_SNDCTL_DSP_SPEED;
+ extern unsigned IOCTL_SNDCTL_DSP_STEREO;
+ extern unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE;
+ extern unsigned IOCTL_SNDCTL_DSP_SYNC;
+ extern unsigned IOCTL_SNDCTL_FM_4OP_ENABLE;
+ extern unsigned IOCTL_SNDCTL_FM_LOAD_INSTR;
+ extern unsigned IOCTL_SNDCTL_MIDI_INFO;
+ extern unsigned IOCTL_SNDCTL_MIDI_PRETIME;
+ extern unsigned IOCTL_SNDCTL_SEQ_CTRLRATE;
+ extern unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT;
+ extern unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT;
+ extern unsigned IOCTL_SNDCTL_SEQ_NRMIDIS;
+ extern unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS;
+ extern unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND;
+ extern unsigned IOCTL_SNDCTL_SEQ_PANIC;
+ extern unsigned IOCTL_SNDCTL_SEQ_PERCMODE;
+ extern unsigned IOCTL_SNDCTL_SEQ_RESET;
+ extern unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES;
+ extern unsigned IOCTL_SNDCTL_SEQ_SYNC;
+ extern unsigned IOCTL_SNDCTL_SEQ_TESTMIDI;
+ extern unsigned IOCTL_SNDCTL_SEQ_THRESHOLD;
+ extern unsigned IOCTL_SNDCTL_SYNTH_INFO;
+ extern unsigned IOCTL_SNDCTL_SYNTH_MEMAVL;
+ extern unsigned IOCTL_SNDCTL_TMR_CONTINUE;
+ extern unsigned IOCTL_SNDCTL_TMR_METRONOME;
+ extern unsigned IOCTL_SNDCTL_TMR_SELECT;
+ extern unsigned IOCTL_SNDCTL_TMR_SOURCE;
+ extern unsigned IOCTL_SNDCTL_TMR_START;
+ extern unsigned IOCTL_SNDCTL_TMR_STOP;
+ extern unsigned IOCTL_SNDCTL_TMR_TEMPO;
+ extern unsigned IOCTL_SNDCTL_TMR_TIMEBASE;
+ extern unsigned IOCTL_SOUND_MIXER_READ_ALTPCM;
+ extern unsigned IOCTL_SOUND_MIXER_READ_BASS;
+ extern unsigned IOCTL_SOUND_MIXER_READ_CAPS;
+ extern unsigned IOCTL_SOUND_MIXER_READ_CD;
+ extern unsigned IOCTL_SOUND_MIXER_READ_DEVMASK;
+ extern unsigned IOCTL_SOUND_MIXER_READ_ENHANCE;
+ extern unsigned IOCTL_SOUND_MIXER_READ_IGAIN;
+ extern unsigned IOCTL_SOUND_MIXER_READ_IMIX;
+ extern unsigned IOCTL_SOUND_MIXER_READ_LINE1;
+ extern unsigned IOCTL_SOUND_MIXER_READ_LINE2;
+ extern unsigned IOCTL_SOUND_MIXER_READ_LINE3;
+ extern unsigned IOCTL_SOUND_MIXER_READ_LINE;
+ extern unsigned IOCTL_SOUND_MIXER_READ_LOUD;
+ extern unsigned IOCTL_SOUND_MIXER_READ_MIC;
+ extern unsigned IOCTL_SOUND_MIXER_READ_MUTE;
+ extern unsigned IOCTL_SOUND_MIXER_READ_OGAIN;
+ extern unsigned IOCTL_SOUND_MIXER_READ_PCM;
+ extern unsigned IOCTL_SOUND_MIXER_READ_RECLEV;
+ extern unsigned IOCTL_SOUND_MIXER_READ_RECMASK;
+ extern unsigned IOCTL_SOUND_MIXER_READ_RECSRC;
+ extern unsigned IOCTL_SOUND_MIXER_READ_SPEAKER;
+ extern unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS;
+ extern unsigned IOCTL_SOUND_MIXER_READ_SYNTH;
+ extern unsigned IOCTL_SOUND_MIXER_READ_TREBLE;
+ extern unsigned IOCTL_SOUND_MIXER_READ_VOLUME;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_BASS;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_CD;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_IMIX;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE1;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE2;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE3;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_LOUD;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_MIC;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_MUTE;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_PCM;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME;
+ extern unsigned IOCTL_SOUND_PCM_READ_BITS;
+ extern unsigned IOCTL_SOUND_PCM_READ_CHANNELS;
+ extern unsigned IOCTL_SOUND_PCM_READ_FILTER;
+ extern unsigned IOCTL_SOUND_PCM_READ_RATE;
+ extern unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS;
+ extern unsigned IOCTL_SOUND_PCM_WRITE_FILTER;
+ extern unsigned IOCTL_VT_ACTIVATE;
+ extern unsigned IOCTL_VT_GETMODE;
+ extern unsigned IOCTL_VT_OPENQRY;
+ extern unsigned IOCTL_VT_RELDISP;
+ extern unsigned IOCTL_VT_SETMODE;
+ extern unsigned IOCTL_VT_WAITACTIVE;
+ extern unsigned IOCTL_GIO_SCRNMAP;
+ extern unsigned IOCTL_KDDISABIO;
+ extern unsigned IOCTL_KDENABIO;
+ extern unsigned IOCTL_KDGETLED;
+ extern unsigned IOCTL_KDGETMODE;
+ extern unsigned IOCTL_KDGKBMODE;
+ extern unsigned IOCTL_KDGKBTYPE;
+ extern unsigned IOCTL_KDMKTONE;
+ extern unsigned IOCTL_KDSETLED;
+ extern unsigned IOCTL_KDSETMODE;
+ extern unsigned IOCTL_KDSKBMODE;
+
+ extern const int si_SEGV_MAPERR;
+ extern const int si_SEGV_ACCERR;
+
+ struct __sanitizer_cap_rights {
+ u64 cr_rights[2];
+ };
+
+ typedef struct __sanitizer_cap_rights __sanitizer_cap_rights_t;
+ extern unsigned struct_cap_rights_sz;
+
+ extern unsigned struct_fstab_sz;
+ extern unsigned struct_StringList_sz;
+} // namespace __sanitizer
+
+#define CHECK_TYPE_SIZE(TYPE) \
+ COMPILER_CHECK(sizeof(__sanitizer_##TYPE) == sizeof(TYPE))
+
+#define CHECK_SIZE_AND_OFFSET(CLASS, MEMBER) \
+ COMPILER_CHECK(sizeof(((__sanitizer_##CLASS *) NULL)->MEMBER) == \
+ sizeof(((CLASS *) NULL)->MEMBER)); \
+ COMPILER_CHECK(offsetof(__sanitizer_##CLASS, MEMBER) == \
+ offsetof(CLASS, MEMBER))
+
+// For sigaction, which is a function and struct at the same time,
+// and thus requires explicit "struct" in sizeof() expression.
+#define CHECK_STRUCT_SIZE_AND_OFFSET(CLASS, MEMBER) \
+ COMPILER_CHECK(sizeof(((struct __sanitizer_##CLASS *) NULL)->MEMBER) == \
+ sizeof(((struct CLASS *) NULL)->MEMBER)); \
+ COMPILER_CHECK(offsetof(struct __sanitizer_##CLASS, MEMBER) == \
+ offsetof(struct CLASS, MEMBER))
+
+#define SIGACTION_SYMNAME sigaction
+
+#endif
+
+#endif // SANITIZER_FREEBSD
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_netbsd.cc b/lib/sanitizer_common/sanitizer_platform_limits_netbsd.cc
index 3c6bb2090fa5..b23b430d9e5b 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_netbsd.cc
+++ b/lib/sanitizer_common/sanitizer_platform_limits_netbsd.cc
@@ -15,9 +15,80 @@
#include "sanitizer_platform.h"
#if SANITIZER_NETBSD
+
+#define _KMEMUSER
+#define RAY_DO_SIGLEV
+
+// clang-format off
#include <sys/param.h>
#include <sys/types.h>
-
+#include <sys/sysctl.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/mount.h>
+#include <sys/agpio.h>
+#include <sys/ataio.h>
+#include <sys/audioio.h>
+#include <sys/cdbr.h>
+#include <sys/cdio.h>
+#include <sys/chio.h>
+#include <sys/clockctl.h>
+#include <sys/cpuio.h>
+#include <sys/dkio.h>
+#include <sys/drvctlio.h>
+#include <sys/dvdio.h>
+#include <sys/envsys.h>
+#include <sys/event.h>
+#include <sys/fdio.h>
+#include <sys/filio.h>
+#include <sys/gpio.h>
+#include <sys/ioctl.h>
+#include <sys/ioctl_compat.h>
+#include <sys/joystick.h>
+#include <sys/ksyms.h>
+#include <sys/lua.h>
+#include <sys/midiio.h>
+#include <sys/mtio.h>
+#include <sys/power.h>
+#include <sys/radioio.h>
+#include <sys/rndio.h>
+#include <sys/scanio.h>
+#include <sys/scsiio.h>
+#include <sys/sockio.h>
+#include <sys/timepps.h>
+#include <sys/ttycom.h>
+#include <sys/verified_exec.h>
+#include <sys/videoio.h>
+#include <sys/wdog.h>
+#include <sys/event.h>
+#include <sys/filio.h>
+#include <sys/ipc.h>
+#include <sys/mman.h>
+#include <sys/module.h>
+#include <sys/mount.h>
+#include <sys/mqueue.h>
+#include <sys/msg.h>
+#include <sys/mtio.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/sem.h>
+#include <sys/sha1.h>
+#include <sys/sha2.h>
+#include <sys/shm.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/soundcard.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <sys/timeb.h>
+#include <sys/times.h>
+#include <sys/timespec.h>
+#include <sys/timex.h>
+#include <sys/types.h>
+#include <sys/ucontext.h>
+#include <sys/utsname.h>
#include <altq/altq.h>
#include <altq/altq_afmap.h>
#include <altq/altq_blue.h>
@@ -47,47 +118,41 @@
#include <dev/ic/icp_ioctl.h>
#include <dev/ic/isp_ioctl.h>
#include <dev/ic/mlxio.h>
+#include <dev/ic/qemufwcfgio.h>
#include <dev/ic/nvmeio.h>
#include <dev/ir/irdaio.h>
#include <dev/isa/isvio.h>
#include <dev/isa/satlinkio.h>
#include <dev/isa/wtreg.h>
#include <dev/iscsi/iscsi_ioctl.h>
+#include <dev/nvmm/nvmm_ioctl.h>
#include <dev/ofw/openfirmio.h>
#include <dev/pci/amrio.h>
-
#include <dev/pci/mlyreg.h>
#include <dev/pci/mlyio.h>
-
#include <dev/pci/pciio.h>
#include <dev/pci/tweio.h>
#include <dev/pcmcia/if_cnwioctl.h>
-#include <dirent.h>
-#include <glob.h>
-#include <grp.h>
-#include <ifaddrs.h>
-#include <limits.h>
-#include <link_elf.h>
-#include <net/if.h>
-#include <net/if_ether.h>
+#include <net/bpf.h>
+#include <net/if_gre.h>
#include <net/ppp_defs.h>
-#include <net/route.h>
-#include <netdb.h>
-#include <netinet/in.h>
+#include <net/if_ppp.h>
+#include <net/if_pppoe.h>
+#include <net/if_sppp.h>
+#include <net/if_srt.h>
+#include <net/if_tap.h>
+#include <net/if_tun.h>
+#include <net/npf.h>
+#include <net/pfvar.h>
+#include <net/slip.h>
+#include <netbt/hci.h>
#include <netinet/ip_compat.h>
#include <netinet/ip_fil.h>
-#include <netinet/ip_mroute.h>
-#include <poll.h>
-#include <pthread.h>
-#include <pwd.h>
-#include <semaphore.h>
-#include <signal.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <sys/disk.h>
-#include <sys/disklabel.h>
-#include <sys/mount.h>
-#define RAY_DO_SIGLEV
+#include <netinet/ip_nat.h>
+#include <netinet/ip_proxy.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+#include <netsmb/smb_dev.h>
#include <dev/biovar.h>
#include <dev/bluetooth/btdev.h>
#include <dev/bluetooth/btsco.h>
@@ -97,7 +162,9 @@
#include <dev/kttcpio.h>
#include <dev/lockstat.h>
#include <dev/md.h>
+#include <net/if_ether.h>
#include <dev/pcmcia/if_rayreg.h>
+#include <stdio.h>
#include <dev/raidframe/raidframeio.h>
#include <dev/sbus/mbppio.h>
#include <dev/scsipi/ses.h>
@@ -115,86 +182,30 @@
#include <dev/vndvar.h>
#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplay_usl_io.h>
-#include <net/bpf.h>
-#include <net/if_atm.h>
-#include <net/if_gre.h>
-#include <net/if_ppp.h>
-#include <net/if_pppoe.h>
-#include <net/if_sppp.h>
-#include <net/if_srt.h>
-#include <net/if_tap.h>
-#include <net/if_tun.h>
-#include <net/npf.h>
-#include <net/pfvar.h>
-#include <net/slip.h>
-#include <netbt/hci.h>
-#include <netinet/ip_nat.h>
-#include <netinet/ip_proxy.h>
-#include <netinet6/in6_var.h>
-#include <netinet6/nd6.h>
-#include <netnatm/natm.h>
-#include <netsmb/smb_dev.h>
+#include <fs/autofs/autofs_ioctl.h>
+#include <dirent.h>
+#include <glob.h>
+#include <grp.h>
+#include <ifaddrs.h>
+#include <limits.h>
+#include <link_elf.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/ip_mroute.h>
+#include <netinet/sctp_uio.h>
+#include <poll.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stddef.h>
+#include <md2.h>
+#include <md4.h>
+#include <md5.h>
+#include <rmd160.h>
#include <soundcard.h>
-#include <sys/agpio.h>
-#include <sys/ataio.h>
-#include <sys/audioio.h>
-#include <sys/cdio.h>
-#include <sys/chio.h>
-#include <sys/clockctl.h>
-#include <sys/cpuio.h>
-#include <sys/dkio.h>
-#include <sys/drvctlio.h>
-#include <sys/dvdio.h>
-#include <sys/envsys.h>
-#include <sys/event.h>
-#include <sys/fdio.h>
-#include <sys/filio.h>
-#include <sys/gpio.h>
-#include <sys/ioctl.h>
-#include <sys/ioctl_compat.h>
-#include <sys/joystick.h>
-#include <sys/ksyms.h>
-#include <sys/lua.h>
-#include <sys/midiio.h>
-#include <sys/mtio.h>
-#include <sys/power.h>
-#include <sys/radioio.h>
-#include <sys/rndio.h>
-#include <sys/scanio.h>
-#include <sys/scsiio.h>
-#include <sys/sockio.h>
-#include <sys/timepps.h>
-#include <sys/ttycom.h>
-#include <sys/verified_exec.h>
-#include <sys/videoio.h>
-#include <sys/wdog.h>
-//#include <xen/xenio.h>
-#include <sys/event.h>
-#include <sys/filio.h>
-#include <sys/ipc.h>
-#include <sys/mman.h>
-#include <sys/mount.h>
-#include <sys/mqueue.h>
-#include <sys/msg.h>
-#include <sys/mtio.h>
-#include <sys/ptrace.h>
-#include <sys/resource.h>
-#include <sys/sem.h>
-#include <sys/shm.h>
-#include <sys/signal.h>
-#include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/soundcard.h>
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-#include <sys/time.h>
-#include <sys/timeb.h>
-#include <sys/times.h>
-#include <sys/timespec.h>
-#include <sys/timex.h>
-#include <sys/types.h>
-#include <sys/ucontext.h>
-#include <sys/utsname.h>
#include <term.h>
#include <termios.h>
#include <time.h>
@@ -202,8 +213,15 @@
#include <utime.h>
#include <utmp.h>
#include <utmpx.h>
+#include <vis.h>
#include <wchar.h>
#include <wordexp.h>
+#include <ttyent.h>
+#include <fts.h>
+#include <regex.h>
+#include <fstab.h>
+#include <stringlist.h>
+// clang-format on
// Include these after system headers to avoid name clashes and ambiguities.
#include "sanitizer_internal_defs.h"
@@ -238,6 +256,11 @@ unsigned struct_rlimit_sz = sizeof(struct rlimit);
unsigned struct_timespec_sz = sizeof(struct timespec);
unsigned struct_sembuf_sz = sizeof(struct sembuf);
unsigned struct_kevent_sz = sizeof(struct kevent);
+unsigned struct_FTS_sz = sizeof(FTS);
+unsigned struct_FTSENT_sz = sizeof(FTSENT);
+unsigned struct_regex_sz = sizeof(regex_t);
+unsigned struct_regmatch_sz = sizeof(regmatch_t);
+unsigned struct_fstab_sz = sizeof(struct fstab);
unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
unsigned struct_timex_sz = sizeof(struct timex);
@@ -251,6 +274,8 @@ const uptr sig_dfl = (uptr)SIG_DFL;
const uptr sig_err = (uptr)SIG_ERR;
const uptr sa_siginfo = (uptr)SA_SIGINFO;
+const unsigned long __sanitizer_bufsiz = BUFSIZ;
+
int ptrace_pt_io = PT_IO;
int ptrace_pt_lwpinfo = PT_LWPINFO;
int ptrace_pt_set_event_mask = PT_SET_EVENT_MASK;
@@ -339,6 +364,14 @@ unsigned path_max = PATH_MAX;
int struct_ttyent_sz = sizeof(struct ttyent);
+struct __sanitizer_nvlist_ref_t {
+ void *buf;
+ uptr len;
+ int flags;
+};
+
+typedef __sanitizer_nvlist_ref_t nvlist_ref_t;
+
// ioctl arguments
unsigned struct_altqreq_sz = sizeof(altqreq);
unsigned struct_amr_user_ioctl_sz = sizeof(amr_user_ioctl);
@@ -350,7 +383,6 @@ unsigned struct_atabusiodetach_args_sz = sizeof(atabusiodetach_args);
unsigned struct_atabusioscan_args_sz = sizeof(atabusioscan_args);
unsigned struct_ath_diag_sz = sizeof(ath_diag);
unsigned struct_atm_flowmap_sz = sizeof(atm_flowmap);
-unsigned struct_atm_pseudoioctl_sz = sizeof(atm_pseudoioctl);
unsigned struct_audio_buf_info_sz = sizeof(audio_buf_info);
unsigned struct_audio_device_sz = sizeof(audio_device);
unsigned struct_audio_encoding_sz = sizeof(audio_encoding);
@@ -596,7 +628,6 @@ unsigned struct_priq_delete_filter_sz = sizeof(priq_delete_filter);
unsigned struct_priq_interface_sz = sizeof(priq_interface);
unsigned struct_priq_modify_class_sz = sizeof(priq_modify_class);
unsigned struct_ptmget_sz = sizeof(ptmget);
-unsigned struct_pvctxreq_sz = sizeof(pvctxreq);
unsigned struct_radio_info_sz = sizeof(radio_info);
unsigned struct_red_conf_sz = sizeof(red_conf);
unsigned struct_red_interface_sz = sizeof(red_interface);
@@ -656,6 +687,9 @@ unsigned struct_usb_config_desc_sz = sizeof(usb_config_desc);
unsigned struct_usb_ctl_report_desc_sz = sizeof(usb_ctl_report_desc);
unsigned struct_usb_ctl_report_sz = sizeof(usb_ctl_report);
unsigned struct_usb_ctl_request_sz = sizeof(usb_ctl_request);
+unsigned struct_autofs_daemon_request_sz = sizeof(autofs_daemon_request);
+unsigned struct_autofs_daemon_done_sz = sizeof(autofs_daemon_done);
+unsigned struct_sctp_connectx_addrs_sz = sizeof(sctp_connectx_addrs);
unsigned struct_usb_device_info_old_sz = sizeof(usb_device_info_old);
unsigned struct_usb_device_info_sz = sizeof(usb_device_info);
unsigned struct_usb_device_stats_sz = sizeof(usb_device_stats);
@@ -797,6 +831,8 @@ unsigned struct_RF_SparetWait_sz = sizeof(RF_SparetWait_t);
unsigned struct_RF_ComponentLabel_sz = sizeof(RF_ComponentLabel_t);
unsigned struct_RF_SingleComponent_sz = sizeof(RF_SingleComponent_t);
unsigned struct_RF_ProgressInfo_sz = sizeof(RF_ProgressInfo_t);
+unsigned struct_nvlist_ref_sz = sizeof(struct __sanitizer_nvlist_ref_t);
+unsigned struct_StringList_sz = sizeof(StringList);
const unsigned IOCTL_NOT_PRESENT = 0;
@@ -1061,6 +1097,7 @@ unsigned IOCTL_MLX_REBUILDSTAT = MLX_REBUILDSTAT;
unsigned IOCTL_MLX_GET_SYSDRIVE = MLX_GET_SYSDRIVE;
unsigned IOCTL_MLX_GET_CINFO = MLX_GET_CINFO;
unsigned IOCTL_NVME_PASSTHROUGH_CMD = NVME_PASSTHROUGH_CMD;
+unsigned IOCTL_FWCFGIO_SET_INDEX = FWCFGIO_SET_INDEX;
unsigned IOCTL_IRDA_RESET_PARAMS = IRDA_RESET_PARAMS;
unsigned IOCTL_IRDA_SET_PARAMS = IRDA_SET_PARAMS;
unsigned IOCTL_IRDA_GET_SPEEDMASK = IRDA_GET_SPEEDMASK;
@@ -1390,6 +1427,24 @@ unsigned IOCTL_SPKRTONE = SPKRTONE;
unsigned IOCTL_SPKRTUNE = SPKRTUNE;
unsigned IOCTL_SPKRGETVOL = SPKRGETVOL;
unsigned IOCTL_SPKRSETVOL = SPKRSETVOL;
+#if 0 /* interfaces are WIP */
+unsigned IOCTL_NVMM_IOC_CAPABILITY = NVMM_IOC_CAPABILITY;
+unsigned IOCTL_NVMM_IOC_MACHINE_CREATE = NVMM_IOC_MACHINE_CREATE;
+unsigned IOCTL_NVMM_IOC_MACHINE_DESTROY = NVMM_IOC_MACHINE_DESTROY;
+unsigned IOCTL_NVMM_IOC_MACHINE_CONFIGURE = NVMM_IOC_MACHINE_CONFIGURE;
+unsigned IOCTL_NVMM_IOC_VCPU_CREATE = NVMM_IOC_VCPU_CREATE;
+unsigned IOCTL_NVMM_IOC_VCPU_DESTROY = NVMM_IOC_VCPU_DESTROY;
+unsigned IOCTL_NVMM_IOC_VCPU_SETSTATE = NVMM_IOC_VCPU_SETSTATE;
+unsigned IOCTL_NVMM_IOC_VCPU_GETSTATE = NVMM_IOC_VCPU_GETSTATE;
+unsigned IOCTL_NVMM_IOC_VCPU_INJECT = NVMM_IOC_VCPU_INJECT;
+unsigned IOCTL_NVMM_IOC_VCPU_RUN = NVMM_IOC_VCPU_RUN;
+unsigned IOCTL_NVMM_IOC_GPA_MAP = NVMM_IOC_GPA_MAP;
+unsigned IOCTL_NVMM_IOC_GPA_UNMAP = NVMM_IOC_GPA_UNMAP;
+unsigned IOCTL_NVMM_IOC_HVA_MAP = NVMM_IOC_HVA_MAP;
+unsigned IOCTL_NVMM_IOC_HVA_UNMAP = NVMM_IOC_HVA_UNMAP;
+#endif
+unsigned IOCTL_AUTOFSREQUEST = AUTOFSREQUEST;
+unsigned IOCTL_AUTOFSDONE = AUTOFSDONE;
unsigned IOCTL_BIOCGBLEN = BIOCGBLEN;
unsigned IOCTL_BIOCSBLEN = BIOCSBLEN;
unsigned IOCTL_BIOCSETF = BIOCSETF;
@@ -1408,19 +1463,12 @@ unsigned IOCTL_BIOCGHDRCMPLT = BIOCGHDRCMPLT;
unsigned IOCTL_BIOCSHDRCMPLT = BIOCSHDRCMPLT;
unsigned IOCTL_BIOCSDLT = BIOCSDLT;
unsigned IOCTL_BIOCGDLTLIST = BIOCGDLTLIST;
-unsigned IOCTL_BIOCGSEESENT = BIOCGSEESENT;
-unsigned IOCTL_BIOCSSEESENT = BIOCSSEESENT;
+unsigned IOCTL_BIOCGDIRECTION = BIOCGDIRECTION;
+unsigned IOCTL_BIOCSDIRECTION = BIOCSDIRECTION;
unsigned IOCTL_BIOCSRTIMEOUT = BIOCSRTIMEOUT;
unsigned IOCTL_BIOCGRTIMEOUT = BIOCGRTIMEOUT;
unsigned IOCTL_BIOCGFEEDBACK = BIOCGFEEDBACK;
unsigned IOCTL_BIOCSFEEDBACK = BIOCSFEEDBACK;
-unsigned IOCTL_SIOCRAWATM = SIOCRAWATM;
-unsigned IOCTL_SIOCATMENA = SIOCATMENA;
-unsigned IOCTL_SIOCATMDIS = SIOCATMDIS;
-unsigned IOCTL_SIOCSPVCTX = SIOCSPVCTX;
-unsigned IOCTL_SIOCGPVCTX = SIOCGPVCTX;
-unsigned IOCTL_SIOCSPVCSIF = SIOCSPVCSIF;
-unsigned IOCTL_SIOCGPVCSIF = SIOCGPVCSIF;
unsigned IOCTL_GRESADDRS = GRESADDRS;
unsigned IOCTL_GRESADDRD = GRESADDRD;
unsigned IOCTL_GREGADDRS = GREGADDRS;
@@ -1575,6 +1623,8 @@ unsigned IOCTL_SIOCRMNAT = SIOCRMNAT;
unsigned IOCTL_SIOCGNATS = SIOCGNATS;
unsigned IOCTL_SIOCGNATL = SIOCGNATL;
unsigned IOCTL_SIOCPURGENAT = SIOCPURGENAT;
+unsigned IOCTL_SIOCCONNECTX = SIOCCONNECTX;
+unsigned IOCTL_SIOCCONNECTXDEL = SIOCCONNECTXDEL;
unsigned IOCTL_SIOCSIFINFO_FLAGS = SIOCSIFINFO_FLAGS;
unsigned IOCTL_SIOCAADDRCTL_POLICY = SIOCAADDRCTL_POLICY;
unsigned IOCTL_SIOCDADDRCTL_POLICY = SIOCDADDRCTL_POLICY;
@@ -1719,6 +1769,8 @@ unsigned IOCTL_FDIOCGETFORMAT = FDIOCGETFORMAT;
unsigned IOCTL_FDIOCFORMAT_TRACK = FDIOCFORMAT_TRACK;
unsigned IOCTL_FIOCLEX = FIOCLEX;
unsigned IOCTL_FIONCLEX = FIONCLEX;
+unsigned IOCTL_FIOSEEKDATA = FIOSEEKDATA;
+unsigned IOCTL_FIOSEEKHOLE = FIOSEEKHOLE;
unsigned IOCTL_FIONREAD = FIONREAD;
unsigned IOCTL_FIONBIO = FIONBIO;
unsigned IOCTL_FIOASYNC = FIOASYNC;
@@ -1804,8 +1856,6 @@ unsigned IOCTL_MTIOCSLOCATE = MTIOCSLOCATE;
unsigned IOCTL_MTIOCHLOCATE = MTIOCHLOCATE;
unsigned IOCTL_POWER_EVENT_RECVDICT = POWER_EVENT_RECVDICT;
unsigned IOCTL_POWER_IOC_GET_TYPE = POWER_IOC_GET_TYPE;
-unsigned IOCTL_POWER_IOC_GET_TYPE_WITH_LOSSAGE =
- POWER_IOC_GET_TYPE_WITH_LOSSAGE;
unsigned IOCTL_RIOCGINFO = RIOCGINFO;
unsigned IOCTL_RIOCSINFO = RIOCSINFO;
unsigned IOCTL_RIOCSSRCH = RIOCSSRCH;
@@ -1840,6 +1890,7 @@ unsigned IOCTL_SIOCGLOWAT = SIOCGLOWAT;
unsigned IOCTL_SIOCATMARK = SIOCATMARK;
unsigned IOCTL_SIOCSPGRP = SIOCSPGRP;
unsigned IOCTL_SIOCGPGRP = SIOCGPGRP;
+unsigned IOCTL_SIOCPEELOFF = SIOCPEELOFF;
unsigned IOCTL_SIOCADDRT = SIOCADDRT;
unsigned IOCTL_SIOCDELRT = SIOCDELRT;
unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR;
@@ -1897,6 +1948,10 @@ unsigned IOCTL_SIOCGLINKSTR = SIOCGLINKSTR;
unsigned IOCTL_SIOCSLINKSTR = SIOCSLINKSTR;
unsigned IOCTL_SIOCGETHERCAP = SIOCGETHERCAP;
unsigned IOCTL_SIOCGIFINDEX = SIOCGIFINDEX;
+unsigned IOCTL_SIOCSETHERCAP = SIOCSETHERCAP;
+unsigned IOCTL_SIOCGUMBINFO = SIOCGUMBINFO;
+unsigned IOCTL_SIOCSUMBPARAM = SIOCSUMBPARAM;
+unsigned IOCTL_SIOCGUMBPARAM = SIOCGUMBPARAM;
unsigned IOCTL_SIOCSETPFSYNC = SIOCSETPFSYNC;
unsigned IOCTL_SIOCGETPFSYNC = SIOCGETPFSYNC;
unsigned IOCTL_PPS_IOC_CREATE = PPS_IOC_CREATE;
@@ -2063,6 +2118,44 @@ unsigned IOCTL_SNDCTL_DSP_SILENCE = SNDCTL_DSP_SILENCE;
const int si_SEGV_MAPERR = SEGV_MAPERR;
const int si_SEGV_ACCERR = SEGV_ACCERR;
+
+const int modctl_load = MODCTL_LOAD;
+const int modctl_unload = MODCTL_UNLOAD;
+const int modctl_stat = MODCTL_STAT;
+const int modctl_exists = MODCTL_EXISTS;
+
+const unsigned SHA1_CTX_sz = sizeof(SHA1_CTX);
+const unsigned SHA1_return_length = SHA1_DIGEST_STRING_LENGTH;
+
+const unsigned MD4_CTX_sz = sizeof(MD4_CTX);
+const unsigned MD4_return_length = MD4_DIGEST_STRING_LENGTH;
+
+const unsigned RMD160_CTX_sz = sizeof(RMD160_CTX);
+const unsigned RMD160_return_length = RMD160_DIGEST_STRING_LENGTH;
+
+const unsigned MD5_CTX_sz = sizeof(MD5_CTX);
+const unsigned MD5_return_length = MD5_DIGEST_STRING_LENGTH;
+
+const unsigned fpos_t_sz = sizeof(fpos_t);
+
+const unsigned MD2_CTX_sz = sizeof(MD2_CTX);
+const unsigned MD2_return_length = MD2_DIGEST_STRING_LENGTH;
+
+#define SHA2_CONST(LEN) \
+ const unsigned SHA##LEN##_CTX_sz = sizeof(SHA##LEN##_CTX); \
+ const unsigned SHA##LEN##_return_length = SHA##LEN##_DIGEST_STRING_LENGTH; \
+ const unsigned SHA##LEN##_block_length = SHA##LEN##_BLOCK_LENGTH; \
+ const unsigned SHA##LEN##_digest_length = SHA##LEN##_DIGEST_LENGTH
+
+SHA2_CONST(224);
+SHA2_CONST(256);
+SHA2_CONST(384);
+SHA2_CONST(512);
+
+#undef SHA2_CONST
+
+const int unvis_valid = UNVIS_VALID;
+const int unvis_validpush = UNVIS_VALIDPUSH;
} // namespace __sanitizer
using namespace __sanitizer;
@@ -2153,6 +2246,29 @@ CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc);
CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv);
CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs);
+COMPILER_CHECK(sizeof(__sanitizer_FILE) <= sizeof(FILE));
+CHECK_SIZE_AND_OFFSET(FILE, _p);
+CHECK_SIZE_AND_OFFSET(FILE, _r);
+CHECK_SIZE_AND_OFFSET(FILE, _w);
+CHECK_SIZE_AND_OFFSET(FILE, _flags);
+CHECK_SIZE_AND_OFFSET(FILE, _file);
+CHECK_SIZE_AND_OFFSET(FILE, _bf);
+CHECK_SIZE_AND_OFFSET(FILE, _lbfsize);
+CHECK_SIZE_AND_OFFSET(FILE, _cookie);
+CHECK_SIZE_AND_OFFSET(FILE, _close);
+CHECK_SIZE_AND_OFFSET(FILE, _read);
+CHECK_SIZE_AND_OFFSET(FILE, _seek);
+CHECK_SIZE_AND_OFFSET(FILE, _write);
+CHECK_SIZE_AND_OFFSET(FILE, _ext);
+CHECK_SIZE_AND_OFFSET(FILE, _up);
+CHECK_SIZE_AND_OFFSET(FILE, _ur);
+CHECK_SIZE_AND_OFFSET(FILE, _ubuf);
+CHECK_SIZE_AND_OFFSET(FILE, _nbuf);
+CHECK_SIZE_AND_OFFSET(FILE, _flush);
+CHECK_SIZE_AND_OFFSET(FILE, _lb_unused);
+CHECK_SIZE_AND_OFFSET(FILE, _blksize);
+CHECK_SIZE_AND_OFFSET(FILE, _offset);
+
CHECK_TYPE_SIZE(tm);
CHECK_SIZE_AND_OFFSET(tm, tm_sec);
CHECK_SIZE_AND_OFFSET(tm, tm_min);
@@ -2224,4 +2340,10 @@ CHECK_SIZE_AND_OFFSET(group, gr_passwd);
CHECK_SIZE_AND_OFFSET(group, gr_gid);
CHECK_SIZE_AND_OFFSET(group, gr_mem);
+CHECK_TYPE_SIZE(modctl_load_t);
+CHECK_SIZE_AND_OFFSET(modctl_load_t, ml_filename);
+CHECK_SIZE_AND_OFFSET(modctl_load_t, ml_flags);
+CHECK_SIZE_AND_OFFSET(modctl_load_t, ml_props);
+CHECK_SIZE_AND_OFFSET(modctl_load_t, ml_propslen);
+
#endif // SANITIZER_NETBSD
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_netbsd.h b/lib/sanitizer_common/sanitizer_platform_limits_netbsd.h
index 1718e3b1e8ea..0c0c8a837b8b 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_netbsd.h
+++ b/lib/sanitizer_common/sanitizer_platform_limits_netbsd.h
@@ -25,10 +25,10 @@
#if defined(__x86_64__)
#define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
- _GET_LINK_MAP_BY_DLOPEN_HANDLE(handle, 312)
+ _GET_LINK_MAP_BY_DLOPEN_HANDLE(handle, 264)
#elif defined(__i386__)
#define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
- _GET_LINK_MAP_BY_DLOPEN_HANDLE(handle, 164)
+ _GET_LINK_MAP_BY_DLOPEN_HANDLE(handle, 136)
#endif
namespace __sanitizer {
@@ -60,6 +60,29 @@ extern unsigned struct_timespec_sz;
extern unsigned struct_sembuf_sz;
extern unsigned struct_kevent_sz;
+extern unsigned struct_FTS_sz;
+extern unsigned struct_FTSENT_sz;
+
+extern unsigned struct_regex_sz;
+extern unsigned struct_regmatch_sz;
+
+extern unsigned struct_fstab_sz;
+
+struct __sanitizer_regmatch {
+ OFF_T rm_so;
+ OFF_T rm_eo;
+};
+
+typedef struct __sanitizer_modctl_load {
+ const char *ml_filename;
+ int ml_flags;
+ const char *ml_props;
+ uptr ml_propslen;
+} __sanitizer_modctl_load_t;
+extern const int modctl_load;
+extern const int modctl_unload;
+extern const int modctl_stat;
+extern const int modctl_exists;
union __sanitizer_sigval {
int sival_int;
@@ -425,8 +448,36 @@ struct __sanitizer_wordexp_t {
uptr we_nbytes;
};
-typedef char __sanitizer_FILE;
-#define SANITIZER_HAS_STRUCT_FILE 0
+struct __sanitizer_FILE {
+ unsigned char *_p;
+ int _r;
+ int _w;
+ unsigned short _flags;
+ short _file;
+ struct {
+ unsigned char *_base;
+ int _size;
+ } _bf;
+ int _lbfsize;
+ void *_cookie;
+ int (*_close)(void *ptr);
+ u64 (*_read)(void *, void *, uptr);
+ u64 (*_seek)(void *, u64, int);
+ uptr (*_write)(void *, const void *, uptr);
+ struct {
+ unsigned char *_base;
+ int _size;
+ } _ext;
+ unsigned char *_up;
+ int _ur;
+ unsigned char _ubuf[3];
+ unsigned char _nbuf[1];
+ int (*_flush)(void *ptr);
+ char _lb_unused[sizeof(uptr)];
+ int _blksize;
+ u64 _offset;
+};
+#define SANITIZER_HAS_STRUCT_FILE 1
extern int shmctl_ipc_stat;
@@ -460,6 +511,8 @@ struct __sanitizer_ttyent {
char *ty_class;
};
+extern const unsigned long __sanitizer_bufsiz;
+
#define IOC_NRBITS 8
#define IOC_TYPEBITS 8
#define IOC_SIZEBITS 14
@@ -496,7 +549,6 @@ extern unsigned struct_atabusiodetach_args_sz;
extern unsigned struct_atabusioscan_args_sz;
extern unsigned struct_ath_diag_sz;
extern unsigned struct_atm_flowmap_sz;
-extern unsigned struct_atm_pseudoioctl_sz;
extern unsigned struct_audio_buf_info_sz;
extern unsigned struct_audio_device_sz;
extern unsigned struct_audio_encoding_sz;
@@ -799,6 +851,9 @@ extern unsigned struct_usb_config_desc_sz;
extern unsigned struct_usb_ctl_report_desc_sz;
extern unsigned struct_usb_ctl_report_sz;
extern unsigned struct_usb_ctl_request_sz;
+extern unsigned struct_autofs_daemon_request_sz;
+extern unsigned struct_autofs_daemon_done_sz;
+extern unsigned struct_sctp_connectx_addrs_sz;
extern unsigned struct_usb_device_info_old_sz;
extern unsigned struct_usb_device_info_sz;
extern unsigned struct_usb_device_stats_sz;
@@ -933,6 +988,8 @@ extern unsigned struct_RF_SparetWait_sz;
extern unsigned struct_RF_ComponentLabel_sz;
extern unsigned struct_RF_SingleComponent_sz;
extern unsigned struct_RF_ProgressInfo_sz;
+extern unsigned struct_nvlist_ref_sz;
+extern unsigned struct_StringList_sz;
// A special value to mark ioctls that are not present on the target platform,
@@ -1201,6 +1258,7 @@ extern unsigned IOCTL_MLX_REBUILDSTAT;
extern unsigned IOCTL_MLX_GET_SYSDRIVE;
extern unsigned IOCTL_MLX_GET_CINFO;
extern unsigned IOCTL_NVME_PASSTHROUGH_CMD;
+extern unsigned IOCTL_FWCFGIO_SET_INDEX;
extern unsigned IOCTL_IRDA_RESET_PARAMS;
extern unsigned IOCTL_IRDA_SET_PARAMS;
extern unsigned IOCTL_IRDA_GET_SPEEDMASK;
@@ -1522,6 +1580,24 @@ extern unsigned IOCTL_SPKRTONE;
extern unsigned IOCTL_SPKRTUNE;
extern unsigned IOCTL_SPKRGETVOL;
extern unsigned IOCTL_SPKRSETVOL;
+#if 0 /* interfaces are WIP */
+extern unsigned IOCTL_NVMM_IOC_CAPABILITY;
+extern unsigned IOCTL_NVMM_IOC_MACHINE_CREATE;
+extern unsigned IOCTL_NVMM_IOC_MACHINE_DESTROY;
+extern unsigned IOCTL_NVMM_IOC_MACHINE_CONFIGURE;
+extern unsigned IOCTL_NVMM_IOC_VCPU_CREATE;
+extern unsigned IOCTL_NVMM_IOC_VCPU_DESTROY;
+extern unsigned IOCTL_NVMM_IOC_VCPU_SETSTATE;
+extern unsigned IOCTL_NVMM_IOC_VCPU_GETSTATE;
+extern unsigned IOCTL_NVMM_IOC_VCPU_INJECT;
+extern unsigned IOCTL_NVMM_IOC_VCPU_RUN;
+extern unsigned IOCTL_NVMM_IOC_GPA_MAP;
+extern unsigned IOCTL_NVMM_IOC_GPA_UNMAP;
+extern unsigned IOCTL_NVMM_IOC_HVA_MAP;
+extern unsigned IOCTL_NVMM_IOC_HVA_UNMAP;
+#endif
+extern unsigned IOCTL_AUTOFSREQUEST;
+extern unsigned IOCTL_AUTOFSDONE;
extern unsigned IOCTL_BIOCGBLEN;
extern unsigned IOCTL_BIOCSBLEN;
extern unsigned IOCTL_BIOCSETF;
@@ -1540,19 +1616,12 @@ extern unsigned IOCTL_BIOCGHDRCMPLT;
extern unsigned IOCTL_BIOCSHDRCMPLT;
extern unsigned IOCTL_BIOCSDLT;
extern unsigned IOCTL_BIOCGDLTLIST;
-extern unsigned IOCTL_BIOCGSEESENT;
-extern unsigned IOCTL_BIOCSSEESENT;
+extern unsigned IOCTL_BIOCGDIRECTION;
+extern unsigned IOCTL_BIOCSDIRECTION;
extern unsigned IOCTL_BIOCSRTIMEOUT;
extern unsigned IOCTL_BIOCGRTIMEOUT;
extern unsigned IOCTL_BIOCGFEEDBACK;
extern unsigned IOCTL_BIOCSFEEDBACK;
-extern unsigned IOCTL_SIOCRAWATM;
-extern unsigned IOCTL_SIOCATMENA;
-extern unsigned IOCTL_SIOCATMDIS;
-extern unsigned IOCTL_SIOCSPVCTX;
-extern unsigned IOCTL_SIOCGPVCTX;
-extern unsigned IOCTL_SIOCSPVCSIF;
-extern unsigned IOCTL_SIOCGPVCSIF;
extern unsigned IOCTL_GRESADDRS;
extern unsigned IOCTL_GRESADDRD;
extern unsigned IOCTL_GREGADDRS;
@@ -1707,6 +1776,8 @@ extern unsigned IOCTL_SIOCRMNAT;
extern unsigned IOCTL_SIOCGNATS;
extern unsigned IOCTL_SIOCGNATL;
extern unsigned IOCTL_SIOCPURGENAT;
+extern unsigned IOCTL_SIOCCONNECTX;
+extern unsigned IOCTL_SIOCCONNECTXDEL;
extern unsigned IOCTL_SIOCSIFINFO_FLAGS;
extern unsigned IOCTL_SIOCAADDRCTL_POLICY;
extern unsigned IOCTL_SIOCDADDRCTL_POLICY;
@@ -1851,6 +1922,8 @@ extern unsigned IOCTL_FDIOCGETFORMAT;
extern unsigned IOCTL_FDIOCFORMAT_TRACK;
extern unsigned IOCTL_FIOCLEX;
extern unsigned IOCTL_FIONCLEX;
+extern unsigned IOCTL_FIOSEEKDATA;
+extern unsigned IOCTL_FIOSEEKHOLE;
extern unsigned IOCTL_FIONREAD;
extern unsigned IOCTL_FIONBIO;
extern unsigned IOCTL_FIOASYNC;
@@ -1936,7 +2009,6 @@ extern unsigned IOCTL_MTIOCSLOCATE;
extern unsigned IOCTL_MTIOCHLOCATE;
extern unsigned IOCTL_POWER_EVENT_RECVDICT;
extern unsigned IOCTL_POWER_IOC_GET_TYPE;
-extern unsigned IOCTL_POWER_IOC_GET_TYPE_WITH_LOSSAGE;
extern unsigned IOCTL_RIOCGINFO;
extern unsigned IOCTL_RIOCSINFO;
extern unsigned IOCTL_RIOCSSRCH;
@@ -1971,6 +2043,7 @@ extern unsigned IOCTL_SIOCGLOWAT;
extern unsigned IOCTL_SIOCATMARK;
extern unsigned IOCTL_SIOCSPGRP;
extern unsigned IOCTL_SIOCGPGRP;
+extern unsigned IOCTL_SIOCPEELOFF;
extern unsigned IOCTL_SIOCADDRT;
extern unsigned IOCTL_SIOCDELRT;
extern unsigned IOCTL_SIOCSIFADDR;
@@ -2028,6 +2101,10 @@ extern unsigned IOCTL_SIOCGLINKSTR;
extern unsigned IOCTL_SIOCSLINKSTR;
extern unsigned IOCTL_SIOCGETHERCAP;
extern unsigned IOCTL_SIOCGIFINDEX;
+extern unsigned IOCTL_SIOCSETHERCAP;
+extern unsigned IOCTL_SIOCGUMBINFO;
+extern unsigned IOCTL_SIOCSUMBPARAM;
+extern unsigned IOCTL_SIOCGUMBPARAM;
extern unsigned IOCTL_SIOCSETPFSYNC;
extern unsigned IOCTL_SIOCGETPFSYNC;
extern unsigned IOCTL_PPS_IOC_CREATE;
@@ -2194,6 +2271,74 @@ extern unsigned IOCTL_SNDCTL_DSP_SILENCE;
extern const int si_SEGV_MAPERR;
extern const int si_SEGV_ACCERR;
+
+extern const unsigned SHA1_CTX_sz;
+extern const unsigned SHA1_return_length;
+
+extern const unsigned MD4_CTX_sz;
+extern const unsigned MD4_return_length;
+
+extern const unsigned RMD160_CTX_sz;
+extern const unsigned RMD160_return_length;
+
+extern const unsigned MD5_CTX_sz;
+extern const unsigned MD5_return_length;
+
+extern const unsigned fpos_t_sz;
+
+extern const unsigned MD2_CTX_sz;
+extern const unsigned MD2_return_length;
+
+#define SHA2_EXTERN(LEN) \
+ extern const unsigned SHA##LEN##_CTX_sz; \
+ extern const unsigned SHA##LEN##_return_length; \
+ extern const unsigned SHA##LEN##_block_length; \
+ extern const unsigned SHA##LEN##_digest_length
+
+SHA2_EXTERN(224);
+SHA2_EXTERN(256);
+SHA2_EXTERN(384);
+SHA2_EXTERN(512);
+
+#undef SHA2_EXTERN
+
+extern const int unvis_valid;
+extern const int unvis_validpush;
+
+struct __sanitizer_cdbr {
+ void (*unmap)(void *, void *, uptr);
+ void *cookie;
+ u8 *mmap_base;
+ uptr mmap_size;
+
+ u8 *hash_base;
+ u8 *offset_base;
+ u8 *data_base;
+
+ u32 data_size;
+ u32 entries;
+ u32 entries_index;
+ u32 seed;
+
+ u8 offset_size;
+ u8 index_size;
+
+ u32 entries_m;
+ u32 entries_index_m;
+ u8 entries_s1, entries_s2;
+ u8 entries_index_s1, entries_index_s2;
+};
+
+struct __sanitizer_cdbw {
+ uptr data_counter;
+ uptr data_allocated;
+ uptr data_size;
+ uptr *data_len;
+ void **data_ptr;
+ uptr hash_size;
+ void *hash;
+ uptr key_counter;
+};
} // namespace __sanitizer
#define CHECK_TYPE_SIZE(TYPE) \
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
index c27055f2aa80..ecc69bcea79b 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
@@ -14,7 +14,7 @@
#include "sanitizer_platform.h"
-#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
+#if SANITIZER_LINUX || SANITIZER_MAC
// Tests in this file assume that off_t-dependent data structures match the
// libc ABI. For example, struct dirent here is what readdir() function (as
// exported from libc) returns, and not the user-facing "dirent", which
@@ -45,7 +45,8 @@
#include <termios.h>
#include <time.h>
#include <wchar.h>
-#if !SANITIZER_MAC && !SANITIZER_FREEBSD
+#include <regex.h>
+#if !SANITIZER_MAC
#include <utmp.h>
#endif
@@ -54,6 +55,7 @@
#endif
#if !SANITIZER_ANDROID
+#include <fstab.h>
#include <sys/mount.h>
#include <sys/timeb.h>
#include <utmpx.h>
@@ -78,43 +80,11 @@
#include <net/if_arp.h>
#endif
-#if SANITIZER_FREEBSD
-# include <sys/mount.h>
-# include <sys/sockio.h>
-# include <sys/socket.h>
-# include <sys/filio.h>
-# include <sys/signal.h>
-# include <sys/timespec.h>
-# include <sys/timex.h>
-# include <sys/mqueue.h>
-# include <sys/msg.h>
-# include <sys/ipc.h>
-# include <sys/msg.h>
-# include <sys/statvfs.h>
-# include <sys/soundcard.h>
-# include <sys/mtio.h>
-# include <sys/consio.h>
-# include <sys/kbio.h>
-# include <sys/link_elf.h>
-# include <netinet/ip_mroute.h>
-# include <netinet/in.h>
-# include <net/ethernet.h>
-# include <net/ppp_defs.h>
-# include <glob.h>
-# include <term.h>
-
-#define _KERNEL // to declare 'shminfo' structure
-# include <sys/shm.h>
-#undef _KERNEL
-
-#undef INLINE // to avoid clashes with sanitizers' definitions
-#endif
-
-#if SANITIZER_FREEBSD || SANITIZER_IOS
+#if SANITIZER_IOS
#undef IOC_DIRMASK
#endif
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SANITIZER_LINUX
# include <utime.h>
# include <sys/ptrace.h>
# if defined(__mips64) || defined(__aarch64__) || defined(__arm__)
@@ -146,8 +116,6 @@ typedef struct user_fpregs elf_fpregset_t;
#include <netrom/netrom.h>
#if HAVE_RPC_XDR_H
# include <rpc/xdr.h>
-#elif HAVE_TIRPC_RPC_XDR_H
-# include <tirpc/rpc/xdr.h>
#endif
#include <scsi/scsi.h>
#include <sys/mtio.h>
@@ -198,9 +166,9 @@ typedef struct user_fpregs elf_fpregset_t;
namespace __sanitizer {
unsigned struct_utsname_sz = sizeof(struct utsname);
unsigned struct_stat_sz = sizeof(struct stat);
-#if !SANITIZER_IOS && !SANITIZER_FREEBSD
+#if !SANITIZER_IOS
unsigned struct_stat64_sz = sizeof(struct stat64);
-#endif // !SANITIZER_IOS && !SANITIZER_FREEBSD
+#endif // !SANITIZER_IOS
unsigned struct_rusage_sz = sizeof(struct rusage);
unsigned struct_tm_sz = sizeof(struct tm);
unsigned struct_passwd_sz = sizeof(struct passwd);
@@ -221,13 +189,15 @@ namespace __sanitizer {
unsigned struct_tms_sz = sizeof(struct tms);
unsigned struct_sigevent_sz = sizeof(struct sigevent);
unsigned struct_sched_param_sz = sizeof(struct sched_param);
-
+ unsigned struct_regex_sz = sizeof(regex_t);
+ unsigned struct_regmatch_sz = sizeof(regmatch_t);
#if SANITIZER_MAC && !SANITIZER_IOS
unsigned struct_statfs64_sz = sizeof(struct statfs64);
#endif // SANITIZER_MAC && !SANITIZER_IOS
#if !SANITIZER_ANDROID
+ unsigned struct_fstab_sz = sizeof(struct fstab);
unsigned struct_statfs_sz = sizeof(struct statfs);
unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
unsigned ucontext_t_sz = sizeof(ucontext_t);
@@ -244,12 +214,12 @@ namespace __sanitizer {
unsigned struct_oldold_utsname_sz = sizeof(struct oldold_utsname);
#endif // SANITIZER_LINUX
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SANITIZER_LINUX
unsigned struct_rlimit_sz = sizeof(struct rlimit);
unsigned struct_timespec_sz = sizeof(struct timespec);
unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+#endif // SANITIZER_LINUX
#if SANITIZER_LINUX && !SANITIZER_ANDROID
// Use pre-computed size of struct ustat to avoid <sys/ustat.h> which
@@ -269,12 +239,12 @@ namespace __sanitizer {
unsigned struct_statvfs64_sz = sizeof(struct statvfs64);
#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
unsigned struct_timex_sz = sizeof(struct timex);
unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
unsigned struct_mq_attr_sz = sizeof(struct mq_attr);
unsigned struct_statvfs_sz = sizeof(struct statvfs);
-#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
const uptr sig_ign = (uptr)SIG_IGN;
const uptr sig_dfl = (uptr)SIG_DFL;
@@ -286,7 +256,7 @@ namespace __sanitizer {
#endif
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
unsigned struct_shminfo_sz = sizeof(struct shminfo);
unsigned struct_shm_info_sz = sizeof(struct shm_info);
int shmctl_ipc_stat = (int)IPC_STAT;
@@ -322,7 +292,7 @@ unsigned struct_ElfW_Phdr_sz = sizeof(ElfW(Phdr));
unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
#endif
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
int glob_nomatch = GLOB_NOMATCH;
int glob_altdirfunc = GLOB_ALTDIRFUNC;
#endif
@@ -447,7 +417,7 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
unsigned struct_vt_stat_sz = sizeof(struct vt_stat);
#endif // SANITIZER_LINUX
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SANITIZER_LINUX
#if SOUND_VERSION >= 0x040000
unsigned struct_copr_buffer_sz = 0;
unsigned struct_copr_debug_buf_sz = 0;
@@ -464,7 +434,7 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec);
unsigned struct_synth_info_sz = sizeof(struct synth_info);
unsigned struct_vt_mode_sz = sizeof(struct vt_mode);
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+#endif // SANITIZER_LINUX
#if SANITIZER_LINUX && !SANITIZER_ANDROID
unsigned struct_ax25_parms_struct_sz = sizeof(struct ax25_parms_struct);
@@ -491,7 +461,7 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
unsigned struct_unimapinit_sz = sizeof(struct unimapinit);
#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info);
unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats);
#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
@@ -501,6 +471,8 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
#endif
+ const unsigned long __sanitizer_bufsiz = BUFSIZ;
+
const unsigned IOCTL_NOT_PRESENT = 0;
unsigned IOCTL_FIOASYNC = FIOASYNC;
@@ -547,7 +519,7 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
unsigned IOCTL_TIOCSTI = TIOCSTI;
unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
-#if ((SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID)
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT;
unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT;
#endif
@@ -737,9 +709,6 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
unsigned IOCTL_VT_RESIZE = VT_RESIZE;
unsigned IOCTL_VT_RESIZEX = VT_RESIZEX;
unsigned IOCTL_VT_SENDSIG = VT_SENDSIG;
-#endif // SANITIZER_LINUX
-
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
unsigned IOCTL_MTIOCGET = MTIOCGET;
unsigned IOCTL_MTIOCTOP = MTIOCTOP;
unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE;
@@ -832,7 +801,7 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
unsigned IOCTL_VT_RELDISP = VT_RELDISP;
unsigned IOCTL_VT_SETMODE = VT_SETMODE;
unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE;
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+#endif // SANITIZER_LINUX
#if SANITIZER_LINUX && !SANITIZER_ANDROID
unsigned IOCTL_CYGETDEFTHRESH = CYGETDEFTHRESH;
@@ -925,7 +894,7 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
unsigned IOCTL_TIOCSSERIAL = TIOCSSERIAL;
#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP;
unsigned IOCTL_KDDISABIO = KDDISABIO;
unsigned IOCTL_KDENABIO = KDENABIO;
@@ -1037,7 +1006,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 +1051,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 +1176,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
@@ -1242,7 +1212,7 @@ CHECK_SIZE_AND_OFFSET(group, gr_passwd);
CHECK_SIZE_AND_OFFSET(group, gr_gid);
CHECK_SIZE_AND_OFFSET(group, gr_mem);
-#if HAVE_RPC_XDR_H || HAVE_TIRPC_RPC_XDR_H
+#if HAVE_RPC_XDR_H
CHECK_TYPE_SIZE(XDR);
CHECK_SIZE_AND_OFFSET(XDR, x_op);
CHECK_SIZE_AND_OFFSET(XDR, x_ops);
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
index f89a11312d8b..de69852d35a6 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.h
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
@@ -15,22 +15,12 @@
#ifndef SANITIZER_PLATFORM_LIMITS_POSIX_H
#define SANITIZER_PLATFORM_LIMITS_POSIX_H
-#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC
+#if SANITIZER_LINUX || SANITIZER_MAC
#include "sanitizer_internal_defs.h"
#include "sanitizer_platform.h"
-#if SANITIZER_FREEBSD
-// FreeBSD's dlopen() returns a pointer to an Obj_Entry structure that
-// incorporates the map structure.
-# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
- ((link_map*)((handle) == nullptr ? nullptr : ((char*)(handle) + 560)))
-// Get sys/_types.h, because that tells us whether 64-bit inodes are
-// used in struct dirent below.
-#include <sys/_types.h>
-#else
# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) ((link_map*)(handle))
-#endif // !SANITIZER_FREEBSD
#ifndef __GLIBC_PREREQ
#define __GLIBC_PREREQ(x, y) 0
@@ -39,7 +29,7 @@
namespace __sanitizer {
extern unsigned struct_utsname_sz;
extern unsigned struct_stat_sz;
-#if !SANITIZER_FREEBSD && !SANITIZER_IOS
+#if !SANITIZER_IOS
extern unsigned struct_stat64_sz;
#endif
extern unsigned struct_rusage_sz;
@@ -59,8 +49,11 @@ namespace __sanitizer {
extern unsigned struct_sigevent_sz;
extern unsigned struct_sched_param_sz;
extern unsigned struct_statfs64_sz;
+ extern unsigned struct_regex_sz;
+ extern unsigned struct_regmatch_sz;
#if !SANITIZER_ANDROID
+ extern unsigned struct_fstab_sz;
extern unsigned struct_statfs_sz;
extern unsigned struct_sockaddr_sz;
extern unsigned ucontext_t_sz;
@@ -123,7 +116,7 @@ namespace __sanitizer {
const unsigned struct_kexec_segment_sz = 4 * sizeof(unsigned long);
#endif // SANITIZER_LINUX
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SANITIZER_LINUX
#if defined(__powerpc64__) || defined(__s390__)
const unsigned struct___old_kernel_stat_sz = 0;
@@ -180,20 +173,18 @@ namespace __sanitizer {
int data;
#elif SANITIZER_LINUX
uptr data[4];
-#elif SANITIZER_FREEBSD
- u32 data[4];
#endif
};
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+#endif // SANITIZER_LINUX
#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];
};
@@ -306,35 +297,14 @@ namespace __sanitizer {
#endif
#endif
};
-#elif SANITIZER_FREEBSD
- struct __sanitizer_ipc_perm {
- unsigned int cuid;
- unsigned int cgid;
- unsigned int uid;
- unsigned int gid;
- unsigned short mode;
- unsigned short seq;
- long key;
- };
-
- struct __sanitizer_shmid_ds {
- __sanitizer_ipc_perm shm_perm;
- unsigned long shm_segsz;
- unsigned int shm_lpid;
- unsigned int shm_cpid;
- int shm_nattch;
- unsigned long shm_atime;
- unsigned long shm_dtime;
- unsigned long shm_ctime;
- };
#endif
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned struct_msqid_ds_sz;
extern unsigned struct_mq_attr_sz;
extern unsigned struct_timex_sz;
extern unsigned struct_statvfs_sz;
-#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
struct __sanitizer_iovec {
void *iov_base;
@@ -384,7 +354,7 @@ namespace __sanitizer {
char *pw_passwd;
int pw_uid;
int pw_gid;
-#if SANITIZER_MAC || SANITIZER_FREEBSD
+#if SANITIZER_MAC
long pw_change;
char *pw_class;
#endif
@@ -393,12 +363,9 @@ namespace __sanitizer {
#endif
char *pw_dir;
char *pw_shell;
-#if SANITIZER_MAC || SANITIZER_FREEBSD
+#if SANITIZER_MAC
long pw_expire;
#endif
-#if SANITIZER_FREEBSD
- int pw_fields;
-#endif
};
struct __sanitizer_group {
@@ -468,7 +435,7 @@ namespace __sanitizer {
};
#endif
-#if SANITIZER_MAC || SANITIZER_FREEBSD
+#if SANITIZER_MAC
struct __sanitizer_msghdr {
void *msg_name;
unsigned msg_namelen;
@@ -514,17 +481,6 @@ namespace __sanitizer {
unsigned short d_reclen;
// more fields that we don't care about
};
-#elif SANITIZER_FREEBSD
- struct __sanitizer_dirent {
-#if defined(__INO64)
- unsigned long long d_fileno;
- unsigned long long d_off;
-#else
- unsigned int d_fileno;
-#endif
- unsigned short d_reclen;
- // more fields that we don't care about
- };
#elif SANITIZER_ANDROID || defined(__x86_64__)
struct __sanitizer_dirent {
unsigned long long d_ino;
@@ -550,20 +506,17 @@ namespace __sanitizer {
};
#endif
-// 'clock_t' is 32 bits wide on x64 FreeBSD
-#if SANITIZER_FREEBSD
- typedef int __sanitizer_clock_t;
-#elif defined(__x86_64__) && !defined(_LP64)
+#if defined(__x86_64__) && !defined(_LP64)
typedef long long __sanitizer_clock_t;
#else
typedef long __sanitizer_clock_t;
#endif
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SANITIZER_LINUX
typedef int __sanitizer_clockid_t;
#endif
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SANITIZER_LINUX
#if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__)\
|| defined(__mips__)
typedef unsigned __sanitizer___kernel_uid_t;
@@ -613,11 +566,6 @@ namespace __sanitizer {
// The size is determined by looking at sizeof of real sigset_t on linux.
uptr val[128 / sizeof(uptr)];
};
-#elif SANITIZER_FREEBSD
- struct __sanitizer_sigset_t {
- // uint32_t * 4
- unsigned int __bits[4];
- };
#endif
struct __sanitizer_siginfo {
@@ -707,9 +655,7 @@ namespace __sanitizer {
};
#endif // !SANITIZER_ANDROID
-#if SANITIZER_FREEBSD
- typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;
-#elif defined(__mips__)
+#if defined(__mips__)
struct __sanitizer_kernel_sigset_t {
uptr sig[2];
};
@@ -755,7 +701,7 @@ namespace __sanitizer {
extern int af_inet6;
uptr __sanitizer_in_addr_sz(int af);
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SANITIZER_LINUX
struct __sanitizer_dl_phdr_info {
uptr dlpi_addr;
const char *dlpi_name;
@@ -771,7 +717,7 @@ namespace __sanitizer {
int ai_family;
int ai_socktype;
int ai_protocol;
-#if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD
+#if SANITIZER_ANDROID || SANITIZER_MAC
unsigned ai_addrlen;
char *ai_canonname;
void *ai_addr;
@@ -797,7 +743,7 @@ namespace __sanitizer {
short revents;
};
-#if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD
+#if SANITIZER_ANDROID || SANITIZER_MAC
typedef unsigned __sanitizer_nfds_t;
#else
typedef unsigned long __sanitizer_nfds_t;
@@ -817,23 +763,9 @@ namespace __sanitizer {
int (*gl_lstat)(const char *, void *);
int (*gl_stat)(const char *, void *);
};
-# elif SANITIZER_FREEBSD
- struct __sanitizer_glob_t {
- uptr gl_pathc;
- uptr gl_matchc;
- uptr gl_offs;
- int gl_flags;
- char **gl_pathv;
- int (*gl_errfunc)(const char*, int);
- void (*gl_closedir)(void *dirp);
- struct dirent *(*gl_readdir)(void *dirp);
- void *(*gl_opendir)(const char*);
- int (*gl_lstat)(const char*, void* /* struct stat* */);
- int (*gl_stat)(const char*, void* /* struct stat* */);
- };
-# endif // SANITIZER_FREEBSD
+# endif // SANITIZER_LINUX
-# if SANITIZER_LINUX || SANITIZER_FREEBSD
+# if SANITIZER_LINUX
extern int glob_nomatch;
extern int glob_altdirfunc;
# endif
@@ -845,10 +777,6 @@ namespace __sanitizer {
uptr we_wordc;
char **we_wordv;
uptr we_offs;
-#if SANITIZER_FREEBSD
- char *we_strings;
- uptr we_nbytes;
-#endif
};
#if SANITIZER_LINUX && !SANITIZER_ANDROID
@@ -902,7 +830,7 @@ namespace __sanitizer {
extern int ptrace_geteventmsg;
#endif
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned struct_shminfo_sz;
extern unsigned struct_shm_info_sz;
extern int shmctl_ipc_stat;
@@ -1039,7 +967,7 @@ struct __sanitizer_cookie_io_functions_t {
extern unsigned struct_vt_stat_sz;
#endif // SANITIZER_LINUX
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SANITIZER_LINUX
extern unsigned struct_copr_buffer_sz;
extern unsigned struct_copr_debug_buf_sz;
extern unsigned struct_copr_msg_sz;
@@ -1051,7 +979,7 @@ struct __sanitizer_cookie_io_functions_t {
extern unsigned struct_seq_event_rec_sz;
extern unsigned struct_synth_info_sz;
extern unsigned struct_vt_mode_sz;
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+#endif // SANITIZER_LINUX
#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned struct_ax25_parms_struct_sz;
@@ -1073,7 +1001,9 @@ struct __sanitizer_cookie_io_functions_t {
extern unsigned struct_unimapinit_sz;
#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+ extern const unsigned long __sanitizer_bufsiz;
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned struct_audio_buf_info_sz;
extern unsigned struct_ppp_stats_sz;
#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
@@ -1133,7 +1063,7 @@ struct __sanitizer_cookie_io_functions_t {
extern unsigned IOCTL_TIOCSPGRP;
extern unsigned IOCTL_TIOCSTI;
extern unsigned IOCTL_TIOCSWINSZ;
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned IOCTL_SIOCGETSGCNT;
extern unsigned IOCTL_SIOCGETVIFCNT;
#endif
@@ -1295,8 +1225,6 @@ struct __sanitizer_cookie_io_functions_t {
extern unsigned IOCTL_VT_RESIZE;
extern unsigned IOCTL_VT_RESIZEX;
extern unsigned IOCTL_VT_SENDSIG;
-#endif // SANITIZER_LINUX
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
extern unsigned IOCTL_MTIOCGET;
extern unsigned IOCTL_MTIOCTOP;
extern unsigned IOCTL_SIOCADDRT;
@@ -1397,7 +1325,7 @@ struct __sanitizer_cookie_io_functions_t {
extern unsigned IOCTL_VT_RELDISP;
extern unsigned IOCTL_VT_SETMODE;
extern unsigned IOCTL_VT_WAITACTIVE;
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+#endif // SANITIZER_LINUX
#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned IOCTL_CYGETDEFTHRESH;
@@ -1484,9 +1412,6 @@ struct __sanitizer_cookie_io_functions_t {
extern unsigned IOCTL_TIOCSERGETMULTI;
extern unsigned IOCTL_TIOCSERSETMULTI;
extern unsigned IOCTL_TIOCSSERIAL;
-#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
-
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
extern unsigned IOCTL_GIO_SCRNMAP;
extern unsigned IOCTL_KDDISABIO;
extern unsigned IOCTL_KDENABIO;
@@ -1525,6 +1450,6 @@ struct __sanitizer_cookie_io_functions_t {
#define SIGACTION_SYMNAME sigaction
-#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC
+#endif // SANITIZER_LINUX || SANITIZER_MAC
#endif
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_solaris.h b/lib/sanitizer_common/sanitizer_platform_limits_solaris.h
index c0aa4cc1b17e..08bd24cd23e0 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_solaris.h
+++ b/lib/sanitizer_common/sanitizer_platform_limits_solaris.h
@@ -210,8 +210,7 @@ struct __sanitizer_cmsghdr {
int cmsg_type;
};
-#if SANITIZER_SOLARIS32 && 0
-// FIXME: need to deal with large file and non-large file cases
+#if SANITIZER_SOLARIS && (defined(_LP64) || _FILE_OFFSET_BITS == 64)
struct __sanitizer_dirent {
unsigned long long d_ino;
long long d_off;
diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc
index f7dfc86f58c6..b965fb0da1aa 100644
--- a/lib/sanitizer_common/sanitizer_posix.cc
+++ b/lib/sanitizer_common/sanitizer_posix.cc
@@ -18,6 +18,7 @@
#include "sanitizer_common.h"
#include "sanitizer_file.h"
+#include "sanitizer_flags.h"
#include "sanitizer_libc.h"
#include "sanitizer_posix.h"
#include "sanitizer_procmaps.h"
@@ -157,6 +158,8 @@ void MprotectMallocZones(void *addr, int prot) {}
#endif
fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
+ if (ShouldMockFailureToOpen(filename))
+ return kInvalidFd;
int flags;
switch (mode) {
case RdOnly: flags = O_RDONLY; break;
@@ -166,7 +169,7 @@ fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
fd_t res = internal_open(filename, flags, 0660);
if (internal_iserror(res, errno_p))
return kInvalidFd;
- return res;
+ return ReserveStandardFds(res);
}
void CloseFile(fd_t fd) {
@@ -193,11 +196,6 @@ bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
return true;
}
-bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) {
- uptr res = internal_rename(oldpath, newpath);
- return !internal_iserror(res, error_p);
-}
-
void *MapFileToMemory(const char *file_name, uptr *buff_size) {
fd_t fd = OpenFile(file_name, RdOnly);
CHECK(fd != kInvalidFd);
@@ -235,6 +233,8 @@ static inline bool IntervalsAreSeparate(uptr start1, uptr end1,
// memory).
bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ if (proc_maps.Error())
+ return true; // and hope for the best
MemoryMappedSegment segment;
while (proc_maps.Next(&segment)) {
if (segment.start == segment.end) continue; // Empty range.
@@ -274,13 +274,8 @@ bool IsAbsolutePath(const char *path) {
void ReportFile::Write(const char *buffer, uptr length) {
SpinMutexLock l(mu);
- static const char *kWriteError =
- "ReportFile::Write() can't output requested buffer!\n";
ReopenIfNecessary();
- if (length != internal_write(fd, buffer, length)) {
- internal_write(fd, kWriteError, internal_strlen(kWriteError));
- Die();
- }
+ internal_write(fd, buffer, length);
}
bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) {
@@ -328,6 +323,27 @@ const char *SignalContext::Describe() const {
return "UNKNOWN SIGNAL";
}
+fd_t ReserveStandardFds(fd_t fd) {
+ CHECK_GE(fd, 0);
+ if (fd > 2)
+ return fd;
+ bool used[3];
+ internal_memset(used, 0, sizeof(used));
+ while (fd <= 2) {
+ used[fd] = true;
+ fd = internal_dup(fd);
+ }
+ for (int i = 0; i <= 2; ++i)
+ if (used[i])
+ internal_close(i);
+ return fd;
+}
+
+bool ShouldMockFailureToOpen(const char *path) {
+ return common_flags()->test_only_emulate_no_memorymap &&
+ internal_strncmp(path, "/proc/", 6) == 0;
+}
+
} // namespace __sanitizer
#endif // SANITIZER_POSIX
diff --git a/lib/sanitizer_common/sanitizer_posix.h b/lib/sanitizer_common/sanitizer_posix.h
index da447002b66c..04a76445eab2 100644
--- a/lib/sanitizer_common/sanitizer_posix.h
+++ b/lib/sanitizer_common/sanitizer_posix.h
@@ -16,6 +16,7 @@
// ----------- ATTENTION -------------
// This header should NOT include any other headers from sanitizer runtime.
#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_freebsd.h"
#include "sanitizer_platform_limits_netbsd.h"
#include "sanitizer_platform_limits_openbsd.h"
#include "sanitizer_platform_limits_posix.h"
@@ -48,6 +49,7 @@ uptr internal_filesize(fd_t fd); // -1 on error.
uptr internal_stat(const char *path, void *buf);
uptr internal_lstat(const char *path, void *buf);
uptr internal_fstat(fd_t fd, void *buf);
+uptr internal_dup(int oldfd);
uptr internal_dup2(int oldfd, int newfd);
uptr internal_readlink(const char *path, char *buf, uptr bufsize);
uptr internal_unlink(const char *path);
@@ -60,6 +62,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
@@ -93,6 +100,11 @@ uptr internal_execve(const char *filename, char *const argv[],
bool IsStateDetached(int state);
+// Move the fd out of {0, 1, 2} range.
+fd_t ReserveStandardFds(fd_t fd);
+
+bool ShouldMockFailureToOpen(const char *path);
+
} // namespace __sanitizer
#endif // SANITIZER_POSIX_H
diff --git a/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_posix_libcdep.cc
index 266e9bdba051..3006e60d89f3 100644
--- a/lib/sanitizer_common/sanitizer_posix_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_posix_libcdep.cc
@@ -94,10 +94,12 @@ static rlim_t getlim(int res) {
}
static void setlim(int res, rlim_t lim) {
- // The following magic is to prevent clang from replacing it with memset.
- volatile struct rlimit rlim;
+ struct rlimit rlim;
+ if (getrlimit(res, const_cast<struct rlimit *>(&rlim))) {
+ Report("ERROR: %s getrlimit() failed %d\n", SanitizerToolName, errno);
+ Die();
+ }
rlim.rlim_cur = lim;
- rlim.rlim_max = lim;
if (setrlimit(res, const_cast<struct rlimit *>(&rlim))) {
Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);
Die();
diff --git a/lib/sanitizer_common/sanitizer_procmaps.h b/lib/sanitizer_common/sanitizer_procmaps.h
index 9fde040a11a6..acb7104f33e6 100644
--- a/lib/sanitizer_common/sanitizer_procmaps.h
+++ b/lib/sanitizer_common/sanitizer_procmaps.h
@@ -70,6 +70,7 @@ class MemoryMappingLayout {
explicit MemoryMappingLayout(bool cache_enabled);
~MemoryMappingLayout();
bool Next(MemoryMappedSegment *segment);
+ bool Error() const;
void Reset();
// In some cases, e.g. when running under a sandbox on Linux, ASan is unable
// to obtain the memory mappings. It should fall back to pre-cached data
diff --git a/lib/sanitizer_common/sanitizer_procmaps_bsd.cc b/lib/sanitizer_common/sanitizer_procmaps_bsd.cc
index e41dc987dcd7..362a424d737a 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;
@@ -99,6 +99,7 @@ void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
}
bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
+ CHECK(!Error()); // can not fail
char *last = data_.proc_self_maps.data + data_.proc_self_maps.len;
if (data_.current >= last)
return false;
diff --git a/lib/sanitizer_common/sanitizer_procmaps_common.cc b/lib/sanitizer_common/sanitizer_procmaps_common.cc
index 1f2b431c7ccd..17d61b6c5e8f 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_common.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_common.cc
@@ -80,12 +80,14 @@ MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
ReadProcMaps(&data_.proc_self_maps);
if (cache_enabled && data_.proc_self_maps.mmaped_size == 0)
LoadFromCache();
- CHECK_GT(data_.proc_self_maps.mmaped_size, 0);
- CHECK_GT(data_.proc_self_maps.len, 0);
Reset();
}
+bool MemoryMappingLayout::Error() const {
+ return data_.current == nullptr;
+}
+
MemoryMappingLayout::~MemoryMappingLayout() {
// Only unmap the buffer if it is different from the cached one. Otherwise
// it will be unmapped when the cache is refreshed.
diff --git a/lib/sanitizer_common/sanitizer_procmaps_linux.cc b/lib/sanitizer_common/sanitizer_procmaps_linux.cc
index 633e9393cce0..cf9cb25ba061 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_linux.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_linux.cc
@@ -31,6 +31,7 @@ static bool IsOneOf(char c, char c1, char c2) {
}
bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
+ if (Error()) return false; // simulate empty maps
char *last = data_.proc_self_maps.data + data_.proc_self_maps.len;
if (data_.current >= last) return false;
char *next_line =
diff --git a/lib/sanitizer_common/sanitizer_procmaps_mac.cc b/lib/sanitizer_common/sanitizer_procmaps_mac.cc
index 0167ab18ba13..267c960b55d4 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_mac.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_mac.cc
@@ -88,6 +88,10 @@ MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
MemoryMappingLayout::~MemoryMappingLayout() {
}
+bool MemoryMappingLayout::Error() const {
+ return false;
+}
+
// More information about Mach-O headers can be found in mach-o/loader.h
// Each Mach-O image has a header (mach_header or mach_header_64) starting with
// a magic number, and a list of linker load commands directly following the
@@ -140,12 +144,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 +152,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_procmaps_solaris.cc b/lib/sanitizer_common/sanitizer_procmaps_solaris.cc
index bfe83170f4e2..49bb46c31b08 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_solaris.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_solaris.cc
@@ -13,17 +13,24 @@
#include "sanitizer_common.h"
#include "sanitizer_procmaps.h"
+// Before Solaris 11.4, <procfs.h> doesn't work in a largefile environment.
+#undef _FILE_OFFSET_BITS
#include <procfs.h>
#include <limits.h>
namespace __sanitizer {
void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
- ReadFileToBuffer("/proc/self/xmap", &proc_maps->data, &proc_maps->mmaped_size,
- &proc_maps->len);
+ if (!ReadFileToBuffer("/proc/self/xmap", &proc_maps->data,
+ &proc_maps->mmaped_size, &proc_maps->len)) {
+ proc_maps->data = nullptr;
+ proc_maps->mmaped_size = 0;
+ proc_maps->len = 0;
+ }
}
bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
+ if (Error()) return false; // simulate empty maps
char *last = data_.proc_self_maps.data + data_.proc_self_maps.len;
if (data_.current >= last) return false;
diff --git a/lib/sanitizer_common/sanitizer_ring_buffer.h b/lib/sanitizer_common/sanitizer_ring_buffer.h
new file mode 100644
index 000000000000..d15f27fd4a83
--- /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_rtems.cc b/lib/sanitizer_common/sanitizer_rtems.cc
index 4be367911e3f..76aebe419034 100644
--- a/lib/sanitizer_common/sanitizer_rtems.cc
+++ b/lib/sanitizer_common/sanitizer_rtems.cc
@@ -95,8 +95,10 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
*tls_addr = *tls_size = 0;
}
+void InitializePlatformEarly() {}
void MaybeReexec() {}
void CheckASLR() {}
+void CheckMPROTECT() {}
void DisableCoreDumperIfNecessary() {}
void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
void SetAlternateSignalStack() {}
@@ -226,11 +228,6 @@ bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
return true;
}
-bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) {
- uptr res = rename(oldpath, newpath);
- return !internal_iserror(res, error_p);
-}
-
void ReleaseMemoryPagesToOS(uptr beg, uptr end) {}
void DumpProcessMap() {}
@@ -240,6 +237,7 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) {
}
char **GetArgv() { return nullptr; }
+char **GetEnviron() { return nullptr; }
const char *GetEnv(const char *name) {
return getenv(name);
diff --git a/lib/sanitizer_common/sanitizer_solaris.cc b/lib/sanitizer_common/sanitizer_solaris.cc
index a5db22994e0a..cc0201c7a37e 100644
--- a/lib/sanitizer_common/sanitizer_solaris.cc
+++ b/lib/sanitizer_common/sanitizer_solaris.cc
@@ -48,10 +48,21 @@ namespace __sanitizer {
DECLARE__REAL(ret_type, func, __VA_ARGS__); \
ret_type internal_ ## func(__VA_ARGS__)
+#if !defined(_LP64) && _FILE_OFFSET_BITS == 64
+#define _REAL64(func) _ ## func ## 64
+#else
+#define _REAL64(func) _REAL(func)
+#endif
+#define DECLARE__REAL64(ret_type, func, ...) \
+ extern "C" ret_type _REAL64(func)(__VA_ARGS__)
+#define DECLARE__REAL_AND_INTERNAL64(ret_type, func, ...) \
+ DECLARE__REAL64(ret_type, func, __VA_ARGS__); \
+ ret_type internal_ ## func(__VA_ARGS__)
+
// ---------------------- sanitizer_libc.h
-DECLARE__REAL_AND_INTERNAL(uptr, mmap, void *addr, uptr /*size_t*/ length,
- int prot, int flags, int fd, OFF_T offset) {
- return (uptr)_REAL(mmap)(addr, length, prot, flags, fd, offset);
+DECLARE__REAL_AND_INTERNAL64(uptr, mmap, void *addr, uptr /*size_t*/ length,
+ int prot, int flags, int fd, OFF_T offset) {
+ return (uptr)_REAL64(mmap)(addr, length, prot, flags, fd, offset);
}
DECLARE__REAL_AND_INTERNAL(uptr, munmap, void *addr, uptr length) {
@@ -66,19 +77,19 @@ DECLARE__REAL_AND_INTERNAL(uptr, close, fd_t fd) {
return _REAL(close)(fd);
}
-extern "C" int _REAL(open)(const char *, int, ...);
+extern "C" int _REAL64(open)(const char *, int, ...);
uptr internal_open(const char *filename, int flags) {
- return _REAL(open)(filename, flags);
+ return _REAL64(open)(filename, flags);
}
uptr internal_open(const char *filename, int flags, u32 mode) {
- return _REAL(open)(filename, flags, mode);
+ return _REAL64(open)(filename, flags, mode);
}
uptr OpenFile(const char *filename, bool write) {
- return internal_open(filename,
- write ? O_WRONLY | O_CREAT : O_RDONLY, 0660);
+ return ReserveStandardFds(
+ internal_open(filename, write ? O_WRONLY | O_CREAT : O_RDONLY, 0660));
}
DECLARE__REAL_AND_INTERNAL(uptr, read, fd_t fd, void *buf, uptr count) {
@@ -94,16 +105,16 @@ DECLARE__REAL_AND_INTERNAL(uptr, ftruncate, fd_t fd, uptr size) {
return ftruncate(fd, size);
}
-DECLARE__REAL_AND_INTERNAL(uptr, stat, const char *path, void *buf) {
- return _REAL(stat)(path, (struct stat *)buf);
+DECLARE__REAL_AND_INTERNAL64(uptr, stat, const char *path, void *buf) {
+ return _REAL64(stat)(path, (struct stat *)buf);
}
-DECLARE__REAL_AND_INTERNAL(uptr, lstat, const char *path, void *buf) {
- return _REAL(lstat)(path, (struct stat *)buf);
+DECLARE__REAL_AND_INTERNAL64(uptr, lstat, const char *path, void *buf) {
+ return _REAL64(lstat)(path, (struct stat *)buf);
}
-DECLARE__REAL_AND_INTERNAL(uptr, fstat, fd_t fd, void *buf) {
- return _REAL(fstat)(fd, (struct stat *)buf);
+DECLARE__REAL_AND_INTERNAL64(uptr, fstat, fd_t fd, void *buf) {
+ return _REAL64(fstat)(fd, (struct stat *)buf);
}
uptr internal_filesize(fd_t fd) {
@@ -153,13 +164,13 @@ DECLARE__REAL_AND_INTERNAL(uptr, getpid, void) {
}
// FIXME: This might be wrong: _getdents doesn't take a struct linux_dirent *.
-DECLARE__REAL_AND_INTERNAL(uptr, getdents, fd_t fd, struct linux_dirent *dirp,
- unsigned int count) {
- return _REAL(getdents)(fd, dirp, count);
+DECLARE__REAL_AND_INTERNAL64(uptr, getdents, fd_t fd, struct linux_dirent *dirp,
+ unsigned int count) {
+ return _REAL64(getdents)(fd, dirp, count);
}
-DECLARE__REAL_AND_INTERNAL(uptr, lseek, fd_t fd, OFF_T offset, int whence) {
- return _REAL(lseek)(fd, offset, whence);
+DECLARE__REAL_AND_INTERNAL64(uptr, lseek, fd_t fd, OFF_T offset, int whence) {
+ return _REAL64(lseek)(fd, offset, whence);
}
// FIXME: This might be wrong: _sigfillset doesn't take a
diff --git a/lib/sanitizer_common/sanitizer_stackdepot.cc b/lib/sanitizer_common/sanitizer_stackdepot.cc
index 3bd5b677a1f8..6aab98485226 100644
--- a/lib/sanitizer_common/sanitizer_stackdepot.cc
+++ b/lib/sanitizer_common/sanitizer_stackdepot.cc
@@ -26,7 +26,7 @@ struct StackDepotNode {
u32 tag;
uptr stack[1]; // [size]
- static const u32 kTabSizeLog = 20;
+ static const u32 kTabSizeLog = SANITIZER_ANDROID ? 16 : 20;
// Lower kTabSizeLog bits are equal for all items in one bucket.
// We use these bits to store the per-stack use counter.
static const u32 kUseCountBits = kTabSizeLog;
diff --git a/lib/sanitizer_common/sanitizer_stackdepot.h b/lib/sanitizer_common/sanitizer_stackdepot.h
index cb7345002a40..e22ed2e38e57 100644
--- a/lib/sanitizer_common/sanitizer_stackdepot.h
+++ b/lib/sanitizer_common/sanitizer_stackdepot.h
@@ -32,7 +32,7 @@ struct StackDepotHandle {
void inc_use_count_unsafe();
};
-const int kStackDepotMaxUseCount = 1U << 20;
+const int kStackDepotMaxUseCount = 1U << (SANITIZER_ANDROID ? 16 : 20);
StackDepotStats *StackDepotGetStats();
u32 StackDepotPut(StackTrace stack);
diff --git a/lib/sanitizer_common/sanitizer_stacktrace.h b/lib/sanitizer_common/sanitizer_stacktrace.h
index 562d2e9f737c..450a40a9069f 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace.h
+++ b/lib/sanitizer_common/sanitizer_stacktrace.h
@@ -19,7 +19,7 @@ namespace __sanitizer {
static const u32 kStackTraceMax = 256;
-#if SANITIZER_LINUX && (defined(__sparc__) || defined(__mips__))
+#if defined(__sparc__) || (SANITIZER_LINUX && defined(__mips__))
# define SANITIZER_CAN_FAST_UNWIND 0
#elif SANITIZER_WINDOWS
# define SANITIZER_CAN_FAST_UNWIND 0
diff --git a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
index 747a4a701728..c87b18e1b696 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_stacktrace_printer.cc b/lib/sanitizer_common/sanitizer_stacktrace_printer.cc
index ac0731d46a6b..f2b337433427 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace_printer.cc
+++ b/lib/sanitizer_common/sanitizer_stacktrace_printer.cc
@@ -98,6 +98,8 @@ static const char *DemangleFunctionName(const char *function) {
return "pthread_equal";
if (!internal_strcmp(function, "__libc_thr_curcpu"))
return "pthread_curcpu_np";
+ if (!internal_strcmp(function, "__libc_thr_sigsetmask"))
+ return "pthread_sigmask";
#endif
return function;
diff --git a/lib/sanitizer_common/sanitizer_stacktrace_sparc.cc b/lib/sanitizer_common/sanitizer_stacktrace_sparc.cc
index 9f9920ece80b..f41a3cefb350 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace_sparc.cc
+++ b/lib/sanitizer_common/sanitizer_stacktrace_sparc.cc
@@ -15,7 +15,7 @@
// This file is ported to Sparc v8, but it should be easy to port to
// Sparc v9.
-#if defined(__sparcv8__)
+#if defined(__sparcv8__) || defined(__sparcv8) || defined(__sparc_v8__)
#include "sanitizer_common.h"
#include "sanitizer_stacktrace.h"
@@ -55,4 +55,5 @@ void BufferedStackTrace::FastUnwindStack(uptr pc, uptr bp, uptr stack_top,
} // namespace __sanitizer
-#endif // !defined(__sparcv8__)
+#endif // !defined(__sparcv8__) && !defined(__sparcv8) &&
+ // !defined(__sparc_v8__)
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_fuchsia.h b/lib/sanitizer_common/sanitizer_symbolizer_fuchsia.h
index 3c1c864c0615..b241b9dbc3c8 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_fuchsia.h
+++ b/lib/sanitizer_common/sanitizer_symbolizer_fuchsia.h
@@ -35,6 +35,9 @@ constexpr const char *kFormatData = "{{{data:%p}}}";
// One frame in a backtrace (printed on a line by itself).
constexpr const char *kFormatFrame = "{{{bt:%u:%p}}}";
+// Dump trigger element.
+#define FORMAT_DUMPFILE "{{{dumpfile:%s:%s}}}"
+
} // namespace __sanitizer
#endif // SANITIZER_SYMBOLIZER_FUCHSIA_H
diff --git a/lib/sanitizer_common/sanitizer_syscall_generic.inc b/lib/sanitizer_common/sanitizer_syscall_generic.inc
index e4ed1b4deee0..ddfb26397f44 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 1f05ed9b6c1c..7ab1d7641449 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 a3fdb9e60d5d..b4fd0962a9fd 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 327aaa80a674..9853a6a675d3 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_syscalls_netbsd.inc b/lib/sanitizer_common/sanitizer_syscalls_netbsd.inc
index 4f766100813d..75aea2760d2c 100644
--- a/lib/sanitizer_common/sanitizer_syscalls_netbsd.inc
+++ b/lib/sanitizer_common/sanitizer_syscalls_netbsd.inc
@@ -43,8 +43,8 @@
// DO NOT EDIT! THIS FILE HAS BEEN GENERATED!
//
// Generated with: generate_netbsd_syscalls.awk
-// Generated date: 2018-03-03
-// Generated from: syscalls.master,v 1.291 2018/01/06 16:41:23 kamil Exp
+// Generated date: 2018-10-30
+// Generated from: syscalls.master,v 1.293 2018/07/31 13:00:13 rjs Exp
//
//===----------------------------------------------------------------------===//
@@ -1454,7 +1454,15 @@ PRE_SYSCALL(fpathconf)(long long fd_, long long name_) { /* Nothing to do */ }
POST_SYSCALL(fpathconf)(long long res, long long fd_, long long name_) {
/* Nothing to do */
}
-/* syscall 193 has been skipped */
+PRE_SYSCALL(getsockopt2)
+(long long s_, long long level_, long long name_, void *val_, void *avalsize_) {
+ /* TODO */
+}
+POST_SYSCALL(getsockopt2)
+(long long res, long long s_, long long level_, long long name_, void *val_,
+ void *avalsize_) {
+ /* TODO */
+}
PRE_SYSCALL(getrlimit)(long long which_, void *rlp_) {
PRE_WRITE(rlp_, struct_rlimit_sz);
}
@@ -2341,20 +2349,8 @@ POST_SYSCALL(__sigaction_sigtramp)
PRE_READ(nsa_, sizeof(__sanitizer_sigaction));
}
}
-PRE_SYSCALL(pmc_get_info)(long long ctr_, long long op_, void *args_) {
- /* TODO */
-}
-POST_SYSCALL(pmc_get_info)
-(long long res, long long ctr_, long long op_, void *args_) {
- /* TODO */
-}
-PRE_SYSCALL(pmc_control)(long long ctr_, long long op_, void *args_) {
- /* TODO */
-}
-POST_SYSCALL(pmc_control)
-(long long res, long long ctr_, long long op_, void *args_) {
- /* TODO */
-}
+/* syscall 341 has been skipped */
+/* syscall 342 has been skipped */
PRE_SYSCALL(rasctl)(void *addr_, long long len_, long long op_) {
/* Nothing to do */
}
@@ -3695,18 +3691,18 @@ POST_SYSCALL(recvmmsg)
PRE_SYSCALL(sendmmsg)
(long long s_, void *mmsg_, long long vlen_, long long flags_) {
struct __sanitizer_mmsghdr *mmsg = (struct __sanitizer_mmsghdr *)mmsg_;
- unsigned int vlen = (vlen_ > 1024 ? 1024 : vlen_);
if (mmsg) {
- PRE_READ(mmsg, sizeof(struct __sanitizer_mmsghdr) * vlen);
+ PRE_READ(mmsg, sizeof(struct __sanitizer_mmsghdr) *
+ (vlen_ > 1024 ? 1024 : vlen_));
}
}
POST_SYSCALL(sendmmsg)
(long long res, long long s_, void *mmsg_, long long vlen_, long long flags_) {
struct __sanitizer_mmsghdr *mmsg = (struct __sanitizer_mmsghdr *)mmsg_;
- unsigned int vlen = (vlen_ > 1024 ? 1024 : vlen_);
if (res >= 0) {
if (mmsg) {
- POST_READ(mmsg, sizeof(struct __sanitizer_mmsghdr) * vlen);
+ POST_READ(mmsg, sizeof(struct __sanitizer_mmsghdr) *
+ (vlen_ > 1024 ? 1024 : vlen_));
}
}
}
diff --git a/lib/sanitizer_common/sanitizer_termination.cc b/lib/sanitizer_common/sanitizer_termination.cc
index 8243fc05d02e..35e4403adfb4 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_thread_registry.cc b/lib/sanitizer_common/sanitizer_thread_registry.cc
index d9fd6549ba48..eb35cb6c5830 100644
--- a/lib/sanitizer_common/sanitizer_thread_registry.cc
+++ b/lib/sanitizer_common/sanitizer_thread_registry.cc
@@ -338,4 +338,15 @@ ThreadContextBase *ThreadRegistry::QuarantinePop() {
return tctx;
}
+void ThreadRegistry::SetThreadUserId(u32 tid, uptr user_id) {
+ BlockingMutexLock l(&mtx_);
+ CHECK_LT(tid, n_contexts_);
+ ThreadContextBase *tctx = threads_[tid];
+ CHECK_NE(tctx, 0);
+ CHECK_NE(tctx->status, ThreadStatusInvalid);
+ CHECK_NE(tctx->status, ThreadStatusDead);
+ CHECK_EQ(tctx->user_id, 0);
+ tctx->user_id = user_id;
+}
+
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_thread_registry.h b/lib/sanitizer_common/sanitizer_thread_registry.h
index b203be2f4dbc..30dc603febd4 100644
--- a/lib/sanitizer_common/sanitizer_thread_registry.h
+++ b/lib/sanitizer_common/sanitizer_thread_registry.h
@@ -122,6 +122,7 @@ class ThreadRegistry {
void JoinThread(u32 tid, void *arg);
void FinishThread(u32 tid);
void StartThread(u32 tid, tid_t os_id, bool workerthread, void *arg);
+ void SetThreadUserId(u32 tid, uptr user_id);
private:
const ThreadContextFactory context_factory_;
diff --git a/lib/sanitizer_common/sanitizer_type_traits.cc b/lib/sanitizer_common/sanitizer_type_traits.cc
new file mode 100644
index 000000000000..27fec6e1f494
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_type_traits.cc
@@ -0,0 +1,21 @@
+//===-- sanitizer_type_traits.cc --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements a subset of C++ type traits. This is so we can avoid depending
+// on system C++ headers.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_type_traits.h"
+
+namespace __sanitizer {
+
+const bool true_type::value;
+const bool false_type::value;
+
+} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_type_traits.h b/lib/sanitizer_common/sanitizer_type_traits.h
new file mode 100644
index 000000000000..4495f2c34b71
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_type_traits.h
@@ -0,0 +1,44 @@
+//===-- sanitizer_type_traits.h ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements a subset of C++ type traits. This is so we can avoid depending
+// on system C++ headers.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_TYPE_TRAITS_H
+#define SANITIZER_TYPE_TRAITS_H
+
+namespace __sanitizer {
+
+struct true_type {
+ static const bool value = true;
+};
+
+struct false_type {
+ static const bool value = false;
+};
+
+// is_same<T, U>
+//
+// Type trait to compare if types are the same.
+// E.g.
+//
+// ```
+// is_same<int,int>::value - True
+// is_same<int,char>::value - False
+// ```
+template <typename T, typename U>
+struct is_same : public false_type {};
+
+template <typename T>
+struct is_same<T, T> : public true_type {};
+
+} // namespace __sanitizer
+
+#endif
diff --git a/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cc
index 9e12c417c71d..c7a5ec86fc7c 100644
--- a/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cc
@@ -97,7 +97,7 @@ uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
// Clear the Thumb bit.
return val & ~(uptr)1;
#else
- return _Unwind_GetIP(ctx);
+ return (uptr)_Unwind_GetIP(ctx);
#endif
}
diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc
index 38e567d9a732..9a574dd23de6 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
@@ -733,10 +737,6 @@ bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
}
}
-bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) {
- UNIMPLEMENTED();
-}
-
uptr internal_sched_yield() {
Sleep(0);
return 0;
@@ -1004,6 +1004,10 @@ void CheckVMASize() {
// Do nothing.
}
+void InitializePlatformEarly() {
+ // Do nothing.
+}
+
void MaybeReexec() {
// No need to re-exec on Windows.
}
@@ -1012,11 +1016,20 @@ void CheckASLR() {
// Do nothing
}
+void CheckMPROTECT() {
+ // Do nothing
+}
+
char **GetArgv() {
// FIXME: Actually implement this function.
return 0;
}
+char **GetEnviron() {
+ // FIXME: Actually implement this function.
+ return 0;
+}
+
pid_t StartSubprocess(const char *program, const char *const argv[],
fd_t stdin_fd, fd_t stdout_fd, fd_t stderr_fd) {
// FIXME: implement on this platform
diff --git a/lib/sanitizer_common/sanitizer_win_defs.h b/lib/sanitizer_common/sanitizer_win_defs.h
index 077ff9ccc8df..10fc2d021ee0 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/scripts/gen_dynamic_list.py b/lib/sanitizer_common/scripts/gen_dynamic_list.py
index 25632ed77b94..4a9c7af95548 100755
--- a/lib/sanitizer_common/scripts/gen_dynamic_list.py
+++ b/lib/sanitizer_common/scripts/gen_dynamic_list.py
@@ -14,6 +14,7 @@
# gen_dynamic_list.py libclang_rt.*san*.a [ files ... ]
#
#===------------------------------------------------------------------------===#
+from __future__ import print_function
import argparse
import os
import re
@@ -84,6 +85,7 @@ def main(argv):
parser.add_argument('--version-list', action='store_true')
parser.add_argument('--extra', default=[], action='append')
parser.add_argument('libraries', default=[], nargs='+')
+ parser.add_argument('-o', '--output', required=True)
args = parser.parse_args()
result = []
@@ -117,16 +119,17 @@ def main(argv):
for line in f:
result.append(line.rstrip())
# Print the resulting list in the format recognized by ld.
- print('{')
- if args.version_list:
- print('global:')
- result.sort()
- for f in result:
- print(u' %s;' % f)
- if args.version_list:
- print('local:')
- print(' *;')
- print('};')
+ with open(args.output, 'w') as f:
+ print('{', file=f)
+ if args.version_list:
+ print('global:', file=f)
+ result.sort()
+ for sym in result:
+ print(u' %s;' % sym, file=f)
+ if args.version_list:
+ print('local:', file=f)
+ print(' *;', file=f)
+ print('};', file=f)
if __name__ == '__main__':
main(sys.argv)
diff --git a/lib/sanitizer_common/symbolizer/scripts/global_symbols.txt b/lib/sanitizer_common/symbolizer/scripts/global_symbols.txt
index f77648d7818a..beee0acf484e 100644
--- a/lib/sanitizer_common/symbolizer/scripts/global_symbols.txt
+++ b/lib/sanitizer_common/symbolizer/scripts/global_symbols.txt
@@ -49,6 +49,7 @@ dup U
dup2 U
environ U
execv U
+execve U
exit U
fclose U
fflush U
@@ -65,6 +66,7 @@ getcwd U
getenv U
getpagesize U
getpid U
+getrlimit U
gettimeofday U
ioctl U
isalpha U
@@ -101,6 +103,7 @@ rand U
readlink U
realloc U
remove U
+setrlimit U
setvbuf U
sigfillset U
sigprocmask U
diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt
index 401682baa07b..21ffe2528c25 100644
--- a/lib/sanitizer_common/tests/CMakeLists.txt
+++ b/lib/sanitizer_common/tests/CMakeLists.txt
@@ -5,7 +5,7 @@ clang_compiler_add_cxx_check()
# FIXME: use SANITIZER_COMMON_SUPPORTED_ARCH here
filter_available_targets(SANITIZER_UNITTEST_SUPPORTED_ARCH x86_64 i386 mips64 mips64el)
if(APPLE)
- darwin_filter_host_archs(SANITIZER_UNITTEST_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH)
+ darwin_filter_host_archs(SANITIZER_UNITTEST_SUPPORTED_ARCH SANITIZER_UNITTEST_SUPPORTED_ARCH)
endif()
set(SANITIZER_UNITTESTS
@@ -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
@@ -35,6 +36,7 @@ set(SANITIZER_UNITTESTS
sanitizer_symbolizer_test.cc
sanitizer_test_main.cc
sanitizer_thread_registry_test.cc
+ sanitizer_type_traits_test.cc
sanitizer_vector_test.cc)
set(SANITIZER_TEST_HEADERS
@@ -57,11 +59,6 @@ set(SANITIZER_TEST_CFLAGS_COMMON
-Wno-non-virtual-dtor
-Wno-gnu-zero-variadic-macro-arguments)
-if(MSVC)
- # Disable exceptions on Windows until they work reliably.
- list(APPEND SANITIZER_TEST_CFLAGS_COMMON -fno-exceptions -DGTEST_HAS_SEH=0)
-endif()
-
# -gline-tables-only must be enough for these tests, so use it if possible.
if(COMPILER_RT_TEST_COMPILER_ID MATCHES "Clang")
list(APPEND SANITIZER_TEST_CFLAGS_COMMON -gline-tables-only)
diff --git a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
index ef4c10b8de5d..3123a1d5abf4 100644
--- a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
@@ -53,6 +53,7 @@ static const u64 kAddressSpaceSize = 1ULL << 47;
typedef DefaultSizeClassMap SizeClassMap;
#endif
+template <typename AddressSpaceViewTy>
struct AP64 { // Allocator Params. Short name for shorter demangled names..
static const uptr kSpaceBeg = kAllocatorSpace;
static const uptr kSpaceSize = kAllocatorSize;
@@ -60,8 +61,10 @@ struct AP64 { // Allocator Params. Short name for shorter demangled names..
typedef ::SizeClassMap SizeClassMap;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = AddressSpaceViewTy;
};
+template <typename AddressSpaceViewTy>
struct AP64Dyn {
static const uptr kSpaceBeg = ~(uptr)0;
static const uptr kSpaceSize = kAllocatorSize;
@@ -69,8 +72,10 @@ struct AP64Dyn {
typedef ::SizeClassMap SizeClassMap;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = AddressSpaceViewTy;
};
+template <typename AddressSpaceViewTy>
struct AP64Compact {
static const uptr kSpaceBeg = ~(uptr)0;
static const uptr kSpaceSize = kAllocatorSize;
@@ -78,8 +83,10 @@ struct AP64Compact {
typedef CompactSizeClassMap SizeClassMap;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = AddressSpaceViewTy;
};
+template <typename AddressSpaceViewTy>
struct AP64VeryCompact {
static const uptr kSpaceBeg = ~(uptr)0;
static const uptr kSpaceSize = 1ULL << 37;
@@ -87,13 +94,43 @@ struct AP64VeryCompact {
typedef VeryCompactSizeClassMap SizeClassMap;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = AddressSpaceViewTy;
};
+template <typename AddressSpaceViewTy>
+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;
+ using AddressSpaceView = AddressSpaceViewTy;
+};
+
+template <typename AddressSpaceView>
+using Allocator64ASVT = SizeClassAllocator64<AP64<AddressSpaceView>>;
+using Allocator64 = Allocator64ASVT<LocalAddressSpaceView>;
+
+template <typename AddressSpaceView>
+using Allocator64DynamicASVT = SizeClassAllocator64<AP64Dyn<AddressSpaceView>>;
+using Allocator64Dynamic = Allocator64DynamicASVT<LocalAddressSpaceView>;
+
+template <typename AddressSpaceView>
+using Allocator64CompactASVT =
+ SizeClassAllocator64<AP64Compact<AddressSpaceView>>;
+using Allocator64Compact = Allocator64CompactASVT<LocalAddressSpaceView>;
+
+template <typename AddressSpaceView>
+using Allocator64VeryCompactASVT =
+ SizeClassAllocator64<AP64VeryCompact<AddressSpaceView>>;
+using Allocator64VeryCompact =
+ Allocator64VeryCompactASVT<LocalAddressSpaceView>;
+
+template <typename AddressSpaceView>
+using Allocator64DenseASVT = SizeClassAllocator64<AP64Dense<AddressSpaceView>>;
+using Allocator64Dense = Allocator64DenseASVT<LocalAddressSpaceView>;
-typedef SizeClassAllocator64<AP64> Allocator64;
-typedef SizeClassAllocator64<AP64Dyn> Allocator64Dynamic;
-typedef SizeClassAllocator64<AP64Compact> Allocator64Compact;
-typedef SizeClassAllocator64<AP64VeryCompact> Allocator64VeryCompact;
#elif defined(__mips64)
static const u64 kAddressSpaceSize = 1ULL << 40;
#elif defined(__aarch64__)
@@ -109,17 +146,22 @@ static const u64 kAddressSpaceSize = 1ULL << 32;
static const uptr kRegionSizeLog = FIRST_32_SECOND_64(20, 24);
static const uptr kFlatByteMapSize = kAddressSpaceSize >> kRegionSizeLog;
+template <typename AddressSpaceViewTy>
struct AP32Compact {
static const uptr kSpaceBeg = 0;
static const u64 kSpaceSize = kAddressSpaceSize;
static const uptr kMetadataSize = 16;
typedef CompactSizeClassMap SizeClassMap;
static const uptr kRegionSizeLog = ::kRegionSizeLog;
- typedef FlatByteMap<kFlatByteMapSize> ByteMap;
+ using AddressSpaceView = AddressSpaceViewTy;
+ using ByteMap = FlatByteMap<kFlatByteMapSize, AddressSpaceView>;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
-typedef SizeClassAllocator32<AP32Compact> Allocator32Compact;
+template <typename AddressSpaceView>
+using Allocator32CompactASVT =
+ SizeClassAllocator32<AP32Compact<AddressSpaceView>>;
+using Allocator32Compact = Allocator32CompactASVT<LocalAddressSpaceView>;
template <class SizeClassMap>
void TestSizeClassMap() {
@@ -144,6 +186,10 @@ TEST(SanitizerCommon, InternalSizeClassMap) {
TestSizeClassMap<InternalSizeClassMap>();
}
+TEST(SanitizerCommon, DenseSizeClassMap) {
+ TestSizeClassMap<VeryCompactSizeClassMap>();
+}
+
template <class Allocator>
void TestSizeClassAllocator() {
Allocator *a = new Allocator;
@@ -226,9 +272,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) {
@@ -241,18 +292,24 @@ TEST(SanitizerCommon, SizeClassAllocator32Compact) {
TestSizeClassAllocator<Allocator32Compact>();
}
+template <typename AddressSpaceViewTy>
struct AP32SeparateBatches {
static const uptr kSpaceBeg = 0;
static const u64 kSpaceSize = kAddressSpaceSize;
static const uptr kMetadataSize = 16;
typedef DefaultSizeClassMap SizeClassMap;
static const uptr kRegionSizeLog = ::kRegionSizeLog;
- typedef FlatByteMap<kFlatByteMapSize> ByteMap;
+ using AddressSpaceView = AddressSpaceViewTy;
+ using ByteMap = FlatByteMap<kFlatByteMapSize, AddressSpaceView>;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags =
SizeClassAllocator32FlagMasks::kUseSeparateSizeClassForBatch;
};
-typedef SizeClassAllocator32<AP32SeparateBatches> Allocator32SeparateBatches;
+template <typename AddressSpaceView>
+using Allocator32SeparateBatchesASVT =
+ SizeClassAllocator32<AP32SeparateBatches<AddressSpaceView>>;
+using Allocator32SeparateBatches =
+ Allocator32SeparateBatchesASVT<LocalAddressSpaceView>;
TEST(SanitizerCommon, SizeClassAllocator32SeparateBatches) {
TestSizeClassAllocator<Allocator32SeparateBatches>();
@@ -375,6 +432,7 @@ int TestMapUnmapCallback::unmap_count;
// to run them all at the same time. FIXME: Make them not flaky and reenable.
#if !SANITIZER_WINDOWS
+template <typename AddressSpaceViewTy = LocalAddressSpaceView>
struct AP64WithCallback {
static const uptr kSpaceBeg = kAllocatorSpace;
static const uptr kSpaceSize = kAllocatorSize;
@@ -382,12 +440,13 @@ struct AP64WithCallback {
typedef ::SizeClassMap SizeClassMap;
typedef TestMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = AddressSpaceViewTy;
};
TEST(SanitizerCommon, SizeClassAllocator64MapUnmapCallback) {
TestMapUnmapCallback::map_count = 0;
TestMapUnmapCallback::unmap_count = 0;
- typedef SizeClassAllocator64<AP64WithCallback> Allocator64WithCallBack;
+ typedef SizeClassAllocator64<AP64WithCallback<>> Allocator64WithCallBack;
Allocator64WithCallBack *a = new Allocator64WithCallBack;
a->Init(kReleaseToOSIntervalNever);
EXPECT_EQ(TestMapUnmapCallback::map_count, 1); // Allocator state.
@@ -408,13 +467,15 @@ TEST(SanitizerCommon, SizeClassAllocator64MapUnmapCallback) {
#endif
#endif
+template <typename AddressSpaceViewTy = LocalAddressSpaceView>
struct AP32WithCallback {
static const uptr kSpaceBeg = 0;
static const u64 kSpaceSize = kAddressSpaceSize;
static const uptr kMetadataSize = 16;
typedef CompactSizeClassMap SizeClassMap;
static const uptr kRegionSizeLog = ::kRegionSizeLog;
- typedef FlatByteMap<kFlatByteMapSize> ByteMap;
+ using AddressSpaceView = AddressSpaceViewTy;
+ using ByteMap = FlatByteMap<kFlatByteMapSize, AddressSpaceView>;
typedef TestMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
@@ -422,7 +483,7 @@ struct AP32WithCallback {
TEST(SanitizerCommon, SizeClassAllocator32MapUnmapCallback) {
TestMapUnmapCallback::map_count = 0;
TestMapUnmapCallback::unmap_count = 0;
- typedef SizeClassAllocator32<AP32WithCallback> Allocator32WithCallBack;
+ typedef SizeClassAllocator32<AP32WithCallback<>> Allocator32WithCallBack;
Allocator32WithCallBack *a = new Allocator32WithCallBack;
a->Init(kReleaseToOSIntervalNever);
EXPECT_EQ(TestMapUnmapCallback::map_count, 0);
@@ -597,6 +658,22 @@ void TestCombinedAllocator() {
std::shuffle(allocated.begin(), allocated.end(), r);
+ // Test ForEachChunk(...)
+ {
+ std::set<void *> reported_chunks;
+ auto cb = [](uptr chunk, void *arg) {
+ auto reported_chunks_ptr = reinterpret_cast<std::set<void *> *>(arg);
+ auto pair =
+ reported_chunks_ptr->insert(reinterpret_cast<void *>(chunk));
+ // Check chunk is never reported more than once.
+ ASSERT_TRUE(pair.second);
+ };
+ a->ForEachChunk(cb, reinterpret_cast<void *>(&reported_chunks));
+ for (const auto &allocated_ptr : allocated) {
+ ASSERT_NE(reported_chunks.find(allocated_ptr), reported_chunks.end());
+ }
+ }
+
for (uptr i = 0; i < kNumAllocs; i++) {
void *x = allocated[i];
uptr *meta = reinterpret_cast<uptr*>(a->GetMetaData(x));
@@ -956,6 +1033,7 @@ TEST(SanitizerCommon, LargeMmapAllocatorBlockBegin) {
// machine to OOM.
#if SANITIZER_CAN_USE_ALLOCATOR64 && !SANITIZER_WINDOWS64 && !SANITIZER_ANDROID
typedef SizeClassMap<3, 4, 8, 63, 128, 16> SpecialSizeClassMap;
+template <typename AddressSpaceViewTy = LocalAddressSpaceView>
struct AP64_SpecialSizeClassMap {
static const uptr kSpaceBeg = kAllocatorSpace;
static const uptr kSpaceSize = kAllocatorSize;
@@ -963,12 +1041,13 @@ struct AP64_SpecialSizeClassMap {
typedef SpecialSizeClassMap SizeClassMap;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = AddressSpaceViewTy;
};
// Regression test for out-of-memory condition in PopulateFreeList().
TEST(SanitizerCommon, SizeClassAllocator64PopulateFreeListOOM) {
// In a world where regions are small and chunks are huge...
- typedef SizeClassAllocator64<AP64_SpecialSizeClassMap> SpecialAllocator64;
+ typedef SizeClassAllocator64<AP64_SpecialSizeClassMap<>> SpecialAllocator64;
const uptr kRegionSize =
kAllocatorSize / SpecialSizeClassMap::kNumClassesRounded;
SpecialAllocator64 *a = new SpecialAllocator64;
@@ -1303,8 +1382,10 @@ TEST(SanitizerCommon, TwoLevelByteMap) {
m.TestOnlyUnmap();
}
-
-typedef TwoLevelByteMap<1 << 12, 1 << 13, TestMapUnmapCallback> TestByteMap;
+template <typename AddressSpaceView>
+using TestByteMapASVT =
+ TwoLevelByteMap<1 << 12, 1 << 13, AddressSpaceView, TestMapUnmapCallback>;
+using TestByteMap = TestByteMapASVT<LocalAddressSpaceView>;
struct TestByteMapParam {
TestByteMap *m;
diff --git a/lib/sanitizer_common/tests/sanitizer_common_test.cc b/lib/sanitizer_common/tests/sanitizer_common_test.cc
index 0177484a55a8..6b091de60d6d 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 000000000000..80aa57c5290e
--- /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/sanitizer_common/tests/sanitizer_type_traits_test.cc b/lib/sanitizer_common/tests/sanitizer_type_traits_test.cc
new file mode 100644
index 000000000000..0dce02fac170
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_type_traits_test.cc
@@ -0,0 +1,28 @@
+//===-- sanitizer_type_traits_test.cc -------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_type_traits.h"
+#include "gtest/gtest.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+
+using namespace __sanitizer;
+
+TEST(SanitizerCommon, IsSame) {
+ ASSERT_TRUE((is_same<unsigned, unsigned>::value));
+ ASSERT_TRUE((is_same<uptr, uptr>::value));
+ ASSERT_TRUE((is_same<sptr, sptr>::value));
+ ASSERT_TRUE((is_same<const uptr, const uptr>::value));
+
+ ASSERT_FALSE((is_same<unsigned, signed>::value));
+ ASSERT_FALSE((is_same<uptr, sptr>::value));
+ ASSERT_FALSE((is_same<uptr, const uptr>::value));
+}
diff --git a/lib/scudo/CMakeLists.txt b/lib/scudo/CMakeLists.txt
index 0646c3dd4f53..79f69e934f22 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 4a11bf5fcc21..fb04fb281c4b 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/scudo/scudo_allocator.h b/lib/scudo/scudo_allocator.h
index 0002b4a44b78..814bb08ab013 100644
--- a/lib/scudo/scudo_allocator.h
+++ b/lib/scudo/scudo_allocator.h
@@ -81,6 +81,7 @@ struct AP64 {
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags =
SizeClassAllocator64FlagMasks::kRandomShuffleChunks;
+ using AddressSpaceView = LocalAddressSpaceView;
};
typedef SizeClassAllocator64<AP64> PrimaryT;
#else
@@ -96,7 +97,8 @@ struct AP32 {
static const uptr kMetadataSize = 0;
typedef __scudo::SizeClassMap SizeClassMap;
static const uptr kRegionSizeLog = RegionSizeLog;
- typedef __scudo::ByteMap ByteMap;
+ using AddressSpaceView = LocalAddressSpaceView;
+ using ByteMap = __scudo::ByteMap;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags =
SizeClassAllocator32FlagMasks::kRandomShuffleChunks |
diff --git a/lib/scudo/scudo_malloc.cpp b/lib/scudo/scudo_malloc.cpp
index 91a77b365823..eef776809bd2 100644
--- a/lib/scudo/scudo_malloc.cpp
+++ b/lib/scudo/scudo_malloc.cpp
@@ -79,7 +79,7 @@ INTERCEPTOR_ATTRIBUTE size_t malloc_usable_size(void *ptr) {
#if SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
INTERCEPTOR_ATTRIBUTE int mallopt(int cmd, int value) {
- return -1;
+ return 0;
}
#endif
} // extern "C"
diff --git a/lib/tsan/CMakeLists.txt b/lib/tsan/CMakeLists.txt
index 4a2ea3f4ac75..e1da319ccc87 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.
@@ -211,11 +220,12 @@ else()
endif()
# Make sure that non-platform-specific files don't include any system headers.
-# FreeBSD does not install a number of Clang-provided headers for the compiler
-# in the base system due to incompatibilities between FreeBSD's and Clang's
-# versions. As a workaround do not use --sysroot=. on FreeBSD until this is
-# addressed.
-if(COMPILER_RT_HAS_SYSROOT_FLAG AND NOT CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+# FreeBSD/NetBSD do not install a number of Clang-provided headers for the
+# compiler in the base system due to incompatibilities between FreeBSD/NetBSD's
+# and Clang's versions. As a workaround do not use --sysroot=. on FreeBSD/NetBSD
+# until this is addressed.
+if(COMPILER_RT_HAS_SYSROOT_FLAG AND NOT CMAKE_SYSTEM_NAME MATCHES "FreeBSD"
+ AND NOT CMAKE_SYSTEM_NAME MATCHES "NetBSD")
file(GLOB _tsan_generic_sources rtl/tsan*)
file(GLOB _tsan_platform_sources rtl/tsan*posix* rtl/tsan*mac*
rtl/tsan*linux*)
diff --git a/lib/tsan/check_analyze.sh b/lib/tsan/check_analyze.sh
index 9b5abc317fbc..65c34d466da3 100755
--- a/lib/tsan/check_analyze.sh
+++ b/lib/tsan/check_analyze.sh
@@ -8,7 +8,7 @@
# performance has not regressed by running the following benchmarks before and
# after the breaking change to verify that the values in this file are safe to
# update:
-# ./projects/compiler-rt/lib/tsan/tests/rtl/TsanRtlTest
+# ./projects/compiler-rt/lib/tsan/tests/rtl/TsanRtlTest-x86_64-Test
# --gtest_also_run_disabled_tests --gtest_filter=DISABLED_BENCH.Mop*
set -u
@@ -34,16 +34,16 @@ check() {
fi
}
-for f in write1 write2 write4 write8; do
+for f in write1 write2 write4 write8 read2 read4; 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 read8; 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 7f570ca81139..eec4cf15e5aa 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_debugging.cc b/lib/tsan/rtl/tsan_debugging.cc
index 9e5465d37557..067aeef97c0e 100644
--- a/lib/tsan/rtl/tsan_debugging.cc
+++ b/lib/tsan/rtl/tsan_debugging.cc
@@ -20,31 +20,37 @@
using namespace __tsan;
static const char *ReportTypeDescription(ReportType typ) {
- if (typ == ReportTypeRace) return "data-race";
- if (typ == ReportTypeVptrRace) return "data-race-vptr";
- if (typ == ReportTypeUseAfterFree) return "heap-use-after-free";
- if (typ == ReportTypeVptrUseAfterFree) return "heap-use-after-free-vptr";
- if (typ == ReportTypeExternalRace) return "external-race";
- if (typ == ReportTypeThreadLeak) return "thread-leak";
- if (typ == ReportTypeMutexDestroyLocked) return "locked-mutex-destroy";
- if (typ == ReportTypeMutexDoubleLock) return "mutex-double-lock";
- if (typ == ReportTypeMutexInvalidAccess) return "mutex-invalid-access";
- if (typ == ReportTypeMutexBadUnlock) return "mutex-bad-unlock";
- if (typ == ReportTypeMutexBadReadLock) return "mutex-bad-read-lock";
- if (typ == ReportTypeMutexBadReadUnlock) return "mutex-bad-read-unlock";
- if (typ == ReportTypeSignalUnsafe) return "signal-unsafe-call";
- if (typ == ReportTypeErrnoInSignal) return "errno-in-signal-handler";
- if (typ == ReportTypeDeadlock) return "lock-order-inversion";
- return "";
+ switch (typ) {
+ case ReportTypeRace: return "data-race";
+ case ReportTypeVptrRace: return "data-race-vptr";
+ case ReportTypeUseAfterFree: return "heap-use-after-free";
+ case ReportTypeVptrUseAfterFree: return "heap-use-after-free-vptr";
+ case ReportTypeExternalRace: return "external-race";
+ case ReportTypeThreadLeak: return "thread-leak";
+ case ReportTypeMutexDestroyLocked: return "locked-mutex-destroy";
+ case ReportTypeMutexDoubleLock: return "mutex-double-lock";
+ case ReportTypeMutexInvalidAccess: return "mutex-invalid-access";
+ case ReportTypeMutexBadUnlock: return "mutex-bad-unlock";
+ case ReportTypeMutexBadReadLock: return "mutex-bad-read-lock";
+ case ReportTypeMutexBadReadUnlock: return "mutex-bad-read-unlock";
+ case ReportTypeSignalUnsafe: return "signal-unsafe-call";
+ case ReportTypeErrnoInSignal: return "errno-in-signal-handler";
+ case ReportTypeDeadlock: return "lock-order-inversion";
+ // No default case so compiler warns us if we miss one
+ }
+ UNREACHABLE("missing case");
}
static const char *ReportLocationTypeDescription(ReportLocationType typ) {
- if (typ == ReportLocationGlobal) return "global";
- if (typ == ReportLocationHeap) return "heap";
- if (typ == ReportLocationStack) return "stack";
- if (typ == ReportLocationTLS) return "tls";
- if (typ == ReportLocationFD) return "fd";
- return "";
+ switch (typ) {
+ case ReportLocationGlobal: return "global";
+ case ReportLocationHeap: return "heap";
+ case ReportLocationStack: return "stack";
+ case ReportLocationTLS: return "tls";
+ case ReportLocationFD: return "fd";
+ // No default case so compiler warns us if we miss one
+ }
+ UNREACHABLE("missing case");
}
static void CopyTrace(SymbolizedStack *first_frame, void **trace,
diff --git a/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc
index 89e22a132786..877fc8b819fd 100644
--- a/lib/tsan/rtl/tsan_flags.cc
+++ b/lib/tsan/rtl/tsan_flags.cc
@@ -61,8 +61,7 @@ void InitializeFlags(Flags *f, const char *env) {
// Does not work as expected for Go: runtime handles SIGABRT and crashes.
cf.abort_on_error = false;
// Go does not have mutexes.
- } else {
- cf.detect_deadlocks = true;
+ cf.detect_deadlocks = false;
}
cf.print_suppressions = false;
cf.stack_trace_format = " #%n %f %S %M";
diff --git a/lib/tsan/rtl/tsan_flags.inc b/lib/tsan/rtl/tsan_flags.inc
index e9b3e35f07e5..d3b678fdd151 100644
--- a/lib/tsan/rtl/tsan_flags.inc
+++ b/lib/tsan/rtl/tsan_flags.inc
@@ -77,8 +77,6 @@ TSAN_FLAG(int, io_sync, 1,
TSAN_FLAG(bool, die_after_fork, true,
"Die after multi-threaded fork if the child creates new threads.")
TSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
-TSAN_FLAG(bool, ignore_interceptors_accesses, false,
- "Ignore reads and writes from all interceptors.")
TSAN_FLAG(bool, ignore_noninstrumented_modules, SANITIZER_MAC ? true : false,
"Interceptors should only detect races when called from instrumented "
"modules.")
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
index 901997b3e85d..9e49dfe5d582 100644
--- a/lib/tsan/rtl/tsan_interceptors.cc
+++ b/lib/tsan/rtl/tsan_interceptors.cc
@@ -42,17 +42,15 @@ using namespace __tsan; // NOLINT
#if SANITIZER_NETBSD
#define dirfd(dirp) (*(int *)(dirp))
-#define fileno_unlocked fileno
+#define fileno_unlocked(fp) \
+ (((__sanitizer_FILE*)fp)->_file == -1 ? -1 : \
+ (int)(unsigned short)(((__sanitizer_FILE*)fp)->_file)) // NOLINT
-#if _LP64
-#define __sF_size 152
-#else
-#define __sF_size 88
-#endif
-
-#define stdout ((char*)&__sF + (__sF_size * 1))
-#define stderr ((char*)&__sF + (__sF_size * 2))
+#define stdout ((__sanitizer_FILE*)&__sF[1])
+#define stderr ((__sanitizer_FILE*)&__sF[2])
+#define nanosleep __nanosleep50
+#define vfork __vfork14
#endif
#if SANITIZER_ANDROID
@@ -94,8 +92,8 @@ DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr size)
DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
extern "C" void *pthread_self();
extern "C" void _exit(int status);
-extern "C" int fileno_unlocked(void *stream);
#if !SANITIZER_NETBSD
+extern "C" int fileno_unlocked(void *stream);
extern "C" int dirfd(void *dirp);
#endif
#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_NETBSD
@@ -226,6 +224,16 @@ void InitializeLibIgnore() {
libignore()->OnLibraryLoaded(0);
}
+// The following two hooks can be used by for cooperative scheduling when
+// locking.
+#ifdef TSAN_EXTERNAL_HOOKS
+void OnPotentiallyBlockingRegionBegin();
+void OnPotentiallyBlockingRegionEnd();
+#else
+SANITIZER_WEAK_CXX_DEFAULT_IMPL void OnPotentiallyBlockingRegionBegin() {}
+SANITIZER_WEAK_CXX_DEFAULT_IMPL void OnPotentiallyBlockingRegionEnd() {}
+#endif
+
} // namespace __tsan
static ThreadSignalContext *SigCtx(ThreadState *thr) {
@@ -246,8 +254,7 @@ ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname,
if (!thr_->ignore_interceptors) FuncEntry(thr, pc);
DPrintf("#%d: intercept %s()\n", thr_->tid, fname);
ignoring_ =
- !thr_->in_ignored_lib && (flags()->ignore_interceptors_accesses ||
- libignore()->IsIgnored(pc, &in_ignored_lib_));
+ !thr_->in_ignored_lib && libignore()->IsIgnored(pc, &in_ignored_lib_);
EnableIgnores();
}
@@ -508,7 +515,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
@@ -863,6 +871,8 @@ TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
// Used in thread-safe function static initialization.
STDCXX_INTERCEPTOR(int, __cxa_guard_acquire, atomic_uint32_t *g) {
SCOPED_INTERCEPTOR_RAW(__cxa_guard_acquire, g);
+ OnPotentiallyBlockingRegionBegin();
+ auto on_exit = at_scope_exit(&OnPotentiallyBlockingRegionEnd);
for (;;) {
u32 cmp = atomic_load(g, memory_order_acquire);
if (cmp == 0) {
@@ -1041,6 +1051,35 @@ TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
return res;
}
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(int, pthread_tryjoin_np, void *th, void **ret) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_tryjoin_np, th, ret);
+ int tid = ThreadTid(thr, pc, (uptr)th);
+ ThreadIgnoreBegin(thr, pc);
+ int res = REAL(pthread_tryjoin_np)(th, ret);
+ ThreadIgnoreEnd(thr, pc);
+ if (res == 0)
+ ThreadJoin(thr, pc, tid);
+ else
+ ThreadNotJoined(thr, pc, tid, (uptr)th);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_timedjoin_np, void *th, void **ret,
+ const struct timespec *abstime) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_timedjoin_np, th, ret, abstime);
+ int tid = ThreadTid(thr, pc, (uptr)th);
+ ThreadIgnoreBegin(thr, pc);
+ int res = BLOCK_REAL(pthread_timedjoin_np)(th, ret, abstime);
+ ThreadIgnoreEnd(thr, pc);
+ if (res == 0)
+ ThreadJoin(thr, pc, tid);
+ else
+ ThreadNotJoined(thr, pc, tid, (uptr)th);
+ return res;
+}
+#endif
+
// Problem:
// NPTL implementation of pthread_cond has 2 versions (2.2.5 and 2.3.2).
// pthread_cond_t has different size in the different versions.
@@ -2210,7 +2249,8 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc,
(void) ctx;
#define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path) \
- Acquire(thr, pc, File2addr(path)); \
+ if (path) \
+ Acquire(thr, pc, File2addr(path)); \
if (file) { \
int fd = fileno_unlocked(file); \
if (fd >= 0) FdFileCreate(thr, pc, fd); \
@@ -2559,6 +2599,8 @@ TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_wrlock, void *m)
TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_trywrlock, void *m)
TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_unlock, void *m)
TSAN_INTERCEPTOR_NETBSD_ALIAS_THR(int, once, void *o, void (*f)())
+TSAN_INTERCEPTOR_NETBSD_ALIAS_THR2(int, sigsetmask, sigmask, int a, void *b,
+ void *c)
namespace __tsan {
@@ -2635,6 +2677,10 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(pthread_create);
TSAN_INTERCEPT(pthread_join);
TSAN_INTERCEPT(pthread_detach);
+ #if SANITIZER_LINUX
+ TSAN_INTERCEPT(pthread_tryjoin_np);
+ TSAN_INTERCEPT(pthread_timedjoin_np);
+ #endif
TSAN_INTERCEPT_VER(pthread_cond_init, PTHREAD_ABI_BASE);
TSAN_INTERCEPT_VER(pthread_cond_signal, PTHREAD_ABI_BASE);
@@ -2768,6 +2814,7 @@ void InitializeInterceptors() {
TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_trywrlock);
TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_unlock);
TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS_THR(once);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS_THR(sigsetmask);
FdInit();
}
diff --git a/lib/tsan/rtl/tsan_interceptors.h b/lib/tsan/rtl/tsan_interceptors.h
index 959a39465e33..763b46b88c8f 100644
--- a/lib/tsan/rtl/tsan_interceptors.h
+++ b/lib/tsan/rtl/tsan_interceptors.h
@@ -56,9 +56,13 @@ LibIgnore *libignore();
# define TSAN_INTERCEPTOR_NETBSD_ALIAS_THR(ret, func, ...) \
TSAN_INTERCEPTOR(ret, __libc_thr_##func, __VA_ARGS__) \
ALIAS(WRAPPER_NAME(pthread_##func));
+# define TSAN_INTERCEPTOR_NETBSD_ALIAS_THR2(ret, func, func2, ...) \
+ TSAN_INTERCEPTOR(ret, __libc_thr_##func, __VA_ARGS__) \
+ ALIAS(WRAPPER_NAME(pthread_##func2));
#else
# define TSAN_INTERCEPTOR_NETBSD_ALIAS(ret, func, ...)
# define TSAN_INTERCEPTOR_NETBSD_ALIAS_THR(ret, func, ...)
+# define TSAN_INTERCEPTOR_NETBSD_ALIAS_THR2(ret, func, func2, ...)
#endif
#endif // TSAN_INTERCEPTORS_H
diff --git a/lib/tsan/rtl/tsan_interceptors_mac.cc b/lib/tsan/rtl/tsan_interceptors_mac.cc
index b58e6b7071cf..579c4d0c0b81 100644
--- a/lib/tsan/rtl/tsan_interceptors_mac.cc
+++ b/lib/tsan/rtl/tsan_interceptors_mac.cc
@@ -19,8 +19,10 @@
#include "tsan_interceptors.h"
#include "tsan_interface.h"
#include "tsan_interface_ann.h"
+#include "sanitizer_common/sanitizer_addrhashmap.h"
#include <libkern/OSAtomic.h>
+#include <objc/objc-sync.h>
#if defined(__has_include) && __has_include(<xpc/xpc.h>)
#include <xpc/xpc.h>
@@ -294,41 +296,62 @@ TSAN_INTERCEPTOR(void, xpc_connection_cancel, xpc_connection_t connection) {
#endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)
-// Is the Obj-C object a tagged pointer (i.e. isn't really a valid pointer and
-// contains data in the pointers bits instead)?
-static bool IsTaggedObjCPointer(void *obj) {
+// Determines whether the Obj-C object pointer is a tagged pointer. Tagged
+// pointers encode the object data directly in their pointer bits and do not
+// have an associated memory allocation. The Obj-C runtime uses tagged pointers
+// to transparently optimize small objects.
+static bool IsTaggedObjCPointer(id obj) {
const uptr kPossibleTaggedBits = 0x8000000000000001ull;
return ((uptr)obj & kPossibleTaggedBits) != 0;
}
-// Return an address on which we can synchronize (Acquire and Release) for a
-// Obj-C tagged pointer (which is not a valid pointer). Ideally should be a
-// derived address from 'obj', but for now just return the same global address.
-// TODO(kubamracek): Return different address for different pointers.
-static uptr SyncAddressForTaggedPointer(void *obj) {
- (void)obj;
- static u64 addr;
- return (uptr)&addr;
+// Returns an address which can be used to inform TSan about synchronization
+// points (MutexLock/Unlock). The TSan infrastructure expects this to be a valid
+// address in the process space. We do a small allocation here to obtain a
+// stable address (the array backing the hash map can change). The memory is
+// never free'd (leaked) and allocation and locking are slow, but this code only
+// runs for @synchronized with tagged pointers, which is very rare.
+static uptr GetOrCreateSyncAddress(uptr addr, ThreadState *thr, uptr pc) {
+ typedef AddrHashMap<uptr, 5> Map;
+ static Map Addresses;
+ Map::Handle h(&Addresses, addr);
+ if (h.created()) {
+ ThreadIgnoreBegin(thr, pc);
+ *h = (uptr) user_alloc(thr, pc, /*size=*/1);
+ ThreadIgnoreEnd(thr, pc);
+ }
+ return *h;
}
-// Address on which we can synchronize for an Objective-C object. Supports
-// tagged pointers.
-static uptr SyncAddressForObjCObject(void *obj) {
- if (IsTaggedObjCPointer(obj)) return SyncAddressForTaggedPointer(obj);
+// Returns an address on which we can synchronize given an Obj-C object pointer.
+// For normal object pointers, this is just the address of the object in memory.
+// Tagged pointers are not backed by an actual memory allocation, so we need to
+// synthesize a valid address.
+static uptr SyncAddressForObjCObject(id obj, ThreadState *thr, uptr pc) {
+ if (IsTaggedObjCPointer(obj))
+ return GetOrCreateSyncAddress((uptr)obj, thr, pc);
return (uptr)obj;
}
-TSAN_INTERCEPTOR(int, objc_sync_enter, void *obj) {
+TSAN_INTERCEPTOR(int, objc_sync_enter, id obj) {
SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj);
+ if (!obj) return REAL(objc_sync_enter)(obj);
+ uptr addr = SyncAddressForObjCObject(obj, thr, pc);
+ MutexPreLock(thr, pc, addr, MutexFlagWriteReentrant);
int result = REAL(objc_sync_enter)(obj);
- if (obj) Acquire(thr, pc, SyncAddressForObjCObject(obj));
+ CHECK_EQ(result, OBJC_SYNC_SUCCESS);
+ MutexPostLock(thr, pc, addr, MutexFlagWriteReentrant);
return result;
}
-TSAN_INTERCEPTOR(int, objc_sync_exit, void *obj) {
- SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj);
- if (obj) Release(thr, pc, SyncAddressForObjCObject(obj));
- return REAL(objc_sync_exit)(obj);
+TSAN_INTERCEPTOR(int, objc_sync_exit, id obj) {
+ SCOPED_TSAN_INTERCEPTOR(objc_sync_exit, obj);
+ if (!obj) return REAL(objc_sync_exit)(obj);
+ uptr addr = SyncAddressForObjCObject(obj, thr, pc);
+ MutexUnlock(thr, pc, addr);
+ int result = REAL(objc_sync_exit)(obj);
+ if (result != OBJC_SYNC_SUCCESS) MutexInvalidAccess(thr, pc, addr);
+ return result;
}
// On macOS, libc++ is always linked dynamically, so intercepting works the
diff --git a/lib/tsan/rtl/tsan_libdispatch_mac.cc b/lib/tsan/rtl/tsan_libdispatch_mac.cc
index d6c1ca6622f4..df22888b3f1c 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 6b3e6bac27cb..8303c241809b 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 Mapping {
+ 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
@@ -523,7 +549,7 @@ uptr MappingImpl(void) {
template<int Type>
uptr MappingArchImpl(void) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
case 39: return MappingImpl<Mapping39, Type>();
case 42: return MappingImpl<Mapping42, Type>();
@@ -680,7 +706,7 @@ bool IsAppMemImpl(uptr mem) {
ALWAYS_INLINE
bool IsAppMem(uptr mem) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
case 39: return IsAppMemImpl<Mapping39>(mem);
case 42: return IsAppMemImpl<Mapping42>(mem);
@@ -711,7 +737,7 @@ bool IsShadowMemImpl(uptr mem) {
ALWAYS_INLINE
bool IsShadowMem(uptr mem) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
case 39: return IsShadowMemImpl<Mapping39>(mem);
case 42: return IsShadowMemImpl<Mapping42>(mem);
@@ -742,7 +768,7 @@ bool IsMetaMemImpl(uptr mem) {
ALWAYS_INLINE
bool IsMetaMem(uptr mem) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
case 39: return IsMetaMemImpl<Mapping39>(mem);
case 42: return IsMetaMemImpl<Mapping42>(mem);
@@ -783,7 +809,7 @@ uptr MemToShadowImpl(uptr x) {
ALWAYS_INLINE
uptr MemToShadow(uptr x) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
case 39: return MemToShadowImpl<Mapping39>(x);
case 42: return MemToShadowImpl<Mapping42>(x);
@@ -826,7 +852,7 @@ u32 *MemToMetaImpl(uptr x) {
ALWAYS_INLINE
u32 *MemToMeta(uptr x) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
case 39: return MemToMetaImpl<Mapping39>(x);
case 42: return MemToMetaImpl<Mapping42>(x);
@@ -882,7 +908,7 @@ uptr ShadowToMemImpl(uptr s) {
ALWAYS_INLINE
uptr ShadowToMem(uptr s) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
case 39: return ShadowToMemImpl<Mapping39>(s);
case 42: return ShadowToMemImpl<Mapping42>(s);
@@ -921,7 +947,7 @@ uptr GetThreadTraceImpl(int tid) {
ALWAYS_INLINE
uptr GetThreadTrace(int tid) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
case 39: return GetThreadTraceImpl<Mapping39>(tid);
case 42: return GetThreadTraceImpl<Mapping42>(tid);
@@ -955,7 +981,7 @@ uptr GetThreadTraceHeaderImpl(int tid) {
ALWAYS_INLINE
uptr GetThreadTraceHeader(int tid) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
case 39: return GetThreadTraceHeaderImpl<Mapping39>(tid);
case 42: return GetThreadTraceHeaderImpl<Mapping42>(tid);
diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc
index de989b780883..d2ce60709067 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 f8d7324b763c..7e3a47387b6d 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_report.cc b/lib/tsan/rtl/tsan_report.cc
index af47076968d3..629c3e9337ab 100644
--- a/lib/tsan/rtl/tsan_report.cc
+++ b/lib/tsan/rtl/tsan_report.cc
@@ -77,39 +77,42 @@ const char *thread_name(char *buf, int tid) {
}
static const char *ReportTypeString(ReportType typ, uptr tag) {
- if (typ == ReportTypeRace)
- return "data race";
- if (typ == ReportTypeVptrRace)
- return "data race on vptr (ctor/dtor vs virtual call)";
- if (typ == ReportTypeUseAfterFree)
- return "heap-use-after-free";
- if (typ == ReportTypeVptrUseAfterFree)
- return "heap-use-after-free (virtual call vs free)";
- if (typ == ReportTypeExternalRace) {
- const char *str = GetReportHeaderFromTag(tag);
- return str ? str : "race on external object";
+ switch (typ) {
+ case ReportTypeRace:
+ return "data race";
+ case ReportTypeVptrRace:
+ return "data race on vptr (ctor/dtor vs virtual call)";
+ case ReportTypeUseAfterFree:
+ return "heap-use-after-free";
+ case ReportTypeVptrUseAfterFree:
+ return "heap-use-after-free (virtual call vs free)";
+ case ReportTypeExternalRace: {
+ const char *str = GetReportHeaderFromTag(tag);
+ return str ? str : "race on external object";
+ }
+ case ReportTypeThreadLeak:
+ return "thread leak";
+ case ReportTypeMutexDestroyLocked:
+ return "destroy of a locked mutex";
+ case ReportTypeMutexDoubleLock:
+ return "double lock of a mutex";
+ case ReportTypeMutexInvalidAccess:
+ return "use of an invalid mutex (e.g. uninitialized or destroyed)";
+ case ReportTypeMutexBadUnlock:
+ return "unlock of an unlocked mutex (or by a wrong thread)";
+ case ReportTypeMutexBadReadLock:
+ return "read lock of a write locked mutex";
+ case ReportTypeMutexBadReadUnlock:
+ return "read unlock of a write locked mutex";
+ case ReportTypeSignalUnsafe:
+ return "signal-unsafe call inside of a signal";
+ case ReportTypeErrnoInSignal:
+ return "signal handler spoils errno";
+ case ReportTypeDeadlock:
+ return "lock-order-inversion (potential deadlock)";
+ // No default case so compiler warns us if we miss one
}
- if (typ == ReportTypeThreadLeak)
- return "thread leak";
- if (typ == ReportTypeMutexDestroyLocked)
- return "destroy of a locked mutex";
- if (typ == ReportTypeMutexDoubleLock)
- return "double lock of a mutex";
- if (typ == ReportTypeMutexInvalidAccess)
- return "use of an invalid mutex (e.g. uninitialized or destroyed)";
- if (typ == ReportTypeMutexBadUnlock)
- return "unlock of an unlocked mutex (or by a wrong thread)";
- if (typ == ReportTypeMutexBadReadLock)
- return "read lock of a write locked mutex";
- if (typ == ReportTypeMutexBadReadUnlock)
- return "read unlock of a write locked mutex";
- if (typ == ReportTypeSignalUnsafe)
- return "signal-unsafe call inside of a signal";
- if (typ == ReportTypeErrnoInSignal)
- return "signal handler spoils errno";
- if (typ == ReportTypeDeadlock)
- return "lock-order-inversion (potential deadlock)";
- return "";
+ UNREACHABLE("missing case");
}
#if SANITIZER_MAC
diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc
index 5841222927b3..f038e9682d89 100644
--- a/lib/tsan/rtl/tsan_rtl.cc
+++ b/lib/tsan/rtl/tsan_rtl.cc
@@ -359,7 +359,9 @@ void Initialize(ThreadState *thr) {
CheckASLR();
InitializeFlags(&ctx->flags, options);
AvoidCVE_2016_2143();
- InitializePlatformEarly();
+ __sanitizer::InitializePlatformEarly();
+ __tsan::InitializePlatformEarly();
+
#if !SANITIZER_GO
// Re-exec ourselves if we need to set additional env or command line args.
MaybeReexec();
diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h
index 523b69aaa6bc..3410be294502 100644
--- a/lib/tsan/rtl/tsan_rtl.h
+++ b/lib/tsan/rtl/tsan_rtl.h
@@ -59,15 +59,16 @@ struct MapUnmapCallback;
static const uptr kAllocatorRegionSizeLog = 20;
static const uptr kAllocatorNumRegions =
SANITIZER_MMAP_RANGE_SIZE >> kAllocatorRegionSizeLog;
-typedef TwoLevelByteMap<(kAllocatorNumRegions >> 12), 1 << 12,
- MapUnmapCallback> ByteMap;
+using ByteMap = TwoLevelByteMap<(kAllocatorNumRegions >> 12), 1 << 12,
+ LocalAddressSpaceView, MapUnmapCallback>;
struct AP32 {
static const uptr kSpaceBeg = 0;
static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
static const uptr kMetadataSize = 0;
typedef __sanitizer::CompactSizeClassMap SizeClassMap;
static const uptr kRegionSizeLog = kAllocatorRegionSizeLog;
- typedef __tsan::ByteMap ByteMap;
+ using AddressSpaceView = LocalAddressSpaceView;
+ using ByteMap = __tsan::ByteMap;
typedef __tsan::MapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
@@ -80,6 +81,7 @@ struct AP64 { // Allocator64 parameters. Deliberately using a short name.
typedef DefaultSizeClassMap SizeClassMap;
typedef __tsan::MapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = LocalAddressSpaceView;
};
typedef SizeClassAllocator64<AP64> PrimaryAllocator;
#endif
@@ -772,6 +774,7 @@ void ThreadFinalize(ThreadState *thr);
void ThreadSetName(ThreadState *thr, const char *name);
int ThreadCount(ThreadState *thr);
void ProcessPendingSignals(ThreadState *thr);
+void ThreadNotJoined(ThreadState *thr, uptr pc, int tid, uptr uid);
Processor *ProcCreate();
void ProcDestroy(Processor *proc);
diff --git a/lib/tsan/rtl/tsan_rtl_aarch64.S b/lib/tsan/rtl/tsan_rtl_aarch64.S
index 844c2e23db4f..3d02bf22f8a5 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 8af61bf0e892..34ef51c2a725 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/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc
index e4d65b9a909b..766a0f5a505f 100644
--- a/lib/tsan/rtl/tsan_rtl_thread.cc
+++ b/lib/tsan/rtl/tsan_rtl_thread.cc
@@ -312,6 +312,12 @@ void ThreadDetach(ThreadState *thr, uptr pc, int tid) {
ctx->thread_registry->DetachThread(tid, thr);
}
+void ThreadNotJoined(ThreadState *thr, uptr pc, int tid, uptr uid) {
+ CHECK_GT(tid, 0);
+ CHECK_LT(tid, kMaxTid);
+ ctx->thread_registry->SetThreadUserId(tid, uid);
+}
+
void ThreadSetName(ThreadState *thr, const char *name) {
ctx->thread_registry->SetThreadName(thr->tid, name);
}
diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc
index be38f331ad96..6df074118a10 100644
--- a/lib/tsan/rtl/tsan_suppressions.cc
+++ b/lib/tsan/rtl/tsan_suppressions.cc
@@ -66,38 +66,30 @@ SuppressionContext *Suppressions() {
}
static const char *conv(ReportType typ) {
- if (typ == ReportTypeRace)
- return kSuppressionRace;
- else if (typ == ReportTypeVptrRace)
- return kSuppressionRace;
- else if (typ == ReportTypeUseAfterFree)
- return kSuppressionRace;
- else if (typ == ReportTypeVptrUseAfterFree)
- return kSuppressionRace;
- else if (typ == ReportTypeExternalRace)
- return kSuppressionRace;
- else if (typ == ReportTypeThreadLeak)
- return kSuppressionThread;
- else if (typ == ReportTypeMutexDestroyLocked)
- return kSuppressionMutex;
- else if (typ == ReportTypeMutexDoubleLock)
- return kSuppressionMutex;
- else if (typ == ReportTypeMutexInvalidAccess)
- return kSuppressionMutex;
- else if (typ == ReportTypeMutexBadUnlock)
- return kSuppressionMutex;
- else if (typ == ReportTypeMutexBadReadLock)
- return kSuppressionMutex;
- else if (typ == ReportTypeMutexBadReadUnlock)
- return kSuppressionMutex;
- else if (typ == ReportTypeSignalUnsafe)
- return kSuppressionSignal;
- else if (typ == ReportTypeErrnoInSignal)
- return kSuppressionNone;
- else if (typ == ReportTypeDeadlock)
- return kSuppressionDeadlock;
- Printf("ThreadSanitizer: unknown report type %d\n", typ);
- Die();
+ switch (typ) {
+ case ReportTypeRace:
+ case ReportTypeVptrRace:
+ case ReportTypeUseAfterFree:
+ case ReportTypeVptrUseAfterFree:
+ case ReportTypeExternalRace:
+ return kSuppressionRace;
+ case ReportTypeThreadLeak:
+ return kSuppressionThread;
+ case ReportTypeMutexDestroyLocked:
+ case ReportTypeMutexDoubleLock:
+ case ReportTypeMutexInvalidAccess:
+ case ReportTypeMutexBadUnlock:
+ case ReportTypeMutexBadReadLock:
+ case ReportTypeMutexBadReadUnlock:
+ return kSuppressionMutex;
+ case ReportTypeSignalUnsafe:
+ case ReportTypeErrnoInSignal:
+ return kSuppressionSignal;
+ case ReportTypeDeadlock:
+ return kSuppressionDeadlock;
+ // No default case so compiler warns us if we miss one
+ }
+ UNREACHABLE("missing case");
}
static uptr IsSuppressed(const char *stype, const AddressInfo &info,
diff --git a/lib/ubsan/ubsan_checks.inc b/lib/ubsan/ubsan_checks.inc
index 5a7bdec2df12..ea82f89e129a 100644
--- a/lib/ubsan/ubsan_checks.inc
+++ b/lib/ubsan/ubsan_checks.inc
@@ -21,6 +21,7 @@ UBSAN_CHECK(GenericUB, "undefined-behavior", "undefined")
UBSAN_CHECK(NullPointerUse, "null-pointer-use", "null")
UBSAN_CHECK(PointerOverflow, "pointer-overflow", "pointer-overflow")
UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use", "alignment")
+UBSAN_CHECK(AlignmentAssumption, "alignment-assumption", "alignment")
UBSAN_CHECK(InsufficientObjectSize, "insufficient-object-size", "object-size")
UBSAN_CHECK(SignedIntegerOverflow, "signed-integer-overflow",
"signed-integer-overflow")
@@ -30,8 +31,18 @@ UBSAN_CHECK(IntegerDivideByZero, "integer-divide-by-zero",
"integer-divide-by-zero")
UBSAN_CHECK(FloatDivideByZero, "float-divide-by-zero", "float-divide-by-zero")
UBSAN_CHECK(InvalidBuiltin, "invalid-builtin-use", "invalid-builtin-use")
-UBSAN_CHECK(ImplicitIntegerTruncation, "implicit-integer-truncation",
- "implicit-integer-truncation")
+UBSAN_CHECK(ImplicitUnsignedIntegerTruncation,
+ "implicit-unsigned-integer-truncation",
+ "implicit-unsigned-integer-truncation")
+UBSAN_CHECK(ImplicitSignedIntegerTruncation,
+ "implicit-signed-integer-truncation",
+ "implicit-signed-integer-truncation")
+UBSAN_CHECK(ImplicitIntegerSignChange,
+ "implicit-integer-sign-change",
+ "implicit-integer-sign-change")
+UBSAN_CHECK(ImplicitSignedIntegerTruncationOrSignChange,
+ "implicit-signed-integer-truncation-or-sign-change",
+ "implicit-signed-integer-truncation,implicit-integer-sign-change")
UBSAN_CHECK(InvalidShiftBase, "invalid-shift-base", "shift-base")
UBSAN_CHECK(InvalidShiftExponent, "invalid-shift-exponent", "shift-exponent")
UBSAN_CHECK(OutOfBoundsIndex, "out-of-bounds-index", "bounds")
diff --git a/lib/ubsan/ubsan_flags.inc b/lib/ubsan/ubsan_flags.inc
index 1638a054e8f0..e75a4c44e62c 100644
--- a/lib/ubsan/ubsan_flags.inc
+++ b/lib/ubsan/ubsan_flags.inc
@@ -25,5 +25,5 @@ UBSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
UBSAN_FLAG(bool, report_error_type, false,
"Print specific error type instead of 'undefined-behavior' in summary.")
UBSAN_FLAG(bool, silence_unsigned_overflow, false,
- "Do not print error reports for unsigned integer overflow. "
- "Used to provide fuzzing signal without blowing up logs.")
+ "Do not print non-fatal error reports for unsigned integer overflow. "
+ "Used to provide fuzzing signal without blowing up logs.")
diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc
index e72a32cf32c2..11e09b0ff0f5 100644
--- a/lib/ubsan/ubsan_handlers.cc
+++ b/lib/ubsan/ubsan_handlers.cc
@@ -106,6 +106,62 @@ void __ubsan::__ubsan_handle_type_mismatch_v1_abort(TypeMismatchData *Data,
Die();
}
+static void handleAlignmentAssumptionImpl(AlignmentAssumptionData *Data,
+ ValueHandle Pointer,
+ ValueHandle Alignment,
+ ValueHandle Offset,
+ ReportOptions Opts) {
+ Location Loc = Data->Loc.acquire();
+ SourceLocation AssumptionLoc = Data->AssumptionLoc.acquire();
+
+ ErrorType ET = ErrorType::AlignmentAssumption;
+
+ if (ignoreReport(Loc.getSourceLocation(), Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ uptr RealPointer = Pointer - Offset;
+ uptr LSB = LeastSignificantSetBitIndex(RealPointer);
+ uptr ActualAlignment = uptr(1) << LSB;
+
+ uptr Mask = Alignment - 1;
+ uptr MisAlignmentOffset = RealPointer & Mask;
+
+ if (!Offset) {
+ Diag(Loc, DL_Error, ET,
+ "assumption of %0 byte alignment for pointer of type %1 failed")
+ << Alignment << Data->Type;
+ } else {
+ Diag(Loc, DL_Error, ET,
+ "assumption of %0 byte alignment (with offset of %1 byte) for pointer "
+ "of type %2 failed")
+ << Alignment << Offset << Data->Type;
+ }
+
+ if (!AssumptionLoc.isInvalid())
+ Diag(AssumptionLoc, DL_Note, ET, "alignment assumption was specified here");
+
+ Diag(RealPointer, DL_Note, ET,
+ "%0address is %1 aligned, misalignment offset is %2 bytes")
+ << (Offset ? "offset " : "") << ActualAlignment << MisAlignmentOffset;
+}
+
+void __ubsan::__ubsan_handle_alignment_assumption(AlignmentAssumptionData *Data,
+ ValueHandle Pointer,
+ ValueHandle Alignment,
+ ValueHandle Offset) {
+ GET_REPORT_OPTIONS(false);
+ handleAlignmentAssumptionImpl(Data, Pointer, Alignment, Offset, Opts);
+}
+void __ubsan::__ubsan_handle_alignment_assumption_abort(
+ AlignmentAssumptionData *Data, ValueHandle Pointer, ValueHandle Alignment,
+ ValueHandle Offset) {
+ GET_REPORT_OPTIONS(true);
+ handleAlignmentAssumptionImpl(Data, Pointer, Alignment, Offset, Opts);
+ Die();
+}
+
/// \brief Common diagnostic emission for various forms of integer overflow.
template <typename T>
static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS,
@@ -119,7 +175,9 @@ static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS,
if (ignoreReport(Loc, Opts, ET))
return;
- if (!IsSigned && flags()->silence_unsigned_overflow)
+ // If this is an unsigned overflow in non-fatal mode, potentially ignore it.
+ if (!IsSigned && !Opts.FromUnrecoverableHandler &&
+ flags()->silence_unsigned_overflow)
return;
ScopedReport R(Opts, Loc, ET);
@@ -457,18 +515,41 @@ static void handleImplicitConversion(ImplicitConversionData *Data,
SourceLocation Loc = Data->Loc.acquire();
ErrorType ET = ErrorType::GenericUB;
+ const TypeDescriptor &SrcTy = Data->FromType;
+ const TypeDescriptor &DstTy = Data->ToType;
+
+ bool SrcSigned = SrcTy.isSignedIntegerTy();
+ bool DstSigned = DstTy.isSignedIntegerTy();
+
switch (Data->Kind) {
- case ICCK_IntegerTruncation:
- ET = ErrorType::ImplicitIntegerTruncation;
+ case ICCK_IntegerTruncation: { // Legacy, no longer used.
+ // Let's figure out what it should be as per the new types, and upgrade.
+ // If both types are unsigned, then it's an unsigned truncation.
+ // Else, it is a signed truncation.
+ if (!SrcSigned && !DstSigned) {
+ ET = ErrorType::ImplicitUnsignedIntegerTruncation;
+ } else {
+ ET = ErrorType::ImplicitSignedIntegerTruncation;
+ }
+ break;
+ }
+ case ICCK_UnsignedIntegerTruncation:
+ ET = ErrorType::ImplicitUnsignedIntegerTruncation;
+ break;
+ case ICCK_SignedIntegerTruncation:
+ ET = ErrorType::ImplicitSignedIntegerTruncation;
+ break;
+ case ICCK_IntegerSignChange:
+ ET = ErrorType::ImplicitIntegerSignChange;
+ break;
+ case ICCK_SignedIntegerTruncationOrSignChange:
+ ET = ErrorType::ImplicitSignedIntegerTruncationOrSignChange;
break;
}
if (ignoreReport(Loc, Opts, ET))
return;
- const TypeDescriptor &SrcTy = Data->FromType;
- const TypeDescriptor &DstTy = Data->ToType;
-
ScopedReport R(Opts, Loc, ET);
// FIXME: is it possible to dump the values as hex with fixed width?
@@ -477,8 +558,8 @@ static void handleImplicitConversion(ImplicitConversionData *Data,
"implicit conversion from type %0 of value %1 (%2-bit, %3signed) to "
"type %4 changed the value to %5 (%6-bit, %7signed)")
<< SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth()
- << (SrcTy.isSignedIntegerTy() ? "" : "un") << DstTy << Value(DstTy, Dst)
- << DstTy.getIntegerBitWidth() << (DstTy.isSignedIntegerTy() ? "" : "un");
+ << (SrcSigned ? "" : "un") << DstTy << Value(DstTy, Dst)
+ << DstTy.getIntegerBitWidth() << (DstSigned ? "" : "un");
}
void __ubsan::__ubsan_handle_implicit_conversion(ImplicitConversionData *Data,
diff --git a/lib/ubsan/ubsan_handlers.h b/lib/ubsan/ubsan_handlers.h
index 07644b7ea15d..2bf9ff4320e8 100644
--- a/lib/ubsan/ubsan_handlers.h
+++ b/lib/ubsan/ubsan_handlers.h
@@ -39,6 +39,17 @@ struct TypeMismatchData {
/// type.
RECOVERABLE(type_mismatch_v1, TypeMismatchData *Data, ValueHandle Pointer)
+struct AlignmentAssumptionData {
+ SourceLocation Loc;
+ SourceLocation AssumptionLoc;
+ const TypeDescriptor &Type;
+};
+
+/// \brief Handle a runtime alignment assumption check failure,
+/// caused by a misaligned pointer.
+RECOVERABLE(alignment_assumption, AlignmentAssumptionData *Data,
+ ValueHandle Pointer, ValueHandle Alignment, ValueHandle Offset)
+
struct OverflowData {
SourceLocation Loc;
const TypeDescriptor &Type;
@@ -125,7 +136,11 @@ RECOVERABLE(load_invalid_value, InvalidValueData *Data, ValueHandle Val)
/// Known implicit conversion check kinds.
/// Keep in sync with the enum of the same name in CGExprScalar.cpp
enum ImplicitConversionCheckKind : unsigned char {
- ICCK_IntegerTruncation = 0,
+ ICCK_IntegerTruncation = 0, // Legacy, was only used by clang 7.
+ ICCK_UnsignedIntegerTruncation = 1,
+ ICCK_SignedIntegerTruncation = 2,
+ ICCK_IntegerSignChange = 3,
+ ICCK_SignedIntegerTruncationOrSignChange = 4,
};
struct ImplicitConversionData {
diff --git a/lib/ubsan/ubsan_interface.inc b/lib/ubsan/ubsan_interface.inc
index 0be6010ad27b..81e06345dcff 100644
--- a/lib/ubsan/ubsan_interface.inc
+++ b/lib/ubsan/ubsan_interface.inc
@@ -10,6 +10,8 @@
//===----------------------------------------------------------------------===//
INTERFACE_FUNCTION(__ubsan_handle_add_overflow)
INTERFACE_FUNCTION(__ubsan_handle_add_overflow_abort)
+INTERFACE_FUNCTION(__ubsan_handle_alignment_assumption)
+INTERFACE_FUNCTION(__ubsan_handle_alignment_assumption_abort)
INTERFACE_FUNCTION(__ubsan_handle_builtin_unreachable)
INTERFACE_FUNCTION(__ubsan_handle_cfi_bad_type)
INTERFACE_FUNCTION(__ubsan_handle_cfi_check_fail)
diff --git a/lib/ubsan_minimal/CMakeLists.txt b/lib/ubsan_minimal/CMakeLists.txt
index b70246845f84..e0910e80f97e 100644
--- a/lib/ubsan_minimal/CMakeLists.txt
+++ b/lib/ubsan_minimal/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Build for the undefined behavior sanitizer runtime support library.
+# Build for the minimal undefined behavior sanitizer runtime support library.
set(UBSAN_MINIMAL_SOURCES
ubsan_minimal_handlers.cc
@@ -15,7 +15,7 @@ set(UBSAN_DYNAMIC_LIBS ${SANITIZER_COMMON_LINK_LIBS})
add_compiler_rt_component(ubsan-minimal)
-# Common parts of UBSan runtime.
+# Common parts of minimal UBSan runtime.
add_compiler_rt_object_libraries(RTUbsan_minimal
OS ${SANITIZER_COMMON_SUPPORTED_OS}
ARCHS ${UBSAN_COMMON_SUPPORTED_ARCH}
@@ -23,9 +23,7 @@ add_compiler_rt_object_libraries(RTUbsan_minimal
if(COMPILER_RT_HAS_UBSAN_MINIMAL)
- # Initializer of standalone UBSan runtime.
-
- # Standalone UBSan runtimes.
+ # Standalone minimal UBSan runtimes.
add_compiler_rt_runtime(clang_rt.ubsan_minimal
STATIC
OS ${SANITIZER_COMMON_SUPPORTED_OS}
diff --git a/lib/ubsan_minimal/ubsan_minimal_handlers.cc b/lib/ubsan_minimal/ubsan_minimal_handlers.cc
index e8fc3a8499b5..ed62ddd0fa34 100644
--- a/lib/ubsan_minimal/ubsan_minimal_handlers.cc
+++ b/lib/ubsan_minimal/ubsan_minimal_handlers.cc
@@ -95,6 +95,7 @@ void NORETURN CheckFailed(const char *file, int, const char *cond, u64, u64) {
HANDLER_NORECOVER(name, msg)
HANDLER(type_mismatch, "type-mismatch")
+HANDLER(alignment_assumption, "alignment-assumption")
HANDLER(add_overflow, "add-overflow")
HANDLER(sub_overflow, "sub-overflow")
HANDLER(mul_overflow, "mul-overflow")
diff --git a/lib/xray/CMakeLists.txt b/lib/xray/CMakeLists.txt
index 8e18f55658f8..0a86c52e6208 100644
--- a/lib/xray/CMakeLists.txt
+++ b/lib/xray/CMakeLists.txt
@@ -2,6 +2,7 @@
# XRay runtime library implementation files.
set(XRAY_SOURCES
+ xray_buffer_queue.cc
xray_init.cc
xray_flags.cc
xray_interface.cc
@@ -11,7 +12,6 @@ set(XRAY_SOURCES
# Implementation files for all XRay modes.
set(XRAY_FDR_MODE_SOURCES
xray_fdr_flags.cc
- xray_buffer_queue.cc
xray_fdr_logging.cc)
set(XRAY_BASIC_MODE_SOURCES
@@ -67,9 +67,11 @@ set(XRAY_IMPL_HEADERS
xray_basic_logging.h
xray_buffer_queue.h
xray_defs.h
+ xray_fdr_controller.h
xray_fdr_flags.h
xray_fdr_flags.inc
xray_fdr_log_records.h
+ xray_fdr_log_writer.h
xray_fdr_logging.h
xray_flags.h
xray_flags.inc
@@ -147,7 +149,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 11f373167d24..89a2b3b01ed8 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,31 @@ 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)
+ append_list_if(COMPILER_RT_HAS_LLVMTESTINGSUPPORT
+ ${LLVM_TESTINGSUPPORT_LDFLAGS} XRAY_UNITTEST_LINK_FLAGS)
+ append_list_if(COMPILER_RT_HAS_LLVMTESTINGSUPPORT
+ ${LLVM_TESTINGSUPPORT_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 +90,22 @@ 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}
+ "test_helpers.h"
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 b42eb50d0790..d10524b8d030 100644
--- a/lib/xray/tests/unit/CMakeLists.txt
+++ b/lib/xray/tests/unit/CMakeLists.txt
@@ -1,12 +1,16 @@
-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)
+set(TEST_SOURCES
+ allocator_test.cc
+ buffer_queue_test.cc
+ function_call_trie_test.cc
+ profile_collector_test.cc
+ segmented_array_test.cc
+ test_helpers.cc
+ xray_unit_test_main.cc)
+
+if (NOT COMPILER_RT_STANDALONE_BUILD OR COMPILER_RT_HAS_LLVMTESTINGSUPPORT)
+ list(APPEND TEST_SOURCES
+ fdr_controller_test.cc
+ fdr_log_writer_test.cc)
+endif()
+
+add_xray_unittest(XRayTest SOURCES ${TEST_SOURCES})
diff --git a/lib/xray/tests/unit/allocator_test.cc b/lib/xray/tests/unit/allocator_test.cc
index be404160e417..1170741623cb 100644
--- a/lib/xray/tests/unit/allocator_test.cc
+++ b/lib/xray/tests/unit/allocator_test.cc
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "xray_allocator.h"
+#include "xray_buffer_queue.h"
#include "gtest/gtest.h"
namespace __xray {
@@ -33,10 +34,49 @@ TEST(AllocatorTest, Allocate) {
TEST(AllocatorTest, OverAllocate) {
Allocator<sizeof(TestData)> A(sizeof(TestData));
auto B1 = A.Allocate();
- (void)B1;
+ ASSERT_NE(B1.Data, nullptr);
auto B2 = A.Allocate();
ASSERT_EQ(B2.Data, nullptr);
}
+struct OddSizedData {
+ s64 A;
+ s32 B;
+};
+
+TEST(AllocatorTest, AllocateBoundaries) {
+ Allocator<sizeof(OddSizedData)> A(GetPageSizeCached());
+
+ // Keep allocating until we hit a nullptr block.
+ unsigned C = 0;
+ auto Expected =
+ GetPageSizeCached() / RoundUpTo(sizeof(OddSizedData), kCacheLineSize);
+ for (auto B = A.Allocate(); B.Data != nullptr; B = A.Allocate(), ++C)
+ ;
+
+ ASSERT_EQ(C, Expected);
+}
+
+TEST(AllocatorTest, AllocateFromNonOwned) {
+ bool Success = false;
+ BufferQueue BQ(GetPageSizeCached(), 10, Success);
+ ASSERT_TRUE(Success);
+ BufferQueue::Buffer B;
+ ASSERT_EQ(BQ.getBuffer(B), BufferQueue::ErrorCode::Ok);
+ {
+ Allocator<sizeof(OddSizedData)> A(B.Data, B.Size);
+
+ // Keep allocating until we hit a nullptr block.
+ unsigned C = 0;
+ auto Expected =
+ GetPageSizeCached() / RoundUpTo(sizeof(OddSizedData), kCacheLineSize);
+ for (auto B = A.Allocate(); B.Data != nullptr; B = A.Allocate(), ++C)
+ ;
+
+ ASSERT_EQ(C, Expected);
+ }
+ ASSERT_EQ(BQ.releaseBuffer(B), BufferQueue::ErrorCode::Ok);
+}
+
} // namespace
} // namespace __xray
diff --git a/lib/xray/tests/unit/buffer_queue_test.cc b/lib/xray/tests/unit/buffer_queue_test.cc
index c0d4ccb268d6..a30343e188f2 100644
--- a/lib/xray/tests/unit/buffer_queue_test.cc
+++ b/lib/xray/tests/unit/buffer_queue_test.cc
@@ -11,15 +11,21 @@
//
//===----------------------------------------------------------------------===//
#include "xray_buffer_queue.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include <atomic>
#include <future>
+#include <thread>
#include <unistd.h>
namespace __xray {
+namespace {
static constexpr size_t kSize = 4096;
+using ::testing::Eq;
+
TEST(BufferQueueTest, API) {
bool Success = false;
BufferQueue Buffers(kSize, 1, Success);
@@ -55,8 +61,13 @@ TEST(BufferQueueTest, ReleaseUnknown) {
BufferQueue::Buffer Buf;
Buf.Data = reinterpret_cast<void *>(0xdeadbeef);
Buf.Size = kSize;
- EXPECT_EQ(BufferQueue::ErrorCode::UnrecognizedBuffer,
- Buffers.releaseBuffer(Buf));
+ Buf.Generation = Buffers.generation();
+
+ BufferQueue::Buffer Known;
+ EXPECT_THAT(Buffers.getBuffer(Known), Eq(BufferQueue::ErrorCode::Ok));
+ EXPECT_THAT(Buffers.releaseBuffer(Buf),
+ Eq(BufferQueue::ErrorCode::UnrecognizedBuffer));
+ EXPECT_THAT(Buffers.releaseBuffer(Known), Eq(BufferQueue::ErrorCode::Ok));
}
TEST(BufferQueueTest, ErrorsWhenFinalising) {
@@ -70,8 +81,7 @@ TEST(BufferQueueTest, ErrorsWhenFinalising) {
BufferQueue::Buffer OtherBuf;
ASSERT_EQ(BufferQueue::ErrorCode::QueueFinalizing,
Buffers.getBuffer(OtherBuf));
- ASSERT_EQ(BufferQueue::ErrorCode::QueueFinalizing,
- Buffers.finalize());
+ ASSERT_EQ(BufferQueue::ErrorCode::QueueFinalizing, Buffers.finalize());
ASSERT_EQ(Buffers.releaseBuffer(Buf), BufferQueue::ErrorCode::Ok);
}
@@ -111,4 +121,115 @@ TEST(BufferQueueTest, Apply) {
ASSERT_EQ(Count, 10);
}
+TEST(BufferQueueTest, GenerationalSupport) {
+ bool Success = false;
+ BufferQueue Buffers(kSize, 10, Success);
+ ASSERT_TRUE(Success);
+ BufferQueue::Buffer B0;
+ ASSERT_EQ(Buffers.getBuffer(B0), BufferQueue::ErrorCode::Ok);
+ ASSERT_EQ(Buffers.finalize(),
+ BufferQueue::ErrorCode::Ok); // No more new buffers.
+
+ // Re-initialise the queue.
+ ASSERT_EQ(Buffers.init(kSize, 10), BufferQueue::ErrorCode::Ok);
+
+ BufferQueue::Buffer B1;
+ ASSERT_EQ(Buffers.getBuffer(B1), BufferQueue::ErrorCode::Ok);
+
+ // Validate that the buffers come from different generations.
+ ASSERT_NE(B0.Generation, B1.Generation);
+
+ // We stash the current generation, for use later.
+ auto PrevGen = B1.Generation;
+
+ // At this point, we want to ensure that we can return the buffer from the
+ // first "generation" would still be accepted in the new generation...
+ EXPECT_EQ(Buffers.releaseBuffer(B0), BufferQueue::ErrorCode::Ok);
+
+ // ... and that the new buffer is also accepted.
+ EXPECT_EQ(Buffers.releaseBuffer(B1), BufferQueue::ErrorCode::Ok);
+
+ // A next round will do the same, ensure that we are able to do multiple
+ // rounds in this case.
+ ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok);
+ ASSERT_EQ(Buffers.init(kSize, 10), BufferQueue::ErrorCode::Ok);
+ EXPECT_EQ(Buffers.getBuffer(B0), BufferQueue::ErrorCode::Ok);
+ EXPECT_EQ(Buffers.getBuffer(B1), BufferQueue::ErrorCode::Ok);
+
+ // Here we ensure that the generation is different from the previous
+ // generation.
+ EXPECT_NE(B0.Generation, PrevGen);
+ EXPECT_EQ(B1.Generation, B1.Generation);
+ ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok);
+ EXPECT_EQ(Buffers.releaseBuffer(B0), BufferQueue::ErrorCode::Ok);
+ EXPECT_EQ(Buffers.releaseBuffer(B1), BufferQueue::ErrorCode::Ok);
+}
+
+TEST(BufferQueueTest, GenerationalSupportAcrossThreads) {
+ bool Success = false;
+ BufferQueue Buffers(kSize, 10, Success);
+ ASSERT_TRUE(Success);
+
+ std::atomic<int> Counter{0};
+
+ // This function allows us to use thread-local storage to isolate the
+ // instances of the buffers to be used. It also allows us signal the threads
+ // of a new generation, and allow those to get new buffers. This is
+ // representative of how we expect the buffer queue to be used by the XRay
+ // runtime.
+ auto Process = [&] {
+ thread_local BufferQueue::Buffer B;
+ ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok);
+ auto FirstGen = B.Generation;
+
+ // Signal that we've gotten a buffer in the thread.
+ Counter.fetch_add(1, std::memory_order_acq_rel);
+ while (!Buffers.finalizing()) {
+ Buffers.releaseBuffer(B);
+ Buffers.getBuffer(B);
+ }
+
+ // Signal that we've exited the get/release buffer loop.
+ Counter.fetch_sub(1, std::memory_order_acq_rel);
+ if (B.Data != nullptr)
+ Buffers.releaseBuffer(B);
+
+ // Spin until we find that the Buffer Queue is no longer finalizing.
+ while (Buffers.getBuffer(B) != BufferQueue::ErrorCode::Ok)
+ ;
+
+ // Signal that we've successfully gotten a buffer in the thread.
+ Counter.fetch_add(1, std::memory_order_acq_rel);
+
+ EXPECT_NE(FirstGen, B.Generation);
+ EXPECT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok);
+
+ // Signal that we've successfully exited.
+ Counter.fetch_sub(1, std::memory_order_acq_rel);
+ };
+
+ // Spawn two threads running Process.
+ std::thread T0(Process), T1(Process);
+
+ // Spin until we find the counter is up to 2.
+ while (Counter.load(std::memory_order_acquire) != 2)
+ ;
+
+ // Then we finalize, then re-initialize immediately.
+ Buffers.finalize();
+
+ // Spin until we find the counter is down to 0.
+ while (Counter.load(std::memory_order_acquire) != 0)
+ ;
+
+ // Then we re-initialize.
+ EXPECT_EQ(Buffers.init(kSize, 10), BufferQueue::ErrorCode::Ok);
+
+ T0.join();
+ T1.join();
+
+ ASSERT_EQ(Counter.load(std::memory_order_acquire), 0);
+}
+
+} // namespace
} // namespace __xray
diff --git a/lib/xray/tests/unit/fdr_controller_test.cc b/lib/xray/tests/unit/fdr_controller_test.cc
new file mode 100644
index 000000000000..8967c4919ae6
--- /dev/null
+++ b/lib/xray/tests/unit/fdr_controller_test.cc
@@ -0,0 +1,424 @@
+//===-- fdr_controller_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 <algorithm>
+#include <memory>
+#include <time.h>
+
+#include "test_helpers.h"
+#include "xray/xray_records.h"
+#include "xray_buffer_queue.h"
+#include "xray_fdr_controller.h"
+#include "xray_fdr_log_writer.h"
+#include "llvm/Support/DataExtractor.h"
+#include "llvm/Testing/Support/Error.h"
+#include "llvm/XRay/Trace.h"
+#include "llvm/XRay/XRayRecord.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace __xray {
+namespace {
+
+using ::llvm::HasValue;
+using ::llvm::xray::testing::FuncId;
+using ::llvm::xray::testing::HasArg;
+using ::llvm::xray::testing::RecordType;
+using ::llvm::xray::testing::TSCIs;
+using ::testing::AllOf;
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::Gt;
+using ::testing::IsEmpty;
+using ::testing::SizeIs;
+
+class FunctionSequenceTest : public ::testing::Test {
+protected:
+ BufferQueue::Buffer B{};
+ std::unique_ptr<BufferQueue> BQ;
+ std::unique_ptr<FDRLogWriter> W;
+ std::unique_ptr<FDRController<>> C;
+
+public:
+ void SetUp() override {
+ bool Success;
+ BQ = llvm::make_unique<BufferQueue>(4096, 1, Success);
+ ASSERT_TRUE(Success);
+ ASSERT_EQ(BQ->getBuffer(B), BufferQueue::ErrorCode::Ok);
+ W = llvm::make_unique<FDRLogWriter>(B);
+ C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 0);
+ }
+};
+
+TEST_F(FunctionSequenceTest, DefaultInitFinalizeFlush) {
+ ASSERT_TRUE(C->functionEnter(1, 2, 3));
+ ASSERT_TRUE(C->functionExit(1, 2, 3));
+ ASSERT_TRUE(C->flush());
+ ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
+
+ // Serialize the buffers then test to see we find the expected records.
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(
+ TraceOrErr,
+ HasValue(ElementsAre(
+ AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER)),
+ AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT)))));
+}
+
+TEST_F(FunctionSequenceTest, BoundaryFuncIdEncoding) {
+ // We ensure that we can write function id's that are at the boundary of the
+ // acceptable function ids.
+ int32_t FId = (1 << 28) - 1;
+ uint64_t TSC = 2;
+ uint16_t CPU = 1;
+ ASSERT_TRUE(C->functionEnter(FId, TSC++, CPU));
+ ASSERT_TRUE(C->functionExit(FId, TSC++, CPU));
+ ASSERT_TRUE(C->functionEnterArg(FId, TSC++, CPU, 1));
+ ASSERT_TRUE(C->functionTailExit(FId, TSC++, CPU));
+ ASSERT_TRUE(C->flush());
+ ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
+
+ // Serialize the buffers then test to see we find the expected records.
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(
+ TraceOrErr,
+ HasValue(ElementsAre(
+ AllOf(FuncId(FId), RecordType(llvm::xray::RecordTypes::ENTER)),
+ AllOf(FuncId(FId), RecordType(llvm::xray::RecordTypes::EXIT)),
+ AllOf(FuncId(FId), RecordType(llvm::xray::RecordTypes::ENTER_ARG)),
+ AllOf(FuncId(FId), RecordType(llvm::xray::RecordTypes::TAIL_EXIT)))));
+}
+
+TEST_F(FunctionSequenceTest, ThresholdsAreEnforced) {
+ C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
+ ASSERT_TRUE(C->functionEnter(1, 2, 3));
+ ASSERT_TRUE(C->functionExit(1, 2, 3));
+ ASSERT_TRUE(C->flush());
+ ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
+
+ // Serialize the buffers then test to see we find the *no* records, because
+ // the function entry-exit comes under the cycle threshold.
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty()));
+}
+
+TEST_F(FunctionSequenceTest, ArgsAreHandledAndKept) {
+ C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
+ ASSERT_TRUE(C->functionEnterArg(1, 2, 3, 4));
+ ASSERT_TRUE(C->functionExit(1, 2, 3));
+ ASSERT_TRUE(C->flush());
+ ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
+
+ // Serialize the buffers then test to see we find the function enter arg
+ // record with the specified argument.
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(
+ TraceOrErr,
+ HasValue(ElementsAre(
+ AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER_ARG),
+ HasArg(4)),
+ AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT)))));
+}
+
+TEST_F(FunctionSequenceTest, PreservedCallsHaveCorrectTSC) {
+ C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
+ uint64_t TSC = 1;
+ uint16_t CPU = 0;
+ ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
+ ASSERT_TRUE(C->functionEnter(2, TSC++, CPU));
+ ASSERT_TRUE(C->functionExit(2, TSC++, CPU));
+ ASSERT_TRUE(C->functionExit(1, TSC += 1000, CPU));
+ ASSERT_TRUE(C->flush());
+ ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
+
+ // Serialize the buffers then test to see if we find the remaining records,
+ // because the function entry-exit comes under the cycle threshold.
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(
+ TraceOrErr,
+ HasValue(ElementsAre(
+ AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER),
+ TSCIs(Eq(1uL))),
+ AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT),
+ TSCIs(Gt(1000uL))))));
+}
+
+TEST_F(FunctionSequenceTest, PreservedCallsSupportLargeDeltas) {
+ C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
+ uint64_t TSC = 1;
+ uint16_t CPU = 0;
+ const auto LargeDelta = uint64_t{std::numeric_limits<int32_t>::max()};
+ ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
+ ASSERT_TRUE(C->functionExit(1, TSC += LargeDelta, CPU));
+ ASSERT_TRUE(C->flush());
+ ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
+
+ // Serialize the buffer then test to see if we find the right TSC with a large
+ // delta.
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(
+ TraceOrErr,
+ HasValue(ElementsAre(
+ AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER),
+ TSCIs(Eq(1uL))),
+ AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT),
+ TSCIs(Gt(LargeDelta))))));
+}
+
+TEST_F(FunctionSequenceTest, RewindingMultipleCalls) {
+ C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
+
+ // First we construct an arbitrarily deep function enter/call stack.
+ // We also ensure that we are in the same CPU.
+ uint64_t TSC = 1;
+ uint16_t CPU = 1;
+ ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
+ ASSERT_TRUE(C->functionEnter(2, TSC++, CPU));
+ ASSERT_TRUE(C->functionEnter(3, TSC++, CPU));
+
+ // Then we exit them one at a time, in reverse order of entry.
+ ASSERT_TRUE(C->functionExit(3, TSC++, CPU));
+ ASSERT_TRUE(C->functionExit(2, TSC++, CPU));
+ ASSERT_TRUE(C->functionExit(1, TSC++, CPU));
+
+ ASSERT_TRUE(C->flush());
+ ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
+
+ // Serialize the buffers then test to see we find that all the calls have been
+ // unwound because all of them are under the cycle counter threshold.
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty()));
+}
+
+TEST_F(FunctionSequenceTest, RewindingIntermediaryTailExits) {
+ C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
+
+ // First we construct an arbitrarily deep function enter/call stack.
+ // We also ensure that we are in the same CPU.
+ uint64_t TSC = 1;
+ uint16_t CPU = 1;
+ ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
+ ASSERT_TRUE(C->functionEnter(2, TSC++, CPU));
+ ASSERT_TRUE(C->functionEnter(3, TSC++, CPU));
+
+ // Next we tail-exit into a new function multiple times.
+ ASSERT_TRUE(C->functionTailExit(3, TSC++, CPU));
+ ASSERT_TRUE(C->functionEnter(4, TSC++, CPU));
+ ASSERT_TRUE(C->functionTailExit(4, TSC++, CPU));
+ ASSERT_TRUE(C->functionEnter(5, TSC++, CPU));
+ ASSERT_TRUE(C->functionTailExit(5, TSC++, CPU));
+ ASSERT_TRUE(C->functionEnter(6, TSC++, CPU));
+
+ // Then we exit them one at a time, in reverse order of entry.
+ ASSERT_TRUE(C->functionExit(6, TSC++, CPU));
+ ASSERT_TRUE(C->functionExit(2, TSC++, CPU));
+ ASSERT_TRUE(C->functionExit(1, TSC++, CPU));
+ ASSERT_TRUE(C->flush());
+ ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
+
+ // Serialize the buffers then test to see we find that all the calls have been
+ // unwound because all of them are under the cycle counter threshold.
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty()));
+}
+
+TEST_F(FunctionSequenceTest, RewindingAfterMigration) {
+ C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
+
+ // First we construct an arbitrarily deep function enter/call stack.
+ // We also ensure that we are in the same CPU.
+ uint64_t TSC = 1;
+ uint16_t CPU = 1;
+ ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
+ ASSERT_TRUE(C->functionEnter(2, TSC++, CPU));
+ ASSERT_TRUE(C->functionEnter(3, TSC++, CPU));
+
+ // Next we tail-exit into a new function multiple times.
+ ASSERT_TRUE(C->functionTailExit(3, TSC++, CPU));
+ ASSERT_TRUE(C->functionEnter(4, TSC++, CPU));
+ ASSERT_TRUE(C->functionTailExit(4, TSC++, CPU));
+
+ // But before we enter the next function, we migrate to a different CPU.
+ CPU = 2;
+ ASSERT_TRUE(C->functionEnter(5, TSC++, CPU));
+ ASSERT_TRUE(C->functionTailExit(5, TSC++, CPU));
+ ASSERT_TRUE(C->functionEnter(6, TSC++, CPU));
+
+ // Then we exit them one at a time, in reverse order of entry.
+ ASSERT_TRUE(C->functionExit(6, TSC++, CPU));
+ ASSERT_TRUE(C->functionExit(2, TSC++, CPU));
+ ASSERT_TRUE(C->functionExit(1, TSC++, CPU));
+
+ ASSERT_TRUE(C->flush());
+ ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
+
+ // Serialize buffers then test that we can find all the events that span the
+ // CPU migration.
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(
+ TraceOrErr,
+ HasValue(ElementsAre(
+ AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER)),
+ AllOf(FuncId(2), RecordType(llvm::xray::RecordTypes::ENTER)),
+ AllOf(FuncId(2), RecordType(llvm::xray::RecordTypes::EXIT)),
+ AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT)))));
+}
+
+class BufferManagementTest : public ::testing::Test {
+protected:
+ BufferQueue::Buffer B{};
+ std::unique_ptr<BufferQueue> BQ;
+ std::unique_ptr<FDRLogWriter> W;
+ std::unique_ptr<FDRController<>> C;
+
+ static constexpr size_t kBuffers = 10;
+
+public:
+ void SetUp() override {
+ bool Success;
+ BQ = llvm::make_unique<BufferQueue>(sizeof(MetadataRecord) * 5 +
+ sizeof(FunctionRecord) * 2,
+ kBuffers, Success);
+ ASSERT_TRUE(Success);
+ ASSERT_EQ(BQ->getBuffer(B), BufferQueue::ErrorCode::Ok);
+ W = llvm::make_unique<FDRLogWriter>(B);
+ C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 0);
+ }
+};
+
+constexpr size_t BufferManagementTest::kBuffers;
+
+TEST_F(BufferManagementTest, HandlesOverflow) {
+ uint64_t TSC = 1;
+ uint16_t CPU = 1;
+ for (size_t I = 0; I < kBuffers + 1; ++I) {
+ ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
+ ASSERT_TRUE(C->functionExit(1, TSC++, CPU));
+ }
+ ASSERT_TRUE(C->flush());
+ ASSERT_THAT(BQ->finalize(), Eq(BufferQueue::ErrorCode::Ok));
+
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(SizeIs(kBuffers * 2)));
+}
+
+TEST_F(BufferManagementTest, HandlesOverflowWithArgs) {
+ uint64_t TSC = 1;
+ uint16_t CPU = 1;
+ uint64_t ARG = 1;
+ for (size_t I = 0; I < kBuffers + 1; ++I) {
+ ASSERT_TRUE(C->functionEnterArg(1, TSC++, CPU, ARG++));
+ ASSERT_TRUE(C->functionExit(1, TSC++, CPU));
+ }
+ ASSERT_TRUE(C->flush());
+ ASSERT_THAT(BQ->finalize(), Eq(BufferQueue::ErrorCode::Ok));
+
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(SizeIs(kBuffers)));
+}
+
+TEST_F(BufferManagementTest, HandlesOverflowWithCustomEvents) {
+ uint64_t TSC = 1;
+ uint16_t CPU = 1;
+ int32_t D = 0x9009;
+ for (size_t I = 0; I < kBuffers; ++I) {
+ ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
+ ASSERT_TRUE(C->functionExit(1, TSC++, CPU));
+ ASSERT_TRUE(C->customEvent(TSC++, CPU, &D, sizeof(D)));
+ }
+ ASSERT_TRUE(C->flush());
+ ASSERT_THAT(BQ->finalize(), Eq(BufferQueue::ErrorCode::Ok));
+
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+
+ // We expect to also now count the kBuffers/2 custom event records showing up
+ // in the Trace.
+ EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(SizeIs(kBuffers + (kBuffers / 2))));
+}
+
+TEST_F(BufferManagementTest, HandlesFinalizedBufferQueue) {
+ uint64_t TSC = 1;
+ uint16_t CPU = 1;
+
+ // First write one function entry.
+ ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
+
+ // Then we finalize the buffer queue, simulating the case where the logging
+ // has been finalized.
+ ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
+
+ // At this point further calls to the controller must fail.
+ ASSERT_FALSE(C->functionExit(1, TSC++, CPU));
+
+ // But flushing should succeed.
+ ASSERT_TRUE(C->flush());
+
+ // We expect that we'll only be able to find the function enter event, but not
+ // the function exit event.
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(
+ TraceOrErr, HasValue(ElementsAre(AllOf(
+ FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER)))));
+}
+
+TEST_F(BufferManagementTest, HandlesGenerationalBufferQueue) {
+ uint64_t TSC = 1;
+ uint16_t CPU = 1;
+
+ ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
+ ASSERT_THAT(BQ->finalize(), Eq(BufferQueue::ErrorCode::Ok));
+ ASSERT_THAT(BQ->init(sizeof(MetadataRecord) * 4 + sizeof(FunctionRecord) * 2,
+ kBuffers),
+ Eq(BufferQueue::ErrorCode::Ok));
+ EXPECT_TRUE(C->functionExit(1, TSC++, CPU));
+ ASSERT_TRUE(C->flush());
+
+ // We expect that we will only be able to find the function exit event, but
+ // not the function enter event, since we only have information about the new
+ // generation of the buffers.
+ std::string Serialized = serialize(*BQ, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(
+ TraceOrErr, HasValue(ElementsAre(AllOf(
+ FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT)))));
+}
+
+} // namespace
+} // namespace __xray
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 000000000000..f2e7a5cba5d1
--- /dev/null
+++ b/lib/xray/tests/unit/fdr_log_writer_test.cc
@@ -0,0 +1,162 @@
+//===-- 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 "test_helpers.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 ::llvm::xray::testing::FuncId;
+using ::llvm::xray::testing::RecordType;
+using ::testing::AllOf;
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::IsEmpty;
+using ::testing::IsNull;
+
+// 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.
+ std::string Serialized = serialize(Buffers, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(
+ TraceOrErr,
+ HasValue(ElementsAre(
+ AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER)),
+ AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT)))));
+}
+
+// Ensure that we can handle buffer re-use.
+TEST(FdrLogWriterTest, ReuseBuffers) {
+ 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}),
+ };
+
+ // First we write the first set of records into the single buffer in the
+ // queue which includes one enter and one exit record.
+ ASSERT_THAT(Writer.writeMetadataRecords(Preamble),
+ Eq(sizeof(MetadataRecord) * 3));
+ ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(
+ uint16_t{1}, uint64_t{1}));
+ uint64_t TSC = 1;
+ ASSERT_TRUE(
+ Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Enter, 1, TSC++));
+ ASSERT_TRUE(
+ Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, 1, TSC++));
+ ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok);
+ ASSERT_THAT(B.Data, IsNull());
+
+ // Then we re-use the buffer, but only write one record.
+ ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok);
+ Writer.resetRecord();
+ ASSERT_THAT(Writer.writeMetadataRecords(Preamble),
+ Eq(sizeof(MetadataRecord) * 3));
+ ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(
+ uint16_t{1}, uint64_t{1}));
+ ASSERT_TRUE(
+ Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Enter, 1, TSC++));
+ ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok);
+ ASSERT_THAT(B.Data, IsNull());
+ ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok);
+
+ // Then we validate that we only see the single enter record.
+ std::string Serialized = serialize(Buffers, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(
+ TraceOrErr, HasValue(ElementsAre(AllOf(
+ FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER)))));
+}
+
+TEST(FdrLogWriterTest, UnwriteRecords) {
+ 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));
+ Writer.undoWrites(sizeof(FunctionRecord) * 2);
+ ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok);
+ ASSERT_EQ(B.Data, nullptr);
+ ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok);
+
+ // We've un-done the two function records we've written, and now we expect
+ // that we don't have any function records in the trace.
+ std::string Serialized = serialize(Buffers, 3);
+ llvm::DataExtractor DE(Serialized, true, 8);
+ auto TraceOrErr = llvm::xray::loadTrace(DE);
+ EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty()));
+}
+
+} // 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 b6961efbc351..000000000000
--- 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/tests/unit/function_call_trie_test.cc b/lib/xray/tests/unit/function_call_trie_test.cc
index 049ecfb07e01..01be691228f2 100644
--- a/lib/xray/tests/unit/function_call_trie_test.cc
+++ b/lib/xray/tests/unit/function_call_trie_test.cc
@@ -10,9 +10,9 @@
// This file is a part of XRay, a function call tracing system.
//
//===----------------------------------------------------------------------===//
-#include "gtest/gtest.h"
-
#include "xray_function_call_trie.h"
+#include "gtest/gtest.h"
+#include <cstdint>
namespace __xray {
@@ -29,26 +29,54 @@ TEST(FunctionCallTrieTest, EnterAndExitFunction) {
auto A = FunctionCallTrie::InitAllocators();
FunctionCallTrie Trie(A);
- Trie.enterFunction(1, 1);
- Trie.exitFunction(1, 2);
+ uint64_t TSC = 1;
+ uint16_t CPU = 0;
+ Trie.enterFunction(1, TSC++, CPU++);
+ Trie.exitFunction(1, TSC++, CPU++);
+ const auto &R = Trie.getRoots();
- // We need a way to pull the data out. At this point, until we get a data
- // collection service implemented, we're going to export the data as a list of
- // roots, and manually walk through the structure ourselves.
+ ASSERT_EQ(R.size(), 1u);
+ ASSERT_EQ(R.front()->FId, 1);
+ ASSERT_EQ(R.front()->CallCount, 1u);
+ ASSERT_EQ(R.front()->CumulativeLocalTime, 1u);
+}
+
+TEST(FunctionCallTrieTest, HandleTSCOverflow) {
+ profilingFlags()->setDefaults();
+ auto A = FunctionCallTrie::InitAllocators();
+ FunctionCallTrie Trie(A);
+ Trie.enterFunction(1, std::numeric_limits<uint64_t>::max(), 0);
+ Trie.exitFunction(1, 1, 0);
const auto &R = Trie.getRoots();
ASSERT_EQ(R.size(), 1u);
ASSERT_EQ(R.front()->FId, 1);
- ASSERT_EQ(R.front()->CallCount, 1);
+ ASSERT_EQ(R.front()->CallCount, 1u);
ASSERT_EQ(R.front()->CumulativeLocalTime, 1u);
}
+TEST(FunctionCallTrieTest, MaximalCumulativeTime) {
+ profilingFlags()->setDefaults();
+ auto A = FunctionCallTrie::InitAllocators();
+ FunctionCallTrie Trie(A);
+
+ Trie.enterFunction(1, 1, 0);
+ Trie.exitFunction(1, 0, 0);
+ const auto &R = Trie.getRoots();
+
+ ASSERT_EQ(R.size(), 1u);
+ ASSERT_EQ(R.front()->FId, 1);
+ ASSERT_EQ(R.front()->CallCount, 1u);
+ ASSERT_EQ(R.front()->CumulativeLocalTime,
+ std::numeric_limits<uint64_t>::max() - 1);
+}
+
TEST(FunctionCallTrieTest, MissingFunctionEntry) {
profilingFlags()->setDefaults();
auto A = FunctionCallTrie::InitAllocators();
FunctionCallTrie Trie(A);
- Trie.exitFunction(1, 1);
+ Trie.exitFunction(1, 1, 0);
const auto &R = Trie.getRoots();
ASSERT_TRUE(R.empty());
@@ -58,9 +86,9 @@ TEST(FunctionCallTrieTest, NoMatchingEntersForExit) {
profilingFlags()->setDefaults();
auto A = FunctionCallTrie::InitAllocators();
FunctionCallTrie Trie(A);
- Trie.enterFunction(2, 1);
- Trie.enterFunction(3, 3);
- Trie.exitFunction(1, 5);
+ Trie.enterFunction(2, 1, 0);
+ Trie.enterFunction(3, 3, 0);
+ Trie.exitFunction(1, 5, 0);
const auto &R = Trie.getRoots();
ASSERT_FALSE(R.empty());
@@ -71,7 +99,7 @@ TEST(FunctionCallTrieTest, MissingFunctionExit) {
profilingFlags()->setDefaults();
auto A = FunctionCallTrie::InitAllocators();
FunctionCallTrie Trie(A);
- Trie.enterFunction(1, 1);
+ Trie.enterFunction(1, 1, 0);
const auto &R = Trie.getRoots();
ASSERT_FALSE(R.empty());
@@ -84,12 +112,12 @@ TEST(FunctionCallTrieTest, MultipleRoots) {
FunctionCallTrie Trie(A);
// Enter and exit FId = 1.
- Trie.enterFunction(1, 1);
- Trie.exitFunction(1, 2);
+ Trie.enterFunction(1, 1, 0);
+ Trie.exitFunction(1, 2, 0);
// Enter and exit FId = 2.
- Trie.enterFunction(2, 3);
- Trie.exitFunction(2, 4);
+ Trie.enterFunction(2, 3, 0);
+ Trie.exitFunction(2, 4, 0);
const auto &R = Trie.getRoots();
ASSERT_FALSE(R.empty());
@@ -126,11 +154,11 @@ TEST(FunctionCallTrieTest, MissingIntermediaryExit) {
auto A = FunctionCallTrie::InitAllocators();
FunctionCallTrie Trie(A);
- Trie.enterFunction(1, 0);
- Trie.enterFunction(2, 100);
- Trie.enterFunction(3, 200);
- Trie.exitFunction(3, 300);
- Trie.exitFunction(1, 400);
+ Trie.enterFunction(1, 0, 0);
+ Trie.enterFunction(2, 100, 0);
+ Trie.enterFunction(3, 200, 0);
+ Trie.exitFunction(3, 300, 0);
+ Trie.exitFunction(1, 400, 0);
// What we should see at this point is all the functions in the trie in a
// specific order (1 -> 2 -> 3) with the appropriate count(s) and local
@@ -153,12 +181,12 @@ TEST(FunctionCallTrieTest, MissingIntermediaryExit) {
// Now that we've established the preconditions, we check for specific aspects
// of the nodes.
- EXPECT_EQ(F3.CallCount, 1);
- EXPECT_EQ(F2.CallCount, 1);
- EXPECT_EQ(F1.CallCount, 1);
- EXPECT_EQ(F3.CumulativeLocalTime, 100);
- EXPECT_EQ(F2.CumulativeLocalTime, 300);
- EXPECT_EQ(F1.CumulativeLocalTime, 100);
+ EXPECT_EQ(F3.CallCount, 1u);
+ EXPECT_EQ(F2.CallCount, 1u);
+ EXPECT_EQ(F1.CallCount, 1u);
+ EXPECT_EQ(F3.CumulativeLocalTime, 100u);
+ EXPECT_EQ(F2.CumulativeLocalTime, 300u);
+ EXPECT_EQ(F1.CumulativeLocalTime, 100u);
}
TEST(FunctionCallTrieTest, DeepCallStack) {
@@ -168,8 +196,8 @@ TEST(FunctionCallTrieTest, DeepCallStack) {
auto A = FunctionCallTrie::InitAllocators();
FunctionCallTrie Trie(A);
for (int i = 0; i < 32; ++i)
- Trie.enterFunction(i + 1, i);
- Trie.exitFunction(1, 33);
+ Trie.enterFunction(i + 1, i, 0);
+ Trie.exitFunction(1, 33, 0);
// Here, validate that we have a 32-level deep function call path from the
// root (1) down to the leaf (33).
@@ -178,7 +206,7 @@ TEST(FunctionCallTrieTest, DeepCallStack) {
auto F = R[0];
for (int i = 0; i < 32; ++i) {
EXPECT_EQ(F->FId, i + 1);
- EXPECT_EQ(F->CallCount, 1);
+ EXPECT_EQ(F->CallCount, 1u);
if (F->Callees.empty() && i != 31)
FAIL() << "Empty callees for FId " << F->FId;
if (i != 31)
@@ -193,12 +221,12 @@ TEST(FunctionCallTrieTest, DeepCopy) {
auto A = FunctionCallTrie::InitAllocators();
FunctionCallTrie Trie(A);
- Trie.enterFunction(1, 0);
- Trie.enterFunction(2, 1);
- Trie.exitFunction(2, 2);
- Trie.enterFunction(3, 3);
- Trie.exitFunction(3, 4);
- Trie.exitFunction(1, 5);
+ Trie.enterFunction(1, 0, 0);
+ Trie.enterFunction(2, 1, 0);
+ Trie.exitFunction(2, 2, 0);
+ Trie.enterFunction(3, 3, 0);
+ Trie.exitFunction(3, 4, 0);
+ Trie.exitFunction(1, 5, 0);
// We want to make a deep copy and compare notes.
auto B = FunctionCallTrie::InitAllocators();
@@ -236,20 +264,20 @@ TEST(FunctionCallTrieTest, MergeInto) {
FunctionCallTrie T1(A);
// 1 -> 2 -> 3
- T0.enterFunction(1, 0);
- T0.enterFunction(2, 1);
- T0.enterFunction(3, 2);
- T0.exitFunction(3, 3);
- T0.exitFunction(2, 4);
- T0.exitFunction(1, 5);
+ T0.enterFunction(1, 0, 0);
+ T0.enterFunction(2, 1, 0);
+ T0.enterFunction(3, 2, 0);
+ T0.exitFunction(3, 3, 0);
+ T0.exitFunction(2, 4, 0);
+ T0.exitFunction(1, 5, 0);
// 1 -> 2 -> 3
- T1.enterFunction(1, 0);
- T1.enterFunction(2, 1);
- T1.enterFunction(3, 2);
- T1.exitFunction(3, 3);
- T1.exitFunction(2, 4);
- T1.exitFunction(1, 5);
+ T1.enterFunction(1, 0, 0);
+ T1.enterFunction(2, 1, 0);
+ T1.enterFunction(3, 2, 0);
+ T1.exitFunction(3, 3, 0);
+ T1.exitFunction(2, 4, 0);
+ T1.exitFunction(1, 5, 0);
// We use a different allocator here to make sure that we're able to transfer
// data into a FunctionCallTrie which uses a different allocator. This
@@ -264,23 +292,53 @@ TEST(FunctionCallTrieTest, MergeInto) {
ASSERT_EQ(Merged.getRoots().size(), 1u);
const auto &R0 = *Merged.getRoots()[0];
EXPECT_EQ(R0.FId, 1);
- EXPECT_EQ(R0.CallCount, 2);
- EXPECT_EQ(R0.CumulativeLocalTime, 10);
+ EXPECT_EQ(R0.CallCount, 2u);
+ EXPECT_EQ(R0.CumulativeLocalTime, 10u);
EXPECT_EQ(R0.Callees.size(), 1u);
const auto &F1 = *R0.Callees[0].NodePtr;
EXPECT_EQ(F1.FId, 2);
- EXPECT_EQ(F1.CallCount, 2);
- EXPECT_EQ(F1.CumulativeLocalTime, 6);
+ EXPECT_EQ(F1.CallCount, 2u);
+ EXPECT_EQ(F1.CumulativeLocalTime, 6u);
EXPECT_EQ(F1.Callees.size(), 1u);
const auto &F2 = *F1.Callees[0].NodePtr;
EXPECT_EQ(F2.FId, 3);
- EXPECT_EQ(F2.CallCount, 2);
- EXPECT_EQ(F2.CumulativeLocalTime, 2);
+ EXPECT_EQ(F2.CallCount, 2u);
+ EXPECT_EQ(F2.CumulativeLocalTime, 2u);
EXPECT_EQ(F2.Callees.size(), 0u);
}
+TEST(FunctionCallTrieTest, PlacementNewOnAlignedStorage) {
+ profilingFlags()->setDefaults();
+ typename std::aligned_storage<sizeof(FunctionCallTrie::Allocators),
+ alignof(FunctionCallTrie::Allocators)>::type
+ AllocatorsStorage;
+ new (&AllocatorsStorage)
+ FunctionCallTrie::Allocators(FunctionCallTrie::InitAllocators());
+ auto *A =
+ reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage);
+
+ typename std::aligned_storage<sizeof(FunctionCallTrie),
+ alignof(FunctionCallTrie)>::type FCTStorage;
+ new (&FCTStorage) FunctionCallTrie(*A);
+ auto *T = reinterpret_cast<FunctionCallTrie *>(&FCTStorage);
+
+ // Put some data into it.
+ T->enterFunction(1, 0, 0);
+ T->exitFunction(1, 1, 0);
+
+ // Re-initialize the objects in storage.
+ T->~FunctionCallTrie();
+ A->~Allocators();
+ new (A) FunctionCallTrie::Allocators(FunctionCallTrie::InitAllocators());
+ new (T) FunctionCallTrie(*A);
+
+ // Then put some data into it again.
+ T->enterFunction(1, 0, 0);
+ T->exitFunction(1, 1, 0);
+}
+
} // namespace
} // namespace __xray
diff --git a/lib/xray/tests/unit/profile_collector_test.cc b/lib/xray/tests/unit/profile_collector_test.cc
index 67049af2cd5f..df786d46b9d4 100644
--- a/lib/xray/tests/unit/profile_collector_test.cc
+++ b/lib/xray/tests/unit/profile_collector_test.cc
@@ -110,24 +110,31 @@ std::tuple<Profile, const char *> ParseProfile(const char *P) {
TEST(profileCollectorServiceTest, PostSerializeCollect) {
profilingFlags()->setDefaults();
- // The most basic use-case (the one we actually only care about) is the one
- // where we ensure that we can post FunctionCallTrie instances, which are then
- // destroyed but serialized properly.
- //
- // First, we initialise a set of allocators in the local scope. This ensures
- // that we're able to copy the contents of the FunctionCallTrie that uses
- // the local allocators.
- auto Allocators = FunctionCallTrie::InitAllocators();
+ bool Success = false;
+ BufferQueue BQ(profilingFlags()->per_thread_allocator_max,
+ profilingFlags()->buffers_max, Success);
+ ASSERT_EQ(Success, true);
+ FunctionCallTrie::Allocators::Buffers Buffers;
+ ASSERT_EQ(BQ.getBuffer(Buffers.NodeBuffer), BufferQueue::ErrorCode::Ok);
+ ASSERT_EQ(BQ.getBuffer(Buffers.RootsBuffer), BufferQueue::ErrorCode::Ok);
+ ASSERT_EQ(BQ.getBuffer(Buffers.ShadowStackBuffer),
+ BufferQueue::ErrorCode::Ok);
+ ASSERT_EQ(BQ.getBuffer(Buffers.NodeIdPairBuffer), BufferQueue::ErrorCode::Ok);
+ auto Allocators = FunctionCallTrie::InitAllocatorsFromBuffers(Buffers);
FunctionCallTrie T(Allocators);
- // Then, we populate the trie with some data.
- T.enterFunction(1, 1);
- T.enterFunction(2, 2);
- T.exitFunction(2, 3);
- T.exitFunction(1, 4);
+ // Populate the trie with some data.
+ T.enterFunction(1, 1, 0);
+ T.enterFunction(2, 2, 0);
+ T.exitFunction(2, 3, 0);
+ T.exitFunction(1, 4, 0);
+
+ // Reset the collector data structures.
+ profileCollectorService::reset();
// Then we post the data to the global profile collector service.
- profileCollectorService::post(T, 1);
+ profileCollectorService::post(&BQ, std::move(T), std::move(Allocators),
+ std::move(Buffers), 1);
// Then we serialize the data.
profileCollectorService::serialize();
@@ -174,19 +181,37 @@ TEST(profileCollectorServiceTest, PostSerializeCollect) {
// profileCollectorService. This simulates what the threads being profiled would
// be doing anyway, but through the XRay logging implementation.
void threadProcessing() {
- thread_local auto Allocators = FunctionCallTrie::InitAllocators();
+ static bool Success = false;
+ static BufferQueue BQ(profilingFlags()->per_thread_allocator_max,
+ profilingFlags()->buffers_max, Success);
+ thread_local FunctionCallTrie::Allocators::Buffers Buffers = [] {
+ FunctionCallTrie::Allocators::Buffers B;
+ BQ.getBuffer(B.NodeBuffer);
+ BQ.getBuffer(B.RootsBuffer);
+ BQ.getBuffer(B.ShadowStackBuffer);
+ BQ.getBuffer(B.NodeIdPairBuffer);
+ return B;
+ }();
+
+ thread_local auto Allocators =
+ FunctionCallTrie::InitAllocatorsFromBuffers(Buffers);
+
FunctionCallTrie T(Allocators);
- T.enterFunction(1, 1);
- T.enterFunction(2, 2);
- T.exitFunction(2, 3);
- T.exitFunction(1, 4);
+ T.enterFunction(1, 1, 0);
+ T.enterFunction(2, 2, 0);
+ T.exitFunction(2, 3, 0);
+ T.exitFunction(1, 4, 0);
- profileCollectorService::post(T, GetTid());
+ profileCollectorService::post(&BQ, std::move(T), std::move(Allocators),
+ std::move(Buffers), GetTid());
}
TEST(profileCollectorServiceTest, PostSerializeCollectMultipleThread) {
profilingFlags()->setDefaults();
+
+ profileCollectorService::reset();
+
std::thread t1(threadProcessing);
std::thread t2(threadProcessing);
diff --git a/lib/xray/tests/unit/segmented_array_test.cc b/lib/xray/tests/unit/segmented_array_test.cc
index 035674ccfaf5..46aeb88f71b4 100644
--- a/lib/xray/tests/unit/segmented_array_test.cc
+++ b/lib/xray/tests/unit/segmented_array_test.cc
@@ -1,9 +1,16 @@
+#include "test_helpers.h"
#include "xray_segmented_array.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include <algorithm>
+#include <numeric>
+#include <vector>
namespace __xray {
namespace {
+using ::testing::SizeIs;
+
struct TestData {
s64 First;
s64 Second;
@@ -12,6 +19,10 @@ struct TestData {
TestData(s64 F, s64 S) : First(F), Second(S) {}
};
+void PrintTo(const TestData &D, std::ostream *OS) {
+ *OS << "{ " << D.First << ", " << D.Second << " }";
+}
+
TEST(SegmentedArrayTest, ConstructWithAllocators) {
using AllocatorType = typename Array<TestData>::AllocatorType;
AllocatorType A(1 << 4);
@@ -161,6 +172,23 @@ TEST(SegmentedArrayTest, IteratorTrimBehaviour) {
EXPECT_EQ(Data.size(), SegmentX2);
}
+TEST(SegmentedArrayTest, HandleExhaustedAllocator) {
+ using AllocatorType = typename Array<TestData>::AllocatorType;
+ constexpr auto Segment = Array<TestData>::SegmentSize;
+ constexpr auto MaxElements = Array<TestData>::ElementsPerSegment;
+ AllocatorType A(Segment);
+ Array<TestData> Data(A);
+ for (auto i = MaxElements; i > 0u; --i)
+ EXPECT_NE(Data.AppendEmplace(static_cast<s64>(i), static_cast<s64>(i)),
+ nullptr);
+ EXPECT_EQ(Data.AppendEmplace(0, 0), nullptr);
+ EXPECT_THAT(Data, SizeIs(MaxElements));
+
+ // Trimming more elements than there are in the container should be fine.
+ Data.trim(MaxElements + 1);
+ EXPECT_THAT(Data, SizeIs(0u));
+}
+
struct ShadowStackEntry {
uint64_t EntryTSC = 0;
uint64_t *NodePtr = nullptr;
@@ -196,5 +224,126 @@ TEST(SegmentedArrayTest, SimulateStackBehaviour) {
}
}
+TEST(SegmentedArrayTest, PlacementNewOnAlignedStorage) {
+ using AllocatorType = typename Array<ShadowStackEntry>::AllocatorType;
+ typename std::aligned_storage<sizeof(AllocatorType),
+ alignof(AllocatorType)>::type AllocatorStorage;
+ new (&AllocatorStorage) AllocatorType(1 << 10);
+ auto *A = reinterpret_cast<AllocatorType *>(&AllocatorStorage);
+ typename std::aligned_storage<sizeof(Array<ShadowStackEntry>),
+ alignof(Array<ShadowStackEntry>)>::type
+ ArrayStorage;
+ new (&ArrayStorage) Array<ShadowStackEntry>(*A);
+ auto *Data = reinterpret_cast<Array<ShadowStackEntry> *>(&ArrayStorage);
+
+ static uint64_t Dummy = 0;
+ constexpr uint64_t Max = 9;
+
+ for (uint64_t i = 0; i < Max; ++i) {
+ auto P = Data->Append({i, &Dummy});
+ ASSERT_NE(P, nullptr);
+ ASSERT_EQ(P->NodePtr, &Dummy);
+ auto &Back = Data->back();
+ ASSERT_EQ(Back.NodePtr, &Dummy);
+ ASSERT_EQ(Back.EntryTSC, i);
+ }
+
+ // Simulate a stack by checking the data from the end as we're trimming.
+ auto Counter = Max;
+ ASSERT_EQ(Data->size(), size_t(Max));
+ while (!Data->empty()) {
+ const auto &Top = Data->back();
+ uint64_t *TopNode = Top.NodePtr;
+ EXPECT_EQ(TopNode, &Dummy) << "Counter = " << Counter;
+ Data->trim(1);
+ --Counter;
+ ASSERT_EQ(Data->size(), size_t(Counter));
+ }
+
+ // Once the stack is exhausted, we re-use the storage.
+ for (uint64_t i = 0; i < Max; ++i) {
+ auto P = Data->Append({i, &Dummy});
+ ASSERT_NE(P, nullptr);
+ ASSERT_EQ(P->NodePtr, &Dummy);
+ auto &Back = Data->back();
+ ASSERT_EQ(Back.NodePtr, &Dummy);
+ ASSERT_EQ(Back.EntryTSC, i);
+ }
+
+ // We re-initialize the storage, by calling the destructor and
+ // placement-new'ing again.
+ Data->~Array();
+ A->~AllocatorType();
+ new (A) AllocatorType(1 << 10);
+ new (Data) Array<ShadowStackEntry>(*A);
+
+ // Then re-do the test.
+ for (uint64_t i = 0; i < Max; ++i) {
+ auto P = Data->Append({i, &Dummy});
+ ASSERT_NE(P, nullptr);
+ ASSERT_EQ(P->NodePtr, &Dummy);
+ auto &Back = Data->back();
+ ASSERT_EQ(Back.NodePtr, &Dummy);
+ ASSERT_EQ(Back.EntryTSC, i);
+ }
+
+ // Simulate a stack by checking the data from the end as we're trimming.
+ Counter = Max;
+ ASSERT_EQ(Data->size(), size_t(Max));
+ while (!Data->empty()) {
+ const auto &Top = Data->back();
+ uint64_t *TopNode = Top.NodePtr;
+ EXPECT_EQ(TopNode, &Dummy) << "Counter = " << Counter;
+ Data->trim(1);
+ --Counter;
+ ASSERT_EQ(Data->size(), size_t(Counter));
+ }
+
+ // Once the stack is exhausted, we re-use the storage.
+ for (uint64_t i = 0; i < Max; ++i) {
+ auto P = Data->Append({i, &Dummy});
+ ASSERT_NE(P, nullptr);
+ ASSERT_EQ(P->NodePtr, &Dummy);
+ auto &Back = Data->back();
+ ASSERT_EQ(Back.NodePtr, &Dummy);
+ ASSERT_EQ(Back.EntryTSC, i);
+ }
+}
+
+TEST(SegmentedArrayTest, ArrayOfPointersIteratorAccess) {
+ using PtrArray = Array<int *>;
+ PtrArray::AllocatorType Alloc(16384);
+ Array<int *> A(Alloc);
+ static constexpr size_t Count = 100;
+ std::vector<int> Integers(Count);
+ std::iota(Integers.begin(), Integers.end(), 0);
+ for (auto &I : Integers)
+ ASSERT_NE(A.Append(&I), nullptr);
+ int V = 0;
+ ASSERT_EQ(A.size(), Count);
+ for (auto P : A) {
+ ASSERT_NE(P, nullptr);
+ ASSERT_EQ(*P, V++);
+ }
+}
+
+TEST(SegmentedArrayTest, ArrayOfPointersIteratorAccessExhaustion) {
+ using PtrArray = Array<int *>;
+ PtrArray::AllocatorType Alloc(4096);
+ Array<int *> A(Alloc);
+ static constexpr size_t Count = 1000;
+ std::vector<int> Integers(Count);
+ std::iota(Integers.begin(), Integers.end(), 0);
+ for (auto &I : Integers)
+ if (A.Append(&I) == nullptr)
+ break;
+ int V = 0;
+ ASSERT_LT(A.size(), Count);
+ for (auto P : A) {
+ ASSERT_NE(P, nullptr);
+ ASSERT_EQ(*P, V++);
+ }
+}
+
} // namespace
} // namespace __xray
diff --git a/lib/xray/tests/unit/test_helpers.cc b/lib/xray/tests/unit/test_helpers.cc
new file mode 100644
index 000000000000..284492d1050b
--- /dev/null
+++ b/lib/xray/tests/unit/test_helpers.cc
@@ -0,0 +1,95 @@
+//===-- test_helpers.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 "test_helpers.h"
+#include "xray/xray_records.h"
+#include "xray_buffer_queue.h"
+#include "xray_fdr_log_writer.h"
+#include <type_traits>
+
+// TODO: Move these to llvm/include/Testing/XRay/...
+namespace llvm {
+namespace xray {
+
+std::string RecordTypeAsString(RecordTypes T) {
+ switch (T) {
+ case RecordTypes::ENTER:
+ return "llvm::xray::RecordTypes::ENTER";
+ case RecordTypes::EXIT:
+ return "llvm::xray::RecordTypes::EXIT";
+ case RecordTypes::TAIL_EXIT:
+ return "llvm::xray::RecordTypes::TAIL_EXIT";
+ case RecordTypes::ENTER_ARG:
+ return "llvm::xray::RecordTypes::ENTER_ARG";
+ case RecordTypes::CUSTOM_EVENT:
+ return "llvm::xray::RecordTypes::CUSTOM_EVENT";
+ case RecordTypes::TYPED_EVENT:
+ return "llvm::xray::RecordTypes::TYPED_EVENT";
+ }
+ return "<UNKNOWN>";
+}
+
+void PrintTo(RecordTypes T, std::ostream *OS) {
+ *OS << RecordTypeAsString(T);
+}
+
+void PrintTo(const XRayRecord &R, std::ostream *OS) {
+ *OS << "XRayRecord { CPU = " << R.CPU
+ << "; Type = " << RecordTypeAsString(R.Type) << "; FuncId = " << R.FuncId
+ << "; TSC = " << R.TSC << "; TId = " << R.TId << "; PId = " << R.PId
+ << " Args = " << ::testing::PrintToString(R.CallArgs) << " }";
+}
+
+void PrintTo(const Trace &T, std::ostream *OS) {
+ const auto &H = T.getFileHeader();
+ *OS << "XRay Trace:\nHeader: { Version = " << H.Version
+ << "; Type = " << H.Type
+ << "; ConstantTSC = " << ::testing::PrintToString(H.ConstantTSC)
+ << "; NonstopTSC = " << ::testing::PrintToString(H.NonstopTSC)
+ << "; CycleFrequency = " << H.CycleFrequency << "; FreeFormData = '"
+ << ::testing::PrintToString(H.FreeFormData) << "' }\n";
+ for (const auto &R : T) {
+ PrintTo(R, OS);
+ *OS << "\n";
+ }
+}
+
+} // namespace xray
+} // namespace llvm
+
+namespace __xray {
+
+std::string serialize(BufferQueue &Buffers, int32_t Version) {
+ std::string Serialized;
+ std::aligned_storage<sizeof(XRayFileHeader), alignof(XRayFileHeader)>::type
+ HeaderStorage;
+ auto *Header = reinterpret_cast<XRayFileHeader *>(&HeaderStorage);
+ new (Header) XRayFileHeader();
+ Header->Version = Version;
+ Header->Type = FileTypes::FDR_LOG;
+ Header->CycleFrequency = 3e9;
+ Header->ConstantTSC = 1;
+ Header->NonstopTSC = 1;
+ Serialized.append(reinterpret_cast<const char *>(&HeaderStorage),
+ sizeof(XRayFileHeader));
+ Buffers.apply([&](const BufferQueue::Buffer &B) {
+ 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);
+ });
+ return Serialized;
+}
+
+} // namespace __xray
diff --git a/lib/xray/tests/unit/test_helpers.h b/lib/xray/tests/unit/test_helpers.h
new file mode 100644
index 000000000000..ff0311e9bd30
--- /dev/null
+++ b/lib/xray/tests/unit/test_helpers.h
@@ -0,0 +1,78 @@
+//===-- test_helpers.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_TESTS_TEST_HELPERS_H_
+#define COMPILER_RT_LIB_XRAY_TESTS_TEST_HELPERS_H_
+
+#include "xray_buffer_queue.h"
+#include "xray_segmented_array.h"
+#include "llvm/XRay/Trace.h"
+#include "llvm/XRay/XRayRecord.h"
+#include "gmock/gmock.h"
+
+// TODO: Move these to llvm/include/Testing/XRay/...
+namespace llvm {
+namespace xray {
+
+std::string RecordTypeAsString(RecordTypes T);
+void PrintTo(RecordTypes T, std::ostream *OS);
+void PrintTo(const XRayRecord &R, std::ostream *OS);
+void PrintTo(const Trace &T, std::ostream *OS);
+
+namespace testing {
+
+MATCHER_P(FuncId, F, "") {
+ *result_listener << "where the function id is " << F;
+ return arg.FuncId == F;
+}
+
+MATCHER_P(RecordType, T, "") {
+ *result_listener << "where the record type is " << RecordTypeAsString(T);
+ return arg.Type == T;
+}
+
+MATCHER_P(HasArg, A, "") {
+ *result_listener << "where args contains " << A;
+ return !arg.CallArgs.empty() &&
+ std::any_of(arg.CallArgs.begin(), arg.CallArgs.end(),
+ [this](decltype(A) V) { return V == A; });
+}
+
+MATCHER_P(TSCIs, M, std::string("TSC is ") + ::testing::PrintToString(M)) {
+ return ::testing::Matcher<decltype(arg.TSC)>(M).MatchAndExplain(
+ arg.TSC, result_listener);
+}
+
+} // namespace testing
+} // namespace xray
+} // namespace llvm
+
+namespace __xray {
+
+std::string serialize(BufferQueue &Buffers, int32_t Version);
+
+template <class T> void PrintTo(const Array<T> &A, std::ostream *OS) {
+ *OS << "[";
+ bool first = true;
+ for (const auto &E : A) {
+ if (!first) {
+ *OS << ", ";
+ }
+ PrintTo(E, OS);
+ first = false;
+ }
+ *OS << "]";
+}
+
+} // namespace __xray
+
+#endif // COMPILER_RT_LIB_XRAY_TESTS_TEST_HELPERS_H_
diff --git a/lib/xray/xray_allocator.h b/lib/xray/xray_allocator.h
index 8244815284a8..907c54542a56 100644
--- a/lib/xray/xray_allocator.h
+++ b/lib/xray/xray_allocator.h
@@ -19,18 +19,131 @@
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_mutex.h"
+#if SANITIZER_FUCHSIA
+#include <zircon/process.h>
+#include <zircon/status.h>
+#include <zircon/syscalls.h>
+#else
#include "sanitizer_common/sanitizer_posix.h"
+#endif
+#include "xray_defs.h"
#include "xray_utils.h"
-#include <sys/mman.h>
#include <cstddef>
#include <cstdint>
+#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());
+#if SANITIZER_FUCHSIA
+ zx_handle_t Vmo;
+ zx_status_t Status = _zx_vmo_create(RoundedSize, 0, &Vmo);
+ if (Status != ZX_OK) {
+ if (Verbosity())
+ Report("XRay Profiling: Failed to create VMO of size %zu: %s\n",
+ sizeof(T), _zx_status_get_string(Status));
+ return nullptr;
+ }
+ uintptr_t B;
+ Status =
+ _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
+ Vmo, 0, sizeof(T), &B);
+ _zx_handle_close(Vmo);
+ if (Status != ZX_OK) {
+ if (Verbosity())
+ Report("XRay Profiling: Failed to map VMAR of size %zu: %s\n", sizeof(T),
+ _zx_status_get_string(Status));
+ return nullptr;
+ }
+ return reinterpret_cast<T *>(B);
+#else
+ uptr B = internal_mmap(NULL, RoundedSize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ int ErrNo = 0;
+ 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;
+ }
+#endif
+ return reinterpret_cast<T *>(B);
+}
-#ifndef MAP_NORESERVE
-// no-op on NetBSD (at least), unsupported flag on FreeBSD basically because unneeded
-#define MAP_NORESERVE 0
+template <class T> void deallocate(T *B) XRAY_NEVER_INSTRUMENT {
+ if (B == nullptr)
+ return;
+ uptr RoundedSize = RoundUpTo(sizeof(T), GetPageSizeCached());
+#if SANITIZER_FUCHSIA
+ _zx_vmar_unmap(_zx_vmar_root_self(), reinterpret_cast<uintptr_t>(B),
+ RoundedSize);
+#else
+ internal_munmap(B, RoundedSize);
#endif
+}
-namespace __xray {
+template <class T = unsigned char>
+T *allocateBuffer(size_t S) XRAY_NEVER_INSTRUMENT {
+ uptr RoundedSize = RoundUpTo(S * sizeof(T), GetPageSizeCached());
+#if SANITIZER_FUCHSIA
+ zx_handle_t Vmo;
+ zx_status_t Status = _zx_vmo_create(RoundedSize, 0, &Vmo);
+ if (Status != ZX_OK) {
+ if (Verbosity())
+ Report("XRay Profiling: Failed to create VMO of size %zu: %s\n", S,
+ _zx_status_get_string(Status));
+ return nullptr;
+ }
+ uintptr_t B;
+ Status = _zx_vmar_map(_zx_vmar_root_self(),
+ ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, Vmo, 0, S, &B);
+ _zx_handle_close(Vmo);
+ if (Status != ZX_OK) {
+ if (Verbosity())
+ Report("XRay Profiling: Failed to map VMAR of size %zu: %s\n", S,
+ _zx_status_get_string(Status));
+ return nullptr;
+ }
+#else
+ uptr B = internal_mmap(NULL, RoundedSize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ int ErrNo = 0;
+ 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;
+ }
+#endif
+ 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());
+#if SANITIZER_FUCHSIA
+ _zx_vmar_unmap(_zx_vmar_root_self(), reinterpret_cast<uintptr_t>(B),
+ RoundedSize);
+#else
+ internal_munmap(B, RoundedSize);
+#endif
+}
+
+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
@@ -58,20 +171,18 @@ template <size_t N> struct Allocator {
};
private:
- const size_t MaxMemory{0};
- void *BackingStore = nullptr;
- void *AlignedNextBlock = nullptr;
+ size_t MaxMemory{0};
+ unsigned char *BackingStore = nullptr;
+ unsigned char *AlignedNextBlock = nullptr;
size_t AllocatedBlocks = 0;
+ bool Owned;
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 +195,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,34 +203,83 @@ private:
return nullptr;
}
- AlignedNextBlock = reinterpret_cast<void *>(AlignedNextBlockNum);
+ AlignedNextBlock = reinterpret_cast<unsigned char *>(AlignedNextBlockNum);
// Assert that AlignedNextBlock is cache-line aligned.
DCHECK_EQ(reinterpret_cast<uintptr_t>(AlignedNextBlock) % kCacheLineSize,
0);
}
- if ((AllocatedBlocks * Block::Size) >= MaxMemory)
+ if (((AllocatedBlocks + 1) * Block::Size) > MaxMemory)
return nullptr;
// 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<unsigned char *>(AlignedNextBlock) + Block::Size;
++AllocatedBlocks;
return Result;
}
public:
- explicit Allocator(size_t M)
- : MaxMemory(nearest_boundary(M, kCacheLineSize)) {}
+ explicit Allocator(size_t M) XRAY_NEVER_INSTRUMENT
+ : MaxMemory(RoundUpTo(M, kCacheLineSize)),
+ BackingStore(nullptr),
+ AlignedNextBlock(nullptr),
+ AllocatedBlocks(0),
+ Owned(true),
+ Mutex() {}
+
+ explicit Allocator(void *P, size_t M) XRAY_NEVER_INSTRUMENT
+ : MaxMemory(M),
+ BackingStore(reinterpret_cast<unsigned char *>(P)),
+ AlignedNextBlock(reinterpret_cast<unsigned char *>(P)),
+ AllocatedBlocks(0),
+ Owned(false),
+ Mutex() {}
+
+ Allocator(const Allocator &) = delete;
+ Allocator &operator=(const Allocator &) = delete;
+
+ Allocator(Allocator &&O) XRAY_NEVER_INSTRUMENT {
+ SpinMutexLock L0(&Mutex);
+ SpinMutexLock L1(&O.Mutex);
+ MaxMemory = O.MaxMemory;
+ O.MaxMemory = 0;
+ BackingStore = O.BackingStore;
+ O.BackingStore = nullptr;
+ AlignedNextBlock = O.AlignedNextBlock;
+ O.AlignedNextBlock = nullptr;
+ AllocatedBlocks = O.AllocatedBlocks;
+ O.AllocatedBlocks = 0;
+ Owned = O.Owned;
+ O.Owned = false;
+ }
+
+ Allocator &operator=(Allocator &&O) XRAY_NEVER_INSTRUMENT {
+ SpinMutexLock L0(&Mutex);
+ SpinMutexLock L1(&O.Mutex);
+ MaxMemory = O.MaxMemory;
+ O.MaxMemory = 0;
+ if (BackingStore != nullptr)
+ deallocateBuffer(BackingStore, MaxMemory);
+ BackingStore = O.BackingStore;
+ O.BackingStore = nullptr;
+ AlignedNextBlock = O.AlignedNextBlock;
+ O.AlignedNextBlock = nullptr;
+ AllocatedBlocks = O.AllocatedBlocks;
+ O.AllocatedBlocks = 0;
+ Owned = O.Owned;
+ O.Owned = false;
+ return *this;
+ }
- Block Allocate() { return {Alloc()}; }
+ Block Allocate() XRAY_NEVER_INSTRUMENT { return {Alloc()}; }
- ~Allocator() NOEXCEPT {
- if (BackingStore != nullptr) {
- internal_munmap(BackingStore, MaxMemory);
+ ~Allocator() NOEXCEPT XRAY_NEVER_INSTRUMENT {
+ if (Owned && BackingStore != nullptr) {
+ deallocateBuffer(BackingStore, MaxMemory);
}
}
};
diff --git a/lib/xray/xray_basic_logging.cc b/lib/xray/xray_basic_logging.cc
index 585ca641cd0c..ae1cc0ba79dd 100644
--- a/lib/xray/xray_basic_logging.cc
+++ b/lib/xray/xray_basic_logging.cc
@@ -19,7 +19,9 @@
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
+#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD || SANITIZER_MAC
#include <sys/syscall.h>
+#endif
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
@@ -38,8 +40,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 {
@@ -52,21 +55,28 @@ struct alignas(16) StackEntry {
static_assert(sizeof(StackEntry) == 16, "Wrong size for StackEntry");
-struct alignas(64) ThreadLocalData {
+struct XRAY_TLS_ALIGNAS(64) ThreadLocalData {
void *InMemoryBuffer = nullptr;
size_t BufferSize = 0;
size_t BufferOffset = 0;
void *ShadowStack = nullptr;
size_t StackSize = 0;
size_t StackEntries = 0;
- int Fd = -1;
+ __xray::LogWriter *LogWriter = nullptr;
};
+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};
@@ -75,10 +85,10 @@ static atomic_uint64_t ThresholdTicks{0};
static atomic_uint64_t TicksPerSec{0};
static atomic_uint64_t CycleFrequency{NanosecondsPerSecond};
-static int openLogFile() XRAY_NEVER_INSTRUMENT {
- int F = getLogFD();
- if (F == -1)
- return -1;
+static LogWriter *getLog() XRAY_NEVER_INSTRUMENT {
+ LogWriter* LW = LogWriter::Open();
+ if (LW == nullptr)
+ return LW;
static pthread_once_t DetectOnce = PTHREAD_ONCE_INIT;
pthread_once(&DetectOnce, +[] {
@@ -100,16 +110,16 @@ static int openLogFile() XRAY_NEVER_INSTRUMENT {
// before setting the values in the header.
Header.ConstantTSC = 1;
Header.NonstopTSC = 1;
- retryingWriteAll(F, reinterpret_cast<char *>(&Header),
- reinterpret_cast<char *>(&Header) + sizeof(Header));
- return F;
+ LW->WriteAll(reinterpret_cast<char *>(&Header),
+ reinterpret_cast<char *>(&Header) + sizeof(Header));
+ return LW;
}
-static int getGlobalFd() XRAY_NEVER_INSTRUMENT {
+static LogWriter *getGlobalLog() XRAY_NEVER_INSTRUMENT {
static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
- static int Fd = 0;
- pthread_once(&OnceInit, +[] { Fd = openLogFile(); });
- return Fd;
+ static LogWriter *LW = nullptr;
+ pthread_once(&OnceInit, +[] { LW = getLog(); });
+ return LW;
}
static ThreadLocalData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
@@ -121,7 +131,7 @@ static ThreadLocalData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
return false;
}
pthread_setspecific(PThreadKey, &TLD);
- TLD.Fd = getGlobalFd();
+ TLD.LogWriter = getGlobalLog();
TLD.InMemoryBuffer = reinterpret_cast<XRayRecord *>(
InternalAlloc(sizeof(XRayRecord) * GlobalOptions.ThreadBufferSize,
nullptr, alignof(XRayRecord)));
@@ -149,8 +159,8 @@ template <class RDTSC>
void InMemoryRawLog(int32_t FuncId, XRayEntryType Type,
RDTSC ReadTSC) XRAY_NEVER_INSTRUMENT {
auto &TLD = getThreadLocalData();
- int Fd = getGlobalFd();
- if (Fd == -1)
+ LogWriter *LW = getGlobalLog();
+ if (LW == nullptr)
return;
// Use a simple recursion guard, to handle cases where we're already logging
@@ -234,9 +244,9 @@ void InMemoryRawLog(int32_t FuncId, XRayEntryType Type,
auto FirstEntry = reinterpret_cast<XRayRecord *>(TLD.InMemoryBuffer);
internal_memcpy(FirstEntry + TLD.BufferOffset, &R, sizeof(R));
if (++TLD.BufferOffset == TLD.BufferSize) {
- SpinMutexLock L(&LogMutex);
- retryingWriteAll(Fd, reinterpret_cast<char *>(FirstEntry),
- reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
+ SpinMutexLock Lock(&LogMutex);
+ LW->WriteAll(reinterpret_cast<char *>(FirstEntry),
+ reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
TLD.BufferOffset = 0;
TLD.StackEntries = 0;
}
@@ -249,17 +259,17 @@ void InMemoryRawLogWithArg(int32_t FuncId, XRayEntryType Type, uint64_t Arg1,
auto FirstEntry =
reinterpret_cast<XRayArgPayload *>(TLD.InMemoryBuffer);
const auto &BuffLen = TLD.BufferSize;
- int Fd = getGlobalFd();
- if (Fd == -1)
+ LogWriter *LW = getGlobalLog();
+ if (LW == nullptr)
return;
// First we check whether there's enough space to write the data consecutively
// in the thread-local buffer. If not, we first flush the buffer before
// attempting to write the two records that must be consecutive.
if (TLD.BufferOffset + 2 > BuffLen) {
- SpinMutexLock L(&LogMutex);
- retryingWriteAll(Fd, reinterpret_cast<char *>(FirstEntry),
- reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
+ SpinMutexLock Lock(&LogMutex);
+ LW->WriteAll(reinterpret_cast<char *>(FirstEntry),
+ reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
TLD.BufferOffset = 0;
TLD.StackEntries = 0;
}
@@ -280,9 +290,9 @@ void InMemoryRawLogWithArg(int32_t FuncId, XRayEntryType Type, uint64_t Arg1,
R.Arg = Arg1;
internal_memcpy(FirstEntry + TLD.BufferOffset, &R, sizeof(R));
if (++TLD.BufferOffset == BuffLen) {
- SpinMutexLock L(&LogMutex);
- retryingWriteAll(Fd, reinterpret_cast<char *>(FirstEntry),
- reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
+ SpinMutexLock Lock(&LogMutex);
+ LW->WriteAll(reinterpret_cast<char *>(FirstEntry),
+ reinterpret_cast<char *>(FirstEntry + TLD.BufferOffset));
TLD.BufferOffset = 0;
TLD.StackEntries = 0;
}
@@ -339,29 +349,29 @@ static void TLDDestructor(void *P) XRAY_NEVER_INSTRUMENT {
Report("Cleaned up log for TID: %d\n", GetTid());
});
- if (TLD.Fd == -1 || TLD.BufferOffset == 0) {
+ if (TLD.LogWriter == nullptr || TLD.BufferOffset == 0) {
if (Verbosity())
- Report("Skipping buffer for TID: %d; Fd = %d; Offset = %llu\n", GetTid(),
- TLD.Fd, TLD.BufferOffset);
+ Report("Skipping buffer for TID: %d; Offset = %llu\n", GetTid(),
+ TLD.BufferOffset);
return;
}
{
SpinMutexLock L(&LogMutex);
- retryingWriteAll(TLD.Fd, reinterpret_cast<char *>(TLD.InMemoryBuffer),
- reinterpret_cast<char *>(TLD.InMemoryBuffer) +
- (sizeof(XRayRecord) * TLD.BufferOffset));
+ TLD.LogWriter->WriteAll(reinterpret_cast<char *>(TLD.InMemoryBuffer),
+ reinterpret_cast<char *>(TLD.InMemoryBuffer) +
+ (sizeof(XRayRecord) * TLD.BufferOffset));
}
// Because this thread's exit could be the last one trying to write to
// the file and that we're not able to close out the file properly, we
// sync instead and hope that the pending writes are flushed as the
// thread exits.
- fsync(TLD.Fd);
+ TLD.LogWriter->Flush();
}
-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 +395,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 3ce728900787..7d0e5a1f323c 100644
--- a/lib/xray/xray_buffer_queue.cc
+++ b/lib/xray/xray_buffer_queue.cc
@@ -13,141 +13,206 @@
//
//===----------------------------------------------------------------------===//
#include "xray_buffer_queue.h"
+#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
+#if !SANITIZER_FUCHSIA
#include "sanitizer_common/sanitizer_posix.h"
+#endif
+#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);
+namespace {
+
+BufferQueue::ControlBlock *allocControlBlock(size_t Size, size_t Count) {
+ auto B =
+ allocateBuffer((sizeof(BufferQueue::ControlBlock) - 1) + (Size * Count));
+ return B == nullptr ? nullptr
+ : reinterpret_cast<BufferQueue::ControlBlock *>(B);
}
-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;
+void deallocControlBlock(BufferQueue::ControlBlock *C, size_t Size,
+ size_t Count) {
+ deallocateBuffer(reinterpret_cast<unsigned char *>(C),
+ (sizeof(BufferQueue::ControlBlock) - 1) + (Size * Count));
}
-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) {
- Success = false;
+void decRefCount(BufferQueue::ControlBlock *C, size_t Size, size_t Count) {
+ if (C == nullptr)
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);
- Success = false;
+ if (atomic_fetch_sub(&C->RefCount, 1, memory_order_acq_rel) == 1)
+ deallocControlBlock(C, Size, Count);
+}
+
+void incRefCount(BufferQueue::ControlBlock *C) {
+ if (C == nullptr)
return;
+ atomic_fetch_add(&C->RefCount, 1, memory_order_acq_rel);
+}
+
+// We use a struct to ensure that we are allocating one atomic_uint64_t per
+// cache line. This allows us to not worry about false-sharing among atomic
+// objects being updated (constantly) by different threads.
+struct ExtentsPadded {
+ union {
+ atomic_uint64_t Extents;
+ unsigned char Storage[kCacheLineSize];
};
+};
- for (size_t i = 0; i < N; ++i) {
- auto &T = Buffers[i];
- void *Tmp = allocRaw<char>(BufferSize);
- if (Tmp == nullptr) {
- Success = false;
+constexpr size_t kExtentsSize = sizeof(ExtentsPadded);
+
+} // namespace
+
+BufferQueue::ErrorCode BufferQueue::init(size_t BS, size_t BC) {
+ SpinMutexLock Guard(&Mutex);
+
+ if (!finalizing())
+ return BufferQueue::ErrorCode::AlreadyInitialized;
+
+ cleanupBuffers();
+
+ bool Success = false;
+ BufferSize = BS;
+ BufferCount = BC;
+
+ BackingStore = allocControlBlock(BufferSize, BufferCount);
+ if (BackingStore == nullptr)
+ return BufferQueue::ErrorCode::NotEnoughMemory;
+
+ auto CleanupBackingStore = at_scope_exit([&, this] {
+ if (Success)
return;
- }
- auto *Extents = allocRaw<BufferExtents>(1);
- if (Extents == nullptr) {
- Success = false;
+ deallocControlBlock(BackingStore, BufferSize, BufferCount);
+ BackingStore = nullptr;
+ });
+
+ // Initialize enough atomic_uint64_t instances, each
+ ExtentsBackingStore = allocControlBlock(kExtentsSize, BufferCount);
+ if (ExtentsBackingStore == nullptr)
+ return BufferQueue::ErrorCode::NotEnoughMemory;
+
+ auto CleanupExtentsBackingStore = at_scope_exit([&, this] {
+ if (Success)
return;
- }
+ deallocControlBlock(ExtentsBackingStore, kExtentsSize, BufferCount);
+ ExtentsBackingStore = nullptr;
+ });
+
+ Buffers = initArray<BufferRep>(BufferCount);
+ if (Buffers == nullptr)
+ return BufferQueue::ErrorCode::NotEnoughMemory;
+
+ // At this point we increment the generation number to associate the buffers
+ // to the new generation.
+ atomic_fetch_add(&Generation, 1, memory_order_acq_rel);
+
+ // First, we initialize the refcount in the ControlBlock, which we treat as
+ // being at the start of the BackingStore pointer.
+ atomic_store(&BackingStore->RefCount, 1, memory_order_release);
+ atomic_store(&ExtentsBackingStore->RefCount, 1, memory_order_release);
+
+ // Then we initialise the individual buffers that sub-divide the whole backing
+ // store. Each buffer will start at the `Data` member of the ControlBlock, and
+ // will be offsets from these locations.
+ for (size_t i = 0; i < BufferCount; ++i) {
+ auto &T = Buffers[i];
auto &Buf = T.Buff;
- Buf.Data = Tmp;
- Buf.Size = B;
- Buf.Extents = Extents;
- OwnedBuffers[i] = Tmp;
+ auto *E = reinterpret_cast<ExtentsPadded *>(&ExtentsBackingStore->Data +
+ (kExtentsSize * i));
+ Buf.Extents = &E->Extents;
+ atomic_store(Buf.Extents, 0, memory_order_release);
+ Buf.Generation = generation();
+ Buf.Data = &BackingStore->Data + (BufferSize * i);
+ Buf.Size = BufferSize;
+ Buf.BackingStore = BackingStore;
+ Buf.ExtentsBackingStore = ExtentsBackingStore;
+ Buf.Count = BufferCount;
+ T.Used = false;
}
+
+ Next = Buffers;
+ First = Buffers;
+ LiveBuffers = 0;
+ atomic_store(&Finalizing, 0, memory_order_release);
Success = true;
+ return BufferQueue::ErrorCode::Ok;
+}
+
+BufferQueue::BufferQueue(size_t B, size_t N,
+ bool &Success) XRAY_NEVER_INSTRUMENT
+ : BufferSize(B),
+ BufferCount(N),
+ Mutex(),
+ Finalizing{1},
+ BackingStore(nullptr),
+ ExtentsBackingStore(nullptr),
+ Buffers(nullptr),
+ Next(Buffers),
+ First(Buffers),
+ LiveBuffers(0),
+ Generation{0} {
+ Success = init(B, N) == BufferQueue::ErrorCode::Ok;
}
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;
- T.Used = true;
- ++LiveBuffers;
-
- if (++Next == (Buffers + BufferCount))
- Next = Buffers;
+ BufferRep *B = nullptr;
+ {
+ SpinMutexLock Guard(&Mutex);
+ if (LiveBuffers == BufferCount)
+ return ErrorCode::NotEnoughMemory;
+ B = Next++;
+ if (Next == (Buffers + BufferCount))
+ Next = Buffers;
+ ++LiveBuffers;
+ }
+ incRefCount(BackingStore);
+ incRefCount(ExtentsBackingStore);
+ Buf = B->Buff;
+ Buf.Generation = generation();
+ B->Used = true;
return ErrorCode::Ok;
}
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;
+ // Check whether the buffer being referred to is within the bounds of the
+ // backing store's range.
+ BufferRep *B = nullptr;
+ {
+ SpinMutexLock Guard(&Mutex);
+ if (Buf.Generation != generation() || LiveBuffers == 0) {
+ Buf = {};
+ decRefCount(Buf.BackingStore, Buf.Size, Buf.Count);
+ decRefCount(Buf.ExtentsBackingStore, kExtentsSize, Buf.Count);
+ return BufferQueue::ErrorCode::Ok;
}
- }
- if (!Found)
- return ErrorCode::UnrecognizedBuffer;
- SpinMutexLock Guard(&Mutex);
+ if (Buf.Data < &BackingStore->Data ||
+ Buf.Data > &BackingStore->Data + (BufferCount * BufferSize))
+ return BufferQueue::ErrorCode::UnrecognizedBuffer;
- // This points to a semantic bug, we really ought to not be releasing more
- // buffers than we actually get.
- if (LiveBuffers == 0)
- return ErrorCode::NotEnoughMemory;
+ --LiveBuffers;
+ B = First++;
+ if (First == (Buffers + BufferCount))
+ First = Buffers;
+ }
// Now that the buffer has been released, we mark it as "used".
- First->Buff = Buf;
- First->Used = true;
- Buf.Data = nullptr;
- Buf.Size = 0;
- --LiveBuffers;
- if (++First == (Buffers + BufferCount))
- First = Buffers;
-
+ B->Buff = Buf;
+ B->Used = true;
+ decRefCount(Buf.BackingStore, Buf.Size, Buf.Count);
+ decRefCount(Buf.ExtentsBackingStore, kExtentsSize, Buf.Count);
+ atomic_store(B->Buff.Extents, atomic_load(Buf.Extents, memory_order_acquire),
+ memory_order_release);
+ Buf = {};
return ErrorCode::Ok;
}
@@ -157,15 +222,17 @@ BufferQueue::ErrorCode BufferQueue::finalize() {
return ErrorCode::Ok;
}
-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);
- }
+void BufferQueue::cleanupBuffers() {
for (auto B = Buffers, E = Buffers + BufferCount; B != E; ++B)
B->~BufferRep();
- deallocRaw(Buffers, BufferCount);
- deallocRaw(OwnedBuffers, BufferCount);
+ deallocateBuffer(Buffers, BufferCount);
+ decRefCount(BackingStore, BufferSize, BufferCount);
+ decRefCount(ExtentsBackingStore, kExtentsSize, BufferCount);
+ BackingStore = nullptr;
+ ExtentsBackingStore = nullptr;
+ Buffers = nullptr;
+ BufferCount = 0;
+ BufferSize = 0;
}
+
+BufferQueue::~BufferQueue() { cleanupBuffers(); }
diff --git a/lib/xray/xray_buffer_queue.h b/lib/xray/xray_buffer_queue.h
index e76fa7983c90..ef2b433f9a3f 100644
--- a/lib/xray/xray_buffer_queue.h
+++ b/lib/xray/xray_buffer_queue.h
@@ -18,25 +18,51 @@
#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 {
/// BufferQueue implements a circular queue of fixed sized buffers (much like a
-/// freelist) but is concerned mostly with making it really quick to initialise,
-/// finalise, and get/return buffers to the queue. This is one key component of
-/// the "flight data recorder" (FDR) mode to support ongoing XRay function call
+/// freelist) but is concerned with making it quick to initialise, finalise, and
+/// get from or return buffers to the queue. This is one key component of the
+/// "flight data recorder" (FDR) mode to support ongoing XRay function call
/// trace collection.
class BufferQueue {
public:
- struct alignas(64) BufferExtents {
- atomic_uint64_t Size;
+ /// ControlBlock represents the memory layout of how we interpret the backing
+ /// store for all buffers and extents managed by a BufferQueue instance. The
+ /// ControlBlock has the reference count as the first member, sized according
+ /// to platform-specific cache-line size. We never use the Buffer member of
+ /// the union, which is only there for compiler-supported alignment and
+ /// sizing.
+ ///
+ /// This ensures that the `Data` member will be placed at least kCacheLineSize
+ /// bytes from the beginning of the structure.
+ struct ControlBlock {
+ union {
+ atomic_uint64_t RefCount;
+ char Buffer[kCacheLineSize];
+ };
+
+ /// We need to make this size 1, to conform to the C++ rules for array data
+ /// members. Typically, we want to subtract this 1 byte for sizing
+ /// information.
+ char Data[1];
};
struct Buffer {
+ atomic_uint64_t *Extents = nullptr;
+ uint64_t Generation{0};
void *Data = nullptr;
size_t Size = 0;
- BufferExtents *Extents;
+
+ private:
+ friend class BufferQueue;
+ ControlBlock *BackingStore = nullptr;
+ ControlBlock *ExtentsBackingStore = nullptr;
+ size_t Count = 0;
};
struct BufferRep {
@@ -76,8 +102,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 +135,20 @@ 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;
+ // The collocated ControlBlock and buffer storage.
+ ControlBlock *BackingStore;
+
+ // The collocated ControlBlock and extents storage.
+ ControlBlock *ExtentsBackingStore;
+
+ // A dynamically allocated array of BufferRep instances.
+ BufferRep *Buffers;
// Pointer to the next buffer to be handed out.
BufferRep *Next;
@@ -128,6 +160,13 @@ private:
// Count of buffers that have been handed out through 'getBuffer'.
size_t LiveBuffers;
+ // We use a generation number to identify buffers and which generation they're
+ // associated with.
+ atomic_uint64_t Generation;
+
+ /// Releases references to the buffers backed by the current buffer queue.
+ void cleanupBuffers();
+
public:
enum class ErrorCode : unsigned {
Ok,
@@ -135,6 +174,7 @@ public:
QueueFinalizing,
UnrecognizedBuffer,
AlreadyFinalized,
+ AlreadyInitialized,
};
static const char *getErrorString(ErrorCode E) {
@@ -149,6 +189,8 @@ public:
return "buffer being returned not owned by buffer queue";
case ErrorCode::AlreadyFinalized:
return "queue already finalized";
+ case ErrorCode::AlreadyInitialized:
+ return "queue already initialized";
}
return "unknown error";
}
@@ -179,10 +221,23 @@ public:
/// the buffer being released.
ErrorCode releaseBuffer(Buffer &Buf);
+ /// Initializes the buffer queue, starting a new generation. We can re-set the
+ /// size of buffers with |BS| along with the buffer count with |BC|.
+ ///
+ /// Returns:
+ /// - ErrorCode::Ok when we successfully initialize the buffer. This
+ /// requires that the buffer queue is previously finalized.
+ /// - ErrorCode::AlreadyInitialized when the buffer queue is not finalized.
+ ErrorCode init(size_t BS, size_t BC);
+
bool finalizing() const {
return atomic_load(&Finalizing, memory_order_acquire);
}
+ uint64_t generation() const {
+ return atomic_load(&Generation, memory_order_acquire);
+ }
+
/// Returns the configured size of the buffers in the buffer queue.
size_t ConfiguredBufferSize() const { return BufferSize; }
@@ -198,7 +253,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_defs.h b/lib/xray/xray_defs.h
index e5c37c0665db..c009bcc879f1 100644
--- a/lib/xray/xray_defs.h
+++ b/lib/xray/xray_defs.h
@@ -19,4 +19,14 @@
#define XRAY_NEVER_INSTRUMENT
#endif
+#if SANITIZER_NETBSD
+// NetBSD: thread_local is not aligned properly, and the code relying
+// on it segfaults
+#define XRAY_TLS_ALIGNAS(x)
+#define XRAY_HAS_TLS_ALIGNAS 0
+#else
+#define XRAY_TLS_ALIGNAS(x) alignas(x)
+#define XRAY_HAS_TLS_ALIGNAS 1
+#endif
+
#endif // XRAY_XRAY_DEFS_H
diff --git a/lib/xray/xray_fdr_controller.h b/lib/xray/xray_fdr_controller.h
new file mode 100644
index 000000000000..d44d0309b373
--- /dev/null
+++ b/lib/xray/xray_fdr_controller.h
@@ -0,0 +1,373 @@
+//===-- xray_fdr_controller.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_CONTROLLER_H_
+#define COMPILER_RT_LIB_XRAY_XRAY_FDR_CONTROLLER_H_
+
+#include <limits>
+#include <time.h>
+
+#include "xray/xray_interface.h"
+#include "xray/xray_records.h"
+#include "xray_buffer_queue.h"
+#include "xray_fdr_log_writer.h"
+
+namespace __xray {
+
+template <size_t Version = 5> class FDRController {
+ BufferQueue *BQ;
+ BufferQueue::Buffer &B;
+ FDRLogWriter &W;
+ int (*WallClockReader)(clockid_t, struct timespec *) = 0;
+ uint64_t CycleThreshold = 0;
+
+ uint64_t LastFunctionEntryTSC = 0;
+ uint64_t LatestTSC = 0;
+ uint16_t LatestCPU = 0;
+ tid_t TId = 0;
+ pid_t PId = 0;
+ bool First = true;
+
+ uint32_t UndoableFunctionEnters = 0;
+ uint32_t UndoableTailExits = 0;
+
+ bool finalized() const XRAY_NEVER_INSTRUMENT {
+ return BQ == nullptr || BQ->finalizing();
+ }
+
+ bool hasSpace(size_t S) XRAY_NEVER_INSTRUMENT {
+ return B.Data != nullptr && B.Generation == BQ->generation() &&
+ W.getNextRecord() + S <= reinterpret_cast<char *>(B.Data) + B.Size;
+ }
+
+ constexpr int32_t mask(int32_t FuncId) const XRAY_NEVER_INSTRUMENT {
+ return FuncId & ((1 << 29) - 1);
+ }
+
+ bool getNewBuffer() XRAY_NEVER_INSTRUMENT {
+ if (BQ->getBuffer(B) != BufferQueue::ErrorCode::Ok)
+ return false;
+
+ W.resetRecord();
+ DCHECK_EQ(W.getNextRecord(), B.Data);
+ LatestTSC = 0;
+ LatestCPU = 0;
+ First = true;
+ UndoableFunctionEnters = 0;
+ UndoableTailExits = 0;
+ atomic_store(B.Extents, 0, memory_order_release);
+ return true;
+ }
+
+ bool setupNewBuffer() XRAY_NEVER_INSTRUMENT {
+ if (finalized())
+ return false;
+
+ DCHECK(hasSpace(sizeof(MetadataRecord) * 3));
+ TId = GetTid();
+ PId = internal_getpid();
+ struct timespec TS {
+ 0, 0
+ };
+ WallClockReader(CLOCK_MONOTONIC, &TS);
+
+ 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)),
+ };
+
+ if (finalized())
+ return false;
+ return W.writeMetadataRecords(Metadata);
+ }
+
+ bool prepareBuffer(size_t S) XRAY_NEVER_INSTRUMENT {
+ if (finalized())
+ return returnBuffer();
+
+ if (UNLIKELY(!hasSpace(S))) {
+ if (!returnBuffer())
+ return false;
+ if (!getNewBuffer())
+ return false;
+ if (!setupNewBuffer())
+ return false;
+ }
+
+ if (First) {
+ First = false;
+ W.resetRecord();
+ atomic_store(B.Extents, 0, memory_order_release);
+ return setupNewBuffer();
+ }
+
+ return true;
+ }
+
+ bool returnBuffer() XRAY_NEVER_INSTRUMENT {
+ if (BQ == nullptr)
+ return false;
+
+ First = true;
+ if (finalized()) {
+ BQ->releaseBuffer(B); // ignore result.
+ return false;
+ }
+
+ return BQ->releaseBuffer(B) == BufferQueue::ErrorCode::Ok;
+ }
+
+ enum class PreambleResult { NoChange, WroteMetadata, InvalidBuffer };
+ PreambleResult recordPreamble(uint64_t TSC,
+ uint16_t CPU) XRAY_NEVER_INSTRUMENT {
+ if (UNLIKELY(LatestCPU != CPU || LatestTSC == 0)) {
+ // We update our internal tracking state for the Latest TSC and CPU we've
+ // seen, then write out the appropriate metadata and function records.
+ LatestTSC = TSC;
+ LatestCPU = CPU;
+
+ if (B.Generation != BQ->generation())
+ return PreambleResult::InvalidBuffer;
+
+ W.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(CPU, TSC);
+ return PreambleResult::WroteMetadata;
+ }
+
+ DCHECK_EQ(LatestCPU, CPU);
+
+ if (UNLIKELY(LatestTSC > TSC ||
+ TSC - LatestTSC >
+ uint64_t{std::numeric_limits<int32_t>::max()})) {
+ // Either the TSC has wrapped around from the last TSC we've seen or the
+ // delta is too large to fit in a 32-bit signed integer, so we write a
+ // wrap-around record.
+ LatestTSC = TSC;
+
+ if (B.Generation != BQ->generation())
+ return PreambleResult::InvalidBuffer;
+
+ W.writeMetadata<MetadataRecord::RecordKinds::TSCWrap>(TSC);
+ return PreambleResult::WroteMetadata;
+ }
+
+ return PreambleResult::NoChange;
+ }
+
+ bool rewindRecords(int32_t FuncId, uint64_t TSC,
+ uint16_t CPU) XRAY_NEVER_INSTRUMENT {
+ // Undo one enter record, because at this point we are either at the state
+ // of:
+ // - We are exiting a function that we recently entered.
+ // - We are exiting a function that was the result of a sequence of tail
+ // exits, and we can check whether the tail exits can be re-wound.
+ //
+ FunctionRecord F;
+ W.undoWrites(sizeof(FunctionRecord));
+ if (B.Generation != BQ->generation())
+ return false;
+ internal_memcpy(&F, W.getNextRecord(), sizeof(FunctionRecord));
+
+ DCHECK(F.RecordKind ==
+ uint8_t(FunctionRecord::RecordKinds::FunctionEnter) &&
+ "Expected to find function entry recording when rewinding.");
+ DCHECK_EQ(F.FuncId, FuncId & ~(0x0F << 28));
+
+ LatestTSC -= F.TSCDelta;
+ if (--UndoableFunctionEnters != 0) {
+ LastFunctionEntryTSC -= F.TSCDelta;
+ return true;
+ }
+
+ LastFunctionEntryTSC = 0;
+ auto RewindingTSC = LatestTSC;
+ auto RewindingRecordPtr = W.getNextRecord() - sizeof(FunctionRecord);
+ while (UndoableTailExits) {
+ if (B.Generation != BQ->generation())
+ return false;
+ internal_memcpy(&F, RewindingRecordPtr, sizeof(FunctionRecord));
+ DCHECK_EQ(F.RecordKind,
+ uint8_t(FunctionRecord::RecordKinds::FunctionTailExit));
+ RewindingTSC -= F.TSCDelta;
+ RewindingRecordPtr -= sizeof(FunctionRecord);
+ if (B.Generation != BQ->generation())
+ return false;
+ internal_memcpy(&F, RewindingRecordPtr, sizeof(FunctionRecord));
+
+ // This tail call exceeded the threshold duration. It will not be erased.
+ if ((TSC - RewindingTSC) >= CycleThreshold) {
+ UndoableTailExits = 0;
+ return true;
+ }
+
+ --UndoableTailExits;
+ W.undoWrites(sizeof(FunctionRecord) * 2);
+ LatestTSC = RewindingTSC;
+ }
+ return true;
+ }
+
+public:
+ template <class WallClockFunc>
+ FDRController(BufferQueue *BQ, BufferQueue::Buffer &B, FDRLogWriter &W,
+ WallClockFunc R, uint64_t C) XRAY_NEVER_INSTRUMENT
+ : BQ(BQ),
+ B(B),
+ W(W),
+ WallClockReader(R),
+ CycleThreshold(C) {}
+
+ bool functionEnter(int32_t FuncId, uint64_t TSC,
+ uint16_t CPU) XRAY_NEVER_INSTRUMENT {
+ if (finalized() ||
+ !prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))
+ return returnBuffer();
+
+ auto PreambleStatus = recordPreamble(TSC, CPU);
+ if (PreambleStatus == PreambleResult::InvalidBuffer)
+ return returnBuffer();
+
+ if (PreambleStatus == PreambleResult::WroteMetadata) {
+ UndoableFunctionEnters = 1;
+ UndoableTailExits = 0;
+ } else {
+ ++UndoableFunctionEnters;
+ }
+
+ auto Delta = TSC - LatestTSC;
+ LastFunctionEntryTSC = TSC;
+ LatestTSC = TSC;
+ return W.writeFunction(FDRLogWriter::FunctionRecordKind::Enter,
+ mask(FuncId), Delta);
+ }
+
+ bool functionTailExit(int32_t FuncId, uint64_t TSC,
+ uint16_t CPU) XRAY_NEVER_INSTRUMENT {
+ if (finalized())
+ return returnBuffer();
+
+ if (!prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))
+ return returnBuffer();
+
+ auto PreambleStatus = recordPreamble(TSC, CPU);
+ if (PreambleStatus == PreambleResult::InvalidBuffer)
+ return returnBuffer();
+
+ if (PreambleStatus == PreambleResult::NoChange &&
+ UndoableFunctionEnters != 0 &&
+ TSC - LastFunctionEntryTSC < CycleThreshold)
+ return rewindRecords(FuncId, TSC, CPU);
+
+ UndoableTailExits = UndoableFunctionEnters ? UndoableTailExits + 1 : 0;
+ UndoableFunctionEnters = 0;
+ auto Delta = TSC - LatestTSC;
+ LatestTSC = TSC;
+ return W.writeFunction(FDRLogWriter::FunctionRecordKind::TailExit,
+ mask(FuncId), Delta);
+ }
+
+ bool functionEnterArg(int32_t FuncId, uint64_t TSC, uint16_t CPU,
+ uint64_t Arg) XRAY_NEVER_INSTRUMENT {
+ if (finalized() ||
+ !prepareBuffer((2 * sizeof(MetadataRecord)) + sizeof(FunctionRecord)) ||
+ recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
+ return returnBuffer();
+
+ auto Delta = TSC - LatestTSC;
+ LatestTSC = TSC;
+ LastFunctionEntryTSC = 0;
+ UndoableFunctionEnters = 0;
+ UndoableTailExits = 0;
+
+ return W.writeFunctionWithArg(FDRLogWriter::FunctionRecordKind::EnterArg,
+ mask(FuncId), Delta, Arg);
+ }
+
+ bool functionExit(int32_t FuncId, uint64_t TSC,
+ uint16_t CPU) XRAY_NEVER_INSTRUMENT {
+ if (finalized() ||
+ !prepareBuffer(sizeof(MetadataRecord) + sizeof(FunctionRecord)))
+ return returnBuffer();
+
+ auto PreambleStatus = recordPreamble(TSC, CPU);
+ if (PreambleStatus == PreambleResult::InvalidBuffer)
+ return returnBuffer();
+
+ if (PreambleStatus == PreambleResult::NoChange &&
+ UndoableFunctionEnters != 0 &&
+ TSC - LastFunctionEntryTSC < CycleThreshold)
+ return rewindRecords(FuncId, TSC, CPU);
+
+ auto Delta = TSC - LatestTSC;
+ LatestTSC = TSC;
+ UndoableFunctionEnters = 0;
+ UndoableTailExits = 0;
+ return W.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, mask(FuncId),
+ Delta);
+ }
+
+ bool customEvent(uint64_t TSC, uint16_t CPU, const void *Event,
+ int32_t EventSize) XRAY_NEVER_INSTRUMENT {
+ if (finalized() ||
+ !prepareBuffer((2 * sizeof(MetadataRecord)) + EventSize) ||
+ recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
+ return returnBuffer();
+
+ auto Delta = TSC - LatestTSC;
+ LatestTSC = TSC;
+ UndoableFunctionEnters = 0;
+ UndoableTailExits = 0;
+ return W.writeCustomEvent(Delta, Event, EventSize);
+ }
+
+ bool typedEvent(uint64_t TSC, uint16_t CPU, uint16_t EventType,
+ const void *Event, int32_t EventSize) XRAY_NEVER_INSTRUMENT {
+ if (finalized() ||
+ !prepareBuffer((2 * sizeof(MetadataRecord)) + EventSize) ||
+ recordPreamble(TSC, CPU) == PreambleResult::InvalidBuffer)
+ return returnBuffer();
+
+ auto Delta = TSC - LatestTSC;
+ LatestTSC = TSC;
+ UndoableFunctionEnters = 0;
+ UndoableTailExits = 0;
+ return W.writeTypedEvent(Delta, EventType, Event, EventSize);
+ }
+
+ bool flush() XRAY_NEVER_INSTRUMENT {
+ if (finalized()) {
+ returnBuffer(); // ignore result.
+ return true;
+ }
+ return returnBuffer();
+ }
+};
+
+} // namespace __xray
+
+#endif // COMPILER-RT_LIB_XRAY_XRAY_FDR_CONTROLLER_H_
diff --git a/lib/xray/xray_fdr_log_records.h b/lib/xray/xray_fdr_log_records.h
index 87096d4fc29e..e7b1ee562e1b 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 000000000000..7712e1377763
--- /dev/null
+++ b/lib/xray/xray_fdr_log_writer.h
@@ -0,0 +1,232 @@
+//===-- 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 <class Tuple, size_t Index> struct AggregateSizesImpl {
+ static constexpr size_t value =
+ sizeof(typename std::tuple_element<Index, Tuple>::type) +
+ AggregateSizesImpl<Tuple, Index - 1>::value;
+};
+
+template <class Tuple> struct AggregateSizesImpl<Tuple, 0> {
+ static constexpr size_t value =
+ sizeof(typename std::tuple_element<0, Tuple>::type);
+};
+
+template <class Tuple> struct AggregateSizes {
+ static constexpr size_t value =
+ AggregateSizesImpl<Tuple, std::tuple_size<Tuple>::value - 1>::value;
+};
+
+template <MetadataRecord::RecordKinds Kind, class... DataTypes>
+MetadataRecord createMetadataRecord(DataTypes &&... Ds) {
+ static_assert(AggregateSizes<std::tuple<DataTypes...>>::value <=
+ sizeof(MetadataRecord) - 1,
+ "Metadata payload longer than metadata buffer!");
+ 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);
+ // We need this atomic fence here to ensure that other threads attempting to
+ // read the bytes in the buffer will see the writes committed before the
+ // extents are updated.
+ atomic_thread_fence(memory_order_release);
+ 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;
+ // We need this atomic fence here to ensure that other threads attempting to
+ // read the bytes in the buffer will see the writes committed before the
+ // extents are updated.
+ atomic_thread_fence(memory_order_release);
+ 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;
+ }
+
+ bool writeFunctionWithArg(FunctionRecordKind Kind, int32_t FuncId,
+ int32_t Delta, uint64_t Arg) {
+ // We need to write the function with arg into the buffer, and then
+ // atomically update the buffer extents. This ensures that any reads
+ // synchronised on the buffer extents record will always see the writes
+ // that happen before the atomic update.
+ FunctionRecord R;
+ R.Type = 0;
+ R.RecordKind = uint8_t(Kind);
+ R.FuncId = FuncId;
+ R.TSCDelta = Delta;
+ MetadataRecord A =
+ createMetadataRecord<MetadataRecord::RecordKinds::CallArgument>(Arg);
+ NextRecord = reinterpret_cast<char *>(internal_memcpy(
+ NextRecord, reinterpret_cast<char *>(&R), sizeof(R))) +
+ sizeof(R);
+ NextRecord = reinterpret_cast<char *>(internal_memcpy(
+ NextRecord, reinterpret_cast<char *>(&A), sizeof(A))) +
+ sizeof(A);
+ // We need this atomic fence here to ensure that other threads attempting to
+ // read the bytes in the buffer will see the writes committed before the
+ // extents are updated.
+ atomic_thread_fence(memory_order_release);
+ atomic_fetch_add(Buffer.Extents, sizeof(R) + sizeof(A),
+ memory_order_acq_rel);
+ return true;
+ }
+
+ bool writeCustomEvent(int32_t Delta, const void *Event, int32_t EventSize) {
+ // We write the metadata record and the custom event data into the buffer
+ // first, before we atomically update the extents for the buffer. This
+ // allows us to ensure that any threads reading the extents of the buffer
+ // will only ever see the full metadata and custom event payload accounted
+ // (no partial writes accounted).
+ MetadataRecord R =
+ createMetadataRecord<MetadataRecord::RecordKinds::CustomEventMarker>(
+ EventSize, Delta);
+ NextRecord = reinterpret_cast<char *>(internal_memcpy(
+ NextRecord, reinterpret_cast<char *>(&R), sizeof(R))) +
+ sizeof(R);
+ NextRecord = reinterpret_cast<char *>(
+ internal_memcpy(NextRecord, Event, EventSize)) +
+ EventSize;
+
+ // We need this atomic fence here to ensure that other threads attempting to
+ // read the bytes in the buffer will see the writes committed before the
+ // extents are updated.
+ atomic_thread_fence(memory_order_release);
+ atomic_fetch_add(Buffer.Extents, sizeof(R) + EventSize,
+ memory_order_acq_rel);
+ return true;
+ }
+
+ bool writeTypedEvent(int32_t Delta, uint16_t EventType, const void *Event,
+ int32_t EventSize) {
+ // We do something similar when writing out typed events, see
+ // writeCustomEvent(...) above for details.
+ MetadataRecord R =
+ createMetadataRecord<MetadataRecord::RecordKinds::TypedEventMarker>(
+ EventSize, Delta, EventType);
+ NextRecord = reinterpret_cast<char *>(internal_memcpy(
+ NextRecord, reinterpret_cast<char *>(&R), sizeof(R))) +
+ sizeof(R);
+ NextRecord = reinterpret_cast<char *>(
+ internal_memcpy(NextRecord, Event, EventSize)) +
+ EventSize;
+
+ // We need this atomic fence here to ensure that other threads attempting to
+ // read the bytes in the buffer will see the writes committed before the
+ // extents are updated.
+ atomic_thread_fence(memory_order_release);
+ atomic_fetch_add(Buffer.Extents, EventSize, memory_order_acq_rel);
+ return true;
+ }
+
+ char *getNextRecord() const { return NextRecord; }
+
+ void resetRecord() {
+ NextRecord = reinterpret_cast<char *>(Buffer.Data);
+ atomic_store(Buffer.Extents, 0, memory_order_release);
+ }
+
+ void undoWrites(size_t B) {
+ DCHECK_GE(NextRecord - B, reinterpret_cast<char *>(Buffer.Data));
+ NextRecord -= B;
+ atomic_fetch_sub(Buffer.Extents, B, memory_order_acq_rel);
+ }
+
+}; // 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 6cb2dfa0c658..1eda26df7a85 100644
--- a/lib/xray/xray_fdr_logging.cc
+++ b/lib/xray/xray_fdr_logging.cc
@@ -20,7 +20,6 @@
#include <limits>
#include <memory>
#include <pthread.h>
-#include <sys/syscall.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
@@ -30,9 +29,12 @@
#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_controller.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,55 +42,53 @@
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;
- char *RecordPtr = nullptr;
- // The number of FunctionEntry records immediately preceding RecordPtr.
- uint8_t NumConsecutiveFnEnters = 0;
-
- // The number of adjacent, consecutive pairs of FunctionEntry, Tail Exit
- // records preceding RecordPtr.
- uint8_t NumTailCalls = 0;
-
- // We use a thread_local variable to keep track of which CPUs we've already
- // run, and the TSC times for these CPUs. This allows us to stop repeating the
- // CPU field in the function records.
- //
- // We assume that we'll support only 65536 CPUs for x86_64.
- uint16_t CurrentCPU = std::numeric_limits<uint16_t>::max();
- uint64_t LastTSC = 0;
- uint64_t LastFunctionEntryTSC = 0;
-
- // Make sure a thread that's ever called handleArg0 has a thread-local
- // live reference to the buffer queue for this particular instance of
- // FDRLogging, and that we're going to clean it up when the thread exits.
+struct XRAY_TLS_ALIGNAS(64) ThreadLocalData {
+ BufferQueue::Buffer Buffer{};
BufferQueue *BQ = nullptr;
+
+ using LogWriterStorage =
+ typename std::aligned_storage<sizeof(FDRLogWriter),
+ alignof(FDRLogWriter)>::type;
+
+ LogWriterStorage LWStorage;
+ FDRLogWriter *Writer = nullptr;
+
+ using ControllerStorage =
+ typename std::aligned_storage<sizeof(FDRController<>),
+ alignof(FDRController<>)>::type;
+ ControllerStorage CStorage;
+ FDRController<> *Controller = nullptr;
};
+} // namespace
+
static_assert(std::is_trivially_destructible<ThreadLocalData>::value,
"ThreadLocalData must be trivially destructible");
-static constexpr auto MetadataRecSize = sizeof(MetadataRecord);
-static constexpr auto FunctionRecSize = sizeof(FunctionRecord);
-
// Use a global pthread key to identify thread-local data for logging.
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};
+// Global thresholds for function durations.
+static atomic_uint64_t ThresholdTicks{0};
-static FDRLoggingOptions FDROptions;
+// Global for ticks per second.
+static atomic_uint64_t TicksPerSec{0};
-static SpinMutex FDROptionsMutex;
+static atomic_sint32_t LogFlushStatus = {
+ XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
// This function will initialize the thread-local data structure used by the FDR
// logging implementation and return a reference to it. The implementation
@@ -124,8 +124,10 @@ static SpinMutex FDROptionsMutex;
// critical section, calling a function that might be XRay instrumented (and
// thus in turn calling into malloc by virtue of registration of the
// thread_local's destructor).
+#if XRAY_HAS_TLS_ALIGNAS
static_assert(alignof(ThreadLocalData) >= 64,
"ThreadLocalData must be cache line aligned.");
+#endif
static ThreadLocalData &getThreadLocalData() {
thread_local typename std::aligned_storage<
sizeof(ThreadLocalData), alignof(ThreadLocalData)>::type TLDStorage{};
@@ -138,559 +140,36 @@ static ThreadLocalData &getThreadLocalData() {
return *reinterpret_cast<ThreadLocalData *>(&TLDStorage);
}
-static void writeNewBufferPreamble(tid_t Tid, timespec TS,
- pid_t Pid) XRAY_NEVER_INSTRUMENT {
- static constexpr int InitRecordsCount = 3;
- 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));
- }
-
- 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);
-}
-
-static void setupNewBuffer(int (*wall_clock_reader)(
- clockid_t, struct timespec *)) XRAY_NEVER_INSTRUMENT {
- auto &TLD = getThreadLocalData();
- auto &B = TLD.Buffer;
- TLD.RecordPtr = static_cast<char *>(B.Data);
- tid_t Tid = GetTid();
- timespec TS{0, 0};
- pid_t Pid = internal_getpid();
- // This is typically clock_gettime, but callers have injection ability.
- wall_clock_reader(CLOCK_MONOTONIC, &TS);
- writeNewBufferPreamble(Tid, TS, Pid);
- TLD.NumConsecutiveFnEnters = 0;
- TLD.NumTailCalls = 0;
-}
-
-static void incrementExtents(size_t Add) {
- auto &TLD = getThreadLocalData();
- atomic_fetch_add(&TLD.Buffer.Extents->Size, 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);
-}
-
-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);
-
- // 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);
- 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);
-
- // 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);
- 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));
-}
-
-static void writeFunctionRecord(int 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;
-
- auto &TLD = getThreadLocalData();
- 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);
- 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);
- break;
- case XRayEntryType::TAIL:
- // If we just entered the function we're tail exiting from or erased every
- // invocation since then, this function entry tail pair is a candidate to
- // be erased when the child function exits.
- if (TLD.NumConsecutiveFnEnters > 0) {
- ++TLD.NumTailCalls;
- TLD.NumConsecutiveFnEnters = 0;
- } else {
- // We will never be able to erase this tail call since we have logged
- // something in between the function entry and tail exit.
- TLD.NumTailCalls = 0;
- TLD.NumConsecutiveFnEnters = 0;
- }
- FuncRecord.RecordKind =
- uint8_t(FunctionRecord::RecordKinds::FunctionTailExit);
- break;
- case XRayEntryType::CUSTOM_EVENT: {
- // This is a bug in patching, so we'll report it once and move on.
- static atomic_uint8_t ErrorLatch{0};
- if (!atomic_exchange(&ErrorLatch, 1, memory_order_acq_rel))
- Report("Internal error: patched an XRay custom event call as a function; "
- "func id = %d\n",
- FuncId);
- return;
- }
- case XRayEntryType::TYPED_EVENT: {
- static atomic_uint8_t ErrorLatch{0};
- if (!atomic_exchange(&ErrorLatch, 1, memory_order_acq_rel))
- Report("Internal error: patched an XRay typed event call as a function; "
- "func id = %d\n",
- FuncId);
- return;
- }
- }
-
- internal_memcpy(TLD.RecordPtr, &FuncRecord, sizeof(FunctionRecord));
- TLD.RecordPtr += sizeof(FunctionRecord);
- incrementExtents(sizeof(FunctionRecord));
-}
-
-static atomic_uint64_t TicksPerSec{0};
-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) {
- auto &TLD = getThreadLocalData();
- TLD.RecordPtr -= FunctionRecSize;
- decrementExtents(FunctionRecSize);
- FunctionRecord FuncRecord;
- internal_memcpy(&FuncRecord, TLD.RecordPtr, FunctionRecSize);
- DCHECK(FuncRecord.RecordKind ==
- uint8_t(FunctionRecord::RecordKinds::FunctionEnter) &&
- "Expected to find function entry recording when rewinding.");
- DCHECK(FuncRecord.FuncId == (FuncId & ~(0x0F << 28)) &&
- "Expected matching function id when rewinding Exit");
- --TLD.NumConsecutiveFnEnters;
- LastTSC -= FuncRecord.TSCDelta;
-
- // We unwound one call. Update the state and return without writing a log.
- if (TLD.NumConsecutiveFnEnters != 0) {
- LastFunctionEntryTSC -= FuncRecord.TSCDelta;
- return;
- }
-
- // Otherwise we've rewound the stack of all function entries, we might be
- // able to rewind further by erasing tail call functions that are being
- // exited from via this exit.
- LastFunctionEntryTSC = 0;
- auto RewindingTSC = LastTSC;
- auto RewindingRecordPtr = TLD.RecordPtr - FunctionRecSize;
- while (TLD.NumTailCalls > 0) {
- // Rewind the TSC back over the TAIL EXIT record.
- FunctionRecord ExpectedTailExit;
- internal_memcpy(&ExpectedTailExit, RewindingRecordPtr, FunctionRecSize);
-
- DCHECK(ExpectedTailExit.RecordKind ==
- uint8_t(FunctionRecord::RecordKinds::FunctionTailExit) &&
- "Expected to find tail exit when rewinding.");
- RewindingRecordPtr -= FunctionRecSize;
- RewindingTSC -= ExpectedTailExit.TSCDelta;
- FunctionRecord ExpectedFunctionEntry;
- internal_memcpy(&ExpectedFunctionEntry, RewindingRecordPtr,
- FunctionRecSize);
- DCHECK(ExpectedFunctionEntry.RecordKind ==
- uint8_t(FunctionRecord::RecordKinds::FunctionEnter) &&
- "Expected to find function entry when rewinding tail call.");
- DCHECK(ExpectedFunctionEntry.FuncId == ExpectedTailExit.FuncId &&
- "Expected funcids to match when rewinding tail call.");
-
- // This tail call exceeded the threshold duration. It will not be erased.
- if ((TSC - RewindingTSC) >= atomic_load_relaxed(&ThresholdTicks)) {
- TLD.NumTailCalls = 0;
- return;
- }
-
- // We can erase a tail exit pair that we're exiting through since
- // its duration is under threshold.
- --TLD.NumTailCalls;
- RewindingRecordPtr -= FunctionRecSize;
- RewindingTSC -= ExpectedFunctionEntry.TSCDelta;
- TLD.RecordPtr -= 2 * FunctionRecSize;
- LastTSC = RewindingTSC;
- decrementExtents(2 * FunctionRecSize);
- }
-}
-
-static bool releaseThreadLocalBuffer(BufferQueue &BQArg) {
- auto &TLD = getThreadLocalData();
- auto EC = BQArg.releaseBuffer(TLD.Buffer);
- if (EC != BufferQueue::ErrorCode::Ok) {
- Report("Failed to release buffer at %p; error=%s\n", TLD.Buffer.Data,
- BufferQueue::getErrorString(EC));
- return false;
- }
- return true;
-}
-
-static bool prepareBuffer(uint64_t TSC, unsigned char CPU,
- int (*wall_clock_reader)(clockid_t,
- struct timespec *),
- size_t MaxSize) XRAY_NEVER_INSTRUMENT {
- auto &TLD = getThreadLocalData();
- char *BufferStart = static_cast<char *>(TLD.Buffer.Data);
- if ((TLD.RecordPtr + MaxSize) > (BufferStart + TLD.Buffer.Size)) {
- if (!releaseThreadLocalBuffer(*TLD.BQ))
- return false;
- auto EC = TLD.BQ->getBuffer(TLD.Buffer);
- if (EC != BufferQueue::ErrorCode::Ok) {
- Report("Failed to prepare a buffer; error = '%s'\n",
- BufferQueue::getErrorString(EC));
- return false;
- }
- setupNewBuffer(wall_clock_reader);
-
- // Always write the CPU metadata as the first record in the buffer.
- writeNewCPUIdMetadata(CPU, TSC);
- }
- return true;
-}
-
-static bool
-isLogInitializedAndReady(BufferQueue *LBQ, uint64_t TSC, unsigned char CPU,
- int (*wall_clock_reader)(clockid_t, struct timespec *))
- XRAY_NEVER_INSTRUMENT {
- // Bail out right away if logging is not initialized yet.
- // We should take the opportunity to release the buffer though.
- auto Status = atomic_load(&LoggingStatus, memory_order_acquire);
- auto &TLD = getThreadLocalData();
- if (Status != XRayLogInitStatus::XRAY_LOG_INITIALIZED) {
- if (TLD.RecordPtr != nullptr &&
- (Status == XRayLogInitStatus::XRAY_LOG_FINALIZING ||
- Status == XRayLogInitStatus::XRAY_LOG_FINALIZED)) {
- if (!releaseThreadLocalBuffer(*LBQ))
- return false;
- TLD.RecordPtr = nullptr;
- return false;
- }
- return false;
- }
-
- if (atomic_load(&LoggingStatus, memory_order_acquire) !=
- XRayLogInitStatus::XRAY_LOG_INITIALIZED ||
- LBQ->finalizing()) {
- if (!releaseThreadLocalBuffer(*LBQ))
- return false;
- TLD.RecordPtr = nullptr;
- }
-
- if (TLD.Buffer.Data == nullptr) {
- auto EC = LBQ->getBuffer(TLD.Buffer);
- if (EC != BufferQueue::ErrorCode::Ok) {
- auto LS = atomic_load(&LoggingStatus, memory_order_acquire);
- if (LS != XRayLogInitStatus::XRAY_LOG_FINALIZING &&
- LS != XRayLogInitStatus::XRAY_LOG_FINALIZED)
- Report("Failed to acquire a buffer; error = '%s'\n",
- BufferQueue::getErrorString(EC));
- return false;
- }
-
- setupNewBuffer(wall_clock_reader);
-
- // Always write the CPU metadata as the first record in the buffer.
- writeNewCPUIdMetadata(CPU, TSC);
- }
-
- if (TLD.CurrentCPU == std::numeric_limits<uint16_t>::max()) {
- // This means this is the first CPU this thread has ever run on. We set
- // the current CPU and record this as the first TSC we've seen.
- TLD.CurrentCPU = CPU;
- writeNewCPUIdMetadata(CPU, TSC);
- }
-
- return true;
-}
-
-// Compute the TSC difference between the time of measurement and the previous
-// event. There are a few interesting situations we need to account for:
-//
-// - The thread has migrated to a different CPU. If this is the case, then
-// we write down the following records:
-//
-// 1. A 'NewCPUId' Metadata record.
-// 2. A FunctionRecord with a 0 for the TSCDelta field.
-//
-// - The TSC delta is greater than the 32 bits we can store in a
-// FunctionRecord. In this case we write down the following records:
-//
-// 1. A 'TSCWrap' Metadata record.
-// 2. A FunctionRecord with a 0 for the TSCDelta field.
-//
-// - The TSC delta is representable within the 32 bits we can store in a
-// FunctionRecord. In this case we write down just a FunctionRecord with
-// the correct TSC delta.
-static uint32_t writeCurrentCPUTSC(ThreadLocalData &TLD, uint64_t TSC,
- uint8_t CPU) {
- if (CPU != TLD.CurrentCPU) {
- // We've moved to a new CPU.
- 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.
- uint64_t Delta = TSC - TLD.LastTSC;
- if (Delta <= std::numeric_limits<uint32_t>::max())
- return Delta;
-
- writeTSCWrapMetadata(TSC);
- return 0;
-}
-
-static void endBufferIfFull() XRAY_NEVER_INSTRUMENT {
- auto &TLD = getThreadLocalData();
- auto BufferStart = static_cast<char *>(TLD.Buffer.Data);
- if ((TLD.RecordPtr + MetadataRecSize) - BufferStart <=
- ptrdiff_t{MetadataRecSize}) {
- if (!releaseThreadLocalBuffer(*TLD.BQ))
- return;
- TLD.RecordPtr = nullptr;
- }
-}
-
-thread_local atomic_uint8_t Running{0};
-
-/// Here's where the meat of the processing happens. The writer captures
-/// function entry, exit and tail exit points with a time and will create
-/// TSCWrap, NewCPUId and Function records as necessary. The writer might
-/// walk backward through its buffer and erase trivial functions to avoid
-/// polluting the log and may use the buffer queue to obtain or release a
-/// buffer.
-static void processFunctionHook(int32_t FuncId, XRayEntryType Entry,
- uint64_t TSC, unsigned char CPU, uint64_t Arg1,
- int (*wall_clock_reader)(clockid_t,
- struct timespec *))
- XRAY_NEVER_INSTRUMENT {
- __asm volatile("# LLVM-MCA-BEGIN processFunctionHook");
- // Prevent signal handler recursion, so in case we're already in a log writing
- // mode and the signal handler comes in (and is also instrumented) then we
- // don't want to be clobbering potentially partial writes already happening in
- // the thread. We use a simple thread_local latch to only allow one on-going
- // handleArg0 to happen at any given time.
- RecursionGuard Guard{Running};
- if (!Guard) {
- DCHECK(atomic_load_relaxed(&Running) && "RecursionGuard is buggy!");
- return;
- }
-
- auto &TLD = getThreadLocalData();
-
- if (TLD.BQ == nullptr)
- TLD.BQ = BQ;
-
- if (!isLogInitializedAndReady(TLD.BQ, TSC, CPU, wall_clock_reader))
- return;
-
- // Before we go setting up writing new function entries, we need to be really
- // careful about the pointer math we're doing. This means we need to ensure
- // that the record we are about to write is going to fit into the buffer,
- // without overflowing the buffer.
- //
- // To do this properly, we use the following assumptions:
- //
- // - The least number of bytes we will ever write is 8
- // (sizeof(FunctionRecord)) only if the delta between the previous entry
- // and this entry is within 32 bits.
- // - The most number of bytes we will ever write is 8 + 16 + 16 = 40.
- // This is computed by:
- //
- // MaxSize = sizeof(FunctionRecord) + 2 * sizeof(MetadataRecord)
- //
- // These arise in the following cases:
- //
- // 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.
- // 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.
- //
- // 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
- // space to write 40 bytes in the buffer, we need get a new Buffer, set it up
- // properly before doing any further writing.
- size_t MaxSize = FunctionRecSize + 2 * MetadataRecSize;
- if (!prepareBuffer(TSC, CPU, wall_clock_reader, MaxSize)) {
- TLD.BQ = nullptr;
- 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::TAIL:
- case XRayEntryType::EXIT:
- // Break out and write the exit record if we can't erase any functions.
- if (TLD.NumConsecutiveFnEnters == 0 ||
- (TSC - TLD.LastFunctionEntryTSC) >=
- atomic_load_relaxed(&ThresholdTicks))
- break;
- rewindRecentCall(TSC, TLD.LastTSC, TLD.LastFunctionEntryTSC, FuncId);
- return; // without writing log.
- case XRayEntryType::CUSTOM_EVENT: {
- // This is a bug in patching, so we'll report it once and move on.
- static atomic_uint8_t ErrorLatch{0};
- if (!atomic_exchange(&ErrorLatch, 1, memory_order_acq_rel))
- Report("Internal error: patched an XRay custom event call as a function; "
- "func id = %d\n",
- FuncId);
- return;
- }
- case XRayEntryType::TYPED_EVENT: {
- static atomic_uint8_t ErrorLatch{0};
- if (!atomic_exchange(&ErrorLatch, 1, memory_order_acq_rel))
- Report("Internal error: patched an XRay typed event call as a function; "
- "func id = %d\n",
- FuncId);
- return;
- }
- }
-
- writeFunctionRecord(FuncId, RecordTSCDelta, Entry);
- if (Entry == XRayEntryType::LOG_ARGS_ENTRY)
- writeCallArgumentMetadata(Arg1);
-
- // If we've exhausted the buffer by this time, we then release the buffer to
- // make sure that other threads may start using this buffer.
- endBufferIfFull();
- __asm volatile("# LLVM-MCA-END");
-}
-
static XRayFileHeader &fdrCommonHeaderInfo() {
static std::aligned_storage<sizeof(XRayFileHeader)>::type HStorage;
static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
static bool TSCSupported = true;
static uint64_t CycleFrequency = NanosecondsPerSecond;
- pthread_once(&OnceInit, +[] {
- XRayFileHeader &H = reinterpret_cast<XRayFileHeader &>(HStorage);
- // Version 2 of the log writes the extents of the buffer, instead of
- // relying on an end-of-buffer record.
- // Version 3 includes PID metadata record
- H.Version = 3;
- H.Type = FileTypes::FDR_LOG;
-
- // Test for required CPU features and cache the cycle frequency
- TSCSupported = probeRequiredCPUFeatures();
- if (TSCSupported)
- CycleFrequency = getTSCFrequency();
- H.CycleFrequency = CycleFrequency;
-
- // FIXME: Actually check whether we have 'constant_tsc' and
- // 'nonstop_tsc' before setting the values in the header.
- H.ConstantTSC = 1;
- H.NonstopTSC = 1;
- });
+ pthread_once(
+ &OnceInit, +[] {
+ XRayFileHeader &H = reinterpret_cast<XRayFileHeader &>(HStorage);
+ // Version 2 of the log writes the extents of the buffer, instead of
+ // relying on an end-of-buffer record.
+ // Version 3 includes PID metadata record.
+ // Version 4 includes CPU data in the custom event records.
+ // Version 5 uses relative deltas for custom and typed event records,
+ // and removes the CPU data in custom event records (similar to how
+ // function records use deltas instead of full TSCs and rely on other
+ // metadata records for TSC wraparound and CPU migration).
+ H.Version = 5;
+ H.Type = FileTypes::FDR_LOG;
+
+ // Test for required CPU features and cache the cycle frequency
+ TSCSupported = probeRequiredCPUFeatures();
+ if (TSCSupported)
+ CycleFrequency = getTSCFrequency();
+ H.CycleFrequency = CycleFrequency;
+
+ // FIXME: Actually check whether we have 'constant_tsc' and
+ // 'nonstop_tsc' before setting the values in the header.
+ H.ConstantTSC = 1;
+ H.NonstopTSC = 1;
+ });
return reinterpret_cast<XRayFileHeader &>(HStorage);
}
@@ -728,9 +207,11 @@ XRayBuffer fdrIterator(const XRayBuffer B) {
// buffers to expect).
static std::aligned_storage<sizeof(XRayFileHeader)>::type HeaderStorage;
static pthread_once_t HeaderOnce = PTHREAD_ONCE_INIT;
- pthread_once(&HeaderOnce, +[] {
- reinterpret_cast<XRayFileHeader &>(HeaderStorage) = fdrCommonHeaderInfo();
- });
+ pthread_once(
+ &HeaderOnce, +[] {
+ reinterpret_cast<XRayFileHeader &>(HeaderStorage) =
+ fdrCommonHeaderInfo();
+ });
// We use a convenience alias for code referring to Header from here on out.
auto &Header = reinterpret_cast<XRayFileHeader &>(HeaderStorage);
@@ -741,7 +222,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 +233,7 @@ XRayBuffer fdrIterator(const XRayBuffer B) {
}
if (CurrentBuffer != nullptr) {
- InternalFree(CurrentBuffer);
+ deallocateBuffer(CurrentBuffer, SerializedBufferSize);
CurrentBuffer = nullptr;
}
@@ -762,9 +244,16 @@ 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);
+ //
+ // We need this atomic fence here to ensure that writes happening to the
+ // buffer have been committed before we load the extents atomically. Because
+ // the buffer is not explicitly synchronised across threads, we rely on the
+ // fence ordering to ensure that writes we expect to have been completed
+ // before the fence are fully committed before we read the extents.
+ atomic_thread_fence(memory_order_acquire);
+ auto BufferSize = atomic_load(It->Extents, memory_order_acquire);
+ SerializedBufferSize = BufferSize + sizeof(MetadataRecord);
+ CurrentBuffer = allocateBuffer(SerializedBufferSize);
if (CurrentBuffer == nullptr)
return {nullptr, 0};
@@ -827,14 +316,9 @@ XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
});
auto CleanupBuffers = at_scope_exit([] {
- if (BQ != nullptr) {
- auto &TLD = getThreadLocalData();
- if (TLD.RecordPtr != nullptr && TLD.BQ != nullptr)
- releaseThreadLocalBuffer(*TLD.BQ);
- BQ->~BufferQueue();
- InternalFree(BQ);
- BQ = nullptr;
- }
+ auto &TLD = getThreadLocalData();
+ if (TLD.Controller != nullptr)
+ TLD.Controller->flush();
});
if (fdrFlags()->no_file_flush) {
@@ -855,16 +339,8 @@ 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();
- if (Fd == -1) {
+ LogWriter *LW = LogWriter::Open();
+ if (LW == nullptr) {
auto Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
atomic_store(&LogFlushStatus, Result, memory_order_release);
return Result;
@@ -872,8 +348,15 @@ XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
XRayFileHeader Header = fdrCommonHeaderInfo();
Header.FdrData = FdrAdditionalHeaderData{BQ->ConfiguredBufferSize()};
- retryingWriteAll(Fd, reinterpret_cast<char *>(&Header),
- reinterpret_cast<char *>(&Header) + sizeof(Header));
+ LW->WriteAll(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.Controller != nullptr)
+ TLD.Controller->flush();
BQ->apply([&](const BufferQueue::Buffer &B) {
// Starting at version 2 of the FDR logging implementation, we only write
@@ -882,18 +365,18 @@ 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 =
uint8_t(MetadataRecord::RecordKinds::BufferExtents);
internal_memcpy(ExtentsRecord.Data, &BufferExtents, sizeof(BufferExtents));
if (BufferExtents > 0) {
- retryingWriteAll(Fd, reinterpret_cast<char *>(&ExtentsRecord),
- reinterpret_cast<char *>(&ExtentsRecord) +
- sizeof(MetadataRecord));
- retryingWriteAll(Fd, reinterpret_cast<char *>(B.Data),
- reinterpret_cast<char *>(B.Data) + BufferExtents);
+ LW->WriteAll(reinterpret_cast<char *>(&ExtentsRecord),
+ reinterpret_cast<char *>(&ExtentsRecord) +
+ sizeof(MetadataRecord));
+ LW->WriteAll(reinterpret_cast<char *>(B.Data),
+ reinterpret_cast<char *>(B.Data) + BufferExtents);
}
});
@@ -914,7 +397,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);
@@ -935,7 +423,8 @@ static TSCAndCPU getTimestamp() XRAY_NEVER_INSTRUMENT {
// Test once for required CPU features
static pthread_once_t OnceProbe = PTHREAD_ONCE_INIT;
static bool TSCSupported = true;
- pthread_once(&OnceProbe, +[] { TSCSupported = probeRequiredCPUFeatures(); });
+ pthread_once(
+ &OnceProbe, +[] { TSCSupported = probeRequiredCPUFeatures(); });
if (TSCSupported) {
Result.TSC = __xray::readTSC(Result.CPU);
@@ -953,16 +442,115 @@ static TSCAndCPU getTimestamp() XRAY_NEVER_INSTRUMENT {
return Result;
}
+thread_local atomic_uint8_t Running{0};
+
+static bool setupTLD(ThreadLocalData &TLD) XRAY_NEVER_INSTRUMENT {
+ // Check if we're finalizing, before proceeding.
+ {
+ auto Status = atomic_load(&LoggingStatus, memory_order_acquire);
+ if (Status == XRayLogInitStatus::XRAY_LOG_FINALIZING ||
+ Status == XRayLogInitStatus::XRAY_LOG_FINALIZED) {
+ if (TLD.Controller != nullptr) {
+ TLD.Controller->flush();
+ TLD.Controller = nullptr;
+ }
+ return false;
+ }
+ }
+
+ if (UNLIKELY(TLD.Controller == nullptr)) {
+ // Set up the TLD buffer queue.
+ if (UNLIKELY(BQ == nullptr))
+ return false;
+ TLD.BQ = BQ;
+
+ // Check that we have a valid buffer.
+ if (TLD.Buffer.Generation != BQ->generation() &&
+ TLD.BQ->releaseBuffer(TLD.Buffer) != BufferQueue::ErrorCode::Ok)
+ return false;
+
+ // Set up a buffer, before setting up the log writer. Bail out on failure.
+ if (TLD.BQ->getBuffer(TLD.Buffer) != BufferQueue::ErrorCode::Ok)
+ return false;
+
+ // Set up the Log Writer for this thread.
+ if (UNLIKELY(TLD.Writer == nullptr)) {
+ auto *LWStorage = reinterpret_cast<FDRLogWriter *>(&TLD.LWStorage);
+ new (LWStorage) FDRLogWriter(TLD.Buffer);
+ TLD.Writer = LWStorage;
+ } else {
+ TLD.Writer->resetRecord();
+ }
+
+ auto *CStorage = reinterpret_cast<FDRController<> *>(&TLD.CStorage);
+ new (CStorage)
+ FDRController<>(TLD.BQ, TLD.Buffer, *TLD.Writer, clock_gettime,
+ atomic_load_relaxed(&ThresholdTicks));
+ TLD.Controller = CStorage;
+ }
+
+ DCHECK_NE(TLD.Controller, nullptr);
+ return true;
+}
+
void fdrLoggingHandleArg0(int32_t FuncId,
XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
auto TC = getTimestamp();
- processFunctionHook(FuncId, Entry, TC.TSC, TC.CPU, 0, clock_gettime);
+ auto &TSC = TC.TSC;
+ auto &CPU = TC.CPU;
+ RecursionGuard Guard{Running};
+ if (!Guard)
+ return;
+
+ auto &TLD = getThreadLocalData();
+ if (!setupTLD(TLD))
+ return;
+
+ switch (Entry) {
+ case XRayEntryType::ENTRY:
+ case XRayEntryType::LOG_ARGS_ENTRY:
+ TLD.Controller->functionEnter(FuncId, TSC, CPU);
+ return;
+ case XRayEntryType::EXIT:
+ TLD.Controller->functionExit(FuncId, TSC, CPU);
+ return;
+ case XRayEntryType::TAIL:
+ TLD.Controller->functionTailExit(FuncId, TSC, CPU);
+ return;
+ case XRayEntryType::CUSTOM_EVENT:
+ case XRayEntryType::TYPED_EVENT:
+ break;
+ }
}
void fdrLoggingHandleArg1(int32_t FuncId, XRayEntryType Entry,
uint64_t Arg) XRAY_NEVER_INSTRUMENT {
auto TC = getTimestamp();
- processFunctionHook(FuncId, Entry, TC.TSC, TC.CPU, Arg, clock_gettime);
+ auto &TSC = TC.TSC;
+ auto &CPU = TC.CPU;
+ RecursionGuard Guard{Running};
+ if (!Guard)
+ return;
+
+ auto &TLD = getThreadLocalData();
+ if (!setupTLD(TLD))
+ return;
+
+ switch (Entry) {
+ case XRayEntryType::ENTRY:
+ case XRayEntryType::LOG_ARGS_ENTRY:
+ TLD.Controller->functionEnterArg(FuncId, TSC, CPU, Arg);
+ return;
+ case XRayEntryType::EXIT:
+ TLD.Controller->functionExit(FuncId, TSC, CPU);
+ return;
+ case XRayEntryType::TAIL:
+ TLD.Controller->functionTailExit(FuncId, TSC, CPU);
+ return;
+ case XRayEntryType::CUSTOM_EVENT:
+ case XRayEntryType::TYPED_EVENT:
+ break;
+ }
}
void fdrLoggingHandleCustomEvent(void *Event,
@@ -973,40 +561,25 @@ void fdrLoggingHandleCustomEvent(void *Event,
RecursionGuard Guard{Running};
if (!Guard)
return;
- if (EventSize > std::numeric_limits<int32_t>::max()) {
+
+ // Complain when we ever get at least one custom event that's larger than what
+ // we can possibly support.
+ if (EventSize >
+ static_cast<std::size_t>(std::numeric_limits<int32_t>::max())) {
static pthread_once_t Once = PTHREAD_ONCE_INIT;
- pthread_once(&Once, +[] { Report("Event size too large.\n"); });
+ pthread_once(
+ &Once, +[] {
+ Report("Custom event size too large; truncating to %d.\n",
+ std::numeric_limits<int32_t>::max());
+ });
}
- int32_t ReducedEventSize = static_cast<int32_t>(EventSize);
- auto &TLD = getThreadLocalData();
- if (!isLogInitializedAndReady(TLD.BQ, TSC, CPU, clock_gettime))
- return;
- // Here we need to prepare the log to handle:
- // - 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)) {
- TLD.BQ = nullptr;
+ auto &TLD = getThreadLocalData();
+ if (!setupTLD(TLD))
return;
- }
- // Write the custom event metadata record, which consists of the following
- // information:
- // - 8 bytes (64-bits) for the full TSC when the event started.
- // - 4 bytes (32-bits) for the length of the data.
- MetadataRecord CustomEvent;
- CustomEvent.Type = uint8_t(RecordType::Metadata);
- CustomEvent.RecordKind =
- 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(TLD.RecordPtr, &CustomEvent, sizeof(CustomEvent));
- TLD.RecordPtr += sizeof(CustomEvent);
- internal_memcpy(TLD.RecordPtr, Event, ReducedEventSize);
- incrementExtents(MetadataRecSize + EventSize);
- endBufferIfFull();
+ int32_t ReducedEventSize = static_cast<int32_t>(EventSize);
+ TLD.Controller->customEvent(TSC, CPU, Event, ReducedEventSize);
}
void fdrLoggingHandleTypedEvent(
@@ -1018,50 +591,28 @@ void fdrLoggingHandleTypedEvent(
RecursionGuard Guard{Running};
if (!Guard)
return;
- if (EventSize > std::numeric_limits<int32_t>::max()) {
+
+ // Complain when we ever get at least one typed event that's larger than what
+ // we can possibly support.
+ if (EventSize >
+ static_cast<std::size_t>(std::numeric_limits<int32_t>::max())) {
static pthread_once_t Once = PTHREAD_ONCE_INIT;
- pthread_once(&Once, +[] { Report("Event size too large.\n"); });
+ pthread_once(
+ &Once, +[] {
+ Report("Typed event size too large; truncating to %d.\n",
+ std::numeric_limits<int32_t>::max());
+ });
}
- int32_t ReducedEventSize = static_cast<int32_t>(EventSize);
+
auto &TLD = getThreadLocalData();
- if (!isLogInitializedAndReady(TLD.BQ, TSC, CPU, clock_gettime))
+ if (!setupTLD(TLD))
return;
- // Here we need to prepare the log to handle:
- // - 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)) {
- TLD.BQ = nullptr;
- return;
- }
- // Write the custom event metadata record, which consists of the following
- // information:
- // - 8 bytes (64-bits) for the full TSC when the event started.
- // - 4 bytes (32-bits) for the length of the data.
- // - 2 bytes (16-bits) for the event type. 3 bytes remain since one of the
- // bytes has the record type (Metadata Record) and kind (TypedEvent).
- // We'll log the error if the event type is greater than 2 bytes.
- // Event types are generated sequentially, so 2^16 is enough.
- MetadataRecord TypedEvent;
- TypedEvent.Type = uint8_t(RecordType::Metadata);
- TypedEvent.RecordKind =
- uint8_t(MetadataRecord::RecordKinds::TypedEventMarker);
- constexpr auto TSCSize = sizeof(TC.TSC);
- internal_memcpy(&TypedEvent.Data, &ReducedEventSize, sizeof(int32_t));
- internal_memcpy(&TypedEvent.Data[sizeof(int32_t)], &TSC, TSCSize);
- internal_memcpy(&TypedEvent.Data[sizeof(int32_t) + TSCSize], &EventType,
- sizeof(EventType));
- internal_memcpy(TLD.RecordPtr, &TypedEvent, sizeof(TypedEvent));
-
- TLD.RecordPtr += sizeof(TypedEvent);
- internal_memcpy(TLD.RecordPtr, Event, ReducedEventSize);
- incrementExtents(MetadataRecSize + EventSize);
- endBufferIfFull();
+ int32_t ReducedEventSize = static_cast<int32_t>(EventSize);
+ TLD.Controller->typedEvent(TSC, CPU, EventType, Event, ReducedEventSize);
}
-XRayLogInitStatus fdrLoggingInit(size_t BufferSize, size_t BufferMax,
- void *Options,
+XRayLogInitStatus fdrLoggingInit(size_t, size_t, void *Options,
size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
if (Options == nullptr)
return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
@@ -1075,107 +626,81 @@ 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);
- }
-
- bool Success = false;
-
- if (BQ != nullptr) {
- BQ->~BufferQueue();
- InternalFree(BQ);
- BQ = nullptr;
- }
+ 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;
+ auto BufferSize = FDRFlags.buffer_size;
+ auto BufferMax = FDRFlags.buffer_max;
if (BQ == nullptr) {
- BQ = reinterpret_cast<BufferQueue *>(
- InternalAlloc(sizeof(BufferQueue), nullptr, 64));
+ bool Success = false;
+ BQ = reinterpret_cast<BufferQueue *>(&BufferQueueStorage);
new (BQ) BufferQueue(BufferSize, BufferMax, Success);
- }
-
- if (!Success) {
- Report("BufferQueue init failed.\n");
- if (BQ != nullptr) {
- BQ->~BufferQueue();
- InternalFree(BQ);
- BQ = nullptr;
+ if (!Success) {
+ Report("BufferQueue init failed.\n");
+ return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
+ }
+ } else {
+ if (BQ->init(BufferSize, BufferMax) != BufferQueue::ErrorCode::Ok) {
+ if (Verbosity())
+ Report("Failed to re-initialize global buffer queue. Init failed.\n");
+ return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
}
- return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
}
static pthread_once_t OnceInit = PTHREAD_ONCE_INIT;
- pthread_once(&OnceInit, +[] {
- atomic_store(&TicksPerSec,
- probeRequiredCPUFeatures() ? getTSCFrequency()
- : __xray::NanosecondsPerSecond,
- memory_order_release);
- pthread_key_create(&Key, +[](void *TLDPtr) {
- if (TLDPtr == nullptr)
- return;
- auto &TLD = *reinterpret_cast<ThreadLocalData *>(TLDPtr);
- if (TLD.BQ == 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",
- TLD.Buffer.Data, BufferQueue::getErrorString(EC));
- });
- });
+ pthread_once(
+ &OnceInit, +[] {
+ atomic_store(&TicksPerSec,
+ probeRequiredCPUFeatures() ? getTSCFrequency()
+ : __xray::NanosecondsPerSecond,
+ memory_order_release);
+ pthread_key_create(
+ &Key, +[](void *TLDPtr) {
+ if (TLDPtr == nullptr)
+ return;
+ 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",
+ TLD.Buffer.Data, BufferQueue::getErrorString(EC));
+ });
+ });
atomic_store(&ThresholdTicks,
atomic_load_relaxed(&TicksPerSec) *
@@ -1209,11 +734,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 2acf14aa5625..d01ad20e3d71 100644
--- a/lib/xray/xray_function_call_trie.h
+++ b/lib/xray/xray_function_call_trie.h
@@ -15,9 +15,11 @@
#ifndef XRAY_FUNCTION_CALL_TRIE_H
#define XRAY_FUNCTION_CALL_TRIE_H
-#include "sanitizer_common/sanitizer_allocator_internal.h"
+#include "xray_buffer_queue.h"
+#include "xray_defs.h"
#include "xray_profiling_flags.h"
#include "xray_segmented_array.h"
+#include <limits>
#include <memory> // For placement new.
#include <utility>
@@ -97,9 +99,6 @@ public:
struct NodeIdPair {
Node *NodePtr;
int32_t FId;
-
- // Constructor for inplace-construction.
- NodeIdPair(Node *N, int32_t F) : NodePtr(N), FId(F) {}
};
using NodeIdPairArray = Array<NodeIdPair>;
@@ -113,17 +112,10 @@ public:
struct Node {
Node *Parent;
NodeIdPairArray Callees;
- int64_t CallCount;
- int64_t CumulativeLocalTime; // Typically in TSC deltas, not wall-time.
+ uint64_t CallCount;
+ uint64_t CumulativeLocalTime; // Typically in TSC deltas, not wall-time.
int32_t FId;
- // 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) {}
-
// TODO: Include the compact histogram.
};
@@ -131,10 +123,7 @@ private:
struct ShadowStackEntry {
uint64_t EntryTSC;
Node *NodePtr;
-
- // 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} {}
+ uint16_t EntryCPU;
};
using NodeArray = Array<Node>;
@@ -149,103 +138,184 @@ public:
using RootAllocatorType = RootArray::AllocatorType;
using ShadowStackAllocatorType = ShadowStackArray::AllocatorType;
+ // Use hosted aligned storage members to allow for trivial move and init.
+ // This also allows us to sidestep the potential-failing allocation issue.
+ typename std::aligned_storage<sizeof(NodeAllocatorType),
+ alignof(NodeAllocatorType)>::type
+ NodeAllocatorStorage;
+ typename std::aligned_storage<sizeof(RootAllocatorType),
+ alignof(RootAllocatorType)>::type
+ RootAllocatorStorage;
+ typename std::aligned_storage<sizeof(ShadowStackAllocatorType),
+ alignof(ShadowStackAllocatorType)>::type
+ ShadowStackAllocatorStorage;
+ typename std::aligned_storage<sizeof(NodeIdPairAllocatorType),
+ alignof(NodeIdPairAllocatorType)>::type
+ NodeIdPairAllocatorStorage;
+
NodeAllocatorType *NodeAllocator = nullptr;
RootAllocatorType *RootAllocator = nullptr;
ShadowStackAllocatorType *ShadowStackAllocator = nullptr;
NodeIdPairAllocatorType *NodeIdPairAllocator = nullptr;
- Allocators() {}
+ Allocators() = default;
Allocators(const Allocators &) = delete;
Allocators &operator=(const Allocators &) = delete;
- Allocators(Allocators &&O)
- : NodeAllocator(O.NodeAllocator), RootAllocator(O.RootAllocator),
- ShadowStackAllocator(O.ShadowStackAllocator),
- NodeIdPairAllocator(O.NodeIdPairAllocator) {
+ struct Buffers {
+ BufferQueue::Buffer NodeBuffer;
+ BufferQueue::Buffer RootsBuffer;
+ BufferQueue::Buffer ShadowStackBuffer;
+ BufferQueue::Buffer NodeIdPairBuffer;
+ };
+
+ explicit Allocators(Buffers &B) XRAY_NEVER_INSTRUMENT {
+ new (&NodeAllocatorStorage)
+ NodeAllocatorType(B.NodeBuffer.Data, B.NodeBuffer.Size);
+ NodeAllocator =
+ reinterpret_cast<NodeAllocatorType *>(&NodeAllocatorStorage);
+
+ new (&RootAllocatorStorage)
+ RootAllocatorType(B.RootsBuffer.Data, B.RootsBuffer.Size);
+ RootAllocator =
+ reinterpret_cast<RootAllocatorType *>(&RootAllocatorStorage);
+
+ new (&ShadowStackAllocatorStorage) ShadowStackAllocatorType(
+ B.ShadowStackBuffer.Data, B.ShadowStackBuffer.Size);
+ ShadowStackAllocator = reinterpret_cast<ShadowStackAllocatorType *>(
+ &ShadowStackAllocatorStorage);
+
+ new (&NodeIdPairAllocatorStorage) NodeIdPairAllocatorType(
+ B.NodeIdPairBuffer.Data, B.NodeIdPairBuffer.Size);
+ NodeIdPairAllocator = reinterpret_cast<NodeIdPairAllocatorType *>(
+ &NodeIdPairAllocatorStorage);
+ }
+
+ explicit Allocators(uptr Max) XRAY_NEVER_INSTRUMENT {
+ new (&NodeAllocatorStorage) NodeAllocatorType(Max);
+ NodeAllocator =
+ reinterpret_cast<NodeAllocatorType *>(&NodeAllocatorStorage);
+
+ new (&RootAllocatorStorage) RootAllocatorType(Max);
+ RootAllocator =
+ reinterpret_cast<RootAllocatorType *>(&RootAllocatorStorage);
+
+ new (&ShadowStackAllocatorStorage) ShadowStackAllocatorType(Max);
+ ShadowStackAllocator = reinterpret_cast<ShadowStackAllocatorType *>(
+ &ShadowStackAllocatorStorage);
+
+ new (&NodeIdPairAllocatorStorage) NodeIdPairAllocatorType(Max);
+ NodeIdPairAllocator = reinterpret_cast<NodeIdPairAllocatorType *>(
+ &NodeIdPairAllocatorStorage);
+ }
+
+ Allocators(Allocators &&O) XRAY_NEVER_INSTRUMENT {
+ // Here we rely on the safety of memcpy'ing contents of the storage
+ // members, and then pointing the source pointers to nullptr.
+ internal_memcpy(&NodeAllocatorStorage, &O.NodeAllocatorStorage,
+ sizeof(NodeAllocatorType));
+ internal_memcpy(&RootAllocatorStorage, &O.RootAllocatorStorage,
+ sizeof(RootAllocatorType));
+ internal_memcpy(&ShadowStackAllocatorStorage,
+ &O.ShadowStackAllocatorStorage,
+ sizeof(ShadowStackAllocatorType));
+ internal_memcpy(&NodeIdPairAllocatorStorage,
+ &O.NodeIdPairAllocatorStorage,
+ sizeof(NodeIdPairAllocatorType));
+
+ NodeAllocator =
+ reinterpret_cast<NodeAllocatorType *>(&NodeAllocatorStorage);
+ RootAllocator =
+ reinterpret_cast<RootAllocatorType *>(&RootAllocatorStorage);
+ ShadowStackAllocator = reinterpret_cast<ShadowStackAllocatorType *>(
+ &ShadowStackAllocatorStorage);
+ NodeIdPairAllocator = reinterpret_cast<NodeIdPairAllocatorType *>(
+ &NodeIdPairAllocatorStorage);
+
O.NodeAllocator = nullptr;
O.RootAllocator = nullptr;
O.ShadowStackAllocator = nullptr;
O.NodeIdPairAllocator = nullptr;
}
- Allocators &operator=(Allocators &&O) {
- {
- auto Tmp = O.NodeAllocator;
- O.NodeAllocator = this->NodeAllocator;
- this->NodeAllocator = Tmp;
- }
- {
- auto Tmp = O.RootAllocator;
- O.RootAllocator = this->RootAllocator;
- this->RootAllocator = Tmp;
- }
- {
- auto Tmp = O.ShadowStackAllocator;
- O.ShadowStackAllocator = this->ShadowStackAllocator;
- this->ShadowStackAllocator = Tmp;
- }
- {
- auto Tmp = O.NodeIdPairAllocator;
- O.NodeIdPairAllocator = this->NodeIdPairAllocator;
- this->NodeIdPairAllocator = Tmp;
- }
- return *this;
- }
-
- ~Allocators() {
- // 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) {
+ Allocators &operator=(Allocators &&O) XRAY_NEVER_INSTRUMENT {
+ // When moving into an existing instance, we ensure that we clean up the
+ // current allocators.
+ if (NodeAllocator)
NodeAllocator->~NodeAllocatorType();
- InternalFree(NodeAllocator);
+ if (O.NodeAllocator) {
+ new (&NodeAllocatorStorage)
+ NodeAllocatorType(std::move(*O.NodeAllocator));
+ NodeAllocator =
+ reinterpret_cast<NodeAllocatorType *>(&NodeAllocatorStorage);
+ O.NodeAllocator = nullptr;
+ } else {
NodeAllocator = nullptr;
}
- if (RootAllocator != nullptr) {
+
+ if (RootAllocator)
RootAllocator->~RootAllocatorType();
- InternalFree(RootAllocator);
+ if (O.RootAllocator) {
+ new (&RootAllocatorStorage)
+ RootAllocatorType(std::move(*O.RootAllocator));
+ RootAllocator =
+ reinterpret_cast<RootAllocatorType *>(&RootAllocatorStorage);
+ O.RootAllocator = nullptr;
+ } else {
RootAllocator = nullptr;
}
- if (ShadowStackAllocator != nullptr) {
+
+ if (ShadowStackAllocator)
ShadowStackAllocator->~ShadowStackAllocatorType();
- InternalFree(ShadowStackAllocator);
+ if (O.ShadowStackAllocator) {
+ new (&ShadowStackAllocatorStorage)
+ ShadowStackAllocatorType(std::move(*O.ShadowStackAllocator));
+ ShadowStackAllocator = reinterpret_cast<ShadowStackAllocatorType *>(
+ &ShadowStackAllocatorStorage);
+ O.ShadowStackAllocator = nullptr;
+ } else {
ShadowStackAllocator = nullptr;
}
- if (NodeIdPairAllocator != nullptr) {
+
+ if (NodeIdPairAllocator)
NodeIdPairAllocator->~NodeIdPairAllocatorType();
- InternalFree(NodeIdPairAllocator);
+ if (O.NodeIdPairAllocator) {
+ new (&NodeIdPairAllocatorStorage)
+ NodeIdPairAllocatorType(std::move(*O.NodeIdPairAllocator));
+ NodeIdPairAllocator = reinterpret_cast<NodeIdPairAllocatorType *>(
+ &NodeIdPairAllocatorStorage);
+ O.NodeIdPairAllocator = nullptr;
+ } else {
NodeIdPairAllocator = nullptr;
}
+
+ return *this;
+ }
+
+ ~Allocators() XRAY_NEVER_INSTRUMENT {
+ if (NodeAllocator != nullptr)
+ NodeAllocator->~NodeAllocatorType();
+ if (RootAllocator != nullptr)
+ RootAllocator->~RootAllocatorType();
+ if (ShadowStackAllocator != nullptr)
+ ShadowStackAllocator->~ShadowStackAllocatorType();
+ if (NodeIdPairAllocator != nullptr)
+ NodeIdPairAllocator->~NodeIdPairAllocatorType();
}
};
- // 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) {
- Allocators A;
- auto NodeAllocator = reinterpret_cast<Allocators::NodeAllocatorType *>(
- InternalAlloc(sizeof(Allocators::NodeAllocatorType)));
- new (NodeAllocator) Allocators::NodeAllocatorType(Max);
- A.NodeAllocator = NodeAllocator;
-
- auto RootAllocator = reinterpret_cast<Allocators::RootAllocatorType *>(
- InternalAlloc(sizeof(Allocators::RootAllocatorType)));
- new (RootAllocator) Allocators::RootAllocatorType(Max);
- A.RootAllocator = RootAllocator;
-
- auto ShadowStackAllocator =
- reinterpret_cast<Allocators::ShadowStackAllocatorType *>(
- InternalAlloc(sizeof(Allocators::ShadowStackAllocatorType)));
- new (ShadowStackAllocator) Allocators::ShadowStackAllocatorType(Max);
- A.ShadowStackAllocator = ShadowStackAllocator;
-
- auto NodeIdPairAllocator = reinterpret_cast<NodeIdPairAllocatorType *>(
- InternalAlloc(sizeof(NodeIdPairAllocatorType)));
- new (NodeIdPairAllocator) NodeIdPairAllocatorType(Max);
- A.NodeIdPairAllocator = NodeIdPairAllocator;
+ static Allocators InitAllocatorsCustom(uptr Max) XRAY_NEVER_INSTRUMENT {
+ Allocators A(Max);
+ return A;
+ }
+
+ static Allocators
+ InitAllocatorsFromBuffers(Allocators::Buffers &Bufs) XRAY_NEVER_INSTRUMENT {
+ Allocators A(Bufs);
return A;
}
@@ -253,65 +323,135 @@ private:
NodeArray Nodes;
RootArray Roots;
ShadowStackArray ShadowStack;
- NodeIdPairAllocatorType *NodeIdPairAllocator = nullptr;
+ NodeIdPairAllocatorType *NodeIdPairAllocator;
+ uint32_t OverflowedFunctions;
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) {}
+ NodeIdPairAllocator(A.NodeIdPairAllocator),
+ OverflowedFunctions(0) {}
+
+ FunctionCallTrie() = delete;
+ FunctionCallTrie(const FunctionCallTrie &) = delete;
+ FunctionCallTrie &operator=(const FunctionCallTrie &) = delete;
+
+ FunctionCallTrie(FunctionCallTrie &&O) XRAY_NEVER_INSTRUMENT
+ : Nodes(std::move(O.Nodes)),
+ Roots(std::move(O.Roots)),
+ ShadowStack(std::move(O.ShadowStack)),
+ NodeIdPairAllocator(O.NodeIdPairAllocator),
+ OverflowedFunctions(O.OverflowedFunctions) {}
+
+ FunctionCallTrie &operator=(FunctionCallTrie &&O) XRAY_NEVER_INSTRUMENT {
+ Nodes = std::move(O.Nodes);
+ Roots = std::move(O.Roots);
+ ShadowStack = std::move(O.ShadowStack);
+ NodeIdPairAllocator = O.NodeIdPairAllocator;
+ OverflowedFunctions = O.OverflowedFunctions;
+ return *this;
+ }
+
+ ~FunctionCallTrie() XRAY_NEVER_INSTRUMENT {}
- void enterFunction(const int32_t FId, uint64_t TSC) {
+ void enterFunction(const int32_t FId, uint64_t TSC,
+ uint16_t CPU) 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.
+
+ // If we're already overflowed the function call stack, do not bother
+ // attempting to record any more function entries.
+ if (UNLIKELY(OverflowedFunctions)) {
+ ++OverflowedFunctions;
+ return;
+ }
+
+ // If this is the first function we've encountered, we want to set up the
+ // node(s) and treat it as a root.
if (UNLIKELY(ShadowStack.empty())) {
- auto NewRoot =
- Nodes.AppendEmplace(nullptr, *NodeIdPairAllocator, 0, 0, FId);
+ auto *NewRoot = Nodes.AppendEmplace(
+ nullptr, NodeIdPairArray(*NodeIdPairAllocator), 0u, 0u, FId);
if (UNLIKELY(NewRoot == nullptr))
return;
- Roots.Append(NewRoot);
- ShadowStack.AppendEmplace(TSC, NewRoot);
+ if (Roots.AppendEmplace(NewRoot) == nullptr) {
+ Nodes.trim(1);
+ return;
+ }
+ if (ShadowStack.AppendEmplace(TSC, NewRoot, CPU) == nullptr) {
+ Nodes.trim(1);
+ Roots.trim(1);
+ ++OverflowedFunctions;
+ return;
+ }
return;
}
- auto &Top = ShadowStack.back();
- auto TopNode = Top.NodePtr;
+ // From this point on, we require that the stack is not empty.
+ DCHECK(!ShadowStack.empty());
+ auto TopNode = ShadowStack.back().NodePtr;
DCHECK_NE(TopNode, nullptr);
- // If we've seen this callee before, then we just access that node and place
- // that on the top of the stack.
- auto Callee = TopNode->Callees.find_element(
+ // If we've seen this callee before, then we access that node and place that
+ // on the top of the stack.
+ auto* Callee = TopNode->Callees.find_element(
[FId](const NodeIdPair &NR) { return NR.FId == FId; });
if (Callee != nullptr) {
CHECK_NE(Callee->NodePtr, nullptr);
- ShadowStack.AppendEmplace(TSC, Callee->NodePtr);
+ if (ShadowStack.AppendEmplace(TSC, Callee->NodePtr, CPU) == nullptr)
+ ++OverflowedFunctions;
return;
}
// This means we've never seen this stack before, create a new node here.
- auto NewNode =
- Nodes.AppendEmplace(TopNode, *NodeIdPairAllocator, 0, 0, FId);
+ auto* NewNode = Nodes.AppendEmplace(
+ TopNode, NodeIdPairArray(*NodeIdPairAllocator), 0u, 0u, FId);
if (UNLIKELY(NewNode == nullptr))
return;
DCHECK_NE(NewNode, nullptr);
TopNode->Callees.AppendEmplace(NewNode, FId);
- ShadowStack.AppendEmplace(TSC, NewNode);
- DCHECK_NE(ShadowStack.back().NodePtr, nullptr);
+ if (ShadowStack.AppendEmplace(TSC, NewNode, CPU) == nullptr)
+ ++OverflowedFunctions;
return;
}
- void exitFunction(int32_t FId, uint64_t TSC) {
+ void exitFunction(int32_t FId, uint64_t TSC,
+ uint16_t CPU) XRAY_NEVER_INSTRUMENT {
+ // If we're exiting functions that have "overflowed" or don't fit into the
+ // stack due to allocator constraints, we then decrement that count first.
+ if (OverflowedFunctions) {
+ --OverflowedFunctions;
+ return;
+ }
+
// 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
// entry.
uint64_t CumulativeTreeTime = 0;
+
while (!ShadowStack.empty()) {
const auto &Top = ShadowStack.back();
auto TopNode = Top.NodePtr;
DCHECK_NE(TopNode, nullptr);
- auto LocalTime = TSC - Top.EntryTSC;
+
+ // We may encounter overflow on the TSC we're provided, which may end up
+ // being less than the TSC when we first entered the function.
+ //
+ // To get the accurate measurement of cycles, we need to check whether
+ // we've overflowed (TSC < Top.EntryTSC) and then account the difference
+ // between the entry TSC and the max for the TSC counter (max of uint64_t)
+ // then add the value of TSC. We can prove that the maximum delta we will
+ // get is at most the 64-bit unsigned value, since the difference between
+ // a TSC of 0 and a Top.EntryTSC of 1 is (numeric_limits<uint64_t>::max()
+ // - 1) + 1.
+ //
+ // NOTE: This assumes that TSCs are synchronised across CPUs.
+ // TODO: Count the number of times we've seen CPU migrations.
+ uint64_t LocalTime =
+ Top.EntryTSC > TSC
+ ? (std::numeric_limits<uint64_t>::max() - Top.EntryTSC) + TSC
+ : TSC - Top.EntryTSC;
TopNode->CallCount++;
TopNode->CumulativeLocalTime += LocalTime - CumulativeTreeTime;
CumulativeTreeTime += LocalTime;
@@ -323,7 +463,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 +478,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
@@ -356,18 +496,20 @@ public:
for (const auto Root : getRoots()) {
// Add a node in O for this root.
auto NewRoot = O.Nodes.AppendEmplace(
- nullptr, *O.NodeIdPairAllocator, Root->CallCount,
+ nullptr, NodeIdPairArray(*O.NodeIdPairAllocator), Root->CallCount,
Root->CumulativeLocalTime, Root->FId);
// Because we cannot allocate more memory we should bail out right away.
if (UNLIKELY(NewRoot == nullptr))
return;
- O.Roots.Append(NewRoot);
+ if (UNLIKELY(O.Roots.Append(NewRoot) == nullptr))
+ return;
// TODO: Figure out what to do if we fail to allocate any more stack
// space. Maybe warn or report once?
- DFSStack.AppendEmplace(Root, NewRoot);
+ if (DFSStack.AppendEmplace(Root, NewRoot) == nullptr)
+ return;
while (!DFSStack.empty()) {
NodeAndParent NP = DFSStack.back();
DCHECK_NE(NP.Node, nullptr);
@@ -375,12 +517,17 @@ public:
DFSStack.trim(1);
for (const auto Callee : NP.Node->Callees) {
auto NewNode = O.Nodes.AppendEmplace(
- NP.NewNode, *O.NodeIdPairAllocator, Callee.NodePtr->CallCount,
- Callee.NodePtr->CumulativeLocalTime, Callee.FId);
+ NP.NewNode, NodeIdPairArray(*O.NodeIdPairAllocator),
+ Callee.NodePtr->CallCount, Callee.NodePtr->CumulativeLocalTime,
+ Callee.FId);
if (UNLIKELY(NewNode == nullptr))
return;
- NP.NewNode->Callees.AppendEmplace(NewNode, Callee.FId);
- DFSStack.AppendEmplace(Callee.NodePtr, NewNode);
+ if (UNLIKELY(NP.NewNode->Callees.AppendEmplace(NewNode, Callee.FId) ==
+ nullptr))
+ return;
+ if (UNLIKELY(DFSStack.AppendEmplace(Callee.NodePtr, NewNode) ==
+ nullptr))
+ return;
}
}
}
@@ -394,7 +541,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;
@@ -409,8 +556,9 @@ public:
auto R = O.Roots.find_element(
[&](const Node *Node) { return Node->FId == Root->FId; });
if (R == nullptr) {
- TargetRoot = O.Nodes.AppendEmplace(nullptr, *O.NodeIdPairAllocator, 0,
- 0, Root->FId);
+ TargetRoot = O.Nodes.AppendEmplace(
+ nullptr, NodeIdPairArray(*O.NodeIdPairAllocator), 0u, 0u,
+ Root->FId);
if (UNLIKELY(TargetRoot == nullptr))
return;
@@ -419,7 +567,7 @@ public:
TargetRoot = *R;
}
- DFSStack.Append(NodeAndTarget{Root, TargetRoot});
+ DFSStack.AppendEmplace(Root, TargetRoot);
while (!DFSStack.empty()) {
NodeAndTarget NT = DFSStack.back();
DCHECK_NE(NT.OrigNode, nullptr);
@@ -435,7 +583,8 @@ public:
});
if (TargetCallee == nullptr) {
auto NewTargetNode = O.Nodes.AppendEmplace(
- NT.TargetNode, *O.NodeIdPairAllocator, 0, 0, Callee.FId);
+ NT.TargetNode, NodeIdPairArray(*O.NodeIdPairAllocator), 0u, 0u,
+ Callee.FId);
if (UNLIKELY(NewTargetNode == nullptr))
return;
diff --git a/lib/xray/xray_init.cc b/lib/xray/xray_init.cc
index b4e069795195..b0922aa8e379 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;
@@ -58,6 +67,9 @@ void __xray_init() XRAY_NEVER_INSTRUMENT {
if (atomic_load(&XRayInitialized, memory_order_acquire))
return;
+ // XRAY is not compatible with PaX MPROTECT
+ CheckMPROTECT();
+
if (!atomic_load(&XRayFlagsInitialized, memory_order_acquire)) {
initializeFlags();
atomic_store(&XRayFlagsInitialized, true, memory_order_release);
@@ -97,8 +109,8 @@ __attribute__((section(".preinit_array"),
#else
// If we cannot use the .preinit_array section, we should instead use dynamic
// initialisation.
-static bool UNUSED __local_xray_dyninit = [] {
+__attribute__ ((constructor (0)))
+static void __local_xray_dyninit() {
__xray_init();
- return true;
-}();
+}
#endif
diff --git a/lib/xray/xray_interface.cc b/lib/xray/xray_interface.cc
index 01bf6ddc607e..6f7b6615b2c0 100644
--- a/lib/xray/xray_interface.cc
+++ b/lib/xray/xray_interface.cc
@@ -22,6 +22,13 @@
#include <string.h>
#include <sys/mman.h>
+#if SANITIZER_FUCHSIA
+#include <zircon/process.h>
+#include <zircon/sanitizer.h>
+#include <zircon/status.h>
+#include <zircon/syscalls.h>
+#endif
+
#include "sanitizer_common/sanitizer_addrhashmap.h"
#include "sanitizer_common/sanitizer_common.h"
@@ -92,22 +99,48 @@ class MProtectHelper {
public:
explicit MProtectHelper(void *PageAlignedAddr,
- std::size_t MProtectLen) XRAY_NEVER_INSTRUMENT
+ std::size_t MProtectLen,
+ std::size_t PageSize) XRAY_NEVER_INSTRUMENT
: PageAlignedAddr(PageAlignedAddr),
MProtectLen(MProtectLen),
- MustCleanup(false) {}
+ MustCleanup(false) {
+#if SANITIZER_FUCHSIA
+ MProtectLen = RoundUpTo(MProtectLen, PageSize);
+#endif
+ }
int MakeWriteable() XRAY_NEVER_INSTRUMENT {
+#if SANITIZER_FUCHSIA
+ auto R = __sanitizer_change_code_protection(
+ reinterpret_cast<uintptr_t>(PageAlignedAddr), MProtectLen, true);
+ if (R != ZX_OK) {
+ Report("XRay: cannot change code protection: %s\n",
+ _zx_status_get_string(R));
+ return -1;
+ }
+ MustCleanup = true;
+ return 0;
+#else
auto R = mprotect(PageAlignedAddr, MProtectLen,
PROT_READ | PROT_WRITE | PROT_EXEC);
if (R != -1)
MustCleanup = true;
return R;
+#endif
}
~MProtectHelper() XRAY_NEVER_INSTRUMENT {
if (MustCleanup) {
+#if SANITIZER_FUCHSIA
+ auto R = __sanitizer_change_code_protection(
+ reinterpret_cast<uintptr_t>(PageAlignedAddr), MProtectLen, false);
+ if (R != ZX_OK) {
+ Report("XRay: cannot change code protection: %s\n",
+ _zx_status_get_string(R));
+ }
+#else
mprotect(PageAlignedAddr, MProtectLen, PROT_READ | PROT_EXEC);
+#endif
}
}
};
@@ -254,7 +287,7 @@ XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT {
reinterpret_cast<void *>(MinSled.Address & ~(PageSize - 1));
size_t MProtectLen =
(MaxSled.Address - reinterpret_cast<uptr>(PageAlignedAddr)) + cSledLength;
- MProtectHelper Protector(PageAlignedAddr, MProtectLen);
+ MProtectHelper Protector(PageAlignedAddr, MProtectLen, PageSize);
if (Protector.MakeWriteable() == -1) {
Report("Failed mprotect: %d\n", errno);
return XRayPatchingStatus::FAILED;
@@ -319,7 +352,7 @@ XRayPatchingStatus mprotectAndPatchFunction(int32_t FuncId,
reinterpret_cast<void *>(MinSled.Address & ~(PageSize - 1));
size_t MProtectLen =
(MaxSled.Address - reinterpret_cast<uptr>(PageAlignedAddr)) + cSledLength;
- MProtectHelper Protector(PageAlignedAddr, MProtectLen);
+ MProtectHelper Protector(PageAlignedAddr, MProtectLen, PageSize);
if (Protector.MakeWriteable() == -1) {
Report("Failed mprotect: %d\n", errno);
return XRayPatchingStatus::FAILED;
diff --git a/lib/xray/xray_profile_collector.cc b/lib/xray/xray_profile_collector.cc
index 17a611eeacb8..dc3a82069840 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,91 @@ 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;
+struct ThreadData {
+ BufferQueue *BQ;
+ FunctionCallTrie::Allocators::Buffers Buffers;
+ FunctionCallTrie::Allocators Allocators;
+ FunctionCallTrie FCT;
+ tid_t TId;
+};
+
+using ThreadDataArray = Array<ThreadData>;
+using ThreadDataAllocator = ThreadDataArray::AllocatorType;
+
+// We use a separate buffer queue for the backing store for the allocator used
+// by the ThreadData array. This lets us host the buffers, allocators, and tries
+// associated with a thread by moving the data into the array instead of
+// attempting to copy the data to a separately backed set of tries.
+static typename std::aligned_storage<
+ sizeof(BufferQueue), alignof(BufferQueue)>::type BufferQueueStorage;
+static BufferQueue *BQ = nullptr;
+static BufferQueue::Buffer Buffer;
+static typename std::aligned_storage<sizeof(ThreadDataAllocator),
+ alignof(ThreadDataAllocator)>::type
+ ThreadDataAllocatorStorage;
+static typename std::aligned_storage<sizeof(ThreadDataArray),
+ alignof(ThreadDataArray)>::type
+ ThreadDataArrayStorage;
+
+static ThreadDataAllocator *TDAllocator = nullptr;
+static ThreadDataArray *TDArray = nullptr;
+
+using ProfileBufferArray = Array<ProfileBuffer>;
+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(ProfileBufferArray)>::type
+ ProfileBuffersStorage;
+static typename std::aligned_storage<sizeof(ProfileBufferArrayAllocator)>::type
+ ProfileBufferArrayAllocatorStorage;
+
+static ProfileBufferArrayAllocator *ProfileBuffersAllocator = nullptr;
+static ProfileBufferArray *ProfileBuffers = nullptr;
+
+// Use a global flag to determine whether the collector implementation has been
+// initialized.
+static atomic_uint8_t CollectorInitialized{0};
} // namespace
-void post(const FunctionCallTrie &T, tid_t TId) {
- 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);
-
- ThreadTrie *Item = nullptr;
+void post(BufferQueue *Q, FunctionCallTrie &&T,
+ FunctionCallTrie::Allocators &&A,
+ FunctionCallTrie::Allocators::Buffers &&B,
+ tid_t TId) XRAY_NEVER_INSTRUMENT {
+ DCHECK_NE(Q, nullptr);
+
+ // Bail out early if the collector has not been initialized.
+ if (!atomic_load(&CollectorInitialized, memory_order_acquire)) {
+ T.~FunctionCallTrie();
+ A.~Allocators();
+ Q->releaseBuffer(B.NodeBuffer);
+ Q->releaseBuffer(B.RootsBuffer);
+ Q->releaseBuffer(B.ShadowStackBuffer);
+ Q->releaseBuffer(B.NodeIdPairBuffer);
+ B.~Buffers();
+ return;
+ }
+
{
SpinMutexLock Lock(&GlobalMutex);
- if (GlobalAllocators == nullptr)
- return;
-
- Item = ThreadTries->PushBack();
- 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);
+ DCHECK_NE(TDAllocator, nullptr);
+ DCHECK_NE(TDArray, nullptr);
+
+ if (TDArray->AppendEmplace(Q, std::move(B), std::move(A), std::move(T),
+ TId) == nullptr) {
+ // If we fail to add the data to the array, we should destroy the objects
+ // handed us.
+ T.~FunctionCallTrie();
+ A.~Allocators();
+ Q->releaseBuffer(B.NodeBuffer);
+ Q->releaseBuffer(B.RootsBuffer);
+ Q->releaseBuffer(B.ShadowStackBuffer);
+ Q->releaseBuffer(B.NodeIdPairBuffer);
+ B.~Buffers();
+ }
}
-
- T.deepCopyInto(*Item->Trie);
}
// A PathArray represents the function id's representing a stack trace. In this
@@ -127,18 +154,8 @@ 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;
- 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;
- }()),
- Node(N) {}
+ PathArray Path;
+ const FunctionCallTrie::Node *Node;
};
namespace {
@@ -147,19 +164,21 @@ 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);
StackArray DFSStack(StackAlloc);
- for (const auto R : Trie.getRoots()) {
+ for (const auto *R : Trie.getRoots()) {
DFSStack.Append(R);
while (!DFSStack.empty()) {
- auto Node = DFSStack.back();
+ auto *Node = DFSStack.back();
DFSStack.trim(1);
- auto Record = PRs.AppendEmplace(PA, Node);
+ if (Node == nullptr)
+ continue;
+ auto Record = PRs.AppendEmplace(PathArray{PA}, Node);
if (Record == nullptr)
return;
DCHECK_NE(Record, nullptr);
@@ -167,8 +186,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,67 +196,89 @@ 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 {
+ if (!atomic_load(&CollectorInitialized, memory_order_acquire))
+ return;
+
SpinMutexLock Lock(&GlobalMutex);
- // Clear out the global ProfileBuffers.
- for (uptr I = 0; I < ProfileBuffers->Size(); ++I)
- InternalFree((*ProfileBuffers)[I].Data);
- ProfileBuffers->Reset();
+ // Clear out the global ProfileBuffers, if it's not empty.
+ for (auto &B : *ProfileBuffers)
+ deallocateBuffer(reinterpret_cast<unsigned char *>(B.Data), B.Size);
+ ProfileBuffers->trim(ProfileBuffers->size());
- if (ThreadTries->Size() == 0)
+ DCHECK_NE(TDArray, nullptr);
+ if (TDArray->empty())
return;
// Then repopulate the global ProfileBuffers.
- for (u32 I = 0; I < ThreadTries->Size(); ++I) {
+ u32 I = 0;
+ auto MaxSize = profilingFlags()->global_allocator_max;
+ auto ProfileArena = allocateBuffer(MaxSize);
+ if (ProfileArena == nullptr)
+ return;
+
+ auto ProfileArenaCleanup = at_scope_exit(
+ [&]() XRAY_NEVER_INSTRUMENT { deallocateBuffer(ProfileArena, MaxSize); });
+
+ auto PathArena = allocateBuffer(profilingFlags()->global_allocator_max);
+ if (PathArena == nullptr)
+ return;
+
+ auto PathArenaCleanup = at_scope_exit(
+ [&]() XRAY_NEVER_INSTRUMENT { deallocateBuffer(PathArena, MaxSize); });
+
+ for (const auto &ThreadTrie : *TDArray) {
using ProfileRecordAllocator = typename ProfileRecordArray::AllocatorType;
- ProfileRecordAllocator PRAlloc(profilingFlags()->global_allocator_max);
+ ProfileRecordAllocator PRAlloc(ProfileArena,
+ profilingFlags()->global_allocator_max);
ProfileRecord::PathAllocator PathAlloc(
- profilingFlags()->global_allocator_max);
+ PathArena, profilingFlags()->global_allocator_max);
ProfileRecordArray ProfileRecords(PRAlloc);
// First, we want to compute the amount of space we're going to need. We'll
// 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;
- if (Trie.getRoots().empty())
+ if (ThreadTrie.FCT.getRoots().empty())
continue;
- populateRecords(ProfileRecords, PathAlloc, Trie);
- DCHECK(!Trie.getRoots().empty());
+
+ populateRecords(ProfileRecords, PathAlloc, ThreadTrie.FCT);
+ DCHECK(!ThreadTrie.FCT.getRoots().empty());
DCHECK(!ProfileRecords.empty());
// Go through each record, to compute the sizes.
@@ -251,75 +292,103 @@ void serialize() {
// + end of record (8 bytes)
u32 CumulativeSizes = 0;
for (const auto &Record : ProfileRecords)
- CumulativeSizes += 20 + (4 * Record.Path->size());
-
- BlockHeader Header{16 + CumulativeSizes, I, (*ThreadTries)[I].TId};
- auto Buffer = ProfileBuffers->PushBack();
- Buffer->Size = sizeof(Header) + CumulativeSizes;
- Buffer->Data = InternalAlloc(Buffer->Size, nullptr, 64);
- 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);
- }
+ CumulativeSizes += 20 + (4 * Record.Path.size());
+
+ BlockHeader Header{16 + CumulativeSizes, I++, ThreadTrie.TId};
+ auto B = ProfileBuffers->Append({});
+ B->Size = sizeof(Header) + CumulativeSizes;
+ B->Data = allocateBuffer(B->Size);
+ DCHECK_NE(B->Data, nullptr);
+ serializeRecords(B, Header, ProfileRecords);
}
}
-void reset() {
+void reset() XRAY_NEVER_INSTRUMENT {
+ atomic_store(&CollectorInitialized, 0, memory_order_release);
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);
+ for (auto &B : *ProfileBuffers)
+ deallocateBuffer(reinterpret_cast<uint8_t *>(B.Data), B.Size);
+ ProfileBuffers->trim(ProfileBuffers->size());
ProfileBuffers = nullptr;
}
- 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);
+ if (TDArray != nullptr) {
+ // Release the resources as required.
+ for (auto &TD : *TDArray) {
+ TD.BQ->releaseBuffer(TD.Buffers.NodeBuffer);
+ TD.BQ->releaseBuffer(TD.Buffers.RootsBuffer);
+ TD.BQ->releaseBuffer(TD.Buffers.ShadowStackBuffer);
+ TD.BQ->releaseBuffer(TD.Buffers.NodeIdPairBuffer);
}
- ThreadTries->Reset();
- InternalFree(ThreadTries);
- ThreadTries = nullptr;
+ // We don't bother destroying the array here because we've already
+ // potentially freed the backing store for the array. Instead we're going to
+ // reset the pointer to nullptr, and re-use the storage later instead
+ // (placement-new'ing into the storage as-is).
+ TDArray = nullptr;
+ }
+
+ if (TDAllocator != nullptr) {
+ TDAllocator->~Allocator();
+ TDAllocator = nullptr;
+ }
+
+ if (Buffer.Data != nullptr) {
+ BQ->releaseBuffer(Buffer);
}
- // Reset the global allocators.
- if (GlobalAllocators != nullptr) {
- GlobalAllocators->~Allocators();
- InternalFree(GlobalAllocators);
- GlobalAllocators = nullptr;
+ if (BQ == nullptr) {
+ bool Success = false;
+ new (&BufferQueueStorage)
+ BufferQueue(profilingFlags()->global_allocator_max, 1, Success);
+ if (!Success)
+ return;
+ BQ = reinterpret_cast<BufferQueue *>(&BufferQueueStorage);
+ } else {
+ BQ->finalize();
+
+ if (BQ->init(profilingFlags()->global_allocator_max, 1) !=
+ BufferQueue::ErrorCode::Ok)
+ return;
}
- GlobalAllocators = reinterpret_cast<FunctionCallTrie::Allocators *>(
- InternalAlloc(sizeof(FunctionCallTrie::Allocators)));
- 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 (BQ->getBuffer(Buffer) != BufferQueue::ErrorCode::Ok)
+ return;
+
+ new (&ProfileBufferArrayAllocatorStorage)
+ ProfileBufferArrayAllocator(profilingFlags()->global_allocator_max);
+ ProfileBuffersAllocator = reinterpret_cast<ProfileBufferArrayAllocator *>(
+ &ProfileBufferArrayAllocatorStorage);
+
+ new (&ProfileBuffersStorage) ProfileBufferArray(*ProfileBuffersAllocator);
+ ProfileBuffers =
+ reinterpret_cast<ProfileBufferArray *>(&ProfileBuffersStorage);
+
+ new (&ThreadDataAllocatorStorage)
+ ThreadDataAllocator(Buffer.Data, Buffer.Size);
+ TDAllocator =
+ reinterpret_cast<ThreadDataAllocator *>(&ThreadDataAllocatorStorage);
+ new (&ThreadDataArrayStorage) ThreadDataArray(*TDAllocator);
+ TDArray = reinterpret_cast<ThreadDataArray *>(&ThreadDataArrayStorage);
+
+ atomic_store(&CollectorInitialized, 1, memory_order_release);
}
-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;
static typename std::aligned_storage<sizeof(XRayProfilingFileHeader)>::type
FileHeaderStorage;
- pthread_once(&Once,
- +[] { new (&FileHeaderStorage) XRayProfilingFileHeader{}; });
+ pthread_once(
+ &Once, +[]() XRAY_NEVER_INSTRUMENT {
+ new (&FileHeaderStorage) XRayProfilingFileHeader{};
+ });
if (UNLIKELY(B.Data == nullptr)) {
// The first buffer should always contain the file header information.
@@ -336,7 +405,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_profile_collector.h b/lib/xray/xray_profile_collector.h
index 335043db9526..86c4ce853797 100644
--- a/lib/xray/xray_profile_collector.h
+++ b/lib/xray/xray_profile_collector.h
@@ -33,27 +33,13 @@ namespace profileCollectorService {
/// Posts the FunctionCallTrie associated with a specific Thread ID. This
/// will:
///
-/// - Make a copy of the FunctionCallTrie and store that against the Thread
-/// ID. This will use the global allocator for the service-managed
-/// FunctionCallTrie instances.
-/// - Queue up a pointer to the FunctionCallTrie.
-/// - If the queue is long enough (longer than some arbitrary threshold) we
-/// then pre-calculate a single FunctionCallTrie for the whole process.
+/// Moves the collection of FunctionCallTrie, Allocators, and Buffers associated
+/// with a thread's data to the queue. This takes ownership of the memory
+/// associated with a thread, and manages those exclusively.
///
-///
-/// We are making a copy of the FunctionCallTrie because the intent is to have
-/// this function be called at thread exit, or soon after the profiling
-/// handler is finalized through the XRay APIs. By letting threads each
-/// process their own thread-local FunctionCallTrie instances, we're removing
-/// the need for synchronisation across threads while we're profiling.
-/// However, once we're done profiling, we can then collect copies of these
-/// FunctionCallTrie instances and pay the cost of the copy.
-///
-/// NOTE: In the future, if this turns out to be more costly than "moving" the
-/// FunctionCallTrie instances from the owning thread to the collector
-/// service, then we can change the implementation to do it this way (moving)
-/// instead.
-void post(const FunctionCallTrie &T, tid_t TId);
+void post(BufferQueue *Q, FunctionCallTrie &&T,
+ FunctionCallTrie::Allocators &&A,
+ FunctionCallTrie::Allocators::Buffers &&B, tid_t TId);
/// The serialize will process all FunctionCallTrie instances in memory, and
/// turn those into specifically formatted blocks, each describing the
diff --git a/lib/xray/xray_profiling.cc b/lib/xray/xray_profiling.cc
index d4b4345d764a..4323170cd1bb 100644
--- a/lib/xray/xray_profiling.cc
+++ b/lib/xray/xray_profiling.cc
@@ -19,7 +19,7 @@
#include "sanitizer_common/sanitizer_flags.h"
#include "xray/xray_interface.h"
#include "xray/xray_log_interface.h"
-
+#include "xray_buffer_queue.h"
#include "xray_flags.h"
#include "xray_profile_collector.h"
#include "xray_profiling_flags.h"
@@ -32,62 +32,167 @@ namespace __xray {
namespace {
-atomic_sint32_t ProfilerLogFlushStatus = {
+static atomic_sint32_t ProfilerLogFlushStatus = {
XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
-atomic_sint32_t ProfilerLogStatus = {XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
+static atomic_sint32_t ProfilerLogStatus = {
+ XRayLogInitStatus::XRAY_LOG_UNINITIALIZED};
-SpinMutex ProfilerOptionsMutex;
+static SpinMutex ProfilerOptionsMutex;
-struct alignas(64) ProfilingData {
- FunctionCallTrie::Allocators *Allocators = nullptr;
- FunctionCallTrie *FCT = nullptr;
+struct ProfilingData {
+ atomic_uintptr_t Allocators;
+ atomic_uintptr_t FCT;
};
static pthread_key_t ProfilingKey;
-thread_local std::aligned_storage<sizeof(ProfilingData)>::type ThreadStorage{};
-static ProfilingData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
- thread_local auto ThreadOnce = [] {
- new (&ThreadStorage) ProfilingData{};
- pthread_setspecific(ProfilingKey, &ThreadStorage);
+// We use a global buffer queue, which gets initialized once at initialisation
+// time, and gets reset when profiling is "done".
+static std::aligned_storage<sizeof(BufferQueue), alignof(BufferQueue)>::type
+ BufferQueueStorage;
+static BufferQueue *BQ = nullptr;
+
+thread_local FunctionCallTrie::Allocators::Buffers ThreadBuffers;
+thread_local std::aligned_storage<sizeof(FunctionCallTrie::Allocators),
+ alignof(FunctionCallTrie::Allocators)>::type
+ AllocatorsStorage;
+thread_local std::aligned_storage<sizeof(FunctionCallTrie),
+ alignof(FunctionCallTrie)>::type
+ FunctionCallTrieStorage;
+thread_local ProfilingData TLD{{0}, {0}};
+thread_local atomic_uint8_t ReentranceGuard{0};
+
+// We use a separate guard for ensuring that for this thread, if we're already
+// cleaning up, that any signal handlers don't attempt to cleanup nor
+// initialise.
+thread_local atomic_uint8_t TLDInitGuard{0};
+
+// We also use a separate latch to signal that the thread is exiting, and
+// non-essential work should be ignored (things like recording events, etc.).
+thread_local atomic_uint8_t ThreadExitingLatch{0};
+
+static ProfilingData *getThreadLocalData() XRAY_NEVER_INSTRUMENT {
+ thread_local auto ThreadOnce = []() XRAY_NEVER_INSTRUMENT {
+ pthread_setspecific(ProfilingKey, &TLD);
return false;
}();
(void)ThreadOnce;
- auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
+ RecursionGuard TLDInit(TLDInitGuard);
+ if (!TLDInit)
+ return nullptr;
- // 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 (atomic_load_relaxed(&ThreadExitingLatch))
+ return nullptr;
+
+ uptr Allocators = 0;
+ if (atomic_compare_exchange_strong(&TLD.Allocators, &Allocators, 1,
+ memory_order_acq_rel)) {
+ bool Success = false;
+ auto AllocatorsUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT {
+ if (!Success)
+ atomic_store(&TLD.Allocators, 0, memory_order_release);
+ });
+
+ // Acquire a set of buffers for this thread.
+ if (BQ == nullptr)
+ return nullptr;
+
+ if (BQ->getBuffer(ThreadBuffers.NodeBuffer) != BufferQueue::ErrorCode::Ok)
+ return nullptr;
+ auto NodeBufferUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT {
+ if (!Success)
+ BQ->releaseBuffer(ThreadBuffers.NodeBuffer);
+ });
+
+ if (BQ->getBuffer(ThreadBuffers.RootsBuffer) != BufferQueue::ErrorCode::Ok)
+ return nullptr;
+ auto RootsBufferUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT {
+ if (!Success)
+ BQ->releaseBuffer(ThreadBuffers.RootsBuffer);
+ });
+
+ if (BQ->getBuffer(ThreadBuffers.ShadowStackBuffer) !=
+ BufferQueue::ErrorCode::Ok)
+ return nullptr;
+ auto ShadowStackBufferUndo = at_scope_exit([&]() XRAY_NEVER_INSTRUMENT {
+ if (!Success)
+ BQ->releaseBuffer(ThreadBuffers.ShadowStackBuffer);
+ });
+
+ if (BQ->getBuffer(ThreadBuffers.NodeIdPairBuffer) !=
+ BufferQueue::ErrorCode::Ok)
+ return nullptr;
+
+ Success = true;
+ new (&AllocatorsStorage) FunctionCallTrie::Allocators(
+ FunctionCallTrie::InitAllocatorsFromBuffers(ThreadBuffers));
+ Allocators = reinterpret_cast<uptr>(
+ reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage));
+ atomic_store(&TLD.Allocators, Allocators, memory_order_release);
+ }
+
+ if (Allocators == 1)
+ return nullptr;
+
+ uptr FCT = 0;
+ if (atomic_compare_exchange_strong(&TLD.FCT, &FCT, 1, memory_order_acq_rel)) {
+ new (&FunctionCallTrieStorage)
+ FunctionCallTrie(*reinterpret_cast<FunctionCallTrie::Allocators *>(
+ atomic_load_relaxed(&TLD.Allocators)));
+ FCT = reinterpret_cast<uptr>(
+ reinterpret_cast<FunctionCallTrie *>(&FunctionCallTrieStorage));
+ atomic_store(&TLD.FCT, FCT, memory_order_release);
}
- return TLD;
+ if (FCT == 1)
+ return nullptr;
+
+ return &TLD;
}
static void cleanupTLD() XRAY_NEVER_INSTRUMENT {
- auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
- 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;
- }
+ auto FCT = atomic_exchange(&TLD.FCT, 0, memory_order_acq_rel);
+ if (FCT == reinterpret_cast<uptr>(reinterpret_cast<FunctionCallTrie *>(
+ &FunctionCallTrieStorage)))
+ reinterpret_cast<FunctionCallTrie *>(FCT)->~FunctionCallTrie();
+
+ auto Allocators = atomic_exchange(&TLD.Allocators, 0, memory_order_acq_rel);
+ if (Allocators ==
+ reinterpret_cast<uptr>(
+ reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage)))
+ reinterpret_cast<FunctionCallTrie::Allocators *>(Allocators)->~Allocators();
+}
+
+static void postCurrentThreadFCT(ProfilingData &T) XRAY_NEVER_INSTRUMENT {
+ RecursionGuard TLDInit(TLDInitGuard);
+ if (!TLDInit)
+ return;
+
+ uptr P = atomic_exchange(&T.FCT, 0, memory_order_acq_rel);
+ if (P != reinterpret_cast<uptr>(
+ reinterpret_cast<FunctionCallTrie *>(&FunctionCallTrieStorage)))
+ return;
+
+ auto FCT = reinterpret_cast<FunctionCallTrie *>(P);
+ DCHECK_NE(FCT, nullptr);
+
+ uptr A = atomic_exchange(&T.Allocators, 0, memory_order_acq_rel);
+ if (A !=
+ reinterpret_cast<uptr>(
+ reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage)))
+ return;
+
+ auto Allocators = reinterpret_cast<FunctionCallTrie::Allocators *>(A);
+ DCHECK_NE(Allocators, nullptr);
+
+ // Always move the data into the profile collector.
+ profileCollectorService::post(BQ, std::move(*FCT), std::move(*Allocators),
+ std::move(ThreadBuffers), GetTid());
+
+ // Re-initialize the ThreadBuffers object to a known "default" state.
+ ThreadBuffers = FunctionCallTrie::Allocators::Buffers{};
}
} // namespace
@@ -100,9 +205,6 @@ const char *profilingCompilerDefinedFlags() XRAY_NEVER_INSTRUMENT {
#endif
}
-atomic_sint32_t ProfileFlushStatus = {
- XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
-
XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT {
if (atomic_load(&ProfilerLogStatus, memory_order_acquire) !=
XRayLogInitStatus::XRAY_LOG_FINALIZED) {
@@ -111,12 +213,23 @@ XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT {
return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
}
- s32 Result = XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
- if (!atomic_compare_exchange_strong(&ProfilerLogFlushStatus, &Result,
- XRayLogFlushStatus::XRAY_LOG_FLUSHING,
- memory_order_acq_rel)) {
+ RecursionGuard SignalGuard(ReentranceGuard);
+ if (!SignalGuard) {
+ if (Verbosity())
+ Report("Cannot finalize properly inside a signal handler!\n");
+ atomic_store(&ProfilerLogFlushStatus,
+ XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING,
+ memory_order_release);
+ return XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING;
+ }
+
+ s32 Previous = atomic_exchange(&ProfilerLogFlushStatus,
+ XRayLogFlushStatus::XRAY_LOG_FLUSHING,
+ memory_order_acq_rel);
+ if (Previous == XRayLogFlushStatus::XRAY_LOG_FLUSHING) {
if (Verbosity())
- Report("Not flushing profiles, implementation still finalizing.\n");
+ Report("Not flushing profiles, implementation still flushing.\n");
+ return XRayLogFlushStatus::XRAY_LOG_FLUSHING;
}
// At this point, we'll create the file that will contain the profile, but
@@ -129,49 +242,33 @@ XRayLogFlushStatus profilingFlush() XRAY_NEVER_INSTRUMENT {
if (Verbosity())
Report("profiling: No data to flush.\n");
} else {
- int Fd = getLogFD();
- if (Fd == -1) {
+ LogWriter *LW = LogWriter::Open();
+ if (LW == nullptr) {
if (Verbosity())
Report("profiling: Failed to flush to file, dropping data.\n");
} else {
// Now for each of the buffers, write out the profile data as we would
// see it in memory, verbatim.
while (B.Data != nullptr && B.Size != 0) {
- retryingWriteAll(Fd, reinterpret_cast<const char *>(B.Data),
- reinterpret_cast<const char *>(B.Data) + B.Size);
+ LW->WriteAll(reinterpret_cast<const char *>(B.Data),
+ reinterpret_cast<const char *>(B.Data) + B.Size);
B = profileCollectorService::nextBuffer(B);
}
- // Then we close out the file.
- internal_close(Fd);
}
+ LogWriter::Close(LW);
}
}
profileCollectorService::reset();
- // Flush the current thread's local data structures as well.
- cleanupTLD();
-
- atomic_store(&ProfilerLogStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,
+ atomic_store(&ProfilerLogFlushStatus, XRayLogFlushStatus::XRAY_LOG_FLUSHED,
+ memory_order_release);
+ atomic_store(&ProfilerLogStatus, XRayLogInitStatus::XRAY_LOG_UNINITIALIZED,
memory_order_release);
return XRayLogFlushStatus::XRAY_LOG_FLUSHED;
}
-namespace {
-
-thread_local atomic_uint8_t ReentranceGuard{0};
-
-static void postCurrentThreadFCT(ProfilingData &TLD) {
- if (TLD.Allocators == nullptr || TLD.FCT == nullptr)
- return;
-
- profileCollectorService::post(*TLD.FCT, GetTid());
- cleanupTLD();
-}
-
-} // namespace
-
void profilingHandleArg0(int32_t FuncId,
XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
unsigned char CPU;
@@ -181,21 +278,29 @@ void profilingHandleArg0(int32_t FuncId,
return;
auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
- auto &TLD = getThreadLocalData();
+ if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_UNINITIALIZED ||
+ Status == XRayLogInitStatus::XRAY_LOG_INITIALIZING))
+ return;
+
if (UNLIKELY(Status == XRayLogInitStatus::XRAY_LOG_FINALIZED ||
Status == XRayLogInitStatus::XRAY_LOG_FINALIZING)) {
postCurrentThreadFCT(TLD);
return;
}
+ auto T = getThreadLocalData();
+ if (T == nullptr)
+ return;
+
+ auto FCT = reinterpret_cast<FunctionCallTrie *>(atomic_load_relaxed(&T->FCT));
switch (Entry) {
case XRayEntryType::ENTRY:
case XRayEntryType::LOG_ARGS_ENTRY:
- TLD.FCT->enterFunction(FuncId, TSC);
+ FCT->enterFunction(FuncId, TSC, CPU);
break;
case XRayEntryType::EXIT:
case XRayEntryType::TAIL:
- TLD.FCT->exitFunction(FuncId, TSC);
+ FCT->exitFunction(FuncId, TSC, CPU);
break;
default:
// FIXME: Handle bugs.
@@ -218,12 +323,22 @@ XRayLogInitStatus profilingFinalize() XRAY_NEVER_INSTRUMENT {
return static_cast<XRayLogInitStatus>(CurrentStatus);
}
+ // Mark then finalize the current generation of buffers. This allows us to let
+ // the threads currently holding onto new buffers still use them, but let the
+ // last reference do the memory cleanup.
+ DCHECK_NE(BQ, nullptr);
+ BQ->finalize();
+
// Wait a grace period to allow threads to see that we're finalizing.
SleepForMillis(profilingFlags()->grace_period_ms);
- // We also want to make sure that the current thread's data is cleaned up,
- // if we have any.
- auto &TLD = getThreadLocalData();
+ // If we for some reason are entering this function from an instrumented
+ // handler, we bail out.
+ RecursionGuard G(ReentranceGuard);
+ if (!G)
+ return static_cast<XRayLogInitStatus>(CurrentStatus);
+
+ // Post the current thread's data if we have any.
postCurrentThreadFCT(TLD);
// Then we force serialize the log data.
@@ -235,19 +350,16 @@ XRayLogInitStatus profilingFinalize() XRAY_NEVER_INSTRUMENT {
}
XRayLogInitStatus
-profilingLoggingInit(size_t BufferSize, size_t BufferMax, void *Options,
+profilingLoggingInit(size_t, size_t, 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.");
+ RecursionGuard G(ReentranceGuard);
+ if (!G)
return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
- }
s32 CurrentStatus = XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
if (!atomic_compare_exchange_strong(&ProfilerLogStatus, &CurrentStatus,
XRayLogInitStatus::XRAY_LOG_INITIALIZING,
- memory_order_release)) {
+ memory_order_acq_rel)) {
if (Verbosity())
Report("Cannot initialize already initialised profiling "
"implementation.\n");
@@ -276,35 +388,88 @@ profilingLoggingInit(size_t BufferSize, size_t BufferMax, void *Options,
// We need to reset the profile data collection implementation now.
profileCollectorService::reset();
- // We need to set up the exit handlers.
- static pthread_once_t Once = PTHREAD_ONCE_INIT;
- pthread_once(&Once, +[] {
- pthread_key_create(&ProfilingKey, +[](void *P) {
- // This is the thread-exit handler.
- auto &TLD = *reinterpret_cast<ProfilingData *>(P);
- if (TLD.Allocators == nullptr && TLD.FCT == nullptr)
- return;
-
- postCurrentThreadFCT(TLD);
- });
+ // Then also reset the buffer queue implementation.
+ if (BQ == nullptr) {
+ bool Success = false;
+ new (&BufferQueueStorage)
+ BufferQueue(profilingFlags()->per_thread_allocator_max,
+ profilingFlags()->buffers_max, Success);
+ if (!Success) {
+ if (Verbosity())
+ Report("Failed to initialize preallocated memory buffers!");
+ atomic_store(&ProfilerLogStatus,
+ XRayLogInitStatus::XRAY_LOG_UNINITIALIZED,
+ memory_order_release);
+ return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
+ }
- // We also need to set up an exit handler, so that we can get the profile
- // information at exit time. We use the C API to do this, to not rely on C++
- // ABI functions for registering exit handlers.
- Atexit(+[] {
- // Finalize and flush.
- if (profilingFinalize() != XRAY_LOG_FINALIZED) {
- cleanupTLD();
- return;
- }
- if (profilingFlush() != XRAY_LOG_FLUSHED) {
- cleanupTLD();
- return;
- }
+ // If we've succeded, set the global pointer to the initialised storage.
+ BQ = reinterpret_cast<BufferQueue *>(&BufferQueueStorage);
+ } else {
+ BQ->finalize();
+ auto InitStatus = BQ->init(profilingFlags()->per_thread_allocator_max,
+ profilingFlags()->buffers_max);
+
+ if (InitStatus != BufferQueue::ErrorCode::Ok) {
if (Verbosity())
- Report("XRay Profile flushed at exit.");
- });
- });
+ Report("Failed to initialize preallocated memory buffers; error: %s",
+ BufferQueue::getErrorString(InitStatus));
+ atomic_store(&ProfilerLogStatus,
+ XRayLogInitStatus::XRAY_LOG_UNINITIALIZED,
+ memory_order_release);
+ return XRayLogInitStatus::XRAY_LOG_UNINITIALIZED;
+ }
+
+ DCHECK(!BQ->finalizing());
+ }
+
+ // We need to set up the exit handlers.
+ static pthread_once_t Once = PTHREAD_ONCE_INIT;
+ pthread_once(
+ &Once, +[] {
+ pthread_key_create(
+ &ProfilingKey, +[](void *P) XRAY_NEVER_INSTRUMENT {
+ if (atomic_exchange(&ThreadExitingLatch, 1, memory_order_acq_rel))
+ return;
+
+ if (P == nullptr)
+ return;
+
+ auto T = reinterpret_cast<ProfilingData *>(P);
+ if (atomic_load_relaxed(&T->Allocators) == 0)
+ return;
+
+ {
+ // If we're somehow executing this while inside a
+ // non-reentrant-friendly context, we skip attempting to post
+ // the current thread's data.
+ RecursionGuard G(ReentranceGuard);
+ if (!G)
+ return;
+
+ postCurrentThreadFCT(*T);
+ }
+ });
+
+ // We also need to set up an exit handler, so that we can get the
+ // profile information at exit time. We use the C API to do this, to not
+ // rely on C++ ABI functions for registering exit handlers.
+ Atexit(+[]() XRAY_NEVER_INSTRUMENT {
+ if (atomic_exchange(&ThreadExitingLatch, 1, memory_order_acq_rel))
+ return;
+
+ auto Cleanup =
+ at_scope_exit([]() XRAY_NEVER_INSTRUMENT { cleanupTLD(); });
+
+ // Finalize and flush.
+ if (profilingFinalize() != XRAY_LOG_FINALIZED ||
+ profilingFlush() != XRAY_LOG_FLUSHED)
+ return;
+
+ if (Verbosity())
+ Report("XRay Profile flushed at exit.");
+ });
+ });
__xray_log_set_buffer_iterator(profileCollectorService::nextBuffer);
__xray_set_handler(profilingHandleArg0);
diff --git a/lib/xray/xray_profiling_flags.inc b/lib/xray/xray_profiling_flags.inc
index e9230ae64187..ccd70860bf61 100644
--- a/lib/xray/xray_profiling_flags.inc
+++ b/lib/xray/xray_profiling_flags.inc
@@ -14,7 +14,7 @@
#error "Define XRAY_FLAG prior to including this file!"
#endif
-XRAY_FLAG(uptr, per_thread_allocator_max, 2 << 20,
+XRAY_FLAG(uptr, per_thread_allocator_max, 16384,
"Maximum size of any single per-thread allocator.")
XRAY_FLAG(uptr, global_allocator_max, 2 << 24,
"Maximum size of the global allocator for profile storage.")
@@ -27,3 +27,6 @@ XRAY_FLAG(int, grace_period_ms, 1,
XRAY_FLAG(bool, no_flush, false,
"Set to true if we want the profiling implementation to not write "
"out files.")
+XRAY_FLAG(int, buffers_max, 128,
+ "The number of buffers to pre-allocate used by the profiling "
+ "implementation.")
diff --git a/lib/xray/xray_segmented_array.h b/lib/xray/xray_segmented_array.h
index 11dd794fa520..bc7e9379f63b 100644
--- a/lib/xray/xray_segmented_array.h
+++ b/lib/xray/xray_segmented_array.h
@@ -32,14 +32,9 @@ namespace __xray {
/// is destroyed. When an Array is destroyed, it will destroy elements in the
/// backing store but will not free the memory.
template <class T> class Array {
- struct SegmentBase {
- SegmentBase *Prev;
- SegmentBase *Next;
- };
-
- // We want each segment of the array to be cache-line aligned, and elements of
- // the array be offset from the beginning of the segment.
- struct Segment : SegmentBase {
+ struct Segment {
+ Segment *Prev;
+ Segment *Next;
char Data[1];
};
@@ -62,98 +57,46 @@ public:
// kCacheLineSize-multiple segments, minus the size of two pointers.
//
// - Request cacheline-multiple sized elements from the allocator.
- static constexpr size_t AlignedElementStorageSize =
+ static constexpr uint64_t AlignedElementStorageSize =
sizeof(typename std::aligned_storage<sizeof(T), alignof(T)>::type);
- static constexpr size_t SegmentSize =
- nearest_boundary(sizeof(Segment) + next_pow2(sizeof(T)), kCacheLineSize);
+ static constexpr uint64_t SegmentControlBlockSize = sizeof(Segment *) * 2;
+
+ static constexpr uint64_t SegmentSize = nearest_boundary(
+ SegmentControlBlockSize + next_pow2(sizeof(T)), kCacheLineSize);
using AllocatorType = Allocator<SegmentSize>;
- static constexpr size_t ElementsPerSegment =
- (SegmentSize - sizeof(Segment)) / next_pow2(sizeof(T));
+ static constexpr uint64_t ElementsPerSegment =
+ (SegmentSize - SegmentControlBlockSize) / next_pow2(sizeof(T));
static_assert(ElementsPerSegment > 0,
"Must have at least 1 element per segment.");
- static SegmentBase SentinelSegment;
-
-private:
- AllocatorType *Alloc;
- SegmentBase *Head = &SentinelSegment;
- SegmentBase *Tail = &SentinelSegment;
- size_t Size = 0;
-
- // Here we keep track of segments in the freelist, to allow us to re-use
- // segments when elements are trimmed off the end.
- SegmentBase *Freelist = &SentinelSegment;
-
- Segment *NewSegment() {
- // 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
- // just re-use blocks we've already seen before.
- if (Freelist != &SentinelSegment) {
- auto *FreeSegment = Freelist;
- Freelist = FreeSegment->Next;
- FreeSegment->Next = &SentinelSegment;
- Freelist->Prev = &SentinelSegment;
- return static_cast<Segment *>(FreeSegment);
- }
-
- auto SegmentBlock = Alloc->Allocate();
- if (SegmentBlock.Data == nullptr)
- return nullptr;
-
- // Placement-new the Segment element at the beginning of the SegmentBlock.
- auto S = reinterpret_cast<Segment *>(SegmentBlock.Data);
- new (S) SegmentBase{&SentinelSegment, &SentinelSegment};
- return S;
- }
-
- Segment *InitHeadAndTail() {
- DCHECK_EQ(Head, &SentinelSegment);
- DCHECK_EQ(Tail, &SentinelSegment);
- auto Segment = NewSegment();
- if (Segment == nullptr)
- return nullptr;
- DCHECK_EQ(Segment->Next, &SentinelSegment);
- DCHECK_EQ(Segment->Prev, &SentinelSegment);
- Head = Tail = static_cast<SegmentBase *>(Segment);
- return Segment;
- }
+ static Segment SentinelSegment;
- Segment *AppendNewSegment() {
- auto S = NewSegment();
- if (S == nullptr)
- return nullptr;
- DCHECK_NE(Tail, &SentinelSegment);
- DCHECK_EQ(Tail->Next, &SentinelSegment);
- DCHECK_EQ(S->Prev, &SentinelSegment);
- DCHECK_EQ(S->Next, &SentinelSegment);
- Tail->Next = S;
- S->Prev = Tail;
- Tail = S;
- return static_cast<Segment *>(Tail);
- }
+ using size_type = uint64_t;
+private:
// This Iterator models a BidirectionalIterator.
template <class U> class Iterator {
- SegmentBase *S = &SentinelSegment;
- size_t Offset = 0;
- size_t Size = 0;
+ Segment *S = &SentinelSegment;
+ uint64_t Offset = 0;
+ uint64_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(Segment *IS, uint64_t Off, uint64_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 +111,7 @@ private:
return *this;
}
- Iterator &operator--() {
+ Iterator &operator--() XRAY_NEVER_INSTRUMENT {
DCHECK_NE(S, &SentinelSegment);
DCHECK_GT(Offset, 0);
@@ -181,107 +124,295 @@ 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;
// We need to compute the character-aligned pointer, offset from the
// segment's Data location to get the element in the position of Offset.
- auto Base = static_cast<Segment *>(S)->Data;
+ auto Base = &S->Data;
auto AlignedOffset = Base + (RelOff * AlignedElementStorageSize);
return *reinterpret_cast<U *>(AlignedOffset);
}
- U *operator->() const { return &(**this); }
+ U *operator->() const XRAY_NEVER_INSTRUMENT { return &(**this); }
};
+ AllocatorType *Alloc;
+ Segment *Head;
+ Segment *Tail;
+
+ // Here we keep track of segments in the freelist, to allow us to re-use
+ // segments when elements are trimmed off the end.
+ Segment *Freelist;
+ uint64_t Size;
+
+ // ===============================
+ // In the following implementation, we work through the algorithms and the
+ // list operations using the following notation:
+ //
+ // - pred(s) is the predecessor (previous node accessor) and succ(s) is
+ // the successor (next node accessor).
+ //
+ // - S is a sentinel segment, which has the following property:
+ //
+ // pred(S) == succ(S) == S
+ //
+ // - @ is a loop operator, which can imply pred(s) == s if it appears on
+ // the left of s, or succ(s) == S if it appears on the right of s.
+ //
+ // - sL <-> sR : means a bidirectional relation between sL and sR, which
+ // means:
+ //
+ // succ(sL) == sR && pred(SR) == sL
+ //
+ // - sL -> sR : implies a unidirectional relation between sL and SR,
+ // with the following properties:
+ //
+ // succ(sL) == sR
+ //
+ // sL <- sR : implies a unidirectional relation between sR and sL,
+ // with the following properties:
+ //
+ // pred(sR) == sL
+ //
+ // ===============================
+
+ 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
+ // just re-use blocks we've already seen before.
+ if (Freelist != &SentinelSegment) {
+ // The current state of lists resemble something like this at this point:
+ //
+ // Freelist: @S@<-f0->...<->fN->@S@
+ // ^ Freelist
+ //
+ // We want to perform a splice of `f0` from Freelist to a temporary list,
+ // which looks like:
+ //
+ // Templist: @S@<-f0->@S@
+ // ^ FreeSegment
+ //
+ // Our algorithm preconditions are:
+ DCHECK_EQ(Freelist->Prev, &SentinelSegment);
+
+ // Then the algorithm we implement is:
+ //
+ // SFS = Freelist
+ // Freelist = succ(Freelist)
+ // if (Freelist != S)
+ // pred(Freelist) = S
+ // succ(SFS) = S
+ // pred(SFS) = S
+ //
+ auto *FreeSegment = Freelist;
+ Freelist = Freelist->Next;
+
+ // Note that we need to handle the case where Freelist is now pointing to
+ // S, which we don't want to be overwriting.
+ // TODO: Determine whether the cost of the branch is higher than the cost
+ // of the blind assignment.
+ if (Freelist != &SentinelSegment)
+ Freelist->Prev = &SentinelSegment;
+
+ FreeSegment->Next = &SentinelSegment;
+ FreeSegment->Prev = &SentinelSegment;
+
+ // Our postconditions are:
+ DCHECK_EQ(Freelist->Prev, &SentinelSegment);
+ DCHECK_NE(FreeSegment, &SentinelSegment);
+ return FreeSegment;
+ }
+
+ auto SegmentBlock = Alloc->Allocate();
+ if (SegmentBlock.Data == nullptr)
+ return nullptr;
+
+ // Placement-new the Segment element at the beginning of the SegmentBlock.
+ new (SegmentBlock.Data) Segment{&SentinelSegment, &SentinelSegment, {0}};
+ auto SB = reinterpret_cast<Segment *>(SegmentBlock.Data);
+ return SB;
+ }
+
+ Segment *InitHeadAndTail() XRAY_NEVER_INSTRUMENT {
+ DCHECK_EQ(Head, &SentinelSegment);
+ DCHECK_EQ(Tail, &SentinelSegment);
+ auto S = NewSegment();
+ if (S == nullptr)
+ return nullptr;
+ DCHECK_EQ(S->Next, &SentinelSegment);
+ DCHECK_EQ(S->Prev, &SentinelSegment);
+ DCHECK_NE(S, &SentinelSegment);
+ Head = S;
+ Tail = S;
+ DCHECK_EQ(Head, Tail);
+ DCHECK_EQ(Tail->Next, &SentinelSegment);
+ DCHECK_EQ(Tail->Prev, &SentinelSegment);
+ return S;
+ }
+
+ Segment *AppendNewSegment() XRAY_NEVER_INSTRUMENT {
+ auto S = NewSegment();
+ if (S == nullptr)
+ return nullptr;
+ DCHECK_NE(Tail, &SentinelSegment);
+ DCHECK_EQ(Tail->Next, &SentinelSegment);
+ DCHECK_EQ(S->Prev, &SentinelSegment);
+ DCHECK_EQ(S->Next, &SentinelSegment);
+ S->Prev = Tail;
+ Tail->Next = S;
+ Tail = S;
+ DCHECK_EQ(S, S->Prev->Next);
+ DCHECK_EQ(Tail->Next, &SentinelSegment);
+ return S;
+ }
+
public:
- explicit Array(AllocatorType &A) : Alloc(&A) {}
+ explicit Array(AllocatorType &A) XRAY_NEVER_INSTRUMENT
+ : Alloc(&A),
+ Head(&SentinelSegment),
+ Tail(&SentinelSegment),
+ Freelist(&SentinelSegment),
+ Size(0) {}
+
+ Array() XRAY_NEVER_INSTRUMENT : Alloc(nullptr),
+ Head(&SentinelSegment),
+ Tail(&SentinelSegment),
+ Freelist(&SentinelSegment),
+ Size(0) {}
Array(const Array &) = delete;
- Array(Array &&O) NOEXCEPT : Alloc(O.Alloc),
- Head(O.Head),
- Tail(O.Tail),
- Size(O.Size) {
+ Array &operator=(const Array &) = delete;
+
+ Array(Array &&O) XRAY_NEVER_INSTRUMENT : Alloc(O.Alloc),
+ Head(O.Head),
+ Tail(O.Tail),
+ Freelist(O.Freelist),
+ Size(O.Size) {
+ O.Alloc = nullptr;
O.Head = &SentinelSegment;
O.Tail = &SentinelSegment;
O.Size = 0;
+ O.Freelist = &SentinelSegment;
}
- bool empty() const { return Size == 0; }
+ Array &operator=(Array &&O) XRAY_NEVER_INSTRUMENT {
+ Alloc = O.Alloc;
+ O.Alloc = nullptr;
+ Head = O.Head;
+ O.Head = &SentinelSegment;
+ Tail = O.Tail;
+ O.Tail = &SentinelSegment;
+ Freelist = O.Freelist;
+ O.Freelist = &SentinelSegment;
+ Size = O.Size;
+ O.Size = 0;
+ return *this;
+ }
+
+ ~Array() XRAY_NEVER_INSTRUMENT {
+ for (auto &E : *this)
+ (&E)->~T();
+ }
- AllocatorType &allocator() const {
+ bool empty() const XRAY_NEVER_INSTRUMENT { return Size == 0; }
+
+ AllocatorType &allocator() const XRAY_NEVER_INSTRUMENT {
DCHECK_NE(Alloc, nullptr);
return *Alloc;
}
- size_t size() const { return Size; }
+ uint64_t size() const XRAY_NEVER_INSTRUMENT { return Size; }
- T *Append(const T &E) {
- if (UNLIKELY(Head == &SentinelSegment))
- if (InitHeadAndTail() == nullptr)
+ template <class... Args>
+ T *AppendEmplace(Args &&... args) XRAY_NEVER_INSTRUMENT {
+ DCHECK((Size == 0 && Head == &SentinelSegment && Head == Tail) ||
+ (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
+ if (UNLIKELY(Head == &SentinelSegment)) {
+ auto R = InitHeadAndTail();
+ if (R == nullptr)
return nullptr;
+ }
+
+ DCHECK_NE(Head, &SentinelSegment);
+ DCHECK_NE(Tail, &SentinelSegment);
auto Offset = Size % ElementsPerSegment;
if (UNLIKELY(Size != 0 && Offset == 0))
if (AppendNewSegment() == nullptr)
return nullptr;
- auto Base = static_cast<Segment *>(Tail)->Data;
+ DCHECK_NE(Tail, &SentinelSegment);
+ auto Base = &Tail->Data;
auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
- auto Position = reinterpret_cast<T *>(AlignedOffset);
- *Position = E;
+ DCHECK_LE(AlignedOffset + sizeof(T),
+ reinterpret_cast<unsigned char *>(Base) + SegmentSize);
+
+ // In-place construct at Position.
+ new (AlignedOffset) T{std::forward<Args>(args)...};
++Size;
- return Position;
+ return reinterpret_cast<T *>(AlignedOffset);
}
- template <class... Args> T *AppendEmplace(Args &&... args) {
- if (UNLIKELY(Head == &SentinelSegment))
- if (InitHeadAndTail() == nullptr)
+ T *Append(const T &E) XRAY_NEVER_INSTRUMENT {
+ // FIXME: This is a duplication of AppenEmplace with the copy semantics
+ // explicitly used, as a work-around to GCC 4.8 not invoking the copy
+ // constructor with the placement new with braced-init syntax.
+ DCHECK((Size == 0 && Head == &SentinelSegment && Head == Tail) ||
+ (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
+ if (UNLIKELY(Head == &SentinelSegment)) {
+ auto R = InitHeadAndTail();
+ if (R == nullptr)
return nullptr;
+ }
+
+ DCHECK_NE(Head, &SentinelSegment);
+ DCHECK_NE(Tail, &SentinelSegment);
auto Offset = Size % ElementsPerSegment;
- auto *LatestSegment = Tail;
- if (UNLIKELY(Size != 0 && Offset == 0)) {
- LatestSegment = AppendNewSegment();
- if (LatestSegment == nullptr)
+ if (UNLIKELY(Size != 0 && Offset == 0))
+ if (AppendNewSegment() == nullptr)
return nullptr;
- }
DCHECK_NE(Tail, &SentinelSegment);
- auto Base = static_cast<Segment *>(LatestSegment)->Data;
+ auto Base = &Tail->Data;
auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
- auto Position = reinterpret_cast<T *>(AlignedOffset);
+ DCHECK_LE(AlignedOffset + sizeof(T),
+ reinterpret_cast<unsigned char *>(Tail) + SegmentSize);
// In-place construct at Position.
- new (Position) T{std::forward<Args>(args)...};
+ new (AlignedOffset) T(E);
++Size;
- return reinterpret_cast<T *>(Position);
+ return reinterpret_cast<T *>(AlignedOffset);
}
- T &operator[](size_t Offset) const {
+ T &operator[](uint64_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;
@@ -290,19 +421,19 @@ public:
Offset -= ElementsPerSegment;
DCHECK_NE(S, &SentinelSegment);
}
- auto Base = static_cast<Segment *>(S)->Data;
+ auto Base = &S->Data;
auto AlignedOffset = Base + (Offset * AlignedElementStorageSize);
auto Position = reinterpret_cast<T *>(AlignedOffset);
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 +441,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,51 +456,195 @@ 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) {
- DCHECK_LE(Elements, Size);
- DCHECK_GT(Size, 0);
+ void trim(uint64_t Elements) XRAY_NEVER_INSTRUMENT {
auto OldSize = Size;
+ Elements = Elements > Size ? Size : Elements;
Size -= Elements;
- DCHECK_NE(Head, &SentinelSegment);
- DCHECK_NE(Tail, &SentinelSegment);
-
- for (auto SegmentsToTrim = (nearest_boundary(OldSize, ElementsPerSegment) -
- nearest_boundary(Size, ElementsPerSegment)) /
- ElementsPerSegment;
- SegmentsToTrim > 0; --SegmentsToTrim) {
+ // We compute the number of segments we're going to return from the tail by
+ // counting how many elements have been trimmed. Given the following:
+ //
+ // - Each segment has N valid positions, where N > 0
+ // - The previous size > current size
+ //
+ // To compute the number of segments to return, we need to perform the
+ // following calculations for the number of segments required given 'x'
+ // elements:
+ //
+ // f(x) = {
+ // x == 0 : 0
+ // , 0 < x <= N : 1
+ // , N < x <= max : x / N + (x % N ? 1 : 0)
+ // }
+ //
+ // We can simplify this down to:
+ //
+ // f(x) = {
+ // x == 0 : 0,
+ // , 0 < x <= max : x / N + (x < N || x % N ? 1 : 0)
+ // }
+ //
+ // And further down to:
+ //
+ // f(x) = x ? x / N + (x < N || x % N ? 1 : 0) : 0
+ //
+ // We can then perform the following calculation `s` which counts the number
+ // of segments we need to remove from the end of the data structure:
+ //
+ // s(p, c) = f(p) - f(c)
+ //
+ // If we treat p = previous size, and c = current size, and given the
+ // properties above, the possible range for s(...) is [0..max(typeof(p))/N]
+ // given that typeof(p) == typeof(c).
+ auto F = [](uint64_t X) {
+ return X ? (X / ElementsPerSegment) +
+ (X < ElementsPerSegment || X % ElementsPerSegment ? 1 : 0)
+ : 0;
+ };
+ auto PS = F(OldSize);
+ auto CS = F(Size);
+ DCHECK_GE(PS, CS);
+ auto SegmentsToTrim = PS - CS;
+ for (auto I = 0uL; I < SegmentsToTrim; ++I) {
+ // Here we place the current tail segment to the freelist. To do this
+ // appropriately, we need to perform a splice operation on two
+ // bidirectional linked-lists. In particular, we have the current state of
+ // the doubly-linked list of segments:
+ //
+ // @S@ <- s0 <-> s1 <-> ... <-> sT -> @S@
+ //
DCHECK_NE(Head, &SentinelSegment);
DCHECK_NE(Tail, &SentinelSegment);
- // Put the tail into the Freelist.
- auto *FreeSegment = Tail;
- Tail = Tail->Prev;
- if (Tail == &SentinelSegment)
- Head = Tail;
- else
- Tail->Next = &SentinelSegment;
-
DCHECK_EQ(Tail->Next, &SentinelSegment);
- FreeSegment->Next = Freelist;
- FreeSegment->Prev = &SentinelSegment;
- if (Freelist != &SentinelSegment)
- Freelist->Prev = FreeSegment;
- Freelist = FreeSegment;
+
+ if (Freelist == &SentinelSegment) {
+ // Our two lists at this point are in this configuration:
+ //
+ // Freelist: (potentially) @S@
+ // Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@
+ // ^ Head ^ Tail
+ //
+ // The end state for us will be this configuration:
+ //
+ // Freelist: @S@<-sT->@S@
+ // Mainlist: @S@<-s0<->s1<->...<->sPT->@S@
+ // ^ Head ^ Tail
+ //
+ // The first step for us is to hold a reference to the tail of Mainlist,
+ // which in our notation is represented by sT. We call this our "free
+ // segment" which is the segment we are placing on the Freelist.
+ //
+ // sF = sT
+ //
+ // Then, we also hold a reference to the "pre-tail" element, which we
+ // call sPT:
+ //
+ // sPT = pred(sT)
+ //
+ // We want to splice sT into the beginning of the Freelist, which in
+ // an empty Freelist means placing a segment whose predecessor and
+ // successor is the sentinel segment.
+ //
+ // The splice operation then can be performed in the following
+ // algorithm:
+ //
+ // succ(sPT) = S
+ // pred(sT) = S
+ // succ(sT) = Freelist
+ // Freelist = sT
+ // Tail = sPT
+ //
+ auto SPT = Tail->Prev;
+ SPT->Next = &SentinelSegment;
+ Tail->Prev = &SentinelSegment;
+ Tail->Next = Freelist;
+ Freelist = Tail;
+ Tail = SPT;
+
+ // Our post-conditions here are:
+ DCHECK_EQ(Tail->Next, &SentinelSegment);
+ DCHECK_EQ(Freelist->Prev, &SentinelSegment);
+ } else {
+ // In the other case, where the Freelist is not empty, we perform the
+ // following transformation instead:
+ //
+ // This transforms the current state:
+ //
+ // Freelist: @S@<-f0->@S@
+ // ^ Freelist
+ // Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@
+ // ^ Head ^ Tail
+ //
+ // Into the following:
+ //
+ // Freelist: @S@<-sT<->f0->@S@
+ // ^ Freelist
+ // Mainlist: @S@<-s0<->s1<->...<->sPT->@S@
+ // ^ Head ^ Tail
+ //
+ // The algorithm is:
+ //
+ // sFH = Freelist
+ // sPT = pred(sT)
+ // pred(SFH) = sT
+ // succ(sT) = Freelist
+ // pred(sT) = S
+ // succ(sPT) = S
+ // Tail = sPT
+ // Freelist = sT
+ //
+ auto SFH = Freelist;
+ auto SPT = Tail->Prev;
+ auto ST = Tail;
+ SFH->Prev = ST;
+ ST->Next = Freelist;
+ ST->Prev = &SentinelSegment;
+ SPT->Next = &SentinelSegment;
+ Tail = SPT;
+ Freelist = ST;
+
+ // Our post-conditions here are:
+ DCHECK_EQ(Tail->Next, &SentinelSegment);
+ DCHECK_EQ(Freelist->Prev, &SentinelSegment);
+ DCHECK_EQ(Freelist->Next->Prev, Freelist);
+ }
}
+
+ // Now in case we've spliced all the segments in the end, we ensure that the
+ // main list is "empty", or both the head and tail pointing to the sentinel
+ // segment.
+ if (Tail == &SentinelSegment)
+ Head = Tail;
+
+ DCHECK(
+ (Size == 0 && Head == &SentinelSegment && Tail == &SentinelSegment) ||
+ (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment));
+ DCHECK(
+ (Freelist != &SentinelSegment && Freelist->Prev == &SentinelSegment) ||
+ (Freelist == &SentinelSegment && Tail->Next == &SentinelSegment));
}
// 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
// ensure that storage for the SentinelSegment is defined and has a single
// address.
template <class T>
-typename Array<T>::SegmentBase Array<T>::SentinelSegment{
- &Array<T>::SentinelSegment, &Array<T>::SentinelSegment};
+typename Array<T>::Segment Array<T>::SentinelSegment{
+ &Array<T>::SentinelSegment, &Array<T>::SentinelSegment, {'\0'}};
} // namespace __xray
diff --git a/lib/xray/xray_trampoline_x86_64.S b/lib/xray/xray_trampoline_x86_64.S
index 99ad3966ee3a..52985ffd19ab 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
@@ -89,10 +91,10 @@
.text
#if !defined(__APPLE__)
.section .text
+ .file "xray_trampoline_x86.S"
#else
.section __TEXT,__text
#endif
- .file "xray_trampoline_x86.S"
//===----------------------------------------------------------------------===//
diff --git a/lib/xray/xray_tsc.h b/lib/xray/xray_tsc.h
index 4507564e7cd2..180d6df188c1 100644
--- a/lib/xray/xray_tsc.h
+++ b/lib/xray/xray_tsc.h
@@ -13,10 +13,32 @@
#ifndef XRAY_EMULATE_TSC_H
#define XRAY_EMULATE_TSC_H
+#include "sanitizer_common/sanitizer_common.h"
+
namespace __xray {
static constexpr uint64_t NanosecondsPerSecond = 1000ULL * 1000 * 1000;
}
+#if SANITIZER_FUCHSIA
+#include <zircon/syscalls.h>
+
+namespace __xray {
+
+inline bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT { return true; }
+
+ALWAYS_INLINE uint64_t readTSC(uint8_t &CPU) XRAY_NEVER_INSTRUMENT {
+ CPU = 0;
+ return _zx_ticks_get();
+}
+
+inline uint64_t getTSCFrequency() XRAY_NEVER_INSTRUMENT {
+ return _zx_ticks_per_second();
+}
+
+} // namespace __xray
+
+#else // SANITIZER_FUCHSIA
+
#if defined(__x86_64__)
#include "xray_x86_64.inc"
#elif defined(__powerpc64__)
@@ -64,5 +86,6 @@ inline uint64_t getTSCFrequency() XRAY_NEVER_INSTRUMENT {
#else
#error Target architecture is not supported.
#endif // CPU architecture
+#endif // SANITIZER_FUCHSIA
#endif // XRAY_EMULATE_TSC_H
diff --git a/lib/xray/xray_utils.cc b/lib/xray/xray_utils.cc
index 68f4e8c1094c..59ba6c3082b2 100644
--- a/lib/xray/xray_utils.cc
+++ b/lib/xray/xray_utils.cc
@@ -12,7 +12,9 @@
//===----------------------------------------------------------------------===//
#include "xray_utils.h"
+#include "sanitizer_common/sanitizer_allocator_internal.h"
#include "sanitizer_common/sanitizer_common.h"
+#include "xray_allocator.h"
#include "xray_defs.h"
#include "xray_flags.h"
#include <cstdio>
@@ -25,13 +27,113 @@
#include <unistd.h>
#include <utility>
+#if SANITIZER_FUCHSIA
+#include "sanitizer_common/sanitizer_symbolizer_fuchsia.h"
+
+#include <inttypes.h>
+#include <zircon/process.h>
+#include <zircon/sanitizer.h>
+#include <zircon/status.h>
+#include <zircon/syscalls.h>
+#endif
+
namespace __xray {
-void printToStdErr(const char *Buffer) XRAY_NEVER_INSTRUMENT {
- fprintf(stderr, "%s", Buffer);
+#if SANITIZER_FUCHSIA
+constexpr const char* ProfileSinkName = "llvm-xray";
+
+LogWriter::~LogWriter() {
+ _zx_handle_close(Vmo);
+}
+
+void LogWriter::WriteAll(const char *Begin, const char *End) XRAY_NEVER_INSTRUMENT {
+ if (Begin == End)
+ return;
+ auto TotalBytes = std::distance(Begin, End);
+
+ const size_t PageSize = flags()->xray_page_size_override > 0
+ ? flags()->xray_page_size_override
+ : GetPageSizeCached();
+ if (RoundUpTo(Offset, PageSize) != RoundUpTo(Offset + TotalBytes, PageSize)) {
+ // Resize the VMO to ensure there's sufficient space for the data.
+ zx_status_t Status = _zx_vmo_set_size(Vmo, Offset + TotalBytes);
+ if (Status != ZX_OK) {
+ Report("Failed to resize VMO: %s\n", _zx_status_get_string(Status));
+ return;
+ }
+ }
+
+ // Write the data into VMO.
+ zx_status_t Status = _zx_vmo_write(Vmo, Begin, Offset, TotalBytes);
+ if (Status != ZX_OK) {
+ Report("Failed to write: %s\n", _zx_status_get_string(Status));
+ return;
+ }
+ Offset += TotalBytes;
+}
+
+void LogWriter::Flush() XRAY_NEVER_INSTRUMENT {
+ // Nothing to do here since WriteAll writes directly into the VMO.
+}
+
+LogWriter *LogWriter::Open() XRAY_NEVER_INSTRUMENT {
+ // Create VMO to hold the profile data.
+ zx_handle_t Vmo;
+ zx_status_t Status = _zx_vmo_create(0, 0, &Vmo);
+ if (Status != ZX_OK) {
+ Report("XRay: cannot create VMO: %s\n", _zx_status_get_string(Status));
+ return nullptr;
+ }
+
+ // Get the KOID of the current process to use in the VMO name.
+ zx_info_handle_basic_t Info;
+ Status = _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &Info,
+ sizeof(Info), NULL, NULL);
+ if (Status != ZX_OK) {
+ Report("XRay: cannot get basic info about current process handle: %s\n",
+ _zx_status_get_string(Status));
+ return nullptr;
+ }
+
+ // Give the VMO a name including our process KOID so it's easy to spot.
+ char VmoName[ZX_MAX_NAME_LEN];
+ internal_snprintf(VmoName, sizeof(VmoName), "%s.%zu", ProfileSinkName,
+ Info.koid);
+ _zx_object_set_property(Vmo, ZX_PROP_NAME, VmoName, strlen(VmoName));
+
+ // Duplicate the handle since __sanitizer_publish_data consumes it and
+ // LogWriter needs to hold onto it.
+ zx_handle_t Handle;
+ Status =_zx_handle_duplicate(Vmo, ZX_RIGHT_SAME_RIGHTS, &Handle);
+ if (Status != ZX_OK) {
+ Report("XRay: cannot duplicate VMO handle: %s\n",
+ _zx_status_get_string(Status));
+ return nullptr;
+ }
+
+ // Publish the VMO that receives the logging. Note the VMO's contents can
+ // grow and change after publication. The contents won't be read out until
+ // after the process exits.
+ __sanitizer_publish_data(ProfileSinkName, Handle);
+
+ // Use the dumpfile symbolizer markup element to write the name of the VMO.
+ Report("XRay: " FORMAT_DUMPFILE "\n", ProfileSinkName, VmoName);
+
+ LogWriter *LW = reinterpret_cast<LogWriter *>(InternalAlloc(sizeof(LogWriter)));
+ new (LW) LogWriter(Vmo);
+ return LW;
+}
+
+void LogWriter::Close(LogWriter *LW) {
+ LW->~LogWriter();
+ InternalFree(LW);
+}
+#else // SANITIZER_FUCHSIA
+LogWriter::~LogWriter() {
+ internal_close(Fd);
}
-void retryingWriteAll(int Fd, const char *Begin, const char *End) XRAY_NEVER_INSTRUMENT {
+void LogWriter::WriteAll(const char *Begin, const char *End) XRAY_NEVER_INSTRUMENT {
if (Begin == End)
return;
auto TotalBytes = std::distance(Begin, End);
@@ -49,50 +151,11 @@ void retryingWriteAll(int Fd, const char *Begin, const char *End) XRAY_NEVER_INS
}
}
-std::pair<ssize_t, bool> retryingReadSome(int Fd, char *Begin,
- char *End) XRAY_NEVER_INSTRUMENT {
- auto BytesToRead = std::distance(Begin, End);
- ssize_t BytesRead;
- ssize_t TotalBytesRead = 0;
- while (BytesToRead && (BytesRead = read(Fd, Begin, BytesToRead))) {
- if (BytesRead == -1) {
- if (errno == EINTR)
- continue;
- Report("Read error; errno = %d\n", errno);
- return std::make_pair(TotalBytesRead, false);
- }
-
- TotalBytesRead += BytesRead;
- BytesToRead -= BytesRead;
- Begin += BytesRead;
- }
- return std::make_pair(TotalBytesRead, true);
-}
-
-bool readValueFromFile(const char *Filename,
- long long *Value) XRAY_NEVER_INSTRUMENT {
- int Fd = open(Filename, O_RDONLY | O_CLOEXEC);
- if (Fd == -1)
- return false;
- static constexpr size_t BufSize = 256;
- char Line[BufSize] = {};
- ssize_t BytesRead;
- bool Success;
- std::tie(BytesRead, Success) = retryingReadSome(Fd, Line, Line + BufSize);
- if (!Success)
- return false;
- close(Fd);
- const char *End = nullptr;
- long long Tmp = internal_simple_strtoll(Line, &End, 10);
- bool Result = false;
- if (Line[0] != '\0' && (*End == '\n' || *End == '\0')) {
- *Value = Tmp;
- Result = true;
- }
- return Result;
+void LogWriter::Flush() XRAY_NEVER_INSTRUMENT {
+ fsync(Fd);
}
-int getLogFD() XRAY_NEVER_INSTRUMENT {
+LogWriter *LogWriter::Open() XRAY_NEVER_INSTRUMENT {
// Open a temporary file once for the log.
char TmpFilename[256] = {};
char TmpWildcardPattern[] = "XXXXXX";
@@ -103,24 +166,31 @@ 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;
+ return nullptr;
}
int Fd = mkstemp(TmpFilename);
if (Fd == -1) {
Report("XRay: Failed opening temporary file '%s'; not logging events.\n",
TmpFilename);
- return -1;
+ return nullptr;
}
if (Verbosity())
Report("XRay: Log file in '%s'\n", TmpFilename);
- return Fd;
+ LogWriter *LW = allocate<LogWriter>();
+ new (LW) LogWriter(Fd);
+ return LW;
+}
+
+void LogWriter::Close(LogWriter *LW) {
+ LW->~LogWriter();
+ deallocate(LW);
}
+#endif // SANITIZER_FUCHSIA
} // namespace __xray
diff --git a/lib/xray/xray_utils.h b/lib/xray/xray_utils.h
index eafa16e1a9d5..60438973fbd0 100644
--- a/lib/xray/xray_utils.h
+++ b/lib/xray/xray_utils.h
@@ -20,23 +20,40 @@
#include <sys/types.h>
#include <utility>
-namespace __xray {
-
-// Default implementation of the reporting interface for sanitizer errors.
-void printToStdErr(const char *Buffer);
-
-// EINTR-safe write routine, provided a file descriptor and a character range.
-void retryingWriteAll(int Fd, const char *Begin, const char *End);
+#include "sanitizer_common/sanitizer_common.h"
+#if SANITIZER_FUCHSIA
+#include <zircon/types.h>
+#endif
-// Reads a long long value from a provided file.
-bool readValueFromFile(const char *Filename, long long *Value);
-
-// EINTR-safe read routine, providing a file descriptor and a character range.
-std::pair<ssize_t, bool> retryingReadSome(int Fd, char *Begin, char *End);
+namespace __xray {
-// EINTR-safe open routine, uses flag-provided values for initialising a log
-// file.
-int getLogFD();
+class LogWriter {
+public:
+#if SANITIZER_FUCHSIA
+ LogWriter(zx_handle_t Vmo) : Vmo(Vmo) {}
+#else
+ explicit LogWriter(int Fd) : Fd(Fd) {}
+#endif
+ ~LogWriter();
+
+ // Write a character range into a log.
+ void WriteAll(const char *Begin, const char *End);
+
+ void Flush();
+
+ // Returns a new log instance initialized using the flag-provided values.
+ static LogWriter *Open();
+ // Closes and deallocates the log instance.
+ static void Close(LogWriter *LogWriter);
+
+private:
+#if SANITIZER_FUCHSIA
+ zx_handle_t Vmo = ZX_HANDLE_INVALID;
+ uint64_t Offset = 0;
+#else
+ int Fd = -1;
+#endif
+};
constexpr size_t gcd(size_t a, size_t b) {
return (b == 0) ? a : gcd(b, a % b);
diff --git a/lib/xray/xray_x86_64.cc b/lib/xray/xray_x86_64.cc
index 51dc4ce43b1c..e63ee1b3bd02 100644
--- a/lib/xray/xray_x86_64.cc
+++ b/lib/xray/xray_x86_64.cc
@@ -1,15 +1,20 @@
#include "cpuid.h"
#include "sanitizer_common/sanitizer_common.h"
+#if !SANITIZER_FUCHSIA
+#include "sanitizer_common/sanitizer_posix.h"
+#endif
#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>
#include <machine/cpu.h>
#endif
#include <sys/sysctl.h>
+#elif SANITIZER_FUCHSIA
+#include <zircon/syscalls.h>
#endif
#include <atomic>
@@ -81,17 +86,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 {
@@ -100,7 +108,7 @@ uint64_t getTSCFrequency() XRAY_NEVER_INSTRUMENT {
return 0;
}
-#else
+#elif !SANITIZER_FUCHSIA
uint64_t getTSCFrequency() XRAY_NEVER_INSTRUMENT {
/* Not supported */
return 0;
@@ -317,6 +325,7 @@ bool patchTypedEvent(const bool Enable, const uint32_t FuncId,
return false;
}
+#if !SANITIZER_FUCHSIA
// We determine whether the CPU we're running on has the correct features we
// need. In x86_64 this will be rdtscp support.
bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT {
@@ -339,5 +348,6 @@ bool probeRequiredCPUFeatures() XRAY_NEVER_INSTRUMENT {
}
return true;
}
+#endif
} // namespace __xray
diff --git a/test/.clang-format b/test/.clang-format
new file mode 100644
index 000000000000..4799b66f3e9a
--- /dev/null
+++ b/test/.clang-format
@@ -0,0 +1,2 @@
+BasedOnStyle: LLVM
+ColumnLimit: 0
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 554ba5fa0f0e..2e239d54e29c 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -15,8 +15,6 @@ if(COMPILER_RT_BUILD_PROFILE AND COMPILER_RT_HAS_PROFILE)
endif()
if(COMPILER_RT_STANDALONE_BUILD)
- add_executable(FileCheck IMPORTED GLOBAL)
- set_property(TARGET FileCheck PROPERTY IMPORTED_LOCATION ${LLVM_TOOLS_BINARY_DIR}/FileCheck)
list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS FileCheck)
endif()
diff --git a/test/asan/CMakeLists.txt b/test/asan/CMakeLists.txt
index e6d1df5e039a..6c22ef3b10ef 100644
--- a/test/asan/CMakeLists.txt
+++ b/test/asan/CMakeLists.txt
@@ -56,7 +56,7 @@ foreach(arch ${ASAN_TEST_ARCH})
string(TOLOWER "-${arch}-${OS_NAME}" ASAN_TEST_CONFIG_SUFFIX)
get_bits_for_arch(${arch} ASAN_TEST_BITS)
get_test_cc_for_arch(${arch} ASAN_TEST_TARGET_CC ASAN_TEST_TARGET_CFLAGS)
- if(ANDROID)
+ if(ANDROID OR APPLE)
set(ASAN_TEST_DYNAMIC True)
else()
set(ASAN_TEST_DYNAMIC False)
diff --git a/test/asan/TestCases/Darwin/init_for_dlopen.cc b/test/asan/TestCases/Darwin/init_for_dlopen.cc
new file mode 100644
index 000000000000..8a0fbf943b67
--- /dev/null
+++ b/test/asan/TestCases/Darwin/init_for_dlopen.cc
@@ -0,0 +1,46 @@
+// RUN: %clangxx -g -O0 %s -o %t
+
+// Check that trying to dlopen() the ASan dylib fails.
+// We explictly set `abort_on_error=0` because
+// - By default the lit config sets this but we don't want this
+// test to implicitly depend on this.
+// - It avoids requiring `--crash` to be passed to `not`.
+// RUN: APPLE_ASAN_INIT_FOR_DLOPEN=0 %env_asan_opts=abort_on_error=0 not \
+// RUN: %run %t %shared_libasan 2>&1 | \
+// RUN: FileCheck -check-prefix=CHECK-DL-OPEN-FAIL %s
+// RUN: env -u APPLE_ASAN_INIT_FOR_DLOPEN %env_asan_opts=abort_on_error=0 not \
+// RUN: %run %t %shared_libasan 2>&1 | \
+// RUN: FileCheck -check-prefix=CHECK-DL-OPEN-FAIL %s
+
+// Check that we can successfully dlopen the ASan dylib when we set the right
+// environment variable.
+// RUN: env APPLE_ASAN_INIT_FOR_DLOPEN=1 %run %t %shared_libasan 2>&1 | \
+// RUN: FileCheck -check-prefix=CHECK-DL-OPEN-SUCCESS %s
+
+#include <dlfcn.h>
+#include <stdio.h>
+
+// CHECK-DL-OPEN-FAIL: ERROR: Interceptors are not working
+
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <dylib_path>\n", argv[0]);
+ return 1;
+ }
+ const char *dylib_path = argv[1];
+ void *handle = dlopen(dylib_path, RTLD_LAZY);
+ if (!handle) {
+ fprintf(stderr, "Failed to dlopen: %s\n", dlerror());
+ return 1;
+ }
+ // Make sure we can find a function we expect to be in the dylib.
+ void *fn = dlsym(handle, "__sanitizer_mz_size");
+ if (!fn) {
+ fprintf(stderr, "Failed to get symbol: %s\n", dlerror());
+ return 1;
+ }
+ // TODO(dliew): Actually call a function from the dylib that is safe to call.
+ // CHECK-DL-OPEN-SUCCESS: DONE
+ printf("DONE\n");
+ return 0;
+}
diff --git a/test/asan/TestCases/Darwin/odr-lto.cc b/test/asan/TestCases/Darwin/odr-lto.cc
index 56dd89b164c2..e1e454be077f 100644
--- a/test/asan/TestCases/Darwin/odr-lto.cc
+++ b/test/asan/TestCases/Darwin/odr-lto.cc
@@ -1,4 +1,4 @@
-// Check that -asan-use-private-alias and use_odr_indicator=1 silence the false
+// Check that -asan-use-private-alias silence the false
// positive ODR violation on Darwin with LTO.
// REQUIRES: lto
@@ -6,7 +6,7 @@
// RUN: %clangxx_asan -DPART=0 -c %s -o %t-1.o -flto -mllvm -asan-use-private-alias
// RUN: %clangxx_asan -DPART=1 -c %s -o %t-2.o -flto -mllvm -asan-use-private-alias
// RUN: %clangxx_asan %t-1.o %t-2.o -o %t -flto
-// RUN: %env_asan_opts=use_odr_indicator=1 %run %t 2>&1 | FileCheck %s
+// RUN: %run %t 2>&1 | FileCheck %s
#include <stdio.h>
#include <stdlib.h>
diff --git a/test/asan/TestCases/Darwin/segv_read_write.c b/test/asan/TestCases/Darwin/segv_read_write.c
index d8e2d215f832..127365d2f09c 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/asan_rt_confict_test-2.cc b/test/asan/TestCases/Linux/asan_rt_confict_test-2.cc
index 4c935e2b0f3b..6328cbb2ce81 100644
--- a/test/asan/TestCases/Linux/asan_rt_confict_test-2.cc
+++ b/test/asan/TestCases/Linux/asan_rt_confict_test-2.cc
@@ -1,8 +1,8 @@
// Test that mixed static/dynamic sanitization of program objects
// is prohibited.
//
-// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so
-// RUN: %clangxx_asan_static %s %t.so -o %t
+// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %dynamiclib
+// RUN: %clangxx_asan_static %s %ld_flags_rpath_exe -o %t
// RUN: not %run %t 2>&1 | FileCheck %s
// REQUIRES: asan-dynamic-runtime
diff --git a/test/asan/TestCases/Linux/coverage-missing.cc b/test/asan/TestCases/Linux/coverage-missing.cc
index 32aada645deb..10acef9af4b0 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/Linux/local_alias.cc b/test/asan/TestCases/Linux/local_alias.cc
index 266d3fe6bc8f..a8b3d75e375b 100644
--- a/test/asan/TestCases/Linux/local_alias.cc
+++ b/test/asan/TestCases/Linux/local_alias.cc
@@ -4,14 +4,11 @@
// false positive global-buffer-overflow due to sanitized library poisons
// globals from non-sanitized one.
//
-// FIXME: https://github.com/google/sanitizers/issues/316
-// XFAIL: android
-//
-// RUN: %clangxx_asan -DBUILD_INSTRUMENTED_DSO=1 -fPIC -shared -mllvm -asan-use-private-alias %s -o %t-INSTRUMENTED-SO.so
-// RUN: %clangxx -DBUILD_UNINSTRUMENTED_DSO=1 -fPIC -shared %s -o %t-UNINSTRUMENTED-SO.so
+// RUN: %clangxx_asan -DBUILD_INSTRUMENTED_DSO=1 -fPIC -shared -mllvm -asan-use-private-alias %s -o %dynamiclib1
+// RUN: %clangxx -DBUILD_UNINSTRUMENTED_DSO=1 -fPIC -shared %s -o %dynamiclib2
// RUN: %clangxx %s -c -mllvm -asan-use-private-alias -o %t.o
-// RUN: %clangxx_asan %t.o %t-UNINSTRUMENTED-SO.so %t-INSTRUMENTED-SO.so -o %t-EXE
-// RUN: %env_asan_opts=use_odr_indicator=true %run %t-EXE
+// RUN: %clangxx_asan %t.o %ld_flags_rpath_exe2 %ld_flags_rpath_exe1 -o %t-EXE
+// RUN: %run %t-EXE
#if defined (BUILD_INSTRUMENTED_DSO)
long h = 15;
diff --git a/test/asan/TestCases/Linux/new_delete_mismatch.cc b/test/asan/TestCases/Linux/new_delete_mismatch.cc
index 3a71862fb732..05f74bda7549 100644
--- a/test/asan/TestCases/Linux/new_delete_mismatch.cc
+++ b/test/asan/TestCases/Linux/new_delete_mismatch.cc
@@ -14,3 +14,4 @@ int main() {
}
// CHECK: AddressSanitizer: alloc-dealloc-mismatch (operator new [] vs operator delete) on 0x
+// CHECK: is located 0 bytes inside of 10-byte region
diff --git a/test/asan/TestCases/Linux/new_delete_mismatch_global.cc b/test/asan/TestCases/Linux/new_delete_mismatch_global.cc
new file mode 100644
index 000000000000..3f1a78715caf
--- /dev/null
+++ b/test/asan/TestCases/Linux/new_delete_mismatch_global.cc
@@ -0,0 +1,16 @@
+// Check that we report delete on a memory that belongs to a global variable.
+
+// RUN: %clangxx_asan -g %s -o %t && %env_asan_opts=alloc_dealloc_mismatch=1 not %run %t 2>&1 | FileCheck %s
+
+#include <stdlib.h>
+
+static volatile char *x;
+char a[10];
+
+int main() {
+ x = &a[0];
+ delete x;
+}
+
+// CHECK: AddressSanitizer: attempting free on address which was not malloc()-ed
+// CHECK: is located 0 bytes inside of global variable 'a' defined in
diff --git a/test/asan/TestCases/Linux/new_delete_mismatch_stack.cc b/test/asan/TestCases/Linux/new_delete_mismatch_stack.cc
new file mode 100644
index 000000000000..bbf07cc21380
--- /dev/null
+++ b/test/asan/TestCases/Linux/new_delete_mismatch_stack.cc
@@ -0,0 +1,17 @@
+// Check that we report delete on a memory that belongs to a stack variable.
+
+// RUN: %clangxx_asan -g %s -o %t && %env_asan_opts=alloc_dealloc_mismatch=1 not %run %t 2>&1 | FileCheck %s
+
+#include <stdlib.h>
+
+static volatile char *x;
+
+int main() {
+ char a[10];
+ x = &a[0];
+ delete x;
+}
+
+// CHECK: AddressSanitizer: attempting free on address which was not malloc()-ed
+// CHECK: is located in stack of thread T0 at offset
+// CHECK: 'a'{{.*}} <== Memory access at offset {{16|32}} is inside this variable
diff --git a/test/asan/TestCases/Linux/odr-violation.cc b/test/asan/TestCases/Linux/odr-violation.cc
index 70437a8321b9..a1941fcc8d7a 100644
--- a/test/asan/TestCases/Linux/odr-violation.cc
+++ b/test/asan/TestCases/Linux/odr-violation.cc
@@ -5,15 +5,15 @@
// pointers. This setting is not on by default because it's too expensive.
//
// Different size: detect a bug if detect_odr_violation>=1
-// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t-ODR-SO.so
-// RUN: %clangxx_asan %s %t-ODR-SO.so -Wl,-R. -o %t-ODR-EXE
+// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %dynamiclib
+// RUN: %clangxx_asan %s %ld_flags_rpath_exe -o %t-ODR-EXE
// RUN: %env_asan_opts=fast_unwind_on_malloc=0:detect_odr_violation=1 not %run %t-ODR-EXE 2>&1 | FileCheck %s
// RUN: %env_asan_opts=fast_unwind_on_malloc=0:detect_odr_violation=2 not %run %t-ODR-EXE 2>&1 | FileCheck %s
// RUN: %env_asan_opts=fast_unwind_on_malloc=0:detect_odr_violation=0 %run %t-ODR-EXE 2>&1 | FileCheck %s --check-prefix=DISABLED
// RUN: %env_asan_opts=fast_unwind_on_malloc=0 not %run %t-ODR-EXE 2>&1 | FileCheck %s
//
// Same size: report a bug only if detect_odr_violation>=2.
-// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t-ODR-SO.so -DSZ=100
+// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %dynamiclib -DSZ=100
// RUN: %env_asan_opts=fast_unwind_on_malloc=0:detect_odr_violation=1 %run %t-ODR-EXE 2>&1 | FileCheck %s --check-prefix=DISABLED
// RUN: %env_asan_opts=fast_unwind_on_malloc=0:detect_odr_violation=2 not %run %t-ODR-EXE 2>&1 | FileCheck %s
// RUN: %env_asan_opts=fast_unwind_on_malloc=0 not %run %t-ODR-EXE 2>&1 | FileCheck %s
@@ -23,12 +23,20 @@
// RUN: %env_asan_opts=fast_unwind_on_malloc=0:detect_odr_violation=2:suppressions=%t.supp %run %t-ODR-EXE 2>&1 | FileCheck %s --check-prefix=DISABLED
// RUN: rm -f %t.supp
//
+// Use private aliases for global variables without indicator symbol.
+// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared -mllvm -asan-use-private-alias %s -o %dynamiclib -DSZ=100
+// RUN: %clangxx_asan -mllvm -asan-use-private-alias %s %ld_flags_rpath_exe -o %t-ODR-EXE
+// RUN: %env_asan_opts=fast_unwind_on_malloc=0 %run %t-ODR-EXE 2>&1 | FileCheck %s --check-prefix=DISABLED
+
// Use private aliases for global variables: use indicator symbol to detect ODR violation.
-// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared -mllvm -asan-use-private-alias %s -o %t-ODR-SO.so -DSZ=100
-// RUN: %clangxx_asan -mllvm -asan-use-private-alias %s %t-ODR-SO.so -Wl,-R. -o %t-ODR-EXE
-// RUN: %env_asan_opts=fast_unwind_on_malloc=0 %run %t-ODR-EXE 2>&1 | FileCheck %s --check-prefix=DISABLED
-// RUN: %env_asan_opts=fast_unwind_on_malloc=0:use_odr_indicator=false %run %t-ODR-EXE 2>&1 | FileCheck %s --check-prefix=DISABLED
-// RUN: %env_asan_opts=fast_unwind_on_malloc=0:use_odr_indicator=true not %run %t-ODR-EXE 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared -mllvm -asan-use-private-alias -mllvm -asan-use-odr-indicator %s -o %dynamiclib -DSZ=100
+// RUN: %clangxx_asan -mllvm -asan-use-private-alias -mllvm -asan-use-odr-indicator %s %ld_flags_rpath_exe -o %t-ODR-EXE
+// RUN: %env_asan_opts=fast_unwind_on_malloc=0 not %run %t-ODR-EXE 2>&1 | FileCheck %s
+
+// Same as above but with clang switches.
+// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared -fsanitize-address-use-odr-indicator %s -o %dynamiclib -DSZ=100
+// RUN: %clangxx_asan -fsanitize-address-use-odr-indicator %s %ld_flags_rpath_exe -o %t-ODR-EXE
+// RUN: %env_asan_opts=fast_unwind_on_malloc=0 not %run %t-ODR-EXE 2>&1 | FileCheck %s
// GNU driver doesn't handle .so files properly.
// REQUIRES: Clang
@@ -52,6 +60,6 @@ int main(int argc, char **argv) {
// CHECK: These globals were registered at these points:
// CHECK: ODR-EXE
-// CHECK: ODR-SO
+// CHECK: odr-violation.cc.dynamic
// CHECK: SUMMARY: AddressSanitizer: odr-violation: global 'foo::G' at {{.*}}odr-violation.cc
// DISABLED: PASS
diff --git a/test/asan/TestCases/Linux/odr-vtable.cc b/test/asan/TestCases/Linux/odr-vtable.cc
new file mode 100644
index 000000000000..fdbab4bb1fd8
--- /dev/null
+++ b/test/asan/TestCases/Linux/odr-vtable.cc
@@ -0,0 +1,26 @@
+// RUN: %clangxx_asan -fno-rtti -DBUILD_SO1 -fPIC -shared %s -o %dynamiclib1
+// RUN: %clangxx_asan -fno-rtti -DBUILD_SO2 -fPIC -shared %s -o %dynamiclib2
+// RUN: %clangxx_asan -fno-rtti %s %ld_flags_rpath_exe1 %ld_flags_rpath_exe2 -o %t
+// RUN: %env_asan_opts=fast_unwind_on_malloc=0:detect_odr_violation=2 not %run %t 2>&1 | FileCheck %s
+
+struct XYZ {
+ virtual void foo();
+};
+
+#if defined(BUILD_SO1)
+
+void XYZ::foo() {}
+
+#elif defined(BUILD_SO2)
+
+void XYZ::foo() {}
+
+#else
+
+int main() {}
+
+#endif
+
+// CHECK: AddressSanitizer: odr-violation
+// CHECK-NEXT: 'vtable for XYZ'
+// CHECK-NEXT: 'vtable for XYZ'
diff --git a/test/asan/TestCases/Linux/odr_c_test.c b/test/asan/TestCases/Linux/odr_c_test.c
index b1d23493b570..c1423ed30b1b 100644
--- a/test/asan/TestCases/Linux/odr_c_test.c
+++ b/test/asan/TestCases/Linux/odr_c_test.c
@@ -1,13 +1,11 @@
// Test that we can properly report an ODR violation
// between an instrumented global and a non-instrumented global.
-// RUN: %clang_asan %s -fPIC -shared -o %t-1.so -DFILE1
-// RUN: %clang_asan %s -fPIC -shared -o %t-2.so -DFILE2
-// RUN: %clang_asan %s -fPIE %t-1.so %t-2.so -Wl,-R`pwd` -o %t
+// RUN: %clang_asan %s -fPIC -shared -o %dynamiclib1 -DFILE1
+// RUN: %clang_asan %s -fPIC -shared -o %dynamiclib2 -DFILE2
+// RUN: %clang_asan %s -fPIE %ld_flags_rpath_exe1 %ld_flags_rpath_exe2 -o %t
// RUN: not %run %t 2>&1 | FileCheck %s
//
-// REQUIRES: x86_64-target-arch
-//
// CHECK: The following global variable is not properly aligned.
// CHECK: ERROR: AddressSanitizer: odr-violation
#if defined(FILE1)
diff --git a/test/asan/TestCases/Linux/odr_indicators.cc b/test/asan/TestCases/Linux/odr_indicators.cc
new file mode 100644
index 000000000000..36176b552906
--- /dev/null
+++ b/test/asan/TestCases/Linux/odr_indicators.cc
@@ -0,0 +1,26 @@
+// RUN: %clangxx_asan -fPIC %s -o %t
+// RUN: %env_asan_opts=report_globals=2 %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,INDICATOR0
+
+// RUN: %clangxx_asan -fsanitize-address-use-odr-indicator -fPIC %s -o %t
+// RUN: %env_asan_opts=report_globals=2 %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,INDICATOR1
+
+#include <stdio.h>
+
+int test_global_1;
+// INDICATOR0-DAG: Added Global{{.*}} name=test_global_1{{.*}} odr_indicator={{0x0+$}}
+// INDICATOR1-DAG: Added Global{{.*}} name=test_global_1{{.*}} odr_indicator={{0x0*[^0]+.*$}}
+
+static int test_global_2;
+// CHECK-DAG: Added Global{{.*}} name=test_global_2{{.*}} odr_indicator={{0xf+$}}
+
+namespace {
+static int test_global_3;
+// CHECK-DAG: Added Global{{.*}} name={{.*}}::test_global_3{{.*}} odr_indicator={{0xf+$}}
+} // namespace
+
+int main() {
+ const char f[] = "%d %d %d\n";
+ // CHECK-DAG: Added Global{{.*}} name=__const.main.f{{.*}} odr_indicator={{0xf+$}}
+ printf(f, test_global_1, test_global_2, test_global_3);
+ return 0;
+}
diff --git a/test/asan/TestCases/Linux/preinit_test.cc b/test/asan/TestCases/Linux/preinit_test.cc
index 10dde67d6a9b..f8c2b6bf52f1 100644
--- a/test/asan/TestCases/Linux/preinit_test.cc
+++ b/test/asan/TestCases/Linux/preinit_test.cc
@@ -1,8 +1,5 @@
-// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316
-// XFAIL: android
-//
-// RUN: %clangxx -DFUNC=zzzz %s -shared -o %t.so -fPIC
-// RUN: %clangxx_asan -DFUNC=main %s -o %t -Wl,-R. %t.so
+// RUN: %clangxx -DFUNC=zzzz %s -shared -o %dynamiclib -fPIC
+// RUN: %clangxx_asan -DFUNC=main %s -o %t %ld_flags_rpath_exe
// RUN: %run %t
// GNU driver doesn't handle .so files properly.
diff --git a/test/asan/TestCases/Linux/preinstalled_signal.cc b/test/asan/TestCases/Linux/preinstalled_signal.cc
index ac4ea93a5418..2b50944c6f2f 100644
--- a/test/asan/TestCases/Linux/preinstalled_signal.cc
+++ b/test/asan/TestCases/Linux/preinstalled_signal.cc
@@ -1,4 +1,3 @@
-// clang-format off
// RUN: %clangxx -std=c++11 %s -o %t
// RUN: env LD_PRELOAD=%shared_libasan %env_asan_opts=handle_segv=1 not %run %t 2>&1 | FileCheck %s
// RUN: env LD_PRELOAD=%shared_libasan %env_asan_opts=handle_segv=2 not %run %t 2>&1 | FileCheck %s
@@ -17,7 +16,6 @@
// This way of setting LD_PRELOAD does not work with Android test runner.
// REQUIRES: !android
-// clang-format on
#include <assert.h>
#include <signal.h>
diff --git a/test/asan/TestCases/Posix/coverage-reset.cc b/test/asan/TestCases/Posix/coverage-reset.cc
index 6d76a309b76f..152be2ab3a29 100644
--- a/test/asan/TestCases/Posix/coverage-reset.cc
+++ b/test/asan/TestCases/Posix/coverage-reset.cc
@@ -5,6 +5,8 @@
//
// UNSUPPORTED: ios
+// XFAIL: i386-netbsd
+
#include <stdio.h>
#include <sanitizer/coverage_interface.h>
diff --git a/test/asan/TestCases/Posix/coverage.cc b/test/asan/TestCases/Posix/coverage.cc
index 12a88402eb5a..9dbd72eabd2c 100644
--- a/test/asan/TestCases/Posix/coverage.cc
+++ b/test/asan/TestCases/Posix/coverage.cc
@@ -18,6 +18,7 @@
//
// https://code.google.com/p/address-sanitizer/issues/detail?id=263
// XFAIL: android
+// XFAIL: i386-netbsd
// UNSUPPORTED: ios
#include <assert.h>
diff --git a/test/asan/TestCases/Posix/dlclose-test.cc b/test/asan/TestCases/Posix/dlclose-test.cc
index 0aafa3e79f6b..160c1c940e6f 100644
--- a/test/asan/TestCases/Posix/dlclose-test.cc
+++ b/test/asan/TestCases/Posix/dlclose-test.cc
@@ -23,6 +23,8 @@
// RUN: %clangxx_asan -O3 -DSHARED_LIB %s -fPIC -shared -o %t-so.so
// RUN: %clangxx_asan -O3 %s %libdl -o %t && %run %t 2>&1 | FileCheck %s
+// XFAIL: i386-netbsd
+
#if !defined(SHARED_LIB)
#include <assert.h>
#include <dlfcn.h>
diff --git a/test/asan/TestCases/Posix/interception-in-shared-lib-test.cc b/test/asan/TestCases/Posix/interception-in-shared-lib-test.cc
index a7d2bfb9b43c..b31b035f9e40 100644
--- a/test/asan/TestCases/Posix/interception-in-shared-lib-test.cc
+++ b/test/asan/TestCases/Posix/interception-in-shared-lib-test.cc
@@ -5,6 +5,8 @@
// RUN: %clangxx_asan -O0 %s -o %t %ld_flags_rpath_exe && \
// RUN: not %run %t 2>&1 | FileCheck %s
+// XFAIL: i386-netbsd
+
#include <stdio.h>
#include <string.h>
diff --git a/test/asan/TestCases/Posix/mmap_limit_mb.cc b/test/asan/TestCases/Posix/mmap_limit_mb.cc
index 508c03fbcb16..cb613f53577a 100644
--- a/test/asan/TestCases/Posix/mmap_limit_mb.cc
+++ b/test/asan/TestCases/Posix/mmap_limit_mb.cc
@@ -9,7 +9,7 @@
// RUN: %env_asan_opts=mmap_limit_mb=300 not %run %t 500 1000000 2>&1 | FileCheck %s
//
// FIXME: Windows doesn't implement mmap_limit_mb.
-// XFAIL: win32
+// XFAIL: windows-msvc
#include <assert.h>
#include <stdlib.h>
diff --git a/test/asan/TestCases/Posix/no-fd.cc b/test/asan/TestCases/Posix/no-fd.cc
new file mode 100644
index 000000000000..086b01412ddb
--- /dev/null
+++ b/test/asan/TestCases/Posix/no-fd.cc
@@ -0,0 +1,43 @@
+// RUN: %clangxx_asan -std=c++11 -O0 %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+// RUN: %env_asan_opts=debug=1,verbosity=2 %run %t 2>&1 | FileCheck %s
+
+// Test ASan initialization
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+extern "C" const char *__asan_default_options() {
+ return "test_only_emulate_no_memorymap=1";
+}
+
+void parent(int argc, char **argv) {
+ fprintf(stderr, "hello\n");
+ // CHECK: hello
+ close(0);
+ close(1);
+ dup2(2, 3);
+ close(2);
+ char *const newargv[] = {argv[0], (char *)"x", nullptr};
+ execv(argv[0], newargv);
+ perror("execve");
+ exit(1);
+}
+
+void child() {
+ assert(dup(3) == 0);
+ assert(dup(3) == 1);
+ assert(dup(3) == 2);
+ fprintf(stderr, "world\n");
+ // CHECK: world
+}
+
+int main(int argc, char **argv) {
+ if (argc == 1) {
+ parent(argc, argv);
+ } else {
+ child();
+ }
+}
diff --git a/test/asan/TestCases/Posix/stack-use-after-return.cc b/test/asan/TestCases/Posix/stack-use-after-return.cc
index 237c880f8e61..02588d708e22 100644
--- a/test/asan/TestCases/Posix/stack-use-after-return.cc
+++ b/test/asan/TestCases/Posix/stack-use-after-return.cc
@@ -17,7 +17,7 @@
// This test runs out of stack on AArch64.
// UNSUPPORTED: aarch64
// stack size log lower than expected
-// XFAIL: freebsd
+// XFAIL: freebsd,netbsd
// FIXME: Fix this test for dynamic runtime on arm linux.
// UNSUPPORTED: (arm-linux || armhf-linux) && asan-dynamic-runtime
@@ -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/Posix/strndup_oob_test.cc b/test/asan/TestCases/Posix/strndup_oob_test.cc
index 326ddcfd6b06..22bbf7c7ef1c 100644
--- a/test/asan/TestCases/Posix/strndup_oob_test.cc
+++ b/test/asan/TestCases/Posix/strndup_oob_test.cc
@@ -7,7 +7,7 @@
// RUN: %clangxx_asan -O3 -xc %s -o %t && not %run %t 2>&1 | FileCheck %s
// Unwind problem on arm: "main" is missing from the allocation stack trace.
-// UNSUPPORTED: win32,s390,arm && !fast-unwinder-works
+// UNSUPPORTED: windows-msvc,s390,arm && !fast-unwinder-works
#include <string.h>
diff --git a/test/asan/TestCases/Posix/strndup_oob_test2.cc b/test/asan/TestCases/Posix/strndup_oob_test2.cc
index 44df6bda9c83..8cc822d7cb3e 100644
--- a/test/asan/TestCases/Posix/strndup_oob_test2.cc
+++ b/test/asan/TestCases/Posix/strndup_oob_test2.cc
@@ -1,22 +1,22 @@
-// RUN: %clang_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
-// RUN: %clang_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s
-// RUN: %clang_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s
-// RUN: %clang_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s
-
-// When built as C on Linux, strndup is transformed to __strndup.
-// RUN: %clang_asan -O3 -xc %s -o %t && not %run %t 2>&1 | FileCheck %s
-
-// Unwind problem on arm: "main" is missing from the allocation stack trace.
-// UNSUPPORTED: win32,s390,arm && !fast-unwinder-works
-
-#include <string.h>
-
-char kChars[] = { 'f', 'o', 'o' };
-
-int main(int argc, char **argv) {
- char *copy = strndup(kChars, 3);
- copy = strndup(kChars, 10);
- // CHECK: AddressSanitizer: global-buffer-overflow
- // CHECK: {{.*}}main {{.*}}.cc:[[@LINE-2]]
- return *copy;
-}
+// RUN: %clang_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clang_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clang_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clang_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+// When built as C on Linux, strndup is transformed to __strndup.
+// RUN: %clang_asan -O3 -xc %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+// Unwind problem on arm: "main" is missing from the allocation stack trace.
+// UNSUPPORTED: windows-msvc,s390,arm && !fast-unwinder-works
+
+#include <string.h>
+
+char kChars[] = { 'f', 'o', 'o' };
+
+int main(int argc, char **argv) {
+ char *copy = strndup(kChars, 3);
+ copy = strndup(kChars, 10);
+ // CHECK: AddressSanitizer: global-buffer-overflow
+ // CHECK: {{.*}}main {{.*}}.cc:[[@LINE-2]]
+ return *copy;
+}
diff --git a/test/asan/TestCases/Posix/tsd_dtor_leak.cc b/test/asan/TestCases/Posix/tsd_dtor_leak.cc
index 9e71ff61cf02..26109fe1a5f4 100644
--- a/test/asan/TestCases/Posix/tsd_dtor_leak.cc
+++ b/test/asan/TestCases/Posix/tsd_dtor_leak.cc
@@ -2,6 +2,9 @@
// https://code.google.com/p/address-sanitizer/issues/detail?id=233
// RUN: %clangxx_asan -O1 %s -pthread -o %t
// RUN: %env_asan_opts=quarantine_size_mb=0 %run %t
+// XFAIL: x86_64-netbsd
+// Assertion fails
+// XFAIL: x86_64-freebsd
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/test/asan/TestCases/asan_and_llvm_coverage_test.cc b/test/asan/TestCases/asan_and_llvm_coverage_test.cc
index 1574a344399b..2ba5a42d1b7b 100644
--- a/test/asan/TestCases/asan_and_llvm_coverage_test.cc
+++ b/test/asan/TestCases/asan_and_llvm_coverage_test.cc
@@ -2,7 +2,8 @@
// RUN: %env_asan_opts=check_initialization_order=1 %run %t 2>&1 | FileCheck %s
// We don't really support running tests using profile runtime on Windows.
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
+
#include <stdio.h>
int foo() { return 1; }
int XXX = foo();
diff --git a/test/asan/TestCases/atoll_strict.c b/test/asan/TestCases/atoll_strict.c
index 2b02354a92eb..0cb568969250 100644
--- a/test/asan/TestCases/atoll_strict.c
+++ b/test/asan/TestCases/atoll_strict.c
@@ -11,7 +11,7 @@
// RUN: %env_asan_opts=strict_string_checks=true not %run %t test3 2>&1 | FileCheck %s --check-prefix=CHECK3
// FIXME: Needs Windows interceptor.
-// XFAIL: win32
+// XFAIL: windows-msvc
#include <assert.h>
#include <stdlib.h>
diff --git a/test/asan/TestCases/heavy_uar_test.cc b/test/asan/TestCases/heavy_uar_test.cc
index 94df0cefc73b..29a1d196567b 100644
--- a/test/asan/TestCases/heavy_uar_test.cc
+++ b/test/asan/TestCases/heavy_uar_test.cc
@@ -1,6 +1,6 @@
// RUN: %clangxx_asan -O0 %s -o %t && %env_asan_opts=detect_stack_use_after_return=1 not %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_asan -O2 %s -o %t && %env_asan_opts=detect_stack_use_after_return=1 not %run %t 2>&1 | FileCheck %s
-// XFAIL: win32
+// XFAIL: windows-msvc
// FIXME: Fix this test under GCC.
// REQUIRES: Clang
@@ -53,8 +53,8 @@ int main(int argc, char **argv) {
RecursiveFunctionWithStackFrame<1024>(depth);
RecursiveFunctionWithStackFrame<2000>(depth);
// The stack size is tight for the main thread in multithread
- // environment on FreeBSD.
-#if !defined(__FreeBSD__)
+ // environment on FreeBSD and NetBSD.
+#if !defined(__FreeBSD__) && !defined(__NetBSD__)
RecursiveFunctionWithStackFrame<5000>(depth);
RecursiveFunctionWithStackFrame<10000>(depth);
#endif
diff --git a/test/asan/TestCases/initialization-bug.cc b/test/asan/TestCases/initialization-bug.cc
index 6ecc6c836c5c..20fdbfe5f59d 100644
--- a/test/asan/TestCases/initialization-bug.cc
+++ b/test/asan/TestCases/initialization-bug.cc
@@ -6,7 +6,7 @@
// Do not test with optimization -- the error may be optimized away.
// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=186
-// XFAIL: win32
+// XFAIL: windows-msvc
// The test is expected to fail on OS X Yosemite and older
// UNSUPPORTED: osx-no-ld64-live_support
diff --git a/test/asan/TestCases/intercept-rethrow-exception.cc b/test/asan/TestCases/intercept-rethrow-exception.cc
index e81dc5398a98..019092a9e8b9 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/interception_failure_test.cc b/test/asan/TestCases/interception_failure_test.cc
index d85500b50486..4f6698e3a537 100644
--- a/test/asan/TestCases/interception_failure_test.cc
+++ b/test/asan/TestCases/interception_failure_test.cc
@@ -11,6 +11,10 @@
// it works with the dynamic runtime.
// XFAIL: win32-static-asan
+// On NetBSD, defining strtol in a static build results in linker errors, but
+// it works with the dynamic runtime.
+// XFAIL: netbsd && !asan-dynamic-runtime
+
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
diff --git a/test/asan/TestCases/intra-object-overflow.cc b/test/asan/TestCases/intra-object-overflow.cc
index 56b5bb2b729f..b71e951bb0b7 100644
--- a/test/asan/TestCases/intra-object-overflow.cc
+++ b/test/asan/TestCases/intra-object-overflow.cc
@@ -5,7 +5,7 @@
// FIXME: fix 32-bits.
// REQUIRES: asan-64-bits, shadow-scale-3
// FIXME: Implement ASan intra-object padding in Clang's MS record layout
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
#include <stdio.h>
#include <stdlib.h>
class Foo {
diff --git a/test/asan/TestCases/log-path_test.cc b/test/asan/TestCases/log-path_test.cc
index 710d22017700..fd33a31d6df9 100644
--- a/test/asan/TestCases/log-path_test.cc
+++ b/test/asan/TestCases/log-path_test.cc
@@ -31,7 +31,7 @@
// RUN: not cat %t.log.*
// FIXME: log_path is not supported on Windows yet.
-// XFAIL: win32
+// XFAIL: windows-msvc
#include <stdlib.h>
#include <string.h>
diff --git a/test/asan/TestCases/pass-object-byval.cc b/test/asan/TestCases/pass-object-byval.cc
index b99360fa7850..f9191c53d708 100644
--- a/test/asan/TestCases/pass-object-byval.cc
+++ b/test/asan/TestCases/pass-object-byval.cc
@@ -5,7 +5,7 @@
// RUN: Assertion{{.*}}failed
// ASan instrumentation can't insert red-zones around inalloca parameters.
-// XFAIL: win32 && asan-32-bits
+// XFAIL: windows-msvc && asan-32-bits
#include <cassert>
diff --git a/test/asan/TestCases/printf-2.c b/test/asan/TestCases/printf-2.c
index 0544847ff5bf..7127347f883b 100644
--- a/test/asan/TestCases/printf-2.c
+++ b/test/asan/TestCases/printf-2.c
@@ -6,7 +6,7 @@
// RUN: %env_asan_opts=replace_str=0:intercept_strlen=0:replace_intrin=0 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s
// FIXME: printf is not intercepted on Windows yet.
-// XFAIL: win32
+// XFAIL: windows-msvc
#include <stdio.h>
#include <stdlib.h>
diff --git a/test/asan/TestCases/printf-3.c b/test/asan/TestCases/printf-3.c
index 010e6c8ef0c2..4f54ff956e3d 100644
--- a/test/asan/TestCases/printf-3.c
+++ b/test/asan/TestCases/printf-3.c
@@ -4,7 +4,11 @@
// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s
// FIXME: printf is not intercepted on Windows yet.
-// XFAIL: win32
+// XFAIL: windows-msvc
+
+// New Bionic rejects %n
+// https://android.googlesource.com/platform/bionic/+/41398d03b7e8e0dfb951660ae713e682e9fc0336
+// UNSUPPORTED: android
#include <stdio.h>
int main() {
diff --git a/test/asan/TestCases/printf-4.c b/test/asan/TestCases/printf-4.c
index 70f4073cc496..2af81b0efa15 100644
--- a/test/asan/TestCases/printf-4.c
+++ b/test/asan/TestCases/printf-4.c
@@ -4,7 +4,7 @@
// FIXME: sprintf is not intercepted on Windows yet. But this test can
// pass if sprintf calls memmove, which is intercepted, so we can't XFAIL it.
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
#include <stdio.h>
int main() {
diff --git a/test/asan/TestCases/printf-5.c b/test/asan/TestCases/printf-5.c
index a614462d2f4a..2257bb4e61aa 100644
--- a/test/asan/TestCases/printf-5.c
+++ b/test/asan/TestCases/printf-5.c
@@ -5,7 +5,7 @@
// RUN: %env_asan_opts=replace_intrin=0 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s
// FIXME: printf is not intercepted on Windows yet.
-// XFAIL: win32
+// XFAIL: windows-msvc
#include <stdio.h>
#include <string.h>
diff --git a/test/asan/TestCases/printf-m.c b/test/asan/TestCases/printf-m.c
index 9cd5ae1c288e..3998a6956f74 100644
--- a/test/asan/TestCases/printf-m.c
+++ b/test/asan/TestCases/printf-m.c
@@ -1,7 +1,7 @@
// RUN: %clang_asan -O2 %s -o %t && %run %t
// FIXME: printf is not intercepted on Windows yet.
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
#include <stdio.h>
diff --git a/test/asan/TestCases/set_shadow_test.c b/test/asan/TestCases/set_shadow_test.c
index daa79a66a389..95bbd829ba85 100644
--- a/test/asan/TestCases/set_shadow_test.c
+++ b/test/asan/TestCases/set_shadow_test.c
@@ -6,7 +6,7 @@
// RUN: not %run %t 0xf5 2>&1 | FileCheck %s -check-prefix=XF5
// RUN: not %run %t 0xf8 2>&1 | FileCheck %s -check-prefix=XF8
-// XFAIL: win32
+// XFAIL: windows-msvc
#include <assert.h>
#include <sanitizer/asan_interface.h>
diff --git a/test/asan/TestCases/strcasestr-1.c b/test/asan/TestCases/strcasestr-1.c
index dccfbcde7709..bb0863de77d2 100644
--- a/test/asan/TestCases/strcasestr-1.c
+++ b/test/asan/TestCases/strcasestr-1.c
@@ -6,7 +6,7 @@
// RUN: %env_asan_opts=intercept_strstr=false:replace_str=false %run %t 2>&1
// There's no interceptor for strcasestr on Windows
-// XFAIL: win32
+// XFAIL: windows-msvc
#define _GNU_SOURCE
#include <assert.h>
diff --git a/test/asan/TestCases/strcasestr-2.c b/test/asan/TestCases/strcasestr-2.c
index 70de2dda437d..35d05c277eae 100644
--- a/test/asan/TestCases/strcasestr-2.c
+++ b/test/asan/TestCases/strcasestr-2.c
@@ -6,7 +6,7 @@
// RUN: %env_asan_opts=intercept_strstr=false:replace_str=false:intercept_strlen=false %run %t 2>&1
// There's no interceptor for strcasestr on Windows
-// XFAIL: win32
+// XFAIL: windows-msvc
#define _GNU_SOURCE
#include <assert.h>
diff --git a/test/asan/TestCases/strcasestr_strict.c b/test/asan/TestCases/strcasestr_strict.c
index 956bee71a390..16adae184b53 100644
--- a/test/asan/TestCases/strcasestr_strict.c
+++ b/test/asan/TestCases/strcasestr_strict.c
@@ -4,7 +4,7 @@
// RUN: %env_asan_opts=strict_string_checks=true not %run %t 2>&1 | FileCheck %s
// There's no interceptor for strcasestr on Windows
-// XFAIL: win32
+// XFAIL: windows-msvc
#define _GNU_SOURCE
#include <assert.h>
diff --git a/test/asan/TestCases/strcat-overlap.cc b/test/asan/TestCases/strcat-overlap.cc
index 89991fbd7881..5539b130f4e2 100644
--- a/test/asan/TestCases/strcat-overlap.cc
+++ b/test/asan/TestCases/strcat-overlap.cc
@@ -31,7 +31,7 @@
// depending on how strcat() is implemented. For now only run
// on platforms where we know the test passes.
// REQUIRES: x86_64h-darwin || x86_64-darwin || i386-darwin || x86_64-linux || i386-linux
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
// UNSUPPORTED: android
#include <string.h>
diff --git a/test/asan/TestCases/strncasecmp_strict.c b/test/asan/TestCases/strncasecmp_strict.c
index aa658402b42b..bd937214092d 100644
--- a/test/asan/TestCases/strncasecmp_strict.c
+++ b/test/asan/TestCases/strncasecmp_strict.c
@@ -14,7 +14,7 @@
// RUN: %env_asan_opts=strict_string_checks=false %run %t i 2>&1
// RUN: %env_asan_opts=strict_string_checks=true not %run %t i 2>&1 | FileCheck %s
-// XFAIL: win32
+// XFAIL: windows-msvc
#include <assert.h>
#include <stdlib.h>
diff --git a/test/asan/TestCases/strtoll_strict.c b/test/asan/TestCases/strtoll_strict.c
index 93cce30f8cb9..3fa800ca4136 100644
--- a/test/asan/TestCases/strtoll_strict.c
+++ b/test/asan/TestCases/strtoll_strict.c
@@ -24,7 +24,7 @@
// FIXME: Enable strtoll interceptor.
// REQUIRES: shadow-scale-3
-// XFAIL: win32
+// XFAIL: windows-msvc
#include <assert.h>
#include <stdlib.h>
diff --git a/test/asan/TestCases/suppressions-exec-relative-location.cc b/test/asan/TestCases/suppressions-exec-relative-location.cc
index d7497566a8c3..3f6d239257d2 100644
--- a/test/asan/TestCases/suppressions-exec-relative-location.cc
+++ b/test/asan/TestCases/suppressions-exec-relative-location.cc
@@ -24,7 +24,7 @@
// RUN: FileCheck --check-prefix=CHECK-WRONG-FILE-NAME %s
// XFAIL: android
-// XFAIL: win32
+// XFAIL: windows-msvc
// UNSUPPORTED: ios
#include <stdio.h>
diff --git a/test/asan/TestCases/suppressions-function.cc b/test/asan/TestCases/suppressions-function.cc
index becefa2ee31c..510a9bc5bb56 100644
--- a/test/asan/TestCases/suppressions-function.cc
+++ b/test/asan/TestCases/suppressions-function.cc
@@ -7,7 +7,7 @@
// RUN: %clangxx_asan -O3 %s -o %t && %env_asan_opts=suppressions='"%t.supp"' %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s
// FIXME: Windows symbolizer needs work to make this pass.
-// XFAIL: android,win32
+// XFAIL: android,windows-msvc
// UNSUPPORTED: ios
// FIXME: atos does not work for inlined functions, yet llvm-symbolizer
diff --git a/test/asan/TestCases/suppressions-library.cc b/test/asan/TestCases/suppressions-library.cc
index 39ede0840105..181ed75826ce 100644
--- a/test/asan/TestCases/suppressions-library.cc
+++ b/test/asan/TestCases/suppressions-library.cc
@@ -11,6 +11,7 @@
// RUN: %env_asan_opts=suppressions='"%t.supp"' %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s
// XFAIL: android
+// XFAIL: i386-netbsd
#include <stdio.h>
#include <stdlib.h>
diff --git a/test/asan/TestCases/throw_catch.cc b/test/asan/TestCases/throw_catch.cc
index 01083510c32f..d7f00ea9e193 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/asan/TestCases/time_interceptor.cc b/test/asan/TestCases/time_interceptor.cc
index 89b2183bcde2..f78af1056af6 100644
--- a/test/asan/TestCases/time_interceptor.cc
+++ b/test/asan/TestCases/time_interceptor.cc
@@ -3,7 +3,7 @@
// Test the time() interceptor.
// There's no interceptor for time() on Windows yet.
-// XFAIL: win32
+// XFAIL: windows-msvc
#include <stdio.h>
#include <stdlib.h>
diff --git a/test/asan/TestCases/verbose-log-path_test.cc b/test/asan/TestCases/verbose-log-path_test.cc
index 8088ff924761..5407d15d6f6e 100644
--- a/test/asan/TestCases/verbose-log-path_test.cc
+++ b/test/asan/TestCases/verbose-log-path_test.cc
@@ -10,7 +10,7 @@
// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t-dir/asan.log.verbose-log-path_test-binary.*
// FIXME: only FreeBSD, NetBSD and Linux have verbose log paths now.
-// XFAIL: win32,android
+// XFAIL: windows-msvc,android
// UNSUPPORTED: ios
#include <stdlib.h>
diff --git a/test/asan/lit.cfg b/test/asan/lit.cfg
index f8994d069ad8..3deb4ccc5f91 100644
--- a/test/asan/lit.cfg
+++ b/test/asan/lit.cfg
@@ -101,8 +101,17 @@ config.substitutions.append( ("%clang ", build_invocation(target_cflags)) )
config.substitutions.append( ("%clangxx ", build_invocation(target_cxxflags)) )
config.substitutions.append( ("%clang_asan ", build_invocation(clang_asan_cflags)) )
config.substitutions.append( ("%clangxx_asan ", build_invocation(clang_asan_cxxflags)) )
-config.substitutions.append( ("%shared_libasan", "libclang_rt.asan%s.so" % config.target_suffix))
if config.asan_dynamic:
+ if config.host_os in ['Linux', 'NetBSD']:
+ shared_libasan_path = os.path.join(config.compiler_rt_libdir, "libclang_rt.asan{}.so".format(config.target_suffix))
+ elif config.host_os == 'Darwin':
+ shared_libasan_path = os.path.join(config.compiler_rt_libdir, 'libclang_rt.asan_{}_dynamic.dylib'.format(config.apple_platform))
+ else:
+ lit_config.warning('%shared_libasan substitution not set but dynamic ASan is available.')
+ shared_libasan_path = None
+
+ if shared_libasan_path is not None:
+ config.substitutions.append( ("%shared_libasan", shared_libasan_path) )
config.substitutions.append( ("%clang_asan_static ", build_invocation(clang_asan_static_cflags)) )
config.substitutions.append( ("%clangxx_asan_static ", build_invocation(clang_asan_static_cxxflags)) )
diff --git a/test/builtins/CMakeLists.txt b/test/builtins/CMakeLists.txt
index a9199ebf10df..eb4391fcab48 100644
--- a/test/builtins/CMakeLists.txt
+++ b/test/builtins/CMakeLists.txt
@@ -13,8 +13,18 @@ configure_lit_site_cfg(
include(builtin-config-ix)
+# Indicate if this is an MSVC environment.
pythonize_bool(MSVC)
+# Indicate if the compiler for the builtins library was MSVC. If the builtins
+# compiler was clang-cl, we will enable some features that the host compiler
+# will not, like C99 _Complex and int128.
+set(BUILTINS_IS_MSVC OFF)
+if (MSVC AND NOT "${CMAKE_C_COMPILER_ID}" MATCHES "Clang")
+ set(BUILTINS_IS_MSVC ON)
+endif()
+pythonize_bool(BUILTINS_IS_MSVC)
+
#TODO: Add support for Apple.
if (NOT APPLE)
foreach(arch ${BUILTIN_SUPPORTED_ARCH})
diff --git a/test/builtins/Unit/absvti2_test.c b/test/builtins/Unit/absvti2_test.c
index 0c0117dfeb45..3c2ded7b76ca 100644
--- a/test/builtins/Unit/absvti2_test.c
+++ b/test/builtins/Unit/absvti2_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- absvti2_test.c - Test __absvti2 -----------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/addvti3_test.c b/test/builtins/Unit/addvti3_test.c
index 3ffcf4b2d822..23e52d345ae2 100644
--- a/test/builtins/Unit/addvti3_test.c
+++ b/test/builtins/Unit/addvti3_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- addvti3_test.c - Test __addvti3 -----------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/ashlti3_test.c b/test/builtins/Unit/ashlti3_test.c
index 06186614ffc3..2dcd4494ffdf 100644
--- a/test/builtins/Unit/ashlti3_test.c
+++ b/test/builtins/Unit/ashlti3_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- ashlti3_test.c - Test __ashlti3 -----------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/ashrti3_test.c b/test/builtins/Unit/ashrti3_test.c
index 1f086535273f..69d0715b7406 100644
--- a/test/builtins/Unit/ashrti3_test.c
+++ b/test/builtins/Unit/ashrti3_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- ashrti3_test.c - Test __ashrti3 -----------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/clzti2_test.c b/test/builtins/Unit/clzti2_test.c
index 157838b6bddc..082583cb3f21 100644
--- a/test/builtins/Unit/clzti2_test.c
+++ b/test/builtins/Unit/clzti2_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- clzti2_test.c - Test __clzti2 -------------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/cmpti2_test.c b/test/builtins/Unit/cmpti2_test.c
index a2215f3c15cb..e74f379c8e7f 100644
--- a/test/builtins/Unit/cmpti2_test.c
+++ b/test/builtins/Unit/cmpti2_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- cmpti2_test.c - Test __cmpti2 -------------------------------------===//
//
// The LLVM Compiler Infrastructure
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 000000000000..79676598089c
--- /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 000000000000..5a4979a4b4da
--- /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 000000000000..2617748e9f14
--- /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/builtins/Unit/ctzti2_test.c b/test/builtins/Unit/ctzti2_test.c
index bef79b6f8694..2ddcf0d0ac82 100644
--- a/test/builtins/Unit/ctzti2_test.c
+++ b/test/builtins/Unit/ctzti2_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- ctzti2_test.c - Test __ctzti2 -------------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/divti3_test.c b/test/builtins/Unit/divti3_test.c
index 9a6bf178b96c..77776e28ab77 100644
--- a/test/builtins/Unit/divti3_test.c
+++ b/test/builtins/Unit/divti3_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- divti3_test.c - Test __divti3 -------------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/ffsti2_test.c b/test/builtins/Unit/ffsti2_test.c
index 3b312c43099f..a7f0ca02ba04 100644
--- a/test/builtins/Unit/ffsti2_test.c
+++ b/test/builtins/Unit/ffsti2_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- ffsti2_test.c - Test __ffsti2 -------------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/fixdfti_test.c b/test/builtins/Unit/fixdfti_test.c
index 0abe187ae953..2b9db0f51396 100644
--- a/test/builtins/Unit/fixdfti_test.c
+++ b/test/builtins/Unit/fixdfti_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- fixdfti_test.c - Test __fixdfti -----------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/fixsfti_test.c b/test/builtins/Unit/fixsfti_test.c
index ec4e8ddb031a..25cf092c4562 100644
--- a/test/builtins/Unit/fixsfti_test.c
+++ b/test/builtins/Unit/fixsfti_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- fixsfti_test.c - Test __fixsfti -----------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/fixunsdfti_test.c b/test/builtins/Unit/fixunsdfti_test.c
index 249d8f886d0b..ca0bb76f1c41 100644
--- a/test/builtins/Unit/fixunsdfti_test.c
+++ b/test/builtins/Unit/fixunsdfti_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- fixunsdfti_test.c - Test __fixunsdfti -----------------------------===//
//
// The LLVM Compiler Infrastructure
@@ -99,25 +100,25 @@ int main()
return 1;
#endif
- if (test__fixunsdfti(0x1.FFFFFFFFFFFFFp+63, 0xFFFFFFFFFFFFF800LL))
+ if (test__fixunsdfti(0x1.FFFFFFFFFFFFFp+63, 0xFFFFFFFFFFFFF800ULL))
return 1;
- if (test__fixunsdfti(0x1.0000000000000p+63, 0x8000000000000000LL))
+ if (test__fixunsdfti(0x1.0000000000000p+63, 0x8000000000000000ULL))
return 1;
- if (test__fixunsdfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00LL))
+ if (test__fixunsdfti(0x1.FFFFFFFFFFFFFp+62, 0x7FFFFFFFFFFFFC00ULL))
return 1;
- if (test__fixunsdfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800LL))
+ if (test__fixunsdfti(0x1.FFFFFFFFFFFFEp+62, 0x7FFFFFFFFFFFF800ULL))
return 1;
- if (test__fixunsdfti(0x1.FFFFFFFFFFFFFp+127, make_ti(0xFFFFFFFFFFFFF800LL, 0)))
+ if (test__fixunsdfti(0x1.FFFFFFFFFFFFFp+127, make_ti(0xFFFFFFFFFFFFF800ULL, 0)))
return 1;
- if (test__fixunsdfti(0x1.0000000000000p+127, make_ti(0x8000000000000000LL, 0)))
+ if (test__fixunsdfti(0x1.0000000000000p+127, make_ti(0x8000000000000000ULL, 0)))
return 1;
- if (test__fixunsdfti(0x1.FFFFFFFFFFFFFp+126, make_ti(0x7FFFFFFFFFFFFC00LL, 0)))
+ if (test__fixunsdfti(0x1.FFFFFFFFFFFFFp+126, make_ti(0x7FFFFFFFFFFFFC00ULL, 0)))
return 1;
- if (test__fixunsdfti(0x1.FFFFFFFFFFFFEp+126, make_ti(0x7FFFFFFFFFFFF800LL, 0)))
+ if (test__fixunsdfti(0x1.FFFFFFFFFFFFEp+126, make_ti(0x7FFFFFFFFFFFF800ULL, 0)))
return 1;
- if (test__fixunsdfti(0x1.0000000000000p+128, make_ti(0xFFFFFFFFFFFFFFFFLL,
- 0xFFFFFFFFFFFFFFFFLL)))
+ if (test__fixunsdfti(0x1.0000000000000p+128, make_ti(0xFFFFFFFFFFFFFFFFULL,
+ 0xFFFFFFFFFFFFFFFFULL)))
return 1;
#if !TARGET_LIBGCC
diff --git a/test/builtins/Unit/fixunssfti_test.c b/test/builtins/Unit/fixunssfti_test.c
index fd2d108f25b9..2306d283b854 100644
--- a/test/builtins/Unit/fixunssfti_test.c
+++ b/test/builtins/Unit/fixunssfti_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- fixunssfti_test.c - Test __fixunssfti -----------------------------===//
//
// The LLVM Compiler Infrastructure
@@ -87,18 +88,18 @@ int main()
return 1;
#endif
- if (test__fixunssfti(0x1.FFFFFEp+63F, 0xFFFFFF0000000000LL))
+ if (test__fixunssfti(0x1.FFFFFEp+63F, 0xFFFFFF0000000000ULL))
return 1;
- if (test__fixunssfti(0x1.000000p+63F, 0x8000000000000000LL))
+ if (test__fixunssfti(0x1.000000p+63F, 0x8000000000000000ULL))
return 1;
if (test__fixunssfti(0x1.FFFFFEp+62F, 0x7FFFFF8000000000LL))
return 1;
if (test__fixunssfti(0x1.FFFFFCp+62F, 0x7FFFFF0000000000LL))
return 1;
- if (test__fixunssfti(0x1.FFFFFEp+127F, make_ti(0xFFFFFF0000000000LL, 0)))
+ if (test__fixunssfti(0x1.FFFFFEp+127F, make_ti(0xFFFFFF0000000000ULL, 0)))
return 1;
- if (test__fixunssfti(0x1.000000p+127F, make_ti(0x8000000000000000LL, 0)))
+ if (test__fixunssfti(0x1.000000p+127F, make_ti(0x8000000000000000ULL, 0)))
return 1;
if (test__fixunssfti(0x1.FFFFFEp+126F, make_ti(0x7FFFFF8000000000LL, 0)))
return 1;
diff --git a/test/builtins/Unit/fixunsxfti_test.c b/test/builtins/Unit/fixunsxfti_test.c
index 256ba0cf452e..4e6fd39dca54 100644
--- a/test/builtins/Unit/fixunsxfti_test.c
+++ b/test/builtins/Unit/fixunsxfti_test.c
@@ -17,7 +17,7 @@
#include "int_lib.h"
#include <stdio.h>
-#ifdef CRT_HAS_128BIT
+#if defined(CRT_HAS_128BIT) && HAS_80_BIT_LONG_DOUBLE
// Returns: convert a to a unsigned long long, rounding toward zero.
// Negative values all become zero.
@@ -55,7 +55,7 @@ char assumption_3[sizeof(long double)*CHAR_BIT == 128] = {0};
int main()
{
-#ifdef CRT_HAS_128BIT
+#if defined(CRT_HAS_128BIT) && HAS_80_BIT_LONG_DOUBLE
if (test__fixunsxfti(0.0, 0))
return 1;
diff --git a/test/builtins/Unit/fixxfdi_test.c b/test/builtins/Unit/fixxfdi_test.c
index 40ad2b04f23b..dcfa75c4b453 100644
--- a/test/builtins/Unit/fixxfdi_test.c
+++ b/test/builtins/Unit/fixxfdi_test.c
@@ -26,7 +26,7 @@
// gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee |
// 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm
-COMPILER_RT_ABI di_int __sfixxfdi(long double a);
+COMPILER_RT_ABI di_int __fixxfdi(long double a);
int test__fixxfdi(long double a, di_int expected)
{
diff --git a/test/builtins/Unit/fixxfti_test.c b/test/builtins/Unit/fixxfti_test.c
index 518ef44fbdb9..86f4c02da5c9 100644
--- a/test/builtins/Unit/fixxfti_test.c
+++ b/test/builtins/Unit/fixxfti_test.c
@@ -17,7 +17,7 @@
#include "int_lib.h"
#include <stdio.h>
-#ifdef CRT_HAS_128BIT
+#if defined(CRT_HAS_128BIT) && HAS_80_BIT_LONG_DOUBLE
// Returns: convert a to a signed long long, rounding toward zero.
@@ -45,15 +45,15 @@ int test__fixxfti(long double a, ti_int expected)
return x != expected;
}
-char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0};
-char assumption_2[sizeof(su_int)*CHAR_BIT == 32] = {0};
-char assumption_3[sizeof(long double)*CHAR_BIT == 128] = {0};
+COMPILE_TIME_ASSERT(sizeof(ti_int) == 2*sizeof(di_int));
+COMPILE_TIME_ASSERT(sizeof(su_int)*CHAR_BIT == 32);
+COMPILE_TIME_ASSERT(sizeof(long double)*CHAR_BIT == 128);
#endif
int main()
{
-#ifdef CRT_HAS_128BIT
+#if defined(CRT_HAS_128BIT) && HAS_80_BIT_LONG_DOUBLE
if (test__fixxfti(0.0, 0))
return 1;
diff --git a/test/builtins/Unit/floattidf_test.c b/test/builtins/Unit/floattidf_test.c
index 9da8c5dc68cb..51178f28e48a 100644
--- a/test/builtins/Unit/floattidf_test.c
+++ b/test/builtins/Unit/floattidf_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- floattidf.c - Test __floattidf ------------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/floattisf_test.c b/test/builtins/Unit/floattisf_test.c
index 9d7282cdea21..9162471a33c5 100644
--- a/test/builtins/Unit/floattisf_test.c
+++ b/test/builtins/Unit/floattisf_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- floattisf_test.c - Test __floattisf -------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/floattixf_test.c b/test/builtins/Unit/floattixf_test.c
index 77a6f7dbf0ce..501a8dc6cd34 100644
--- a/test/builtins/Unit/floattixf_test.c
+++ b/test/builtins/Unit/floattixf_test.c
@@ -18,7 +18,7 @@
#include <float.h>
#include <stdio.h>
-#ifdef CRT_HAS_128BIT
+#if defined(CRT_HAS_128BIT) && HAS_80_BIT_LONG_DOUBLE
// Returns: convert a to a long double, rounding toward even.
@@ -43,15 +43,15 @@ int test__floattixf(ti_int a, long double expected)
return x != expected;
}
-char assumption_1[sizeof(ti_int) == 2*sizeof(di_int)] = {0};
-char assumption_2[sizeof(ti_int)*CHAR_BIT == 128] = {0};
-char assumption_3[sizeof(long double)*CHAR_BIT == 128] = {0};
+COMPILE_TIME_ASSERT(sizeof(ti_int) == 2*sizeof(di_int));
+COMPILE_TIME_ASSERT(sizeof(ti_int)*CHAR_BIT == 128);
+COMPILE_TIME_ASSERT(sizeof(long double)*CHAR_BIT == 128);
#endif
int main()
{
-#ifdef CRT_HAS_128BIT
+#if defined(CRT_HAS_128BIT) && HAS_80_BIT_LONG_DOUBLE
if (test__floattixf(0, 0.0))
return 1;
diff --git a/test/builtins/Unit/floatuntidf_test.c b/test/builtins/Unit/floatuntidf_test.c
index 1bf19ba141b9..f91d0e1bd385 100644
--- a/test/builtins/Unit/floatuntidf_test.c
+++ b/test/builtins/Unit/floatuntidf_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- floatuntidf.c - Test __floatuntidf --------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/floatuntisf_test.c b/test/builtins/Unit/floatuntisf_test.c
index c7c11ba48c52..f460a059e6a1 100644
--- a/test/builtins/Unit/floatuntisf_test.c
+++ b/test/builtins/Unit/floatuntisf_test.c
@@ -16,7 +16,7 @@
#include <float.h>
#include <stdio.h>
-#ifdef CRT_HAS_128BIT
+#if defined(CRT_HAS_128BIT) && HAS_80_BIT_LONG_DOUBLE
// Returns: convert a to a float, rounding toward even.
@@ -40,15 +40,15 @@ int test__floatuntisf(tu_int a, float expected)
return x != expected;
}
-char assumption_1[sizeof(tu_int) == 2*sizeof(du_int)] = {0};
-char assumption_2[sizeof(tu_int)*CHAR_BIT == 128] = {0};
-char assumption_3[sizeof(float)*CHAR_BIT == 32] = {0};
+COMPILE_TIME_ASSERT(sizeof(tu_int) == 2*sizeof(du_int));
+COMPILE_TIME_ASSERT(sizeof(tu_int)*CHAR_BIT == 128);
+COMPILE_TIME_ASSERT(sizeof(float)*CHAR_BIT == 32);
#endif
int main()
{
-#ifdef CRT_HAS_128BIT
+#if defined(CRT_HAS_128BIT) && HAS_80_BIT_LONG_DOUBLE
if (test__floatuntisf(0, 0.0F))
return 1;
diff --git a/test/builtins/Unit/floatuntixf_test.c b/test/builtins/Unit/floatuntixf_test.c
index 0f7ad463450d..a9a8441d0c32 100644
--- a/test/builtins/Unit/floatuntixf_test.c
+++ b/test/builtins/Unit/floatuntixf_test.c
@@ -18,7 +18,7 @@
#include <float.h>
#include <stdio.h>
-#ifdef CRT_HAS_128BIT
+#if defined(CRT_HAS_128BIT) && HAS_80_BIT_LONG_DOUBLE
// Returns: convert a to a long double, rounding toward even.
@@ -43,15 +43,15 @@ int test__floatuntixf(tu_int a, long double expected)
return x != expected;
}
-char assumption_1[sizeof(tu_int) == 2*sizeof(du_int)] = {0};
-char assumption_2[sizeof(tu_int)*CHAR_BIT == 128] = {0};
-char assumption_3[sizeof(long double)*CHAR_BIT == 128] = {0};
+COMPILE_TIME_ASSERT(sizeof(tu_int) == 2*sizeof(du_int));
+COMPILE_TIME_ASSERT(sizeof(tu_int)*CHAR_BIT == 128);
+COMPILE_TIME_ASSERT(sizeof(long double)*CHAR_BIT == 128);
#endif
int main()
{
-#ifdef CRT_HAS_128BIT
+#if defined(CRT_HAS_128BIT) && HAS_80_BIT_LONG_DOUBLE
if (test__floatuntixf(0, 0.0))
return 1;
diff --git a/test/builtins/Unit/lit.cfg b/test/builtins/Unit/lit.cfg
index 4b63948b5ef6..d5fa73b0c64d 100644
--- a/test/builtins/Unit/lit.cfg
+++ b/test/builtins/Unit/lit.cfg
@@ -24,7 +24,7 @@ default_builtins_opts = ''
config.test_source_root = os.path.dirname(__file__)
# Path to the static library
-is_msvc = get_required_attr(config, "builtins_is_msvc")
+is_msvc = get_required_attr(config, "is_msvc")
if is_msvc:
base_lib = os.path.join(config.compiler_rt_libdir, "clang_rt.builtins%s.lib "
% config.target_suffix)
@@ -52,9 +52,15 @@ clang_builtins_static_cxxflags = config.cxx_mode_flags + \
clang_builtins_cflags = clang_builtins_static_cflags
clang_builtins_cxxflags = clang_builtins_static_cxxflags
+# FIXME: Right now we don't compile the C99 complex builtins when using
+# clang-cl. Fix that.
if not is_msvc:
config.available_features.add('c99-complex')
+builtins_is_msvc = get_required_attr(config, "builtins_is_msvc")
+if not builtins_is_msvc:
+ config.available_features.add('int128')
+
clang_wrapper = ""
def build_invocation(compile_flags):
diff --git a/test/builtins/Unit/lit.site.cfg.in b/test/builtins/Unit/lit.site.cfg.in
index 4241bdc9cebc..75d70f703238 100644
--- a/test/builtins/Unit/lit.site.cfg.in
+++ b/test/builtins/Unit/lit.site.cfg.in
@@ -4,7 +4,8 @@ config.name_suffix = "@BUILTINS_TEST_CONFIG_SUFFIX@"
config.builtins_lit_source_dir = "@BUILTINS_LIT_SOURCE_DIR@/Unit"
config.target_cflags = "@BUILTINS_TEST_TARGET_CFLAGS@"
config.target_arch = "@BUILTINS_TEST_TARGET_ARCH@"
-config.builtins_is_msvc = @MSVC_PYBOOL@
+config.is_msvc = @MSVC_PYBOOL@
+config.builtins_is_msvc = @BUILTINS_IS_MSVC_PYBOOL@
# 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/builtins/Unit/lshrti3_test.c b/test/builtins/Unit/lshrti3_test.c
index 91356c8bfe5b..51020edbf84a 100644
--- a/test/builtins/Unit/lshrti3_test.c
+++ b/test/builtins/Unit/lshrti3_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- lshrti3_test.c - Test __lshrti3 -----------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/modti3_test.c b/test/builtins/Unit/modti3_test.c
index 53fbf5bbad06..9faa0b5aff01 100644
--- a/test/builtins/Unit/modti3_test.c
+++ b/test/builtins/Unit/modti3_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- modti3_test.c - Test __modti3 -------------------------------------===//
//
// The LLVM Compiler Infrastructure
@@ -63,17 +64,17 @@ int main()
if (test__modti3(-5, -3, -2))
return 1;
- if (test__modti3(0x8000000000000000LL, 1, 0x0LL))
+ if (test__modti3(0x8000000000000000ULL, 1, 0x0LL))
return 1;
- if (test__modti3(0x8000000000000000LL, -1, 0x0LL))
+ if (test__modti3(0x8000000000000000ULL, -1, 0x0LL))
return 1;
- if (test__modti3(0x8000000000000000LL, 2, 0x0LL))
+ if (test__modti3(0x8000000000000000ULL, 2, 0x0LL))
return 1;
- if (test__modti3(0x8000000000000000LL, -2, 0x0LL))
+ if (test__modti3(0x8000000000000000ULL, -2, 0x0LL))
return 1;
- if (test__modti3(0x8000000000000000LL, 3, 2))
+ if (test__modti3(0x8000000000000000ULL, 3, 2))
return 1;
- if (test__modti3(0x8000000000000000LL, -3, 2))
+ if (test__modti3(0x8000000000000000ULL, -3, 2))
return 1;
if (test__modti3(make_ti(0x8000000000000000LL, 0), 1, 0x0LL))
diff --git a/test/builtins/Unit/muloti4_test.c b/test/builtins/Unit/muloti4_test.c
index e7c78cf16853..9eb56f648a4a 100644
--- a/test/builtins/Unit/muloti4_test.c
+++ b/test/builtins/Unit/muloti4_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- muloti4_test.c - Test __muloti4 -----------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/multi3_test.c b/test/builtins/Unit/multi3_test.c
index 8227f2426860..d92bae68b0c6 100644
--- a/test/builtins/Unit/multi3_test.c
+++ b/test/builtins/Unit/multi3_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- multi3_test.c - Test __multi3 -------------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/mulvti3_test.c b/test/builtins/Unit/mulvti3_test.c
index 36e96ad60f11..f964ed699905 100644
--- a/test/builtins/Unit/mulvti3_test.c
+++ b/test/builtins/Unit/mulvti3_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- mulvti3_test.c - Test __mulvti3 -----------------------------------===//
//
// The LLVM Compiler Infrastructure
@@ -99,13 +100,13 @@ int main()
if (test__mulvti3(-81985529216486895LL, 1, -81985529216486895LL))
return 1;
- if (test__mulvti3(3037000499LL, 3037000499LL, 9223372030926249001LL))
+ if (test__mulvti3(3037000499LL, 3037000499LL, 9223372030926249001ULL))
return 1;
if (test__mulvti3(-3037000499LL, 3037000499LL, -9223372030926249001LL))
return 1;
if (test__mulvti3(3037000499LL, -3037000499LL, -9223372030926249001LL))
return 1;
- if (test__mulvti3(-3037000499LL, -3037000499LL, 9223372030926249001LL))
+ if (test__mulvti3(-3037000499LL, -3037000499LL, 9223372030926249001ULL))
return 1;
if (test__mulvti3(4398046511103LL, 2097152LL, 9223372036852678656LL))
@@ -117,7 +118,7 @@ int main()
if (test__mulvti3(-4398046511103LL, -2097152LL, 9223372036852678656LL))
return 1;
- if (test__mulvti3(2097152LL, 4398046511103LL, 9223372036852678656LL))
+ if (test__mulvti3(2097152LL, 4398046511103LL, 9223372036852678656ULL))
return 1;
if (test__mulvti3(-2097152LL, 4398046511103LL, -9223372036852678656LL))
return 1;
diff --git a/test/builtins/Unit/negti2_test.c b/test/builtins/Unit/negti2_test.c
index bb7379cae2b1..89eb0b04f99e 100644
--- a/test/builtins/Unit/negti2_test.c
+++ b/test/builtins/Unit/negti2_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- negti2_test.c - Test __negti2 -------------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/negvti2_test.c b/test/builtins/Unit/negvti2_test.c
index 980f44869327..9c2765ec6b07 100644
--- a/test/builtins/Unit/negvti2_test.c
+++ b/test/builtins/Unit/negvti2_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- negvti2_test.c - Test __negvti2 -----------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/parityti2_test.c b/test/builtins/Unit/parityti2_test.c
index bcd26d0af846..11d578be50e2 100644
--- a/test/builtins/Unit/parityti2_test.c
+++ b/test/builtins/Unit/parityti2_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- parityti2_test.c - Test __parityti2 -------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/popcountti2_test.c b/test/builtins/Unit/popcountti2_test.c
index ef8b2c3836bc..91c169fb550a 100644
--- a/test/builtins/Unit/popcountti2_test.c
+++ b/test/builtins/Unit/popcountti2_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- popcountti2_test.c - Test __popcountti2 ----------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/ppc/fixtfdi_test.c b/test/builtins/Unit/ppc/fixtfdi_test.c
index ea6c40563777..0dc6636552c4 100644
--- a/test/builtins/Unit/ppc/fixtfdi_test.c
+++ b/test/builtins/Unit/ppc/fixtfdi_test.c
@@ -1,5 +1,5 @@
-// REQUIRES: powerpc-registered-target
-// RUN: %clang_builtins %s -o %t && %run %t
+// REQUIRES: target-is-powerpc64le
+// RUN: %clang_builtins %s %librt -o %t && %run %t
#include <stdio.h>
#include <limits.h>
#include <stdint.h>
@@ -476,4 +476,4 @@ int main(int argc, char *argv[]) {
}
return 0;
-} \ No newline at end of file
+}
diff --git a/test/builtins/Unit/ppc/fixunstfti_test.c b/test/builtins/Unit/ppc/fixunstfti_test.c
new file mode 100644
index 000000000000..0eee31db1b5d
--- /dev/null
+++ b/test/builtins/Unit/ppc/fixunstfti_test.c
@@ -0,0 +1,52 @@
+// REQUIRES: target-is-powerpc64le
+// RUN: %clang_builtins %s %librt -o %t && %run %t
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "fixunstfti_test.h"
+
+/* The long double representation, with the high and low portions of
+ * the long double, and the corresponding bit patterns of each double. */
+typedef union {
+ long double ld;
+ double d[2]; /* [0] is the high double, [1] is the low double. */
+ unsigned long long ull[2]; /* High and low doubles as 64-bit integers. */
+} ldUnion;
+
+__uint128_t __fixunstfti(long double);
+
+int main(int argc, char *argv[]) {
+ /* Necessary long double and (unsigned) 128 bit integer
+ * declarations used to compare the computed and expected results
+ * from converting the IBM double-double to int128. */
+ ldUnion ldInput;
+ __uint128_t expectedResult, computedResult;
+
+ for (int i = 0; i < numTests; ++i) {
+ /* Set the expected 128 bit integer and the high and low
+ * values of the long double input. */
+ ldInput.d[0] = testList[i].hiInput;
+ ldInput.d[1] = testList[i].loInput;
+ expectedResult = testList[i].result128;
+
+ /* Get the computed 128 bit integer from the long double->
+ * uint128 conversion, and check for errors between results. */
+ computedResult = __fixunstfti(ldInput.ld);
+
+ if (computedResult != expectedResult) {
+ printf("Error for __fixunstfti at input %La = ( %a , %a ):\n", ldInput.ld,
+ ldInput.d[0], ldInput.d[1]);
+ printf("\tExpected __uint128_t: 0x%016llx 0x%016llx\n",
+ (unsigned long long)(expectedResult >> 64),
+ (unsigned long long)expectedResult);
+ printf("\tComputed __uint128_t: 0x%016llx 0x%016llx\n\n",
+ (unsigned long long)(computedResult >> 64),
+ (unsigned long long)computedResult);
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/test/builtins/Unit/ppc/fixunstfti_test.h b/test/builtins/Unit/ppc/fixunstfti_test.h
new file mode 100644
index 000000000000..d7de0ce14303
--- /dev/null
+++ b/test/builtins/Unit/ppc/fixunstfti_test.h
@@ -0,0 +1,706 @@
+/*
+* Test case inputs for: __uint128_t __fixunstfti (long double)
+* Conversion from long double (IBM double-double) to 128 bit integer.
+*/
+
+#define INFINITY __builtin_inf()
+#define QNAN __builtin_nan("")
+#define INIT_U128(HI, LO) (((__uint128_t) (HI) << 64) | (LO))
+
+struct testCase {
+ double hiInput;
+ double loInput;
+ __uint128_t result128;
+};
+
+struct testCase testList[] = {
+ { 0x0p+0, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { -0x0p+0, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { -0x0p+0, -0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x0p+0, -0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { -0x1p+0, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x1p+0, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000001 ) },
+ { -INFINITY, 0x0p+0, ((__uint128_t)0x0000000000000000 << 64) | 0x0000000000000000 },
+ { INFINITY, 0x0p+0, ((__uint128_t)0xffffffffffffffff << 64) | 0xffffffffffffffff },
+ { QNAN, 0x0p+0, ((__uint128_t)0x7ff8000000000000 << 64) | 0x0000000000000000 },
+ { -QNAN, 0x0p+0, ((__uint128_t)0x7ff8000000000000 << 64) | 0x0000000000000000 },
+ { -0x1p+127, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x1p+127, -0x1p+0, INIT_U128( 0x7fffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1p+0, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000001 ) },
+ { 0x1p+60, 0x0p+0, INIT_U128( 0x0000000000000000, 0x1000000000000000 ) },
+ { 0x1p+64, -0x1p+0, INIT_U128( 0x0000000000000000, 0xffffffffffffffff ) },
+ { 0x1p+63, -0x1p+0, INIT_U128( 0x0000000000000000, 0x7fffffffffffffff ) },
+ { 0x1p+64, 0x0p+0, INIT_U128( 0x0000000000000001, 0x0000000000000000 ) },
+ { 0x1p+64, 0x1p+0, INIT_U128( 0x0000000000000001, 0x0000000000000001 ) },
+ { 0x1.8p+64, -0x1p+0, INIT_U128( 0x0000000000000001, 0x7fffffffffffffff ) },
+ { 0x1.1p+64, 0x0p+0, INIT_U128( 0x0000000000000001, 0x1000000000000000 ) },
+ { 0x1p+65, -0x1p+0, INIT_U128( 0x0000000000000001, 0xffffffffffffffff ) },
+ { 0x1p+127, -0x1p+64, INIT_U128( 0x7fffffffffffffff, 0x0000000000000000 ) },
+ { 0x1p+127, -0x1.ep+64, INIT_U128( 0x7ffffffffffffffe, 0x2000000000000000 ) },
+ { 0x1p+127, -0x1p+63, INIT_U128( 0x7fffffffffffffff, 0x8000000000000000 ) },
+ { 0x1p+124, 0x0p+0, INIT_U128( 0x1000000000000000, 0x0000000000000000 ) },
+ { 0x1p+124, 0x1p+0, INIT_U128( 0x1000000000000000, 0x0000000000000001 ) },
+ { 0x1p+124, 0x1p+63, INIT_U128( 0x1000000000000000, 0x8000000000000000 ) },
+ { 0x1p+124, 0x1p+64, INIT_U128( 0x1000000000000001, 0x0000000000000000 ) },
+ { -0x1p+64, 0x0p+0, INIT_U128( 0x00000000000000000, 0x0000000000000000 ) },
+ { 0x1.84p+70, 0x1.84p+6, INIT_U128( 0x0000000000000061, 0x0000000000000061 ) },
+ { 0x1.5cp+6, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000057 ) },
+ { 0x1p+64, -0x1.88p+6, INIT_U128( 0x0000000000000000, 0xffffffffffffff9e ) },
+ { 0x1.88p+6, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000062 ) },
+ { 0x1.00cp+10, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000403 ) },
+ { 0x1.fffffffffffffp+63, 0x1.fep+9, INIT_U128( 0x0000000000000000, 0xfffffffffffffbfc ) },
+ { 0x1.028p+10, 0x0p+0, INIT_U128( 0x0000000000000000, 0x000000000000040a ) },
+ { 0x1.44p+10, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000510 ) },
+ { 0x1.fffffffffffffp+63, 0x1.738p+9, INIT_U128( 0x0000000000000000, 0xfffffffffffffae7 ) },
+ { 0x1.808p+10, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000602 ) },
+ { 0x1.fffffffffffffp+63, 0x1.fdp+8, INIT_U128( 0x0000000000000000, 0xfffffffffffff9fd ) },
+ { 0x1.048p+13, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000002090 ) },
+ { 0x1.ffffffffffffbp+63, 0x1.ed8p+9, INIT_U128( 0x0000000000000000, 0xffffffffffffdbdb ) },
+ { 0x1.0101p+17, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000020202 ) },
+ { 0x1.fffffffffffbep+63, -0x1.09p+8, INIT_U128( 0x0000000000000000, 0xfffffffffffdeef7 ) },
+ { 0x1.9002p+17, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000032004 ) },
+ { 0x1.fffffffffff9cp+63, -0x1.4p+2, INIT_U128( 0x0000000000000000, 0xfffffffffffcdffb ) },
+ { 0x1.902p+17, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000032040 ) },
+ { 0x1.ffffffffff7fcp+63, -0x1.14p+6, INIT_U128( 0x0000000000000000, 0xffffffffffbfdfbb ) },
+ { 0x1.00822p+22, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000402088 ) },
+ { 0x1.0010011p+31, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000080080088 ) },
+ { 0x1.0a000001p+35, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000850000008 ) },
+ { 0x1.000000224p+37, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000002000000448 ) },
+ { 0x1.ffffffbffefb8p+63, -0x1p+0, INIT_U128( 0x0000000000000000, 0xffffffdfff7dbfff ) },
+ { 0x1.00080044p+102, 0x1.00080044p+38, INIT_U128( 0x0000004002001100, 0x0000004002001100 ) },
+ { 0x1.00400000018p+107, 0x1.00400000018p+43, INIT_U128( 0x000008020000000c, 0x000008020000000c ) },
+ { 0x1.ffffeffcp+63, -0x1.ap+3, INIT_U128( 0x0000000000000000, 0xfffff7fdfffffff3 ) },
+ { 0x1.000000001048p+47, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000800000000824 ) },
+ { 0x1.fffbffffffp+63, -0x1.808p+9, INIT_U128( 0x0000000000000000, 0xfffdffffff7ffcff ) },
+ { 0x1.000810004p+62, 0x0p+0, INIT_U128( 0x0000000000000000, 0x4002040010000000 ) },
+ { 0x1.7ffbf7ffep+63, -0x1p+0, INIT_U128( 0x0000000000000000, 0xbffdfbffefffffff ) },
+ { 0x1p+63, 0x0p+0, INIT_U128( 0x0000000000000000, 0x8000000000000000 ) },
+ { 0x1.ffffffffe8319p+63, -0x1.1f8p+9, INIT_U128( 0x0000000000000000, 0xfffffffff418c5c1 ) },
+ { 0x1p+68, -0x1p+0, INIT_U128( 0x000000000000000f, 0xffffffffffffffff ) },
+ { 0x1p+72, -0x1p+0, INIT_U128( 0x00000000000000ff, 0xffffffffffffffff ) },
+ { 0x1p+76, -0x1p+0, INIT_U128( 0x0000000000000fff, 0xffffffffffffffff ) },
+ { 0x1p+80, -0x1p+0, INIT_U128( 0x000000000000ffff, 0xffffffffffffffff ) },
+ { 0x1p+84, -0x1p+0, INIT_U128( 0x00000000000fffff, 0xffffffffffffffff ) },
+ { 0x1p+88, -0x1p+0, INIT_U128( 0x0000000000ffffff, 0xffffffffffffffff ) },
+ { 0x1p+92, -0x1p+0, INIT_U128( 0x000000000fffffff, 0xffffffffffffffff ) },
+ { 0x1p+96, -0x1p+0, INIT_U128( 0x00000000ffffffff, 0xffffffffffffffff ) },
+ { 0x1p+100, -0x1p+0, INIT_U128( 0x0000000fffffffff, 0xffffffffffffffff ) },
+ { 0x1p+104, -0x1p+0, INIT_U128( 0x000000ffffffffff, 0xffffffffffffffff ) },
+ { 0x1p+108, -0x1p+0, INIT_U128( 0x00000fffffffffff, 0xffffffffffffffff ) },
+ { 0x1p+112, -0x1p+0, INIT_U128( 0x0000ffffffffffff, 0xffffffffffffffff ) },
+ { 0x1p+116, -0x1p+0, INIT_U128( 0x000fffffffffffff, 0xffffffffffffffff ) },
+ { 0x1p+120, -0x1p+0, INIT_U128( 0x00ffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1p+124, -0x1p+0, INIT_U128( 0x0fffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1p+124, 0x0p+0, INIT_U128( 0x1000000000000000, 0x0000000000000000 ) },
+ { 0x1p+124, 0x1.1p+4, INIT_U128( 0x1000000000000000, 0x0000000000000011 ) },
+ { 0x1p+124, 0x1.11p+8, INIT_U128( 0x1000000000000000, 0x0000000000000111 ) },
+ { 0x1p+124, 0x1.111p+12, INIT_U128( 0x1000000000000000, 0x0000000000001111 ) },
+ { 0x1p+124, 0x1.1111p+16, INIT_U128( 0x1000000000000000, 0x0000000000011111 ) },
+ { 0x1p+124, 0x1.11111p+20, INIT_U128( 0x1000000000000000, 0x0000000000111111 ) },
+ { 0x1p+124, 0x1.111111p+24, INIT_U128( 0x1000000000000000, 0x0000000001111111 ) },
+ { 0x1p+124, 0x1.1111111p+28, INIT_U128( 0x1000000000000000, 0x0000000011111111 ) },
+ { 0x1p+124, 0x1.11111111p+32, INIT_U128( 0x1000000000000000, 0x0000000111111111 ) },
+ { 0x1p+124, 0x1.111111111p+36, INIT_U128( 0x1000000000000000, 0x0000001111111111 ) },
+ { 0x1p+124, 0x1.1111111111p+40, INIT_U128( 0x1000000000000000, 0x0000011111111111 ) },
+ { 0x1p+124, 0x1.11111111111p+44, INIT_U128( 0x1000000000000000, 0x0000111111111111 ) },
+ { 0x1p+124, 0x1.111111111111p+48, INIT_U128( 0x1000000000000000, 0x0001111111111111 ) },
+ { 0x1p+124, 0x1.1111111111111p+52, INIT_U128( 0x1000000000000000, 0x0011111111111111 ) },
+ { 0x1p+124, 0x1.11111111111111p+56, INIT_U128( 0x1000000000000000, 0x0111111111111110 ) },
+ { 0x1p+124, 0x1.111111111111111p+60, INIT_U128( 0x1000000000000000, 0x1111111111111100 ) },
+ { 0x1.6ffffffefp+63, -0x1p+0, INIT_U128( 0x0000000000000000, 0xb7ffffff77ffffff ) },
+ { 0x1p+106, 0x0p+0, INIT_U128( 0x0000040000000000, 0x0000000000000000 ) },
+ { 0x1.ff8p+29, 0x0p+0, INIT_U128( 0x0000000000000000, 0x000000003ff00000 ) },
+ { 0x1.6ff7ffffffffcp+63, -0x1p+0, INIT_U128( 0x0000000000000000, 0xb7fbffffffffdfff ) },
+ { 0x1.2400000000004p+62, 0x0p+0, INIT_U128( 0x0000000000000000, 0x4900000000001000 ) },
+ { 0x1.24000000001p+126, 0x1.24000000001p+62, INIT_U128( 0x4900000000040000, 0x4900000000040000 ) },
+ { 0x1.2400001p+126, 0x1.2400001p+62, INIT_U128( 0x4900000400000000, 0x4900000400000000 ) },
+ { 0x1.240001p+126, 0x1.240001p+62, INIT_U128( 0x4900004000000000, 0x4900004000000000 ) },
+ { 0x1.24001p+126, 0x1.24001p+62, INIT_U128( 0x4900040000000000, 0x4900040000000000 ) },
+ { 0x1.24008p+126, 0x1.24008p+62, INIT_U128( 0x4900200000000000, 0x4900200000000000 ) },
+ { 0x1.2404p+126, 0x1.2404p+62, INIT_U128( 0x4901000000000000, 0x4901000000000000 ) },
+ { 0x1.244p+126, 0x1.244p+62, INIT_U128( 0x4910000000000000, 0x4910000000000000 ) },
+ { 0x1.26p+126, 0x1.26p+62, INIT_U128( 0x4980000000000000, 0x4980000000000000 ) },
+ { 0x1.3p+126, 0x1.3p+62, INIT_U128( 0x4c00000000000000, 0x4c00000000000000 ) },
+ { 0x1.800000000001p+126, 0x1.6000000000004p+64, INIT_U128( 0x6000000000004001, 0x6000000000004000 ) },
+ { 0x1.cp+126, 0x1.00ep+71, INIT_U128( 0x7000000000000080, 0x7000000000000000 ) },
+ { 0x1.c000000000008p+126, 0x1.c000000000008p+62, INIT_U128( 0x7000000000002000, 0x7000000000002000 ) },
+ { 0x1.c00000000004p+126, 0x1.c00000000004p+62, INIT_U128( 0x7000000000010000, 0x7000000000010000 ) },
+ { 0x1.c0000000002p+126, 0x1.c0000000002p+62, INIT_U128( 0x7000000000080000, 0x7000000000080000 ) },
+ { 0x1.c000000002p+126, 0x1.c000000002p+62, INIT_U128( 0x7000000000800000, 0x7000000000800000 ) },
+ { 0x1.c00000002p+126, 0x1.c00000002p+62, INIT_U128( 0x7000000008000000, 0x7000000008000000 ) },
+ { 0x1.c0000008p+126, 0x1.c0000008p+62, INIT_U128( 0x7000000200000000, 0x7000000200000000 ) },
+ { 0x1.c000002p+126, 0x1.c000002p+62, INIT_U128( 0x7000000800000000, 0x7000000800000000 ) },
+ { 0x1.c00004p+126, 0x1.c00004p+62, INIT_U128( 0x7000010000000000, 0x7000010000000000 ) },
+ { 0x1.c0002p+126, 0x1.c0002p+62, INIT_U128( 0x7000080000000000, 0x7000080000000000 ) },
+ { 0x1.c008p+126, 0x1.c008p+62, INIT_U128( 0x7002000000000000, 0x7002000000000000 ) },
+ { 0x1.c02p+126, 0x1.c02p+62, INIT_U128( 0x7008000000000000, 0x7008000000000000 ) },
+ { 0x1.c2p+126, 0x1.c2p+62, INIT_U128( 0x7080000000000000, 0x7080000000000000 ) },
+ { 0x1.dp+126, 0x1.dp+62, INIT_U128( 0x7400000000000000, 0x7400000000000000 ) },
+ { 0x1.80be0cccccccdp+63, 0x1.80bc266666666p+63, INIT_U128( 0x0000000000000001, 0x80bd199999999800 ) },
+ { 0x1.017c19999999ap+62, 0x1.01784cccccccdp+62, INIT_U128( 0x0000000000000000, 0x80bd199999999c00 ) },
+ { 0x1.80be0cccccccdp+63, 0x1.01784cccccccdp+62, INIT_U128( 0x0000000000000001, 0x00bd199999999c00 ) },
+ { 0x1.88a1831790ce8p+63, 0x1.1143062f219d8p+62, INIT_U128( 0x0000000000000001, 0x08a1831790cea000 ) },
+ { 0x1.1143062f219dp+62, 0x1.88a1831790cecp+63, INIT_U128( 0x0000000000000001, 0x08a1831790cea000 ) },
+ { 0x1.88a1831790ce8p+63, 0x1.88a1831790cecp+63, INIT_U128( 0x0000000000000001, 0x88a1831790cea000 ) },
+ { 0x1.00014f3089001p+64, 0x1.e133333333333p+6, INIT_U128( 0x0000000000000001, 0x00014f3089001078 ) },
+ { 0x1.fffffffffffffp+127, 0x1p+75, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x1.fffffffffffffp+127, 0x1.fffffffffffffp+74, INIT_U128( 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFC00000 ) },
+ { 0x1.f066666666666p+6, -0x1.f066666666667p-768, INIT_U128( 0x0000000000000000, 0x000000000000007C ) },
+ { 0x1.f066666666666p+6, 0x1.f066666666667p-768, INIT_U128( 0x0000000000000000, 0x000000000000007C ) },
+ { 0x1.1111111111111p+124, 0x1.1p+68, INIT_U128( 0x1111111111111111, 0x0000000000000000 ) },
+ { 0x0.0000000000001p-1022, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x0.0000001160e9fp-1022, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x0.0000001a26f3cp-1022, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { -0x0.0000001134f35p-1022, -0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x0.00003f9eec3ep-1022, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x0.0a40bec0e4818p-1022, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x0.0d4e1fcc5a9c4p-1022, 0x0p+0, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x1.acf5f5ef59ebfp-1007, 0x0.00000000000a8p-1022, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x1.1f45384e3e8a7p-1007, 0x0.00000000000b4p-1022, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x1.8474983f08e93p-1007, 0x0.00000000000d9p-1022, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x1.47ad6cf88f5aep-1007, 0x0.00000000000bcp-1022, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x1.2da4480a5b489p-1, 0x1.9e20ea6f3c41dp-618, INIT_U128( 0x0000000000000000, 0x0000000000000000 ) },
+ { 0x1.438fba0a871f8p+0, 0x1.086e1f6e10dc4p-992, INIT_U128( 0x0000000000000000, 0x0000000000000001 ) },
+ { 0x1.51c874cca390ep+0, 0x1.5dd30576bba6p-992, INIT_U128( 0x0000000000000000, 0x0000000000000001 ) },
+ { 0x1.3691a8086d235p+1, 0x1.3670ebb66ce1ep-281, INIT_U128( 0x0000000000000000, 0x0000000000000002 ) },
+ { 0x1.b800c73570019p+1, 0x1.1bf9094c37f21p-281, INIT_U128( 0x0000000000000000, 0x0000000000000003 ) },
+ { 0x1.c1e89f3783d14p+1, 0x1.69999266d3332p-281, INIT_U128( 0x0000000000000000, 0x0000000000000003 ) },
+ { 0x1.390b19fa72163p+1, 0x1.25bf7f6c4b7fp-281, INIT_U128( 0x0000000000000000, 0x0000000000000002 ) },
+ { 0x1.4beee12897ddcp+2, 0x1.549d853aa93bp-815, INIT_U128( 0x0000000000000000, 0x0000000000000005 ) },
+ { 0x1.e37742bfc6ee9p+2, 0x1.081a6dda1034ep-815, INIT_U128( 0x0000000000000000, 0x0000000000000007 ) },
+ { 0x1.e6c0b50fcd816p+2, 0x1.c3d7967b87af3p-815, INIT_U128( 0x0000000000000000, 0x0000000000000007 ) },
+ { 0x1.590290feb2052p+2, 0x1.07756f600eeaep-815, INIT_U128( 0x0000000000000000, 0x0000000000000005 ) },
+ { 0x1.66154348cc2a8p+3, 0x1.bb93d1a97727ap-619, INIT_U128( 0x0000000000000000, 0x000000000000000b ) },
+ { 0x1.69cf9e9ed39f4p+3, 0x1.ce842e739d086p-619, INIT_U128( 0x0000000000000000, 0x000000000000000b ) },
+ { 0x1.e884574bd108bp+3, 0x1.9c6561f338cacp-619, INIT_U128( 0x0000000000000000, 0x000000000000000f ) },
+ { 0x1.6263a430c4c74p+4, 0x1.6b327792d664fp-117, INIT_U128( 0x0000000000000000, 0x0000000000000016 ) },
+ { 0x1.43637d1286c7p+4, 0x1.546df60aa8dbfp-117, INIT_U128( 0x0000000000000000, 0x0000000000000014 ) },
+ { 0x1.faa21259f5443p+4, 0x1.b6708ecf6ce12p-117, INIT_U128( 0x0000000000000000, 0x000000000000001f ) },
+ { 0x1.fd3105d1fa62p+4, 0x1.370912046e122p-117, INIT_U128( 0x0000000000000000, 0x000000000000001f ) },
+ { 0x1.cf952d399f2a5p+5, 0x1.0d283ebe1a508p-735, INIT_U128( 0x0000000000000000, 0x0000000000000039 ) },
+ { 0x1.6ac8f880d591fp+5, 0x1.ec284da5d8509p-735, INIT_U128( 0x0000000000000000, 0x000000000000002d ) },
+ { 0x1.919c82bf2339p+5, 0x1.a58971fd4b12ep-735, INIT_U128( 0x0000000000000000, 0x0000000000000032 ) },
+ { 0x1.789a89ccf1351p+5, 0x1.ebd48283d7a91p-735, INIT_U128( 0x0000000000000000, 0x000000000000002f ) },
+ { 0x1.8db2068d1b641p+6, 0x1.056174ca0ac2ep-802, INIT_U128( 0x0000000000000000, 0x0000000000000063 ) },
+ { 0x1.8d618e6f1ac32p+6, 0x1.0643da660c87cp-802, INIT_U128( 0x0000000000000000, 0x0000000000000063 ) },
+ { 0x1.f10fc45fe21f8p+6, 0x1.d71f2d6bae3e5p-802, INIT_U128( 0x0000000000000000, 0x000000000000007c ) },
+ { 0x1.1cafcb76395fap+8, 0x1.026dfe9a04dcp-78, INIT_U128( 0x0000000000000000, 0x000000000000011c ) },
+ { 0x1.8b9a3b5917348p+8, 0x1.f64ef081ec9dep-78, INIT_U128( 0x0000000000000000, 0x000000000000018b ) },
+ { 0x1.347ea22c68fd4p+8, 0x1.a86c77e750d8fp-78, INIT_U128( 0x0000000000000000, 0x0000000000000134 ) },
+ { 0x1.c498c82389319p+8, 0x1.0d4d96061a9b3p-78, INIT_U128( 0x0000000000000000, 0x00000000000001c4 ) },
+ { 0x1.3e4fa8b47c9f5p+9, 0x1.d0718139a0e3p-293, INIT_U128( 0x0000000000000000, 0x000000000000027c ) },
+ { 0x1.5ffd3878bffa7p+9, 0x1.1828a29c30514p-293, INIT_U128( 0x0000000000000000, 0x00000000000002bf ) },
+ { 0x1.bbbb3b8577768p+9, 0x1.ba79b21b74f36p-293, INIT_U128( 0x0000000000000000, 0x0000000000000377 ) },
+ { 0x1.8777cb810eefap+9, 0x1.bb3afdc57676p-293, INIT_U128( 0x0000000000000000, 0x000000000000030e ) },
+ { 0x1.8e7992a11cf32p+10, 0x1.a18e4033431c8p-789, INIT_U128( 0x0000000000000000, 0x0000000000000639 ) },
+ { 0x1.7684faeced0ap+10, 0x1.5f11706abe22ep-789, INIT_U128( 0x0000000000000000, 0x00000000000005da ) },
+ { 0x1.7d7ac11afaf58p+10, 0x1.64553beec8aa8p-789, INIT_U128( 0x0000000000000000, 0x00000000000005f5 ) },
+ { 0x1.a540fa454a81fp+10, 0x1.329efbd6653ep-789, INIT_U128( 0x0000000000000000, 0x0000000000000695 ) },
+ { 0x1.92ed812325dbp+11, 0x0.000012eafc716p-1022, INIT_U128( 0x0000000000000000, 0x0000000000000c97 ) },
+ { 0x1.b10abb4562158p+11, 0x0.00001e4765564p-1022, INIT_U128( 0x0000000000000000, 0x0000000000000d88 ) },
+ { 0x1.f6751879ecea3p+11, 0x0.000014435b4b9p-1022, INIT_U128( 0x0000000000000000, 0x0000000000000fb3 ) },
+ { 0x1.32e6b0a465cd6p+11, 0x0.00001729d5c09p-1022, INIT_U128( 0x0000000000000000, 0x0000000000000997 ) },
+ { 0x1.11f336c623e67p+12, 0x1.9d9af61f3b35fp-489, INIT_U128( 0x0000000000000000, 0x000000000000111f ) },
+ { 0x1.18ebab5631d76p+12, 0x1.47d8de9e8fb1cp-489, INIT_U128( 0x0000000000000000, 0x000000000000118e ) },
+ { 0x1.c25a29f984b45p+12, 0x1.c65b51e78cb6ap-489, INIT_U128( 0x0000000000000000, 0x0000000000001c25 ) },
+ { 0x1.cf37f3299e6ffp+12, 0x1.38601ed270c04p-489, INIT_U128( 0x0000000000000000, 0x0000000000001cf3 ) },
+ { 0x1.00db141c01b62p+13, 0x1.a4ea801149d5p-963, INIT_U128( 0x0000000000000000, 0x000000000000201b ) },
+ { 0x1.b81f3643703e7p+13, 0x1.849a11a509342p-963, INIT_U128( 0x0000000000000000, 0x0000000000003703 ) },
+ { 0x1.6dbbb6a4db777p+13, 0x1.362f46546c5e9p-963, INIT_U128( 0x0000000000000000, 0x0000000000002db7 ) },
+ { 0x1.eeb645abdd6c8p+13, 0x1.1edee6683dbddp-963, INIT_U128( 0x0000000000000000, 0x0000000000003dd6 ) },
+ { 0x1.dc7ac6d3b8f59p+14, 0x1.3a4d2846749a5p-96, INIT_U128( 0x0000000000000000, 0x000000000000771e ) },
+ { 0x1.51b38df4a3672p+14, 0x1.ef5f5533debeap-96, INIT_U128( 0x0000000000000000, 0x000000000000546c ) },
+ { 0x1.5cdcee10b9b9ep+14, 0x1.dd44c049ba898p-96, INIT_U128( 0x0000000000000000, 0x0000000000005737 ) },
+ { 0x1.f5266d45ea4cdp+14, 0x1.3970615e72e0cp-96, INIT_U128( 0x0000000000000000, 0x0000000000007d49 ) },
+ { 0x1.2ab9252a55724p+15, 0x1.a85ba3d950b74p-690, INIT_U128( 0x0000000000000000, 0x000000000000955c ) },
+ { 0x1.707a4edee0f4ap+15, 0x1.00541f2800a84p-690, INIT_U128( 0x0000000000000000, 0x000000000000b83d ) },
+ { 0x1.594c6252b298cp+15, 0x1.837b6c0706f6ep-690, INIT_U128( 0x0000000000000000, 0x000000000000aca6 ) },
+ { 0x1.41fad8e683f5bp+15, 0x1.7cb3a2bef9674p-690, INIT_U128( 0x0000000000000000, 0x000000000000a0fd ) },
+ { 0x1.f150e9b3e2a1dp+16, 0x1.155a86a62ab51p-762, INIT_U128( 0x0000000000000000, 0x000000000001f150 ) },
+ { 0x1.0a3ca44214794p+16, 0x1.5c85e07eb90bcp-762, INIT_U128( 0x0000000000000000, 0x0000000000010a3c ) },
+ { 0x1.0ef4814e1de9p+16, 0x1.2cd0510659a0ap-762, INIT_U128( 0x0000000000000000, 0x0000000000010ef4 ) },
+ { 0x1.99373a97326e7p+16, 0x1.98d239d731a47p-762, INIT_U128( 0x0000000000000000, 0x0000000000019937 ) },
+ { 0x1.0062da5400c5cp+17, 0x1.12b3124825662p-940, INIT_U128( 0x0000000000000000, 0x00000000000200c5 ) },
+ { 0x1.8ac0f0251581ep+17, 0x1.60b254d0c164ap-940, INIT_U128( 0x0000000000000000, 0x0000000000031581 ) },
+ { 0x1.51c0eb94a381ep+17, 0x1.ce1da4059c3b4p-940, INIT_U128( 0x0000000000000000, 0x000000000002a381 ) },
+ { 0x1.5e83e6a4bd07dp+17, 0x1.adcff7815b9ffp-940, INIT_U128( 0x0000000000000000, 0x000000000002bd07 ) },
+ { 0x1.242e55bc485cap+18, 0x0.000000000001fp-1022, INIT_U128( 0x0000000000000000, 0x00000000000490b9 ) },
+ { 0x1.7d59ac2cfab36p+18, 0x0.000000000001p-1022, INIT_U128( 0x0000000000000000, 0x000000000005f566 ) },
+ { 0x1.d19e6101a33ccp+18, 0x0.0000000000014p-1022, INIT_U128( 0x0000000000000000, 0x0000000000074679 ) },
+ { 0x1.34c9981869933p+18, 0x0.0000000000011p-1022, INIT_U128( 0x0000000000000000, 0x000000000004d326 ) },
+ { 0x1.c066e34f80cddp+19, 0x1.e3f363b5c7e6dp-881, INIT_U128( 0x0000000000000000, 0x00000000000e0337 ) },
+ { 0x1.df32bc2fbe658p+19, 0x1.df947163bf28ep-881, INIT_U128( 0x0000000000000000, 0x00000000000ef995 ) },
+ { 0x1.3bbc859e7779p+19, 0x1.0773506c0ee6ap-881, INIT_U128( 0x0000000000000000, 0x000000000009dde4 ) },
+ { 0x1.3b65fdae76ccp+19, 0x1.36924fde6d24ap-881, INIT_U128( 0x0000000000000000, 0x000000000009db2f ) },
+ { 0x1.52c7a810a58f5p+20, 0x1.a11a939f42352p-561, INIT_U128( 0x0000000000000000, 0x0000000000152c7a ) },
+ { 0x1.9546ee252a8dep+20, 0x1.eeb4a28ddd695p-561, INIT_U128( 0x0000000000000000, 0x000000000019546e ) },
+ { 0x1.f50465bdea08cp+20, 0x1.7288f4c2e511ep-561, INIT_U128( 0x0000000000000000, 0x00000000001f5046 ) },
+ { 0x1.b8199d2770334p+20, 0x1.a2d4ddfb45a9cp-561, INIT_U128( 0x0000000000000000, 0x00000000001b8199 ) },
+ { 0x1.efe67d0fdfccfp+21, 0x1.05ac37920b587p-121, INIT_U128( 0x0000000000000000, 0x00000000003dfccf ) },
+ { 0x1.a1c8a86343915p+21, 0x1.ff7f144bfefe2p-121, INIT_U128( 0x0000000000000000, 0x0000000000343915 ) },
+ { 0x1.0b3b76001676fp+21, 0x1.742fba58e85f8p-121, INIT_U128( 0x0000000000000000, 0x000000000021676e ) },
+ { 0x1.cb12f6579625fp+21, 0x1.5e77e020bcefcp-121, INIT_U128( 0x0000000000000000, 0x000000000039625e ) },
+ { 0x1.bd380f437a702p+22, 0x1.491fe5e8923fcp-995, INIT_U128( 0x0000000000000000, 0x00000000006f4e03 ) },
+ { 0x1.46fbb89c8df77p+22, 0x1.a09fc8f7413f9p-995, INIT_U128( 0x0000000000000000, 0x000000000051beee ) },
+ { 0x1.17e871f42fd0ep+22, 0x1.a11fc1a7423f8p-995, INIT_U128( 0x0000000000000000, 0x000000000045fa1c ) },
+ { 0x1.277e999a4efd3p+22, 0x1.4bd3e11097a7cp-995, INIT_U128( 0x0000000000000000, 0x000000000049dfa6 ) },
+ { 0x1.6e4d3250dc9a6p+23, 0x1.edf09145dbe12p-447, INIT_U128( 0x0000000000000000, 0x0000000000b72699 ) },
+ { 0x1.eb0413bfd6083p+23, 0x1.29c840b053908p-447, INIT_U128( 0x0000000000000000, 0x0000000000f58209 ) },
+ { 0x1.283f1d32507e4p+23, 0x1.06daa0fe0db54p-447, INIT_U128( 0x0000000000000000, 0x0000000000941f8e ) },
+ { 0x1.cf7790bd9eef2p+23, 0x1.22ebe99845d7dp-447, INIT_U128( 0x0000000000000000, 0x0000000000e7bbc8 ) },
+ { 0x1.39fb1e1473f64p+24, 0x1.baafa729755f5p-263, INIT_U128( 0x0000000000000000, 0x000000000139fb1e ) },
+ { 0x1.553510f0aa6a2p+24, 0x1.806a3d5d00d48p-263, INIT_U128( 0x0000000000000000, 0x0000000001553510 ) },
+ { 0x1.876715390ece3p+24, 0x1.cdf668119becdp-263, INIT_U128( 0x0000000000000000, 0x0000000001876715 ) },
+ { 0x1.11816ac62302ep+24, 0x1.5e8451ecbd08ap-263, INIT_U128( 0x0000000000000000, 0x000000000111816a ) },
+ { 0x1.3e7e811e7cfdp+25, 0x1.2dc6a5685b8d4p-155, INIT_U128( 0x0000000000000000, 0x00000000027cfd02 ) },
+ { 0x1.6ebc2c8cdd786p+25, 0x1.22f38cd645e72p-155, INIT_U128( 0x0000000000000000, 0x0000000002dd7859 ) },
+ { 0x1.421ccf068439ap+25, 0x1.bef8b6f57df17p-155, INIT_U128( 0x0000000000000000, 0x000000000284399e ) },
+ { 0x1.e10ee555c21dcp+25, 0x1.033cdd7a0679cp-155, INIT_U128( 0x0000000000000000, 0x0000000003c21dca ) },
+ { 0x1.f0b6e7ede16ddp+26, 0x1.bc0a81517815p-634, INIT_U128( 0x0000000000000000, 0x0000000007c2db9f ) },
+ { 0x1.f7b66dc5ef6cdp+26, 0x1.2bd3184a57a63p-634, INIT_U128( 0x0000000000000000, 0x0000000007ded9b7 ) },
+ { 0x1.259706244b2e1p+26, 0x1.8b7dd07716fbap-634, INIT_U128( 0x0000000000000000, 0x0000000004965c18 ) },
+ { 0x1.fdf5519ffbeaap+26, 0x1.19d4925433a92p-634, INIT_U128( 0x0000000000000000, 0x0000000007f7d546 ) },
+ { 0x1.bb13b1d776276p+27, 0x1.e509cb23ca13ap-395, INIT_U128( 0x0000000000000000, 0x000000000dd89d8e ) },
+ { 0x1.20754b2a40eaap+27, 0x1.bcc8cd6b7991ap-395, INIT_U128( 0x0000000000000000, 0x000000000903aa59 ) },
+ { 0x1.dc036999b806dp+27, 0x1.62b438eec5687p-395, INIT_U128( 0x0000000000000000, 0x000000000ee01b4c ) },
+ { 0x1.92b76e3d256eep+27, 0x1.33f6413067ec8p-395, INIT_U128( 0x0000000000000000, 0x000000000c95bb71 ) },
+ { 0x1.7a6c4408f4d88p+28, 0x1.0f6d05b41edap-716, INIT_U128( 0x0000000000000000, 0x0000000017a6c440 ) },
+ { 0x1.25d2c9ae4ba59p+28, 0x1.005f5a6a00becp-716, INIT_U128( 0x0000000000000000, 0x00000000125d2c9a ) },
+ { 0x1.c8949d0b91293p+28, 0x1.6e7ca504dcf94p-716, INIT_U128( 0x0000000000000000, 0x000000001c8949d0 ) },
+ { 0x1.21af1f7a435e4p+28, 0x1.98e05ef731c0cp-716, INIT_U128( 0x0000000000000000, 0x00000000121af1f7 ) },
+ { 0x1.307b63f060f6cp+29, 0x1.17d4949a2fa92p-112, INIT_U128( 0x0000000000000000, 0x00000000260f6c7e ) },
+ { 0x1.053d62740a7acp+29, 0x1.a8881e6551104p-112, INIT_U128( 0x0000000000000000, 0x0000000020a7ac4e ) },
+ { 0x1.9b60d35b36c1ap+29, 0x1.005bb09600b76p-112, INIT_U128( 0x0000000000000000, 0x00000000336c1a6b ) },
+ { 0x1.3ba73afe774e8p+29, 0x1.0efca74e1df95p-112, INIT_U128( 0x0000000000000000, 0x000000002774e75f ) },
+ { 0x1.dd796675baf2dp+30, 0x1.aaba00775574p-990, INIT_U128( 0x0000000000000000, 0x00000000775e599d ) },
+ { 0x1.8b282c8d16506p+30, 0x1.4e04f8449c09fp-990, INIT_U128( 0x0000000000000000, 0x0000000062ca0b23 ) },
+ { 0x1.7d6ab930fad57p+30, 0x1.764a6e3cec94ep-990, INIT_U128( 0x0000000000000000, 0x000000005f5aae4c ) },
+ { 0x1.6906027cd20cp+30, 0x1.d5b4ef4dab69ep-990, INIT_U128( 0x0000000000000000, 0x000000005a41809f ) },
+ { 0x1.04a9da360953cp+31, 0x1.6c11a2e4d8234p-857, INIT_U128( 0x0000000000000000, 0x000000008254ed1b ) },
+ { 0x1.fbba6e93f774ep+31, 0x1.0e9a6e5c1d34ep-857, INIT_U128( 0x0000000000000000, 0x00000000fddd3749 ) },
+ { 0x1.380b108470162p+31, 0x1.65c0e5f6cb81cp-857, INIT_U128( 0x0000000000000000, 0x000000009c058842 ) },
+ { 0x1.050dfc000a1cp+31, 0x1.1df2fc803be6p-857, INIT_U128( 0x0000000000000000, 0x000000008286fe00 ) },
+ { 0x1.88bf9e1d117f4p+32, 0x1.8dd4aa1b1ba95p-126, INIT_U128( 0x0000000000000000, 0x0000000188bf9e1d ) },
+ { 0x1.8b35d7ff166bbp+32, 0x1.91ca210923944p-126, INIT_U128( 0x0000000000000000, 0x000000018b35d7ff ) },
+ { 0x1.286a366250d47p+32, 0x1.012c86ac02591p-126, INIT_U128( 0x0000000000000000, 0x00000001286a3662 ) },
+ { 0x1.ef233cd5de467p+32, 0x1.2292a62645255p-126, INIT_U128( 0x0000000000000000, 0x00000001ef233cd5 ) },
+ { 0x1.917a959722f53p+33, 0x1.85a7d93f0b4fbp-478, INIT_U128( 0x0000000000000000, 0x0000000322f52b2e ) },
+ { 0x1.f630053bec6p+33, 0x1.f79a5227ef34bp-478, INIT_U128( 0x0000000000000000, 0x00000003ec600a77 ) },
+ { 0x1.1a062b14340c6p+33, 0x1.21179acc422f4p-478, INIT_U128( 0x0000000000000000, 0x00000002340c5628 ) },
+ { 0x1.d62acf15ac55ap+33, 0x1.bf1cd2697e39ap-478, INIT_U128( 0x0000000000000000, 0x00000003ac559e2b ) },
+ { 0x1.823ddfaf047bcp+34, 0x1.e2d35df1c5a6cp-649, INIT_U128( 0x0000000000000000, 0x0000000608f77ebc ) },
+ { 0x1.996ef6e332ddfp+34, 0x1.7af28278f5e5p-649, INIT_U128( 0x0000000000000000, 0x0000000665bbdb8c ) },
+ { 0x1.81a2bfc703458p+34, 0x1.acc15cd15982cp-649, INIT_U128( 0x0000000000000000, 0x00000006068aff1c ) },
+ { 0x1.4517e98e8a2fdp+34, 0x1.a3233fc546468p-649, INIT_U128( 0x0000000000000000, 0x00000005145fa63a ) },
+ { 0x1.09f551a013eaap+35, 0x0.0000000000006p-1022, INIT_U128( 0x0000000000000000, 0x000000084faa8d00 ) },
+ { 0x1.a2911b3d45224p+35, 0x0.0000000000005p-1022, INIT_U128( 0x0000000000000000, 0x0000000d1488d9ea ) },
+ { 0x1.77a301deef46p+35, 0x0.0000000000008p-1022, INIT_U128( 0x0000000000000000, 0x0000000bbd180ef7 ) },
+ { 0x1.f60ea85fec1d5p+35, 0x0.0000000000006p-1022, INIT_U128( 0x0000000000000000, 0x0000000fb07542ff ) },
+ { 0x1.75c28ed8eb852p+36, 0x1.64300234c86p-938, INIT_U128( 0x0000000000000000, 0x000000175c28ed8e ) },
+ { 0x1.394f8bae729f2p+36, 0x1.2119204042324p-938, INIT_U128( 0x0000000000000000, 0x0000001394f8bae7 ) },
+ { 0x1.94d6bc7929ad8p+36, 0x1.4a8aa2aa95154p-938, INIT_U128( 0x0000000000000000, 0x000000194d6bc792 ) },
+ { 0x1.1f519dcc3ea34p+36, 0x1.b4fbc9c969f79p-938, INIT_U128( 0x0000000000000000, 0x00000011f519dcc3 ) },
+ { 0x1.bb2fb46f765f6p+37, 0x1.cd4047139a809p-718, INIT_U128( 0x0000000000000000, 0x0000003765f68dee ) },
+ { 0x1.f80989d1f0131p+37, 0x1.c0546f4180a8ep-718, INIT_U128( 0x0000000000000000, 0x0000003f01313a3e ) },
+ { 0x1.90be9171217d2p+37, 0x1.bcc6089d798c1p-718, INIT_U128( 0x0000000000000000, 0x0000003217d22e24 ) },
+ { 0x1.37469d226e8d4p+37, 0x1.06aff8000d5ffp-718, INIT_U128( 0x0000000000000000, 0x00000026e8d3a44d ) },
+ { 0x1.2e0114905c022p+38, 0x1.298bde145317cp-115, INIT_U128( 0x0000000000000000, 0x0000004b80452417 ) },
+ { 0x1.77354c42ee6aap+38, 0x1.d9a75513b34eap-115, INIT_U128( 0x0000000000000000, 0x0000005dcd5310bb ) },
+ { 0x1.0881a80c11035p+38, 0x1.44e5e01889cbcp-115, INIT_U128( 0x0000000000000000, 0x00000042206a0304 ) },
+ { 0x1.a32b590f4656bp+38, 0x1.760fbd2eec1f8p-115, INIT_U128( 0x0000000000000000, 0x00000068cad643d1 ) },
+ { 0x1.e7bc6861cf78dp+39, 0x1.72addca8e55bcp-767, INIT_U128( 0x0000000000000000, 0x000000f3de3430e7 ) },
+ { 0x1.e1e11b1bc3c24p+39, 0x1.713729dae26e5p-767, INIT_U128( 0x0000000000000000, 0x000000f0f08d8de1 ) },
+ { 0x1.8bbb377f17767p+39, 0x1.c296d213852dap-767, INIT_U128( 0x0000000000000000, 0x000000c5dd9bbf8b ) },
+ { 0x1.7cb13d18f9628p+39, 0x1.8dcf87351b9f1p-767, INIT_U128( 0x0000000000000000, 0x000000be589e8c7c ) },
+ { 0x1.1ddbaf383bb76p+40, 0x1.20c8d9d04191bp-497, INIT_U128( 0x0000000000000000, 0x0000011ddbaf383b ) },
+ { 0x1.fde474e3fbc8ep+40, 0x1.7e6de35afcdbcp-497, INIT_U128( 0x0000000000000000, 0x000001fde474e3fb ) },
+ { 0x1.02a202b00544p+40, 0x1.311b40f462368p-497, INIT_U128( 0x0000000000000000, 0x00000102a202b005 ) },
+ { 0x1.ec0b6577d816cp+40, 0x1.8e49e85d1c93dp-497, INIT_U128( 0x0000000000000000, 0x000001ec0b6577d8 ) },
+ { 0x1.95fa4b912bf4ap+41, 0x1.295d953652bb2p-872, INIT_U128( 0x0000000000000000, 0x0000032bf4972257 ) },
+ { 0x1.fd243093fa486p+41, 0x1.91e0a45723c14p-872, INIT_U128( 0x0000000000000000, 0x000003fa486127f4 ) },
+ { 0x1.d0beeb21a17dep+41, 0x1.97f98e272ff32p-872, INIT_U128( 0x0000000000000000, 0x000003a17dd64342 ) },
+ { 0x1.750735faea0e6p+41, 0x1.97a4c8c92f499p-872, INIT_U128( 0x0000000000000000, 0x000002ea0e6bf5d4 ) },
+ { 0x1.ab409c8f56814p+42, 0x1.1e820fc23d042p-84, INIT_U128( 0x0000000000000000, 0x000006ad02723d5a ) },
+ { 0x1.85e79a910bcf3p+42, 0x1.75d44930eba89p-84, INIT_U128( 0x0000000000000000, 0x000006179e6a442f ) },
+ { 0x1.e2fa47dfc5f49p+42, 0x1.f91c1067f2382p-84, INIT_U128( 0x0000000000000000, 0x0000078be91f7f17 ) },
+ { 0x1.ecaf7567d95eep+42, 0x1.dd787be3baf1p-84, INIT_U128( 0x0000000000000000, 0x000007b2bdd59f65 ) },
+ { 0x1.4fd770a89faeep+43, 0x1.883956a11072bp-669, INIT_U128( 0x0000000000000000, 0x00000a7ebb8544fd ) },
+ { 0x1.b2b2aa2d65655p+43, 0x1.0c2516f2184a3p-669, INIT_U128( 0x0000000000000000, 0x00000d9595516b2b ) },
+ { 0x1.5848b5b4b0916p+43, 0x1.d9a0d8cfb341bp-669, INIT_U128( 0x0000000000000000, 0x00000ac245ada584 ) },
+ { 0x1.be7daa1f7cfb5p+43, 0x1.f0c9d223e193bp-669, INIT_U128( 0x0000000000000000, 0x00000df3ed50fbe7 ) },
+ { 0x1.3f6d46f07eda9p+44, 0x1.3d2fa1b27a5f4p-539, INIT_U128( 0x0000000000000000, 0x000013f6d46f07ed ) },
+ { 0x1.5eb8dcaebd71cp+44, 0x1.8e170e7b1c2e2p-539, INIT_U128( 0x0000000000000000, 0x000015eb8dcaebd7 ) },
+ { 0x1.893b63631276cp+44, 0x1.9447ca53288f9p-539, INIT_U128( 0x0000000000000000, 0x00001893b6363127 ) },
+ { 0x1.4e089b389c114p+44, 0x1.d191b053a3236p-539, INIT_U128( 0x0000000000000000, 0x000014e089b389c1 ) },
+ { 0x1.2e36d90e5c6dbp+45, 0x1.314d394c629a7p-889, INIT_U128( 0x0000000000000000, 0x000025c6db21cb8d ) },
+ { 0x1.7f784de4fef0ap+45, 0x1.5413e986a827dp-889, INIT_U128( 0x0000000000000000, 0x00002fef09bc9fde ) },
+ { 0x1.74448388e889p+45, 0x1.752c3c2cea588p-889, INIT_U128( 0x0000000000000000, 0x00002e8890711d11 ) },
+ { 0x1.ebb20c51d7641p+45, 0x1.50a52f7ca14a6p-889, INIT_U128( 0x0000000000000000, 0x00003d76418a3aec ) },
+ { 0x1.4b51066896a21p+46, 0x1.d43cc973a8799p-550, INIT_U128( 0x0000000000000000, 0x000052d4419a25a8 ) },
+ { 0x1.070f0d280e1e2p+46, 0x1.c25aa6dd84b55p-550, INIT_U128( 0x0000000000000000, 0x000041c3c34a0387 ) },
+ { 0x1.7f735ca4fee6cp+46, 0x1.a92889d752511p-550, INIT_U128( 0x0000000000000000, 0x00005fdcd7293fb9 ) },
+ { 0x1.72ed987ae5db3p+46, 0x1.fae14a03f5c29p-550, INIT_U128( 0x0000000000000000, 0x00005cbb661eb976 ) },
+ { 0x1.7352fdf0e6a6p+47, 0x1.1a64275034c85p-857, INIT_U128( 0x0000000000000000, 0x0000b9a97ef87353 ) },
+ { 0x1.9f4b98f33e973p+47, 0x1.657b10c0caf62p-857, INIT_U128( 0x0000000000000000, 0x0000cfa5cc799f4b ) },
+ { 0x1.b12eb79b625d7p+47, 0x1.4826ca96904dap-857, INIT_U128( 0x0000000000000000, 0x0000d8975bcdb12e ) },
+ { 0x1.6148cbcac291ap+47, 0x1.8672a1ad0ce54p-857, INIT_U128( 0x0000000000000000, 0x0000b0a465e56148 ) },
+ { 0x1.1df8159e3bf02p+48, 0x1.7c6dbd68f8db8p-407, INIT_U128( 0x0000000000000000, 0x00011df8159e3bf0 ) },
+ { 0x1.d8e17545b1c2ep+48, 0x1.959fcc5f2b3fap-407, INIT_U128( 0x0000000000000000, 0x0001d8e17545b1c2 ) },
+ { 0x1.ea57e075d4afcp+48, 0x1.56705400ace0ap-407, INIT_U128( 0x0000000000000000, 0x0001ea57e075d4af ) },
+ { 0x1.d2aa9e99a5554p+48, 0x1.9ebcab993d796p-407, INIT_U128( 0x0000000000000000, 0x0001d2aa9e99a555 ) },
+ { 0x1.4fe075a49fc0ep+49, 0x1.e950f533d2a1ep-694, INIT_U128( 0x0000000000000000, 0x00029fc0eb493f81 ) },
+ { 0x1.a936db51526dcp+49, 0x1.8a1397df14273p-694, INIT_U128( 0x0000000000000000, 0x0003526db6a2a4db ) },
+ { 0x1.138ce7682719dp+49, 0x1.7f879c76ff0f4p-694, INIT_U128( 0x0000000000000000, 0x00022719ced04e33 ) },
+ { 0x1.21e981b043d3p+49, 0x1.eaefe1f3d5dfcp-694, INIT_U128( 0x0000000000000000, 0x000243d3036087a6 ) },
+ { 0x1.793e9a3cf27d4p+50, 0x1.40169b90802d4p-934, INIT_U128( 0x0000000000000000, 0x0005e4fa68f3c9f5 ) },
+ { 0x1.7e1caadcfc396p+50, 0x1.371020466e204p-934, INIT_U128( 0x0000000000000000, 0x0005f872ab73f0e5 ) },
+ { 0x1.33a019d667403p+50, 0x1.9c77f7cb38effp-934, INIT_U128( 0x0000000000000000, 0x0004ce8067599d00 ) },
+ { 0x1.b4966ecb692cep+50, 0x1.c4dd0f8989ba2p-934, INIT_U128( 0x0000000000000000, 0x0006d259bb2da4b3 ) },
+ { 0x1.822ad9630455bp+51, 0x1.3fade6a07f5bdp-561, INIT_U128( 0x0000000000000000, 0x000c1156cb1822ad ) },
+ { 0x1.3a77dd3c74efcp+51, 0x1.55dd3018abba6p-561, INIT_U128( 0x0000000000000000, 0x0009d3bee9e3a77e ) },
+ { 0x1.d375773da6eafp+51, 0x1.ad40b9955a817p-561, INIT_U128( 0x0000000000000000, 0x000e9babb9ed3757 ) },
+ { 0x1.16059bfe2c0b4p+51, 0x1.ff801c63ff003p-561, INIT_U128( 0x0000000000000000, 0x0008b02cdff1605a ) },
+ { 0x1.af8f1e955f1e4p+52, 0x1.2c4cc4fa58998p-237, INIT_U128( 0x0000000000000000, 0x001af8f1e955f1e4 ) },
+ { 0x1.579e5322af3cap+52, 0x1.e9974457d32e8p-237, INIT_U128( 0x0000000000000000, 0x001579e5322af3ca ) },
+ { 0x1.2b9d921a573b2p+52, 0x1.d8798265b0f3p-237, INIT_U128( 0x0000000000000000, 0x0012b9d921a573b2 ) },
+ { 0x1.b746d5596e8dbp+52, 0x1.a75bfc954eb8p-237, INIT_U128( 0x0000000000000000, 0x001b746d5596e8db ) },
+ { 0x1.497ec4f092fd8p+53, 0x1.5c597ab2b8b3p-364, INIT_U128( 0x0000000000000000, 0x00292fd89e125fb0 ) },
+ { 0x1.8a65536914caap+53, 0x1.958565492b0adp-364, INIT_U128( 0x0000000000000000, 0x00314caa6d229954 ) },
+ { 0x1.11b7146c236e2p+53, 0x1.3154f5b662a9ep-364, INIT_U128( 0x0000000000000000, 0x002236e28d846dc4 ) },
+ { 0x1.f71b5e7bee36cp+53, 0x1.efc7aa5ddf8f6p-364, INIT_U128( 0x0000000000000000, 0x003ee36bcf7dc6d8 ) },
+ { 0x1.5a71157ab4e22p+54, 0x1.b4fd1c6b69fa4p-39, INIT_U128( 0x0000000000000000, 0x00569c455ead3888 ) },
+ { 0x1.4ab52e26956a6p+54, 0x1.204833ee40906p-39, INIT_U128( 0x0000000000000000, 0x0052ad4b89a55a98 ) },
+ { 0x1.7b9298b4f7253p+54, 0x1.084ca6f410995p-39, INIT_U128( 0x0000000000000000, 0x005ee4a62d3dc94c ) },
+ { 0x1.8be06c0317c0ep+54, 0x1.9677f6df2ceffp-39, INIT_U128( 0x0000000000000000, 0x0062f81b00c5f038 ) },
+ { 0x1.53534daca6a6ap+55, 0x1.af26be6f5e4d8p-905, INIT_U128( 0x0000000000000000, 0x00a9a9a6d6535350 ) },
+ { 0x1.ee7424c1dce84p+55, 0x1.38a375387146ep-905, INIT_U128( 0x0000000000000000, 0x00f73a1260ee7420 ) },
+ { 0x1.4275718484eaep+55, 0x1.693f342ed27e6p-905, INIT_U128( 0x0000000000000000, 0x00a13ab8c2427570 ) },
+ { 0x1.4e48bc049c918p+55, 0x1.30d3b39661a76p-905, INIT_U128( 0x0000000000000000, 0x00a7245e024e48c0 ) },
+ { 0x1.6fcb01f0df96p+56, 0x1.f3322f93e6646p-339, INIT_U128( 0x0000000000000000, 0x016fcb01f0df9600 ) },
+ { 0x1.6f16f2e4de2dep+56, 0x1.b50cb2d16a196p-339, INIT_U128( 0x0000000000000000, 0x016f16f2e4de2de0 ) },
+ { 0x1.6fcb3cb2df968p+56, 0x1.f7623e45eec48p-339, INIT_U128( 0x0000000000000000, 0x016fcb3cb2df9680 ) },
+ { 0x1.a41a78314834fp+56, 0x1.ee812a93dd026p-339, INIT_U128( 0x0000000000000000, 0x01a41a78314834f0 ) },
+ { 0x1.73544f7ce6a8ap+57, 0x1.fbf6a069f7ed4p-786, INIT_U128( 0x0000000000000000, 0x02e6a89ef9cd5140 ) },
+ { 0x1.8d4beb3f1a97ep+57, 0x1.6f6e15a0dedc2p-786, INIT_U128( 0x0000000000000000, 0x031a97d67e352fc0 ) },
+ { 0x1.70dfc328e1bf8p+57, 0x1.56963a34ad2c8p-786, INIT_U128( 0x0000000000000000, 0x02e1bf8651c37f00 ) },
+ { 0x1.6e5e39acdcbc7p+57, 0x1.62dfb7d4c5bf7p-786, INIT_U128( 0x0000000000000000, 0x02dcbc7359b978e0 ) },
+ { 0x1.10a375142146ep+58, 0x1.dde963f1bbd2cp-687, INIT_U128( 0x0000000000000000, 0x04428dd450851b80 ) },
+ { 0x1.7eacb1acfd596p+58, 0x1.e59952a9cb32bp-687, INIT_U128( 0x0000000000000000, 0x05fab2c6b3f56580 ) },
+ { 0x1.3f2bac4a7e576p+58, 0x1.d21ee367a43ddp-687, INIT_U128( 0x0000000000000000, 0x04fcaeb129f95d80 ) },
+ { 0x1.be738acb7ce71p+58, 0x1.d4b6334fa96c7p-687, INIT_U128( 0x0000000000000000, 0x06f9ce2b2df39c40 ) },
+ { 0x1.b322eff56645ep+59, 0x0.00000014b8158p-1022, INIT_U128( 0x0000000000000000, 0x0d99177fab322f00 ) },
+ { 0x1.b8dfbdbd71bf8p+59, 0x0.00000010ac2d6p-1022, INIT_U128( 0x0000000000000000, 0x0dc6fdedeb8dfc00 ) },
+ { 0x1.e45f6d33c8bedp+59, 0x0.0000001c79003p-1022, INIT_U128( 0x0000000000000000, 0x0f22fb699e45f680 ) },
+ { 0x1.10c7106e218e2p+59, 0x0.0000001ea2457p-1022, INIT_U128( 0x0000000000000000, 0x08863883710c7100 ) },
+ { 0x1.c48c230989185p+60, 0x1.a60d3fb34c1a8p-116, INIT_U128( 0x0000000000000000, 0x1c48c23098918500 ) },
+ { 0x1.5e9345fabd268p+60, 0x1.4898e6d49131dp-116, INIT_U128( 0x0000000000000000, 0x15e9345fabd26800 ) },
+ { 0x1.b56942576ad28p+60, 0x1.aff4a0655fe94p-116, INIT_U128( 0x0000000000000000, 0x1b56942576ad2800 ) },
+ { 0x1.7f865930ff0cbp+60, 0x1.13a0876e27411p-116, INIT_U128( 0x0000000000000000, 0x17f865930ff0cb00 ) },
+ { 0x1.ef482c31de906p+61, 0x1.43e655d887ccap-501, INIT_U128( 0x0000000000000000, 0x3de905863bd20c00 ) },
+ { 0x1.9fa15a7d3f42bp+61, 0x1.b00fcc55601fap-501, INIT_U128( 0x0000000000000000, 0x33f42b4fa7e85600 ) },
+ { 0x1.d2c465fda588dp+61, 0x1.98c2f6e73185fp-501, INIT_U128( 0x0000000000000000, 0x3a588cbfb4b11a00 ) },
+ { 0x1.f038608de070cp+61, 0x1.7b4fa8a0f69f5p-501, INIT_U128( 0x0000000000000000, 0x3e070c11bc0e1800 ) },
+ { 0x1.adfb2db35bf66p+62, 0x1.38efaf6271df6p+8, INIT_U128( 0x0000000000000000, 0x6b7ecb6cd6fd9938 ) },
+ { 0x1.1679474c2cf29p+62, 0x1.ae04d7f95c09bp+8, INIT_U128( 0x0000000000000000, 0x459e51d30b3ca5ae ) },
+ { 0x1.890c63b91218cp+62, 0x1.133030ac26606p+8, INIT_U128( 0x0000000000000000, 0x624318ee44863113 ) },
+ { 0x1.08fc576811f8bp+62, 0x1.521d194ea43a3p+8, INIT_U128( 0x0000000000000000, 0x423f15da047e2d52 ) },
+ { 0x1.5c2e1ea2b85c4p+63, 0x1.bbf1e79d77e3dp-836, INIT_U128( 0x0000000000000000, 0xae170f515c2e2000 ) },
+ { 0x1.3a1d0742743a1p+63, 0x1.849ecbad093dap-836, INIT_U128( 0x0000000000000000, 0x9d0e83a13a1d0800 ) },
+ { 0x1.ac698c2758d32p+63, 0x1.7a316edaf462ep-836, INIT_U128( 0x0000000000000000, 0xd634c613ac699000 ) },
+ { 0x1.8542412f0a848p+63, 0x1.a53fa9cd4a7f5p-836, INIT_U128( 0x0000000000000000, 0xc2a1209785424000 ) },
+ { 0x1.f526fb77ea4ep+64, 0x1.170327882e065p-848, INIT_U128( 0x0000000000000001, 0xf526fb77ea4e0000 ) },
+ { 0x1.acca54155994ap+64, 0x1.4c44fdb4988ap-848, INIT_U128( 0x0000000000000001, 0xacca54155994a000 ) },
+ { 0x1.b47ed77768fdbp+64, 0x1.e6883245cd107p-848, INIT_U128( 0x0000000000000001, 0xb47ed77768fdb000 ) },
+ { 0x1.bf32165b7e643p+64, 0x1.7da93100fb526p-848, INIT_U128( 0x0000000000000001, 0xbf32165b7e643000 ) },
+ { 0x1.c6aa72a58d54fp+65, 0x1.700d04ece01ap-810, INIT_U128( 0x0000000000000003, 0x8d54e54b1aa9e000 ) },
+ { 0x1.651ffffcca4p+65, 0x1.b6e3b8e56dc77p-810, INIT_U128( 0x0000000000000002, 0xca3ffff994800000 ) },
+ { 0x1.f59076c9eb20fp+65, 0x1.41622b1082c46p-810, INIT_U128( 0x0000000000000003, 0xeb20ed93d641e000 ) },
+ { 0x1.2362224a46c44p+65, 0x1.0fe4f0321fc9ep-810, INIT_U128( 0x0000000000000002, 0x46c444948d888000 ) },
+ { 0x1.96643d852cc88p+66, 0x1.5aadaff0b55b6p-820, INIT_U128( 0x0000000000000006, 0x5990f614b3220000 ) },
+ { 0x1.38a95f0e7152cp+66, 0x1.8432d89b0865bp-820, INIT_U128( 0x0000000000000004, 0xe2a57c39c54b0000 ) },
+ { 0x1.b674a85b6ce95p+66, 0x1.3adbee1a75b7ep-820, INIT_U128( 0x0000000000000006, 0xd9d2a16db3a54000 ) },
+ { 0x1.81b2bc3303658p+66, 0x1.0e771c4e1cee4p-820, INIT_U128( 0x0000000000000006, 0x06caf0cc0d960000 ) },
+ { 0x1.017e066002fc1p+67, 0x1.69eb9d80d3d74p-860, INIT_U128( 0x0000000000000008, 0x0bf0330017e08000 ) },
+ { 0x1.b75b9b136eb74p+67, 0x1.ddf2ec69bbe5ep-860, INIT_U128( 0x000000000000000d, 0xbadcd89b75ba0000 ) },
+ { 0x1.71432fe4e2866p+67, 0x1.cbea0a3797d41p-860, INIT_U128( 0x000000000000000b, 0x8a197f2714330000 ) },
+ { 0x1.65e3ce88cbc7ap+67, 0x1.dd466e4dba8cep-860, INIT_U128( 0x000000000000000b, 0x2f1e74465e3d0000 ) },
+ { 0x1.d76842dfaed09p+68, 0x1.d4739f6ba8e74p-740, INIT_U128( 0x000000000000001d, 0x76842dfaed090000 ) },
+ { 0x1.9180cb312301ap+68, 0x1.5961b442b2c36p-740, INIT_U128( 0x0000000000000019, 0x180cb312301a0000 ) },
+ { 0x1.5ea7abd8bd4f6p+68, 0x1.0afd825415fbp-740, INIT_U128( 0x0000000000000015, 0xea7abd8bd4f60000 ) },
+ { 0x1.bcf6493f79ec9p+68, 0x1.39f6643a73eccp-740, INIT_U128( 0x000000000000001b, 0xcf6493f79ec90000 ) },
+ { 0x1.6c264bbad84cap+69, 0x1.3d2b92de7a572p-358, INIT_U128( 0x000000000000002d, 0x84c9775b09940000 ) },
+ { 0x1.13b3e09a2767cp+69, 0x1.a3ead92f47d5bp-358, INIT_U128( 0x0000000000000022, 0x767c1344ecf80000 ) },
+ { 0x1.8518219d0a304p+69, 0x1.c9a99edf93534p-358, INIT_U128( 0x0000000000000030, 0xa30433a146080000 ) },
+ { 0x1.afa032e75f406p+69, 0x1.76f3e70cede7dp-358, INIT_U128( 0x0000000000000035, 0xf4065cebe80c0000 ) },
+ { 0x1.1aa2f5343545ep+70, 0x1.cd612ccd9ac25p-491, INIT_U128( 0x0000000000000046, 0xa8bd4d0d51780000 ) },
+ { 0x1.2c8c2e1a59186p+70, 0x1.53ac1260a7582p-491, INIT_U128( 0x000000000000004b, 0x230b869646180000 ) },
+ { 0x1.b92d16ef725a3p+70, 0x1.05faddde0bf5cp-491, INIT_U128( 0x000000000000006e, 0x4b45bbdc968c0000 ) },
+ { 0x1.9fc802a33f9p+70, 0x1.203a627a4074cp-491, INIT_U128( 0x0000000000000067, 0xf200a8cfe4000000 ) },
+ { 0x1.240746b6480e9p+71, 0x1.78c39518f1872p-676, INIT_U128( 0x0000000000000092, 0x03a35b2407480000 ) },
+ { 0x1.863a24750c744p+71, 0x1.96d2b31d2da56p-676, INIT_U128( 0x00000000000000c3, 0x1d123a863a200000 ) },
+ { 0x1.597fbe8ab2ff8p+71, 0x1.93afb023275f6p-676, INIT_U128( 0x00000000000000ac, 0xbfdf45597fc00000 ) },
+ { 0x1.e1080a67c2102p+71, 0x1.b5c9f2a36b93ep-676, INIT_U128( 0x00000000000000f0, 0x840533e108100000 ) },
+ { 0x1.5c1897a6b8313p+72, 0x1.e08b1a6fc1164p-272, INIT_U128( 0x000000000000015c, 0x1897a6b831300000 ) },
+ { 0x1.9ba232cf37446p+72, 0x1.5f66bf90becd8p-272, INIT_U128( 0x000000000000019b, 0xa232cf3744600000 ) },
+ { 0x1.595f744cb2beep+72, 0x1.f7a95a67ef52cp-272, INIT_U128( 0x0000000000000159, 0x5f744cb2bee00000 ) },
+ { 0x1.a763ae594ec76p+72, 0x1.c295524f852aap-272, INIT_U128( 0x00000000000001a7, 0x63ae594ec7600000 ) },
+ { 0x1.06eca6c40dd95p+73, 0x1.f918431ff2309p-572, INIT_U128( 0x000000000000020d, 0xd94d881bb2a00000 ) },
+ { 0x1.4f9fc82a9f3f9p+73, 0x1.257089f24ae11p-572, INIT_U128( 0x000000000000029f, 0x3f90553e7f200000 ) },
+ { 0x1.0fa3bdc41f478p+73, 0x1.1ca9162039523p-572, INIT_U128( 0x000000000000021f, 0x477b883e8f000000 ) },
+ { 0x1.be3be7ef7c77dp+73, 0x1.ae73d50d5ce7bp-572, INIT_U128( 0x000000000000037c, 0x77cfdef8efa00000 ) },
+ { 0x1.da6d4389b4da8p+74, 0x1.1806570a300cbp-230, INIT_U128( 0x0000000000000769, 0xb50e26d36a000000 ) },
+ { 0x1.55276624aa4edp+74, 0x1.004fb390009f6p-230, INIT_U128( 0x0000000000000554, 0x9d9892a93b400000 ) },
+ { 0x1.aeab3c995d568p+74, 0x1.08d9156011b22p-230, INIT_U128( 0x00000000000006ba, 0xacf265755a000000 ) },
+ { 0x1.a281549f4502ap+74, 0x1.cb98cbdf9731ap-230, INIT_U128( 0x000000000000068a, 0x05527d140a800000 ) },
+ { 0x1.35dae4746bb5cp+75, 0x1.492edd3c925dcp-684, INIT_U128( 0x00000000000009ae, 0xd723a35dae000000 ) },
+ { 0x1.e6b8db83cd71cp+75, 0x1.e8282f8fd0506p-684, INIT_U128( 0x0000000000000f35, 0xc6dc1e6b8e000000 ) },
+ { 0x1.17587f082eb1p+75, 0x1.45ebb9f28bd77p-684, INIT_U128( 0x00000000000008ba, 0xc3f8417588000000 ) },
+ { 0x1.957ac7292af59p+75, 0x1.35b408566b681p-684, INIT_U128( 0x0000000000000cab, 0xd6394957ac800000 ) },
+ { 0x1.6e0e0850dc1c1p+76, 0x1.9e623e393cc48p-1002, INIT_U128( 0x00000000000016e0, 0xe0850dc1c1000000 ) },
+ { 0x1.90fee6ff21fddp+76, 0x1.7ceca2caf9d94p-1002, INIT_U128( 0x000000000000190f, 0xee6ff21fdd000000 ) },
+ { 0x1.5798708eaf30ep+76, 0x1.d3322f4fa6646p-1002, INIT_U128( 0x0000000000001579, 0x8708eaf30e000000 ) },
+ { 0x1.c2ef5f4185decp+76, 0x1.96ad4d692d5aap-1002, INIT_U128( 0x0000000000001c2e, 0xf5f4185dec000000 ) },
+ { 0x1.ae14b81f5c297p+77, 0x1.062f208c0c5e4p-169, INIT_U128( 0x00000000000035c2, 0x9703eb852e000000 ) },
+ { 0x1.1f2ef58a3e5dep+77, 0x1.97a029192f405p-169, INIT_U128( 0x00000000000023e5, 0xdeb147cbbc000000 ) },
+ { 0x1.74861d64e90c4p+77, 0x1.fb289c69f6513p-169, INIT_U128( 0x0000000000002e90, 0xc3ac9d2188000000 ) },
+ { 0x1.11782bc422f06p+77, 0x1.fe294db5fc529p-169, INIT_U128( 0x000000000000222f, 0x0578845e0c000000 ) },
+ { 0x1.3af34bd275e6ap+78, 0x1.ba66054574cc1p-910, INIT_U128( 0x0000000000004ebc, 0xd2f49d79a8000000 ) },
+ { 0x1.0708e3fc0e11cp+78, 0x1.09ae142c135c2p-910, INIT_U128( 0x00000000000041c2, 0x38ff038470000000 ) },
+ { 0x1.c313a69786275p+78, 0x1.fc165a27f82ccp-910, INIT_U128( 0x00000000000070c4, 0xe9a5e189d4000000 ) },
+ { 0x1.990c9ad532193p+78, 0x1.072499060e493p-910, INIT_U128( 0x0000000000006643, 0x26b54c864c000000 ) },
+ { 0x1.dd2363c1ba46cp+79, 0x1.d163c99ba2c79p-815, INIT_U128( 0x000000000000ee91, 0xb1e0dd2360000000 ) },
+ { 0x1.b0ae4ad1615c9p+79, 0x1.8f2f90f91e5f2p-815, INIT_U128( 0x000000000000d857, 0x2568b0ae48000000 ) },
+ { 0x1.9a26bbb7344d8p+79, 0x1.ed90d6d9db21bp-815, INIT_U128( 0x000000000000cd13, 0x5ddb9a26c0000000 ) },
+ { 0x1.71ec17ace3d83p+79, 0x1.ec2b79cfd856fp-815, INIT_U128( 0x000000000000b8f6, 0x0bd671ec18000000 ) },
+ { 0x1.689f9cb2d13f4p+80, 0x1.adfbd9175bf7bp-935, INIT_U128( 0x000000000001689f, 0x9cb2d13f40000000 ) },
+ { 0x1.cde2888d9bc51p+80, 0x1.0d3598f01a6b3p-935, INIT_U128( 0x000000000001cde2, 0x888d9bc510000000 ) },
+ { 0x1.6866c948d0cd9p+80, 0x1.0d9da3cc1b3b4p-935, INIT_U128( 0x0000000000016866, 0xc948d0cd90000000 ) },
+ { 0x1.eee79cbdddcf3p+80, 0x1.a60bf9374c17fp-935, INIT_U128( 0x000000000001eee7, 0x9cbdddcf30000000 ) },
+ { 0x1.3a27b29c744f6p+81, 0x1.1270039224ep-231, INIT_U128( 0x000000000002744f, 0x6538e89ec0000000 ) },
+ { 0x1.608a6c38c114ep+81, 0x1.a6ff1b154dfe4p-231, INIT_U128( 0x000000000002c114, 0xd8718229c0000000 ) },
+ { 0x1.b8ddf2c971bbep+81, 0x1.7a6c452cf4d88p-231, INIT_U128( 0x00000000000371bb, 0xe592e377c0000000 ) },
+ { 0x1.34056f04680aep+81, 0x1.3aa39ba075474p-231, INIT_U128( 0x000000000002680a, 0xde08d015c0000000 ) },
+ { 0x1.df36567dbe6cbp+82, 0x1.948aa54b29155p-104, INIT_U128( 0x0000000000077cd9, 0x59f6f9b2c0000000 ) },
+ { 0x1.00c5bf20018b8p+82, 0x1.0354f44e06a9ep-104, INIT_U128( 0x0000000000040316, 0xfc80062e00000000 ) },
+ { 0x1.5a6d471ab4da9p+82, 0x1.ea755ca5d4eabp-104, INIT_U128( 0x00000000000569b5, 0x1c6ad36a40000000 ) },
+ { 0x1.58acff6eb15ap+82, 0x1.f6c3b1b9ed876p-104, INIT_U128( 0x00000000000562b3, 0xfdbac56800000000 ) },
+ { 0x1.9288c20b25118p+83, 0x1.477be5208ef7cp-445, INIT_U128( 0x00000000000c9446, 0x1059288c00000000 ) },
+ { 0x1.3556fa5c6aaep+83, 0x1.f200a591e4014p-445, INIT_U128( 0x000000000009aab7, 0xd2e3557000000000 ) },
+ { 0x1.88dec0dd11bd8p+83, 0x1.a1ceac19439d6p-445, INIT_U128( 0x00000000000c46f6, 0x06e88dec00000000 ) },
+ { 0x1.603498e4c0693p+83, 0x1.94ccf0d52999ep-445, INIT_U128( 0x00000000000b01a4, 0xc726034980000000 ) },
+ { 0x1.1dfbb7a43bf77p+84, 0x1.d7dd8bdbafbb2p-926, INIT_U128( 0x000000000011dfbb, 0x7a43bf7700000000 ) },
+ { 0x1.5f5d18b8beba3p+84, 0x1.ac1b923558372p-926, INIT_U128( 0x000000000015f5d1, 0x8b8beba300000000 ) },
+ { 0x1.8b32b85d16657p+84, 0x1.37ae11cc6f5c2p-926, INIT_U128( 0x000000000018b32b, 0x85d1665700000000 ) },
+ { 0x1.506f56eca0debp+84, 0x1.185445da30a88p-926, INIT_U128( 0x00000000001506f5, 0x6eca0deb00000000 ) },
+ { 0x1.506cf3dea0d9ep+85, 0x1.2e68e2945cd1cp-635, INIT_U128( 0x00000000002a0d9e, 0x7bd41b3c00000000 ) },
+ { 0x1.99ef268733de5p+85, 0x1.2ce0960e59c13p-635, INIT_U128( 0x0000000000333de4, 0xd0e67bca00000000 ) },
+ { 0x1.d46cd273a8d9ap+85, 0x1.fb0bae61f6176p-635, INIT_U128( 0x00000000003a8d9a, 0x4e751b3400000000 ) },
+ { 0x1.31deaa8263bd6p+85, 0x1.8a752b4514ea6p-635, INIT_U128( 0x0000000000263bd5, 0x504c77ac00000000 ) },
+ { 0x1.95956f032b2aep+86, 0x1.ddd18753bba31p-535, INIT_U128( 0x000000000065655b, 0xc0cacab800000000 ) },
+ { 0x1.db4b6705b696dp+86, 0x1.fc438061f887p-535, INIT_U128( 0x000000000076d2d9, 0xc16da5b400000000 ) },
+ { 0x1.42c9320885926p+86, 0x1.9258f3ab24b1ep-535, INIT_U128( 0x000000000050b24c, 0x8221649800000000 ) },
+ { 0x1.08ca2a0e11946p+86, 0x1.1c860974390c1p-535, INIT_U128( 0x000000000042328a, 0x8384651800000000 ) },
+ { 0x1.ddf27f51bbe5p+87, 0x1.a776e8c94eeddp-479, INIT_U128( 0x0000000000eef93f, 0xa8ddf28000000000 ) },
+ { 0x1.3f5b6af47eb6ep+87, 0x1.cf47dd9d9e8fcp-479, INIT_U128( 0x00000000009fadb5, 0x7a3f5b7000000000 ) },
+ { 0x1.4b9d6480973acp+87, 0x1.c249100d84922p-479, INIT_U128( 0x0000000000a5ceb2, 0x404b9d6000000000 ) },
+ { 0x1.eea053a5dd40bp+87, 0x1.26e6f8d24dcdfp-479, INIT_U128( 0x0000000000f75029, 0xd2eea05800000000 ) },
+ { 0x1.4e0a84329c15p+88, 0x1.b969043772d2p-931, INIT_U128( 0x00000000014e0a84, 0x329c150000000000 ) },
+ { 0x1.20fcf75c41f9fp+88, 0x1.a487f5bf490ffp-931, INIT_U128( 0x000000000120fcf7, 0x5c41f9f000000000 ) },
+ { 0x1.0937a468126f4p+88, 0x1.dd15685bba2adp-931, INIT_U128( 0x00000000010937a4, 0x68126f4000000000 ) },
+ { 0x1.3a1b94d674372p+88, 0x1.0e98a7441d315p-931, INIT_U128( 0x00000000013a1b94, 0xd674372000000000 ) },
+ { 0x1.b9b702f1736ep+89, 0x1.afdf8f8b5fbf2p-758, INIT_U128( 0x0000000003736e05, 0xe2e6dc0000000000 ) },
+ { 0x1.f93973b7f272fp+89, 0x1.77696130eed2cp-758, INIT_U128( 0x0000000003f272e7, 0x6fe4e5e000000000 ) },
+ { 0x1.85595b3b0ab2cp+89, 0x1.4c1c459298388p-758, INIT_U128( 0x00000000030ab2b6, 0x7615658000000000 ) },
+ { 0x1.89e4ed0b13c9ep+89, 0x1.4c88ddd89911cp-758, INIT_U128( 0x000000000313c9da, 0x162793c000000000 ) },
+ { 0x1.6d0dfe6ada1cp+90, 0x1.0873b16610e76p+20, INIT_U128( 0x0000000005b437f9, 0xab6870000010873b ) },
+ { 0x1.403e37e4807c7p+90, 0x1.630feeecc61fep+20, INIT_U128( 0x000000000500f8df, 0x9201f1c0001630fe ) },
+ { 0x1.1d3cdade3a79cp+90, 0x1.ecceb30bd99d7p+20, INIT_U128( 0x000000000474f36b, 0x78e9e700001ecceb ) },
+ { 0x1.f50698adea0d3p+90, 0x1.4ed5f3749dabep+20, INIT_U128( 0x0000000007d41a62, 0xb7a834c00014ed5f ) },
+ { 0x1.d869b87fb0d37p+91, 0x1.53d108a0a7a21p-525, INIT_U128( 0x000000000ec34dc3, 0xfd869b8000000000 ) },
+ { 0x1.cd078fb39a0f2p+91, 0x1.228b56084516bp-525, INIT_U128( 0x000000000e683c7d, 0x9cd0790000000000 ) },
+ { 0x1.c67dbd798cfb7p+91, 0x1.65e1ed28cbc3ep-525, INIT_U128( 0x000000000e33edeb, 0xcc67db8000000000 ) },
+ { 0x1.c41a2ed388346p+91, 0x1.4799717a8f32ep-525, INIT_U128( 0x000000000e20d176, 0x9c41a30000000000 ) },
+ { 0x1.db416739b682dp+92, 0x1.fea650affd4cap-706, INIT_U128( 0x000000001db41673, 0x9b682d0000000000 ) },
+ { 0x1.872189f10e431p+92, 0x1.e1de445fc3bc8p-706, INIT_U128( 0x000000001872189f, 0x10e4310000000000 ) },
+ { 0x1.d2ad0fd1a55a2p+92, 0x1.0a06ac7e140d6p-706, INIT_U128( 0x000000001d2ad0fd, 0x1a55a20000000000 ) },
+ { 0x1.2535c3a84a6b8p+92, 0x1.65205312ca40ap-706, INIT_U128( 0x0000000012535c3a, 0x84a6b80000000000 ) },
+ { 0x1.dd518ce9baa31p+93, 0x1.b123211362464p-663, INIT_U128( 0x000000003baa319d, 0x3754620000000000 ) },
+ { 0x1.9a89da733513bp+93, 0x1.8c0a0b2b18142p-663, INIT_U128( 0x0000000033513b4e, 0x66a2760000000000 ) },
+ { 0x1.01fc693203f8dp+93, 0x1.81040a6d02081p-663, INIT_U128( 0x00000000203f8d26, 0x407f1a0000000000 ) },
+ { 0x1.1d9ae72e3b35dp+93, 0x1.fb816ce9f702dp-663, INIT_U128( 0x0000000023b35ce5, 0xc766ba0000000000 ) },
+ { 0x1.1b8311c037062p+94, 0x1.3b413a6c76828p-946, INIT_U128( 0x0000000046e0c470, 0x0dc1880000000000 ) },
+ { 0x1.e7c1239fcf825p+94, 0x1.e89cbe61d1398p-946, INIT_U128( 0x0000000079f048e7, 0xf3e0940000000000 ) },
+ { 0x1.3ba2c92677459p+94, 0x1.4c19bb4098338p-946, INIT_U128( 0x000000004ee8b249, 0x9dd1640000000000 ) },
+ { 0x1.e81787a3d02f1p+94, 0x1.21981a6a43304p-946, INIT_U128( 0x000000007a05e1e8, 0xf40bc40000000000 ) },
+ { 0x1.e18ca9a3c3195p+95, 0x1.d2ce473ba59c9p-353, INIT_U128( 0x00000000f0c654d1, 0xe18ca80000000000 ) },
+ { 0x1.ec1a5457d834ap+95, 0x1.673af5f4ce75ep-353, INIT_U128( 0x00000000f60d2a2b, 0xec1a500000000000 ) },
+ { 0x1.3d4fcc727a9fap+95, 0x1.d577142daaee2p-353, INIT_U128( 0x000000009ea7e639, 0x3d4fd00000000000 ) },
+ { 0x1.6b318358d663p+95, 0x1.d96b9445b2d72p-353, INIT_U128( 0x00000000b598c1ac, 0x6b31800000000000 ) },
+ { 0x1.f8fedfa1f1fdcp+96, 0x1.6e54dd28dca9cp-828, INIT_U128( 0x00000001f8fedfa1, 0xf1fdc00000000000 ) },
+ { 0x1.4b5ec85896bd9p+96, 0x1.e4251105c84a2p-828, INIT_U128( 0x000000014b5ec858, 0x96bd900000000000 ) },
+ { 0x1.bc8f0397791ep+96, 0x1.b514998d6a293p-828, INIT_U128( 0x00000001bc8f0397, 0x791e000000000000 ) },
+ { 0x1.056a2dfe0ad46p+96, 0x1.c98d1a15931a3p-828, INIT_U128( 0x00000001056a2dfe, 0x0ad4600000000000 ) },
+ { 0x1.21192da842326p+97, 0x1.64dbdea4c9b7cp-314, INIT_U128( 0x0000000242325b50, 0x8464c00000000000 ) },
+ { 0x1.95c269f12b84dp+97, 0x1.31bfb62e637f7p-314, INIT_U128( 0x000000032b84d3e2, 0x5709a00000000000 ) },
+ { 0x1.f4a37e69e947p+97, 0x1.2c5cc8d658b99p-314, INIT_U128( 0x00000003e946fcd3, 0xd28e000000000000 ) },
+ { 0x1.73c765e6e78ecp+97, 0x1.34b3a3f469674p-314, INIT_U128( 0x00000002e78ecbcd, 0xcf1d800000000000 ) },
+ { 0x1.cafa773f95f4fp+98, 0x1.28d3338851a66p-207, INIT_U128( 0x000000072be9dcfe, 0x57d3c00000000000 ) },
+ { 0x1.7b7b6aa4f6f6ep+98, 0x1.2c99252259324p-207, INIT_U128( 0x00000005ededaa93, 0xdbdb800000000000 ) },
+ { 0x1.8c39a15918734p+98, 0x1.cdcd60b79b9acp-207, INIT_U128( 0x0000000630e68564, 0x61cd000000000000 ) },
+ { 0x1.da220eb5b4442p+98, 0x1.cd0b52b19a16bp-207, INIT_U128( 0x0000000768883ad6, 0xd110800000000000 ) },
+ { 0x1.0f34e6fa1e69dp+99, 0x1.596be5d4b2d7cp-575, INIT_U128( 0x0000000879a737d0, 0xf34e800000000000 ) },
+ { 0x1.5318af1aa6316p+99, 0x1.b2d7a70f65af5p-575, INIT_U128( 0x0000000a98c578d5, 0x318b000000000000 ) },
+ { 0x1.2f3f14005e7e2p+99, 0x1.bf1a11977e342p-575, INIT_U128( 0x0000000979f8a002, 0xf3f1000000000000 ) },
+ { 0x1.c95da71792bb5p+99, 0x1.b673d5896ce7bp-575, INIT_U128( 0x0000000e4aed38bc, 0x95da800000000000 ) },
+ { 0x1.fdead1dffbd5ap+100, 0x1.5a750c74b4ea2p-189, INIT_U128( 0x0000001fdead1dff, 0xbd5a000000000000 ) },
+ { 0x1.fe8116bbfd023p+100, 0x1.c0314d4580629p-189, INIT_U128( 0x0000001fe8116bbf, 0xd023000000000000 ) },
+ { 0x1.9177917922ef2p+100, 0x1.b5d94c0f6bb2ap-189, INIT_U128( 0x0000001917791792, 0x2ef2000000000000 ) },
+ { 0x1.39ab1bf273564p+100, 0x1.4de96f889bd2ep-189, INIT_U128( 0x000000139ab1bf27, 0x3564000000000000 ) },
+ { 0x1.52e21a8aa5c44p+101, 0x0.0000075bfbb25p-1022, INIT_U128( 0x0000002a5c435154, 0xb888000000000000 ) },
+ { 0x1.c362c16186c58p+101, 0x0.00000417370ap-1022, INIT_U128( 0x000000386c582c30, 0xd8b0000000000000 ) },
+ { 0x1.dcbb50a5b976ap+101, 0x0.000005dcb4b01p-1022, INIT_U128( 0x0000003b976a14b7, 0x2ed4000000000000 ) },
+ { 0x1.fae77febf5cfp+101, 0x0.00000567e1492p-1022, INIT_U128( 0x0000003f5ceffd7e, 0xb9e0000000000000 ) },
+ { 0x1.b94feaf3729fdp+102, 0x1.eb191b6bd6324p-32, INIT_U128( 0x0000006e53fabcdc, 0xa7f4000000000000 ) },
+ { 0x1.ffcd483fff9a9p+102, 0x1.f6c951e5ed92ap-32, INIT_U128( 0x0000007ff3520fff, 0xe6a4000000000000 ) },
+ { 0x1.f4a75991e94ebp+102, 0x1.5d81e52ebb03cp-32, INIT_U128( 0x0000007d29d6647a, 0x53ac000000000000 ) },
+ { 0x1.d9014a81b202ap+102, 0x1.1d1a5c4c3a34cp-32, INIT_U128( 0x000000764052a06c, 0x80a8000000000000 ) },
+ { 0x1.d33430cda6686p+103, 0x1.58855d10b10acp-383, INIT_U128( 0x000000e99a1866d3, 0x3430000000000000 ) },
+ { 0x1.5e801048bd002p+103, 0x1.54a52a72a94a6p-383, INIT_U128( 0x000000af4008245e, 0x8010000000000000 ) },
+ { 0x1.590a254ab2144p+103, 0x1.0307881c060f1p-383, INIT_U128( 0x000000ac8512a559, 0x0a20000000000000 ) },
+ { 0x1.6dfaecf0dbf5ep+103, 0x1.54a4b130a9496p-383, INIT_U128( 0x000000b6fd76786d, 0xfaf0000000000000 ) },
+ { 0x1.011eb1b0023d6p+104, 0x1.39df71da73beep-20, INIT_U128( 0x000001011eb1b002, 0x3d60000000000000 ) },
+ { 0x1.5ee2142ebdc42p+104, 0x1.43553d9086aa8p-20, INIT_U128( 0x0000015ee2142ebd, 0xc420000000000000 ) },
+ { 0x1.100502e4200ap+104, 0x1.534d9720a69b3p-20, INIT_U128( 0x000001100502e420, 0x0a00000000000000 ) },
+ { 0x1.2aefc5ac55df8p+104, 0x1.f553d14beaa7ap-20, INIT_U128( 0x0000012aefc5ac55, 0xdf80000000000000 ) },
+ { 0x1.d0559891a0ab3p+105, 0x1.87b2239d0f644p-316, INIT_U128( 0x000003a0ab312341, 0x5660000000000000 ) },
+ { 0x1.0929084e12521p+105, 0x1.e345fe55c68cp-316, INIT_U128( 0x0000021252109c24, 0xa420000000000000 ) },
+ { 0x1.70c26be4e184ep+105, 0x1.7a6011c6f4c02p-316, INIT_U128( 0x000002e184d7c9c3, 0x09c0000000000000 ) },
+ { 0x1.8c345d471868cp+105, 0x1.b4a5d903694bbp-316, INIT_U128( 0x0000031868ba8e30, 0xd180000000000000 ) },
+ { 0x1.66bf48cecd7e9p+106, 0x1.d926e3a9b24dcp-461, INIT_U128( 0x0000059afd233b35, 0xfa40000000000000 ) },
+ { 0x1.caf9648595f2cp+106, 0x1.3edd8e587dbb2p-461, INIT_U128( 0x0000072be5921657, 0xcb00000000000000 ) },
+ { 0x1.0ff9c5341ff38p+106, 0x1.5ecf4954bd9e9p-461, INIT_U128( 0x0000043fe714d07f, 0xce00000000000000 ) },
+ { 0x1.010bbf3602178p+106, 0x1.90183dfb20308p-461, INIT_U128( 0x000004042efcd808, 0x5e00000000000000 ) },
+ { 0x1.90f1198321e23p+107, 0x1.18ec849e31d9p-508, INIT_U128( 0x00000c8788cc190f, 0x1180000000000000 ) },
+ { 0x1.88876447110ecp+107, 0x1.3dbc4a967b78ap-508, INIT_U128( 0x00000c443b223888, 0x7600000000000000 ) },
+ { 0x1.e955d221d2abap+107, 0x1.1f1f0f983e3e2p-508, INIT_U128( 0x00000f4aae910e95, 0x5d00000000000000 ) },
+ { 0x1.79931aacf3264p+107, 0x1.5c6d47c2b8da9p-508, INIT_U128( 0x00000bcc98d56799, 0x3200000000000000 ) },
+ { 0x1.49a28e4c93452p+108, 0x1.0819b1c610336p-949, INIT_U128( 0x0000149a28e4c934, 0x5200000000000000 ) },
+ { 0x1.b61387f56c271p+108, 0x1.fda021a7fb404p-949, INIT_U128( 0x00001b61387f56c2, 0x7100000000000000 ) },
+ { 0x1.8364b71506c97p+108, 0x1.972f04852e5ep-949, INIT_U128( 0x000018364b71506c, 0x9700000000000000 ) },
+ { 0x1.a902b10952056p+108, 0x1.aee9cfcf5dd3ap-949, INIT_U128( 0x00001a902b109520, 0x5600000000000000 ) },
+ { 0x1.d96f1c29b2de4p+109, 0x1.8415bcdf082b8p-695, INIT_U128( 0x00003b2de385365b, 0xc800000000000000 ) },
+ { 0x1.e8dfe3f9d1bfcp+109, 0x1.c31bb2ed86377p-695, INIT_U128( 0x00003d1bfc7f3a37, 0xf800000000000000 ) },
+ { 0x1.3a0cc61474199p+109, 0x1.06f2795a0de4fp-695, INIT_U128( 0x0000274198c28e83, 0x3200000000000000 ) },
+ { 0x1.1f3e5f4a3e7ccp+109, 0x1.d7cc8b85af992p-695, INIT_U128( 0x000023e7cbe947cf, 0x9800000000000000 ) },
+ { 0x1.dc6af865b8d5fp+110, 0x1.b5d225136ba45p-22, INIT_U128( 0x0000771abe196e35, 0x7c00000000000000 ) },
+ { 0x1.4ca9fcc69954p+110, 0x1.eaa024f7d5404p-22, INIT_U128( 0x0000532a7f31a655, 0x0000000000000000 ) },
+ { 0x1.414c72988298ep+110, 0x1.a488753b4910fp-22, INIT_U128( 0x000050531ca620a6, 0x3800000000000000 ) },
+ { 0x1.dc6b39dbb8d67p+110, 0x1.068d04580d1ap-22, INIT_U128( 0x0000771ace76ee35, 0x9c00000000000000 ) },
+ { 0x1.74e50e90e9ca2p+111, 0x1.3f289f007e514p-487, INIT_U128( 0x0000ba72874874e5, 0x1000000000000000 ) },
+ { 0x1.5f0a2632be145p+111, 0x1.f790c8d9ef219p-487, INIT_U128( 0x0000af8513195f0a, 0x2800000000000000 ) },
+ { 0x1.d422bf03a8458p+111, 0x1.fbacd695f759bp-487, INIT_U128( 0x0000ea115f81d422, 0xc000000000000000 ) },
+ { 0x1.d9b5cefdb36bap+111, 0x1.c968397b92d07p-487, INIT_U128( 0x0000ecdae77ed9b5, 0xd000000000000000 ) },
+ { 0x1.f3e78001e7cfp+112, 0x1.4b4970649692ep-744, INIT_U128( 0x0001f3e78001e7cf, 0x0000000000000000 ) },
+ { 0x1.17c200042f84p+112, 0x1.8d8e8a711b1d1p-744, INIT_U128( 0x000117c200042f84, 0x0000000000000000 ) },
+ { 0x1.0fe4fde81fcap+112, 0x1.256616f84acc3p-744, INIT_U128( 0x00010fe4fde81fca, 0x0000000000000000 ) },
+ { 0x1.f2773487e4ee6p+112, 0x1.075dd87a0ebbbp-744, INIT_U128( 0x0001f2773487e4ee, 0x6000000000000000 ) },
+ { 0x1.7f2646f4fe4c9p+113, 0x1.bde0f7d57bc1fp-552, INIT_U128( 0x0002fe4c8de9fc99, 0x2000000000000000 ) },
+ { 0x1.552a71c4aa54ep+113, 0x1.5f75cb84beebap-552, INIT_U128( 0x0002aa54e38954a9, 0xc000000000000000 ) },
+ { 0x1.016211fe02c42p+113, 0x1.755a3ebeeab48p-552, INIT_U128( 0x000202c423fc0588, 0x4000000000000000 ) },
+ { 0x1.2da4a1405b494p+113, 0x1.c7b5a5b78f6b5p-552, INIT_U128( 0x00025b494280b692, 0x8000000000000000 ) },
+ { 0x1.454dcac88a9bap+114, 0x1.39e2f64073c5fp-541, INIT_U128( 0x000515372b222a6e, 0x8000000000000000 ) },
+ { 0x1.f5769ad1eaed4p+114, 0x1.273e2b1a4e7c6p-541, INIT_U128( 0x0007d5da6b47abb5, 0x0000000000000000 ) },
+ { 0x1.52bf04cca57ep+114, 0x1.2d92613e5b24cp-541, INIT_U128( 0x00054afc133295f8, 0x0000000000000000 ) },
+ { 0x1.1b84a7fc37095p+114, 0x1.8b3360311666cp-541, INIT_U128( 0x00046e129ff0dc25, 0x4000000000000000 ) },
+ { 0x1.82ef082b05de1p+115, 0x1.5205f44aa40bep-62, INIT_U128( 0x000c177841582ef0, 0x8000000000000000 ) },
+ { 0x1.bafcbdf175f98p+115, 0x1.a9ea203153d44p-62, INIT_U128( 0x000dd7e5ef8bafcc, 0x0000000000000000 ) },
+ { 0x1.64b56bd8c96aep+115, 0x1.42c3a3da85874p-62, INIT_U128( 0x000b25ab5ec64b57, 0x0000000000000000 ) },
+ { 0x1.55c9b3b6ab936p+115, 0x1.48f9d11a91f3ap-62, INIT_U128( 0x000aae4d9db55c9b, 0x0000000000000000 ) },
+ { 0x1.5321473ca6429p+116, 0x1.181db1c6303b6p-92, INIT_U128( 0x0015321473ca6429, 0x0000000000000000 ) },
+ { 0x1.cdc20a3f9b841p+116, 0x1.50077ebca00fp-92, INIT_U128( 0x001cdc20a3f9b841, 0x0000000000000000 ) },
+ { 0x1.92fee4f925fdcp+116, 0x1.028f8f16051f2p-92, INIT_U128( 0x00192fee4f925fdc, 0x0000000000000000 ) },
+ { 0x1.1bab55e43756ap+116, 0x1.40855e9c810acp-92, INIT_U128( 0x0011bab55e43756a, 0x0000000000000000 ) },
+ { 0x1.db43e365b687dp+117, 0x1.41baf8828375fp-732, INIT_U128( 0x003b687c6cb6d0fa, 0x0000000000000000 ) },
+ { 0x1.a552a1674aa54p+117, 0x1.baa689f9754d1p-732, INIT_U128( 0x0034aa542ce954a8, 0x0000000000000000 ) },
+ { 0x1.fe56ea4dfcadep+117, 0x1.5684197ead083p-732, INIT_U128( 0x003fcadd49bf95bc, 0x0000000000000000 ) },
+ { 0x1.6c9b7a08d937p+117, 0x1.2337f65a466ffp-732, INIT_U128( 0x002d936f411b26e0, 0x0000000000000000 ) },
+ { 0x1.076705de0ecep+118, 0x1.98bce7cb3179dp-844, INIT_U128( 0x0041d9c17783b380, 0x0000000000000000 ) },
+ { 0x1.9b22f3533645ep+118, 0x1.74f31c32e9e64p-844, INIT_U128( 0x0066c8bcd4cd9178, 0x0000000000000000 ) },
+ { 0x1.c1d136e583a27p+118, 0x1.767b76ceecf6fp-844, INIT_U128( 0x0070744db960e89c, 0x0000000000000000 ) },
+ { 0x1.c885934b910b3p+118, 0x1.cd65a24d9acb4p-844, INIT_U128( 0x00722164d2e442cc, 0x0000000000000000 ) },
+ { 0x1.fb2c8085f659p+119, 0x1.4485cbfc890bap-269, INIT_U128( 0x00fd964042fb2c80, 0x0000000000000000 ) },
+ { 0x1.78eebbd0f1dd8p+119, 0x1.373c5ea66e78cp-269, INIT_U128( 0x00bc775de878eec0, 0x0000000000000000 ) },
+ { 0x1.1cbf9dfc397f4p+119, 0x1.3a787bfa74f1p-269, INIT_U128( 0x008e5fcefe1cbfa0, 0x0000000000000000 ) },
+ { 0x1.0b37d4fc166fap+119, 0x1.3ec2f1ca7d85ep-269, INIT_U128( 0x00859bea7e0b37d0, 0x0000000000000000 ) },
+ { 0x1.f7d41d9befa83p+119, 0x1.737e360ee6fc7p+45, INIT_U128( 0x00fbea0ecdf7d418, 0x00002e6fc6c1dcdf ) },
+ { 0x1.5f9adea4bf35cp+119, 0x1.f2a34d73e5469p+45, INIT_U128( 0x00afcd6f525f9ae0, 0x00003e5469ae7ca8 ) },
+ { 0x1.1625a2842c4b4p+119, 0x1.dffa968fbff53p+45, INIT_U128( 0x008b12d1421625a0, 0x00003bff52d1f7fe ) },
+ { 0x1.77b9ea5cef73ep+119, 0x1.03d8c57207b18p+45, INIT_U128( 0x00bbdcf52e77b9f0, 0x0000207b18ae40f6 ) },
+ { 0x1.80c91d3d01924p+119, 0x1.dc2e84abb85dp+45, INIT_U128( 0x00c0648e9e80c920, 0x00003b85d095770b ) },
+ { 0x1.a13e2abb427c5p+119, 0x1.be5baad37cb75p+45, INIT_U128( 0x00d09f155da13e28, 0x000037cb755a6f96 ) },
+ { 0x1.3d0a39607a147p+119, 0x1.5d047beeba09p+45, INIT_U128( 0x009e851cb03d0a38, 0x00002ba08f7dd741 ) },
+ { 0x1.e914f2edd229fp+119, 0x1.a87d6b5150faep+45, INIT_U128( 0x00f48a7976e914f8, 0x0000350fad6a2a1f ) },
+ { 0x1.1bfba16037f74p+119, 0x1.f1667f23e2cdp+45, INIT_U128( 0x008dfdd0b01bfba0, 0x00003e2ccfe47c59 ) },
+ { 0x1.e409cc97c8139p+119, 0x1.760ec180ec1d8p+45, INIT_U128( 0x00f204e64be409c8, 0x00002ec1d8301d83 ) },
+ { 0x1.56095810ac12bp+119, 0x1.fd72c88bfae59p+45, INIT_U128( 0x00ab04ac08560958, 0x00003fae59117f5c ) },
+ { 0x1.014b8dfe02972p+119, 0x1.5051ec7ca0a3ep+45, INIT_U128( 0x0080a5c6ff014b90, 0x00002a0a3d8f9414 ) },
+ { 0x1.2c8071285900ep+119, 0x1.2df331a65be66p+45, INIT_U128( 0x00964038942c8070, 0x000025be6634cb7c ) },
+ { 0x1.ec4edc7bd89dbp+119, 0x1.5689910cad132p+45, INIT_U128( 0x00f6276e3dec4ed8, 0x00002ad1322195a2 ) },
+ { 0x1.acab22af59564p+120, 0x1.4302395486047p-789, INIT_U128( 0x01acab22af595640, 0x0000000000000000 ) },
+ { 0x1.6d777264daeeep+120, 0x1.a94793a7528f2p-789, INIT_U128( 0x016d777264daeee0, 0x0000000000000000 ) },
+ { 0x1.179807f22f301p+120, 0x1.f73f61cbee7ecp-789, INIT_U128( 0x01179807f22f3010, 0x0000000000000000 ) },
+ { 0x1.2306ea1a460dep+120, 0x1.7f322c14fe646p-789, INIT_U128( 0x012306ea1a460de0, 0x0000000000000000 ) },
+ { 0x1.4b89313897126p+121, 0x1.24f0cf5649e1ap-97, INIT_U128( 0x02971262712e24c0, 0x0000000000000000 ) },
+ { 0x1.977d3ba32efa8p+121, 0x1.8d5ae3291ab5cp-97, INIT_U128( 0x032efa77465df500, 0x0000000000000000 ) },
+ { 0x1.4951cf1a92a3ap+121, 0x1.ff3e4eaffe7cap-97, INIT_U128( 0x0292a39e35254740, 0x0000000000000000 ) },
+ { 0x1.6487918ac90f2p+121, 0x1.8e5a3b671cb48p-97, INIT_U128( 0x02c90f2315921e40, 0x0000000000000000 ) },
+ { 0x1.a71b395f4e367p+122, 0x1.4851ea7e90a3ep-824, INIT_U128( 0x069c6ce57d38d9c0, 0x0000000000000000 ) },
+ { 0x1.58097738b012fp+122, 0x1.a4e92d4749d26p-824, INIT_U128( 0x056025dce2c04bc0, 0x0000000000000000 ) },
+ { 0x1.431d2f4a863a6p+122, 0x1.ff84386fff087p-824, INIT_U128( 0x050c74bd2a18e980, 0x0000000000000000 ) },
+ { 0x1.9f1393ad3e272p+122, 0x1.d7c0a6ddaf815p-824, INIT_U128( 0x067c4e4eb4f89c80, 0x0000000000000000 ) },
+ { 0x1.8983ab7913076p+123, 0x1.fa01bdc9f4037p-825, INIT_U128( 0x0c4c1d5bc8983b00, 0x0000000000000000 ) },
+ { 0x1.f3551e67e6aa4p+123, 0x1.029c51f00538ap-825, INIT_U128( 0x0f9aa8f33f355200, 0x0000000000000000 ) },
+ { 0x1.a9da0d1d53b42p+123, 0x1.b5c718d36b8e3p-825, INIT_U128( 0x0d4ed068ea9da100, 0x0000000000000000 ) },
+ { 0x1.95c79e2b2b8f4p+123, 0x1.384e4c32709cap-825, INIT_U128( 0x0cae3cf1595c7a00, 0x0000000000000000 ) },
+ { 0x1.73af4650e75e9p+124, 0x1.ec4b7265d896fp-92, INIT_U128( 0x173af4650e75e900, 0x0000000000000000 ) },
+ { 0x1.5e15a3a0bc2b4p+124, 0x1.2ff859825ff0bp-92, INIT_U128( 0x15e15a3a0bc2b400, 0x0000000000000000 ) },
+ { 0x1.4dbe24a69b7c4p+124, 0x1.f5b685e5eb6d1p-92, INIT_U128( 0x14dbe24a69b7c400, 0x0000000000000000 ) },
+ { 0x1.cd65ac439acb6p+124, 0x1.164d910a2c9b2p-92, INIT_U128( 0x1cd65ac439acb600, 0x0000000000000000 ) },
+ { 0x1.f5bbadd5eb775p+125, 0x1.b16aeddb62d5ep-83, INIT_U128( 0x3eb775babd6eea00, 0x0000000000000000 ) },
+ { 0x1.b66841396cd08p+125, 0x1.f9957d69f32afp-83, INIT_U128( 0x36cd08272d9a1000, 0x0000000000000000 ) },
+ { 0x1.05be50760b7cap+125, 0x1.f9ebf8ddf3d7fp-83, INIT_U128( 0x20b7ca0ec16f9400, 0x0000000000000000 ) },
+ { 0x1.51312262a2624p+125, 0x1.11c69084238d2p-83, INIT_U128( 0x2a26244c544c4800, 0x0000000000000000 ) },
+ { 0x1.30eab12c61d56p+126, 0x1.f1e0e851e3c1dp-773, INIT_U128( 0x4c3aac4b18755800, 0x0000000000000000 ) },
+ { 0x1.ac34bcf158698p+126, 0x1.a55b126f4ab62p-773, INIT_U128( 0x6b0d2f3c561a6000, 0x0000000000000000 ) },
+ { 0x1.02246a040448ep+126, 0x1.61a83652c3507p-773, INIT_U128( 0x40891a8101123800, 0x0000000000000000 ) },
+ { 0x1.ae881f955d104p+126, 0x1.c694aaa18d296p-773, INIT_U128( 0x6ba207e557441000, 0x0000000000000000 ) },
+ { 0x1.08016b641002ep+127, 0x1.23a890d047512p-406, INIT_U128( 0x8400b5b208017000, 0x0000000000000000 ) },
+ { 0x1.3d90e7327b21dp+127, 0x1.0aa664c0154ccp-406, INIT_U128( 0x9ec873993d90e800, 0x0000000000000000 ) },
+ { 0x1.ff931617ff263p+127, 0x1.47d5e2a08fabcp-406, INIT_U128( 0xffc98b0bff931800, 0x0000000000000000 ) },
+ { 0x1.f565d1dfeacbap+127, 0x1.827bc0c304f78p-406, INIT_U128( 0xfab2e8eff565d000, 0x0000000000000000 ) },
+ { 0x1.fe1985fbfc33p+128, 0x1.a6e724cd4dce4p-1021, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.54709ed6a8e14p+128, 0x1.74a32d1ce9466p-1021, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.a8407dcf5081p+128, 0x1.ffb065f3ff60cp-1021, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.7a1cfcf2f43ap+130, 0x1.84bb5ea30976cp-548, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.6bddaae8d7bb6p+194, 0x1.de64c609bcc99p-754, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.5c6c29d4b8d85p+146, 0x1.0644f6d60c89fp-345, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.c6ce8bcf8d9d2p+158, 0x1.443b2e7088766p-26, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.747f6aa0e8feep+168, 0x1.fe663ae9fccc8p-143, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.26ee550a4ddcap+173, 0x1.951f708f2a3eep-45, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.3ecfe0ea7d9fcp+189, 0x1.9cb53593396a7p-619, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.9d394a313a729p+190, 0x1.9ecfff853dap-150, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.244a568a4894bp+241, 0x1.e23f42d7c47e9p-956, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.b564bb276ac98p+398, 0x1.7af8d1ccf5f1ap+215, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.ee06b389dc0d7p+468, 0x1.b54fa74f6a9f5p+322, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.ac0045bd58009p+588, 0x1.ba99775b7532fp-365, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.692078cad240fp+661, 0x1.bc4a88a978951p+356, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.be1e09dd7c3c1p+765, 0x1.428bc5dc85178p-848, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.5f5554c4beaaap+853, 0x1.a9e23bdd53c48p-807, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.748e96ace91d3p+993, 0x1.ab54ab4b56a96p-22, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { 0x1.0629e7380c53dp+1023, 0x1.c0b4b15581696p+323, INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ) },
+ { INFINITY, 0x1.21bff4bc437fep-333, ((__uint128_t) 0xffffffffffffffff << 64) | 0xffffffffffffffff },
+ { INFINITY, 0x1.47e9a0228fd34p-333, ((__uint128_t) 0xffffffffffffffff << 64) | 0xffffffffffffffff }
+};
+
+static const int numTests = sizeof(testList) / sizeof(struct testCase);
+
diff --git a/test/builtins/Unit/ppc/floatditf_test.c b/test/builtins/Unit/ppc/floatditf_test.c
index 5c08ade4bdf8..e9b9458349ee 100644
--- a/test/builtins/Unit/ppc/floatditf_test.c
+++ b/test/builtins/Unit/ppc/floatditf_test.c
@@ -1,7 +1,8 @@
-// REQUIRES: powerpc-registered-target
-// RUN: %clang_builtins %s -o %t && %run %t
+// REQUIRES: target-is-powerpc64le
+// RUN: %clang_builtins %s %librt -o %t && %run %t
#include <stdint.h>
#include <stdio.h>
+#include "int_lib.h"
COMPILER_RT_ABI long double __floatditf(int64_t);
diff --git a/test/builtins/Unit/ppc/floattitf_test.c b/test/builtins/Unit/ppc/floattitf_test.c
new file mode 100644
index 000000000000..e89e5d946ddb
--- /dev/null
+++ b/test/builtins/Unit/ppc/floattitf_test.c
@@ -0,0 +1,59 @@
+// REQUIRES: target-is-powerpc64le
+// RUN: %clang_builtins %s %librt -o %t && %run %t
+
+/*
+ * Test case execution for: long double __floattitf (__int128_t)
+ * Conversion from 128 bit integer to long double (IBM double-double).
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "floattitf_test.h"
+
+/* The long double representation, with the high and low portions of
+ * the long double, and the corresponding bit patterns of each double. */
+typedef union {
+ long double ld;
+ double d[2]; /* [0] is the high double, [1] is the low double. */
+ unsigned long long ull[2]; /* High and low doubles as 64-bit integers. */
+} ldUnion;
+
+long double __floattitf(__int128_t);
+
+int main(int argc, char *argv[]) {
+ /* Necessary long double and 128 bit integer declarations used to
+ * compare computed and expected high and low portions of the
+ * IBM double-double. */
+ ldUnion expectedLongDouble;
+ ldUnion computedLongDouble;
+ __int128_t result128;
+
+ for (int i = 0; i < numTests; ++i) {
+ /* Set the expected high and low values of the long double,
+ * and the 128 bit integer input to be converted. */
+ expectedLongDouble.d[0] = tests[i].hi;
+ expectedLongDouble.d[1] = tests[i].lo;
+ result128 = tests[i].input128;
+
+ /* Get the computed long double from the int128->long double conversion
+ * and check for errors between high and low portions. */
+ computedLongDouble.ld = __floattitf(result128);
+
+ if ((computedLongDouble.d[0] != expectedLongDouble.d[0]) ||
+ (computedLongDouble.d[1] != expectedLongDouble.d[1])) {
+
+ printf("Error on __floattitf( 0x%016llx 0x%016llx ):\n",
+ (long long)(tests[i].input128 >> 64),
+ (long long)tests[i].input128);
+ printf("\tExpected value - %La = ( %a, %a )\n", expectedLongDouble.ld,
+ expectedLongDouble.d[0], expectedLongDouble.d[1]);
+ printf("\tComputed value - %La = ( %a, %a )\n\n", computedLongDouble.ld,
+ computedLongDouble.d[0], computedLongDouble.d[1]);
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/test/builtins/Unit/ppc/floattitf_test.h b/test/builtins/Unit/ppc/floattitf_test.h
new file mode 100644
index 000000000000..1da8a6b52065
--- /dev/null
+++ b/test/builtins/Unit/ppc/floattitf_test.h
@@ -0,0 +1,197 @@
+/*
+* Test case inputs for: long double __floattitf (__int128_t)
+* Conversion from 128 bit integer to long double (IBM double-double).
+*/
+
+#define INIT_U128(HI, LO) (((__uint128_t) (HI) << 64) | (LO))
+
+struct testCase {
+ __int128_t input128;
+ double hi;
+ double lo;
+};
+
+struct testCase tests[] = {
+ { INIT_U128( 0x0000000000000000, 0x0000000000000000 ), 0x0p+0, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0x0000000000000001 ), 0x1p+0, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0x1000000000000000 ), 0x1p+60, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xffffffffffffffff ), 0x1p+64, -0x1p+0 },
+ { INIT_U128( 0x0000000000000000, 0x7fffffffffffffff ), 0x1p+63, -0x1p+0 },
+ { INIT_U128( 0x0000000000000001, 0x0000000000000000 ), 0x1p+64, 0x0p+0 },
+ { INIT_U128( 0x0000000000000001, 0x0000000000000001 ), 0x1p+64, 0x1p+0 },
+ { INIT_U128( 0x0000000000000001, 0x7fffffffffffffff ), 0x1.8p+64, -0x1p+0 },
+ { INIT_U128( 0x0000000000000001, 0x1000000000000000 ), 0x1.1p+64, 0x0p+0 },
+ { INIT_U128( 0x0000000000000001, 0xffffffffffffffff ), 0x1p+65, -0x1p+0 },
+ { INIT_U128( 0x7fffffffffffffff, 0x0000000000000000 ), 0x1p+127, -0x1p+64 },
+ { INIT_U128( 0x7fffffffffffffff, 0x0000000000000001 ), 0x1p+127, -0x1p+64 },
+ { INIT_U128( 0x7fffffffffffffff, 0x7fffffffffffffff ), 0x1p+127, -0x1p+63 },
+ { INIT_U128( 0x7fffffffffffffff, 0x1000000000000000 ), 0x1p+127, -0x1.ep+63 },
+ { INIT_U128( 0x7fffffffffffffff, 0xffffffffffffffff ), 0x1p+127, -0x1p+0 },
+ { INIT_U128( 0x1000000000000000, 0x0000000000000000 ), 0x1p+124, 0x0p+0 },
+ { INIT_U128( 0x1000000000000000, 0x0000000000000001 ), 0x1p+124, 0x1p+0 },
+ { INIT_U128( 0x1000000000000000, 0x7fffffffffffffff ), 0x1p+124, 0x1p+63 },
+ { INIT_U128( 0x1000000000000000, 0x1000000000000000 ), 0x1p+124, 0x1p+60 },
+ { INIT_U128( 0x1000000000000000, 0xffffffffffffffff ), 0x1p+124, 0x1p+64 },
+ { INIT_U128( 0xffffffffffffffff, 0x0000000000000000 ), -0x1p+64, 0x0p+0 },
+ { INIT_U128( 0xffffffffffffffff, 0x0000000000000001 ), -0x1p+64, 0x1p+0 },
+ { INIT_U128( 0xffffffffffffffff, 0x7fffffffffffffff ), -0x1p+63, -0x1p+0 },
+ { INIT_U128( 0xffffffffffffffff, 0x1000000000000000 ), -0x1.ep+63, 0x0p+0 },
+ { INIT_U128( 0xffffffffffffffff, 0xffffffffffffffff ), -0x1p+0, 0x0p+0 },
+ { INIT_U128( 0x0000000000000061, 0x0000000000000061 ), 0x1.84p+70, 0x1.84p+6 },
+ { INIT_U128( 0x0000000000000000, 0x0000000000000057 ), 0x1.5cp+6, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xffffffffffffff9e ), 0x1p+64, -0x1.88p+6 },
+ { INIT_U128( 0x0000000000000000, 0x0000000000000062 ), 0x1.88p+6, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0x0000000000000403 ), 0x1.00cp+10, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xfffffffffffffbfc ), 0x1.fffffffffffffp+63, 0x1.fep+9 },
+ { INIT_U128( 0x0000000000000000, 0x000000000000040a ), 0x1.028p+10, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0x000000000000040c ), 0x1.03p+10, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0x000000000000041a ), 0x1.068p+10, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0x0000000000000510 ), 0x1.44p+10, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0x0000000000000518 ), 0x1.46p+10, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xfffffffffffffae7 ), 0x1.fffffffffffffp+63, 0x1.738p+9 },
+ { INIT_U128( 0x0000000000000000, 0x0000000000000602 ), 0x1.808p+10, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xfffffffffffff9fd ), 0x1.fffffffffffffp+63, 0x1.fdp+8 },
+ { INIT_U128( 0x0000000000000000, 0x0000000000002090 ), 0x1.048p+13, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xffffffffffffdbdb ), 0x1.ffffffffffffbp+63, 0x1.ed8p+9 },
+ { INIT_U128( 0x0000000000000000, 0x0000000000002430 ), 0x1.218p+13, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0x0000000000020202 ), 0x1.0101p+17, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xfffffffffffdeef7 ), 0x1.fffffffffffbep+63, -0x1.09p+8 },
+ { INIT_U128( 0x0000000000000000, 0x0000000000032004 ), 0x1.9002p+17, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xfffffffffffcdffb ), 0x1.fffffffffff9cp+63, -0x1.4p+2 },
+ { INIT_U128( 0x0000000000000000, 0x0000000000032040 ), 0x1.902p+17, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xffffffffffbfdfbb ), 0x1.ffffffffff7fcp+63, -0x1.14p+6 },
+ { INIT_U128( 0x0000000000000000, 0x0000000000402088 ), 0x1.00822p+22, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0x0000000080080088 ), 0x1.0010011p+31, 0x0p+0 },
+ { INIT_U128( 0xffffffff7ff7ff77, 0xffffffff7ff7ff77 ), -0x1.0010011p+95, -0x1.00100112p+31 },
+ { INIT_U128( 0x0000000000000000, 0x0000000850000008 ), 0x1.0a000001p+35, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0x0000002000000448 ), 0x1.000000224p+37, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xffffffdfff7dbfff ), 0x1.ffffffbffefb8p+63, -0x1p+0 },
+ { INIT_U128( 0x0000004002001100, 0x0000004002001100 ), 0x1.00080044p+102, 0x1.00080044p+38 },
+ { INIT_U128( 0x000008020000000c, 0x000008020000000c ), 0x1.00400000018p+107, 0x1.00400000018p+43 },
+ { INIT_U128( 0x0000000000000000, 0xfffff7fdfffffff3 ), 0x1.ffffeffcp+63, -0x1.ap+3 },
+ { INIT_U128( 0x0000000000000000, 0x0000800000000824 ), 0x1.000000001048p+47, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xfffdffffff7ffcff ), 0x1.fffbffffffp+63, -0x1.808p+9 },
+ { INIT_U128( 0x0000000000000000, 0x4002040010000000 ), 0x1.000810004p+62, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xbffdfbffefffffff ), 0x1.7ffbf7ffep+63, -0x1p+0 },
+ { INIT_U128( 0x0000000000000000, 0x8000000000000000 ), 0x1p+63, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xfffffffff418c5c1 ), 0x1.ffffffffe8319p+63, -0x1.1f8p+9 },
+ { INIT_U128( 0x000000000000000f, 0xffffffffffffffff ), 0x1p+68, -0x1p+0 },
+ { INIT_U128( 0x00000000000000ff, 0xffffffffffffffff ), 0x1p+72, -0x1p+0 },
+ { INIT_U128( 0x0000000000000fff, 0xffffffffffffffff ), 0x1p+76, -0x1p+0 },
+ { INIT_U128( 0x000000000000ffff, 0xffffffffffffffff ), 0x1p+80, -0x1p+0 },
+ { INIT_U128( 0x00000000000fffff, 0xffffffffffffffff ), 0x1p+84, -0x1p+0 },
+ { INIT_U128( 0x0000000000ffffff, 0xffffffffffffffff ), 0x1p+88, -0x1p+0 },
+ { INIT_U128( 0x000000000fffffff, 0xffffffffffffffff ), 0x1p+92, -0x1p+0 },
+ { INIT_U128( 0x00000000ffffffff, 0xffffffffffffffff ), 0x1p+96, -0x1p+0 },
+ { INIT_U128( 0x0000000fffffffff, 0xffffffffffffffff ), 0x1p+100, -0x1p+0 },
+ { INIT_U128( 0x000000ffffffffff, 0xffffffffffffffff ), 0x1p+104, -0x1p+0 },
+ { INIT_U128( 0x00000fffffffffff, 0xffffffffffffffff ), 0x1p+108, -0x1p+0 },
+ { INIT_U128( 0x0000ffffffffffff, 0xffffffffffffffff ), 0x1p+112, -0x1p+0 },
+ { INIT_U128( 0x000fffffffffffff, 0xffffffffffffffff ), 0x1p+116, -0x1p+0 },
+ { INIT_U128( 0x00ffffffffffffff, 0xffffffffffffffff ), 0x1p+120, -0x1p+0 },
+ { INIT_U128( 0x0fffffffffffffff, 0xffffffffffffffff ), 0x1p+124, -0x1p+0 },
+ { INIT_U128( 0x1000000000000000, 0x0000000000000000 ), 0x1p+124, 0x0p+0 },
+ { INIT_U128( 0x1000000000000000, 0x0000000000000011 ), 0x1p+124, 0x1.1p+4 },
+ { INIT_U128( 0x1000000000000000, 0x0000000000000111 ), 0x1p+124, 0x1.11p+8 },
+ { INIT_U128( 0x1000000000000000, 0x0000000000001111 ), 0x1p+124, 0x1.111p+12 },
+ { INIT_U128( 0x1000000000000000, 0x0000000000011111 ), 0x1p+124, 0x1.1111p+16 },
+ { INIT_U128( 0x1000000000000000, 0x0000000000111111 ), 0x1p+124, 0x1.11111p+20 },
+ { INIT_U128( 0x1000000000000000, 0x0000000001111111 ), 0x1p+124, 0x1.111111p+24 },
+ { INIT_U128( 0x1000000000000000, 0x0000000011111111 ), 0x1p+124, 0x1.1111111p+28 },
+ { INIT_U128( 0x1000000000000000, 0x0000000111111111 ), 0x1p+124, 0x1.11111111p+32 },
+ { INIT_U128( 0x1000000000000000, 0x0000001111111111 ), 0x1p+124, 0x1.111111111p+36 },
+ { INIT_U128( 0x1000000000000000, 0x0000011111111111 ), 0x1p+124, 0x1.1111111111p+40 },
+ { INIT_U128( 0x1000000000000000, 0x0000111111111111 ), 0x1p+124, 0x1.11111111111p+44 },
+ { INIT_U128( 0x1000000000000000, 0x0001111111111111 ), 0x1p+124, 0x1.111111111111p+48 },
+ { INIT_U128( 0x1000000000000000, 0x0011111111111111 ), 0x1p+124, 0x1.1111111111111p+52 },
+ { INIT_U128( 0x1000000000000000, 0x0111111111111111 ), 0x1p+124, 0x1.11111111111111p+56 },
+ { INIT_U128( 0x1000000000000000, 0x1111111111111111 ), 0x1p+124, 0x1.111111111111111p+60 },
+ { INIT_U128( 0xffffffffffffffff, 0xf000000000000000 ), -0x1p+60, 0x0p+0 },
+ { INIT_U128( 0xffffffffffffffff, 0xff00000000000000 ), -0x1p+56, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xb7ffffff77ffffff ), 0x1.6ffffffefp+63, -0x1p+0 },
+ { INIT_U128( 0xb7ffffff3fffffff, 0xb7ffffff3fffffff ), -0x1.20000003p+126, -0x1.20000003p+62 },
+ { INIT_U128( 0xb7fffffefffffffe, 0xb7fffffefffffffe ), -0x1.20000004p+126, -0x1.48000001p+64 },
+ { INIT_U128( 0xb7fffffeffffefff, 0xb7fffffeffffefff ), -0x1.2000000400004p+126, -0x1.2000000400004p+62 },
+ { INIT_U128( 0x0000040000000000, 0x0000000000000000 ), 0x1p+106, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0x000000003ff00000 ), 0x1.ff8p+29, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xb7fbffffffffdfff ), 0x1.6ff7ffffffffcp+63, -0x1p+0 },
+ { INIT_U128( 0x0000000000000000, 0x4804000000010000 ), 0x1.201000000004p+62, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0x4900000000001000 ), 0x1.2400000000004p+62, 0x0p+0 },
+ { INIT_U128( 0x0000000000000000, 0xb6ffffffffffefff ), 0x1.6dffffffffffep+63, -0x1p+0 },
+ { INIT_U128( 0x0000000000000000, 0x4900000000008000 ), 0x1.240000000002p+62, 0x0p+0 },
+ { INIT_U128( 0xb6ffffffffff7fff, 0xb6ffffffffff7fff ), -0x1.240000000002p+126, -0x1.240000000002p+62 },
+ { INIT_U128( 0x4900000000040000, 0x4900000000040000 ), 0x1.24000000001p+126, 0x1.24000000001p+62 },
+ { INIT_U128( 0xb6fffffffffbffff, 0xb6fffffffffbffff ), -0x1.24000000001p+126, -0x1.24000000001p+62 },
+ { INIT_U128( 0x4900000000200000, 0x4900000000200000 ), 0x1.24000000008p+126, 0x1.24000000008p+62 },
+ { INIT_U128( 0xb6ffffffffdfffff, 0xb6ffffffffdfffff ), -0x1.24000000008p+126, -0x1.24000000008p+62 },
+ { INIT_U128( 0x4900000000800000, 0x4900000000800000 ), 0x1.2400000002p+126, 0x1.2400000002p+62 },
+ { INIT_U128( 0xb6ffffffff7fffff, 0xb6ffffffff7fffff ), -0x1.2400000002p+126, -0x1.2400000002p+62 },
+ { INIT_U128( 0x4900000008000000, 0x4900000008000000 ), 0x1.240000002p+126, 0x1.240000002p+62 },
+ { INIT_U128( 0xb6fffffff7ffffff, 0xb6fffffff7ffffff ), -0x1.240000002p+126, -0x1.240000002p+62 },
+ { INIT_U128( 0x4900000040000000, 0x4900000040000000 ), 0x1.24000001p+126, 0x1.24000001p+62 },
+ { INIT_U128( 0xb6ffffffbfffffff, 0xb6ffffffbfffffff ), -0x1.24000001p+126, -0x1.24000001p+62 },
+ { INIT_U128( 0x4900000080000000, 0x4900000080000000 ), 0x1.24000002p+126, 0x1.24000002p+62 },
+ { INIT_U128( 0xb6ffffff7fffffff, 0xb6ffffff7fffffff ), -0x1.24000002p+126, -0x1.24000002p+62 },
+ { INIT_U128( 0x4900000400000000, 0x4900000400000000 ), 0x1.2400001p+126, 0x1.2400001p+62 },
+ { INIT_U128( 0xb6fffffbffffffff, 0xb6fffffbffffffff ), -0x1.2400001p+126, -0x1.2400001p+62 },
+ { INIT_U128( 0x4900001000000000, 0x4900001000000000 ), 0x1.2400004p+126, 0x1.2400004p+62 },
+ { INIT_U128( 0xb6ffffefffffffff, 0xb6ffffefffffffff ), -0x1.2400004p+126, -0x1.2400004p+62 },
+ { INIT_U128( 0x4900004000000000, 0x4900004000000000 ), 0x1.240001p+126, 0x1.240001p+62 },
+ { INIT_U128( 0xb6ffffbfffffffff, 0xb6ffffbfffffffff ), -0x1.240001p+126, -0x1.240001p+62 },
+ { INIT_U128( 0x4900040000000000, 0x4900040000000000 ), 0x1.24001p+126, 0x1.24001p+62 },
+ { INIT_U128( 0xb6fffbffffffffff, 0xb6fffbffffffffff ), -0x1.24001p+126, -0x1.24001p+62 },
+ { INIT_U128( 0x4900200000000000, 0x4900200000000000 ), 0x1.24008p+126, 0x1.24008p+62 },
+ { INIT_U128( 0xb6ffdfffffffffff, 0xb6ffdfffffffffff ), -0x1.24008p+126, -0x1.24008p+62 },
+ { INIT_U128( 0x4901000000000000, 0x4901000000000000 ), 0x1.2404p+126, 0x1.2404p+62 },
+ { INIT_U128( 0xb6feffffffffffff, 0xb6feffffffffffff ), -0x1.2404p+126, -0x1.2404p+62 },
+ { INIT_U128( 0x4910000000000000, 0x4910000000000000 ), 0x1.244p+126, 0x1.244p+62 },
+ { INIT_U128( 0xb6efffffffffffff, 0xb6efffffffffffff ), -0x1.244p+126, -0x1.244p+62 },
+ { INIT_U128( 0x4980000000000000, 0x4980000000000000 ), 0x1.26p+126, 0x1.26p+62 },
+ { INIT_U128( 0xb67fffffffffffff, 0xb67fffffffffffff ), -0x1.26p+126, -0x1.26p+62 },
+ { INIT_U128( 0x4c00000000000000, 0x4c00000000000000 ), 0x1.3p+126, 0x1.3p+62 },
+ { INIT_U128( 0xb3ffffffffffffff, 0xb3ffffffffffffff ), -0x1.3p+126, -0x1.3p+62 },
+ { INIT_U128( 0x9fffffffffffbfff, 0x9fffffffffffbfff ), -0x1.800000000001p+126, -0x1.800000000001p+62 },
+ { INIT_U128( 0x6000000000004001, 0x6000000000004001 ), 0x1.800000000001p+126, 0x1.6000000000004p+64 },
+ { INIT_U128( 0x8fffffffffffffbf, 0x8fffffffffffffbf ), -0x1.cp+126, -0x1.01cp+70 },
+ { INIT_U128( 0x7000000000000080, 0x7000000000000080 ), 0x1.cp+126, 0x1.00ep+71 },
+ { INIT_U128( 0x8fffffffffffff7f, 0x8fffffffffffff7f ), -0x1.cp+126, -0x1.00ep+71 },
+ { INIT_U128( 0x7000000000000800, 0x7000000000000800 ), 0x1.c000000000002p+126, 0x1.c000000000002p+62 },
+ { INIT_U128( 0x8ffffffffffff7ff, 0x8ffffffffffff7ff ), -0x1.c000000000002p+126, -0x1.c000000000002p+62 },
+ { INIT_U128( 0x7000000000002000, 0x7000000000002000 ), 0x1.c000000000008p+126, 0x1.c000000000008p+62 },
+ { INIT_U128( 0x8fffffffffffdfff, 0x8fffffffffffdfff ), -0x1.c000000000008p+126, -0x1.c000000000008p+62 },
+ { INIT_U128( 0x7000000000010000, 0x7000000000010000 ), 0x1.c00000000004p+126, 0x1.c00000000004p+62 },
+ { INIT_U128( 0x8ffffffffffeffff, 0x8ffffffffffeffff ), -0x1.c00000000004p+126, -0x1.c00000000004p+62 },
+ { INIT_U128( 0x7000000000040000, 0x7000000000040000 ), 0x1.c0000000001p+126, 0x1.c0000000001p+62 },
+ { INIT_U128( 0x8ffffffffffbffff, 0x8ffffffffffbffff ), -0x1.c0000000001p+126, -0x1.c0000000001p+62 },
+ { INIT_U128( 0x7000000000080000, 0x7000000000080000 ), 0x1.c0000000002p+126, 0x1.c0000000002p+62 },
+ { INIT_U128( 0x8ffffffffff7ffff, 0x8ffffffffff7ffff ), -0x1.c0000000002p+126, -0x1.c0000000002p+62 },
+ { INIT_U128( 0x7000000000800000, 0x7000000000800000 ), 0x1.c000000002p+126, 0x1.c000000002p+62 },
+ { INIT_U128( 0x8fffffffff7fffff, 0x8fffffffff7fffff ), -0x1.c000000002p+126, -0x1.c000000002p+62 },
+ { INIT_U128( 0x7000000008000000, 0x7000000008000000 ), 0x1.c00000002p+126, 0x1.c00000002p+62 },
+ { INIT_U128( 0x8ffffffff7ffffff, 0x8ffffffff7ffffff ), -0x1.c00000002p+126, -0x1.c00000002p+62 },
+ { INIT_U128( 0x7000000010000000, 0x7000000010000000 ), 0x1.c00000004p+126, 0x1.c00000004p+62 },
+ { INIT_U128( 0x8fffffffefffffff, 0x8fffffffefffffff ), -0x1.c00000004p+126, -0x1.c00000004p+62 },
+ { INIT_U128( 0x7000000100000000, 0x7000000100000000 ), 0x1.c0000004p+126, 0x1.c0000004p+62 },
+ { INIT_U128( 0x8ffffffeffffffff, 0x8ffffffeffffffff ), -0x1.c0000004p+126, -0x1.c0000004p+62 },
+ { INIT_U128( 0x7000000200000000, 0x7000000200000000 ), 0x1.c0000008p+126, 0x1.c0000008p+62 },
+ { INIT_U128( 0x8ffffffdffffffff, 0x8ffffffdffffffff ), -0x1.c0000008p+126, -0x1.c0000008p+62 },
+ { INIT_U128( 0x7000000800000000, 0x7000000800000000 ), 0x1.c000002p+126, 0x1.c000002p+62 },
+ { INIT_U128( 0x8ffffff7ffffffff, 0x8ffffff7ffffffff ), -0x1.c000002p+126, -0x1.c000002p+62 },
+ { INIT_U128( 0x7000008000000000, 0x7000008000000000 ), 0x1.c00002p+126, 0x1.c00002p+62 },
+ { INIT_U128( 0x8fffff7fffffffff, 0x8fffff7fffffffff ), -0x1.c00002p+126, -0x1.c00002p+62 },
+ { INIT_U128( 0x7000010000000000, 0x7000010000000000 ), 0x1.c00004p+126, 0x1.c00004p+62 },
+ { INIT_U128( 0x8ffffeffffffffff, 0x8ffffeffffffffff ), -0x1.c00004p+126, -0x1.c00004p+62 },
+ { INIT_U128( 0x7000080000000000, 0x7000080000000000 ), 0x1.c0002p+126, 0x1.c0002p+62 },
+ { INIT_U128( 0x8ffff7ffffffffff, 0x8ffff7ffffffffff ), -0x1.c0002p+126, -0x1.c0002p+62 },
+ { INIT_U128( 0x7000800000000000, 0x7000800000000000 ), 0x1.c002p+126, 0x1.c002p+62 },
+ { INIT_U128( 0x8fff7fffffffffff, 0x8fff7fffffffffff ), -0x1.c002p+126, -0x1.c002p+62 },
+ { INIT_U128( 0x7002000000000000, 0x7002000000000000 ), 0x1.c008p+126, 0x1.c008p+62 },
+ { INIT_U128( 0x8ffdffffffffffff, 0x8ffdffffffffffff ), -0x1.c008p+126, -0x1.c008p+62 },
+ { INIT_U128( 0x7008000000000000, 0x7008000000000000 ), 0x1.c02p+126, 0x1.c02p+62 },
+ { INIT_U128( 0x8ff7ffffffffffff, 0x8ff7ffffffffffff ), -0x1.c02p+126, -0x1.c02p+62 },
+ { INIT_U128( 0x7080000000000000, 0x7080000000000000 ), 0x1.c2p+126, 0x1.c2p+62 },
+ { INIT_U128( 0x8f7fffffffffffff, 0x8f7fffffffffffff ), -0x1.c2p+126, -0x1.c2p+62 },
+ { INIT_U128( 0x7400000000000000, 0x7400000000000000 ), 0x1.dp+126, 0x1.dp+62 }
+};
+
+static const int numTests = sizeof(tests) / sizeof(struct testCase);
diff --git a/test/builtins/Unit/ppc/floatunditf_test.c b/test/builtins/Unit/ppc/floatunditf_test.c
index 3e50128578e3..fc6090be1782 100644
--- a/test/builtins/Unit/ppc/floatunditf_test.c
+++ b/test/builtins/Unit/ppc/floatunditf_test.c
@@ -1,7 +1,8 @@
-// REQUIRES: powerpc-registered-target
-// RUN: %clang_builtins %s -o %t && %run %t
+// REQUIRES: target-is-powerpc64le
+// RUN: %clang_builtins %s %librt -o %t && %run %t
#include <stdint.h>
#include <stdio.h>
+#include "int_lib.h"
COMPILER_RT_ABI long double __floatunditf(uint64_t);
diff --git a/test/builtins/Unit/ppc/qadd_test.c b/test/builtins/Unit/ppc/qadd_test.c
index 327fd21e8aa7..242850fff377 100644
--- a/test/builtins/Unit/ppc/qadd_test.c
+++ b/test/builtins/Unit/ppc/qadd_test.c
@@ -1,5 +1,5 @@
-// REQUIRES: powerpc-registered-target
-// RUN: %clang_builtins %s -o %t && %run %t
+// REQUIRES: target-is-powerpc64le
+// RUN: %clang_builtins %s %librt -o %t && %run %t
#include <stdio.h>
#include "DD.h"
diff --git a/test/builtins/Unit/ppc/qdiv_test.c b/test/builtins/Unit/ppc/qdiv_test.c
index 7530e428fa72..15990f0c36fb 100644
--- a/test/builtins/Unit/ppc/qdiv_test.c
+++ b/test/builtins/Unit/ppc/qdiv_test.c
@@ -1,5 +1,5 @@
-// REQUIRES: powerpc-registered-target
-// RUN: %clang_builtins %s -o %t && %run %t
+// REQUIRES: target-is-powerpc64le
+// RUN: %clang_builtins %s %librt -o %t && %run %t
#include <stdio.h>
#include "DD.h"
diff --git a/test/builtins/Unit/ppc/qmul_test.c b/test/builtins/Unit/ppc/qmul_test.c
index dbe3536f3098..c8e5bb096a25 100644
--- a/test/builtins/Unit/ppc/qmul_test.c
+++ b/test/builtins/Unit/ppc/qmul_test.c
@@ -1,5 +1,5 @@
-// REQUIRES: powerpc-registered-target
-// RUN: %clang_builtins %s -o %t && %run %t
+// REQUIRES: target-is-powerpc64le
+// RUN: %clang_builtins %s %librt -o %t && %run %t
#include <stdio.h>
#include "DD.h"
diff --git a/test/builtins/Unit/ppc/qsub_test.c b/test/builtins/Unit/ppc/qsub_test.c
index e21224096f5c..1e456f83aea3 100644
--- a/test/builtins/Unit/ppc/qsub_test.c
+++ b/test/builtins/Unit/ppc/qsub_test.c
@@ -1,5 +1,5 @@
-// REQUIRES: powerpc-registered-target
-// RUN: %clang_builtins %s -o %t && %run %t
+// REQUIRES: target-is-powerpc64le
+// RUN: %clang_builtins %s %librt -o %t && %run %t
#include <stdio.h>
#include "DD.h"
diff --git a/test/builtins/Unit/subvti3_test.c b/test/builtins/Unit/subvti3_test.c
index 66806ba0bb71..3add1ebc4bb6 100644
--- a/test/builtins/Unit/subvti3_test.c
+++ b/test/builtins/Unit/subvti3_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- subvti3_test.c - Test __subvti3 -----------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/ucmpti2_test.c b/test/builtins/Unit/ucmpti2_test.c
index 17a857fb59ea..39a6f5b95eb2 100644
--- a/test/builtins/Unit/ucmpti2_test.c
+++ b/test/builtins/Unit/ucmpti2_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- ucmpti2_test.c - Test __ucmpti2 -----------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/udivmodti4_test.c b/test/builtins/Unit/udivmodti4_test.c
index b07ce8c24200..38da855ae023 100644
--- a/test/builtins/Unit/udivmodti4_test.c
+++ b/test/builtins/Unit/udivmodti4_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- udivmodti4_test.c - Test __udivmodti4 -----------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/udivti3_test.c b/test/builtins/Unit/udivti3_test.c
index 81cedadff243..2db440d5b927 100644
--- a/test/builtins/Unit/udivti3_test.c
+++ b/test/builtins/Unit/udivti3_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- udivti3_test.c - Test __udivti3 -----------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/builtins/Unit/umodti3_test.c b/test/builtins/Unit/umodti3_test.c
index 5bce0258ae38..adfc8eba3402 100644
--- a/test/builtins/Unit/umodti3_test.c
+++ b/test/builtins/Unit/umodti3_test.c
@@ -1,4 +1,5 @@
// RUN: %clang_builtins %s %librt -o %t && %run %t
+// REQUIRES: int128
//===-- umodti3_test.c - Test __umodti3 -----------------------------------===//
//
// The LLVM Compiler Infrastructure
diff --git a/test/cfi/bad-split.cpp b/test/cfi/bad-split.cpp
index 37e635aef55b..dbbd7ecef0ce 100644
--- a/test/cfi/bad-split.cpp
+++ b/test/cfi/bad-split.cpp
@@ -1,7 +1,7 @@
// GlobalSplit used to lose type metadata for classes with virtual bases but no virtual methods.
// RUN: %clangxx_cfi -o %t1 %s && %run %t1
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
struct Z {
};
diff --git a/test/cfi/cross-dso-diagnostic.cpp b/test/cfi/cross-dso-diagnostic.cpp
index f3782dae0272..b8a88722768e 100644
--- a/test/cfi/cross-dso-diagnostic.cpp
+++ b/test/cfi/cross-dso-diagnostic.cpp
@@ -4,7 +4,7 @@
// RUN: %clangxx_cfi_diag -g -o %t_exe_suffix %s %ld_flags_rpath_exe
// RUN: %t_exe_suffix 2>&1 | FileCheck -DDSONAME=%xdynamiclib_namespec %s
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
// REQUIRES: cxxabi
#include <dlfcn.h>
diff --git a/test/cfi/cross-dso/lit.local.cfg b/test/cfi/cross-dso/lit.local.cfg
index afdac4246223..245d434faed9 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/cfi/mfcall.cpp b/test/cfi/mfcall.cpp
index 6e22e3f3d711..4d561d943f41 100644
--- a/test/cfi/mfcall.cpp
+++ b/test/cfi/mfcall.cpp
@@ -1,4 +1,4 @@
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
// RUN: %clangxx_cfi -o %t %s
// RUN: %expect_crash %run %t a
diff --git a/test/cfi/target_uninstrumented.cpp b/test/cfi/target_uninstrumented.cpp
index 6379b7e12f44..c2db9d609f47 100644
--- a/test/cfi/target_uninstrumented.cpp
+++ b/test/cfi/target_uninstrumented.cpp
@@ -3,7 +3,7 @@
// RUN: %run %t 2>&1 | FileCheck %s
// REQUIRES: cxxabi
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
#include <stdio.h>
#include <string.h>
diff --git a/test/cfi/two-vcalls.cpp b/test/cfi/two-vcalls.cpp
index fbe4d971ace8..ff823c498027 100644
--- a/test/cfi/two-vcalls.cpp
+++ b/test/cfi/two-vcalls.cpp
@@ -4,7 +4,7 @@
// This test checks that we don't generate two type checks,
// if two virtual calls are in the same function.
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
// REQUIRES: cxxabi
// TODO(krasin): implement the optimization to not emit two type checks.
diff --git a/test/esan/TestCases/large-stack-linux.c b/test/esan/TestCases/large-stack-linux.c
index 1af32f8ba25e..17d88674ba1c 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 1c420c368ca9..971285b3f815 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 9c972ec7a738..56ed2f5b7c4f 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 38c376554380..acd1eed1761d 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 d97b62ba4e8a..1f8e97dadcc1 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 ba776fc02ed8..6f9787bd73ef 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 f1ac2ecfe132..dc17bcfd5401 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 8b8457d66e00..1bb34ee0865a 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/AlignmentAssumptionTest.cpp b/test/fuzzer/AlignmentAssumptionTest.cpp
new file mode 100644
index 000000000000..be51d37e8fe2
--- /dev/null
+++ b/test/fuzzer/AlignmentAssumptionTest.cpp
@@ -0,0 +1,27 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Test for alignment assumption failure.
+
+#include <assert.h>
+#include <climits>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <iostream>
+
+static volatile int32_t Sink;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ assert(Data);
+ if (Size > 0 && Data[0] == 'H') {
+ Sink = 1;
+ if (Size > 1 && Data[1] == 'i') {
+ Sink = 2;
+ if (Size > 2 && Data[2] == '!') {
+ __builtin_assume_aligned(Data + 1, 0x8000);
+ }
+ }
+ }
+ return 0;
+}
diff --git a/test/fuzzer/ImplicitIntegerSignChangeTest.cpp b/test/fuzzer/ImplicitIntegerSignChangeTest.cpp
new file mode 100644
index 000000000000..0fd7df0e2d2e
--- /dev/null
+++ b/test/fuzzer/ImplicitIntegerSignChangeTest.cpp
@@ -0,0 +1,27 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Test for implicit-integer-sign-change.
+#include <assert.h>
+#include <climits>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <iostream>
+
+static volatile uint32_t Sink;
+static volatile int32_t Storage = -1;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ assert(Data);
+ if (Size > 0 && Data[0] == 'H') {
+ Sink = 1;
+ if (Size > 1 && Data[1] == 'i') {
+ Sink = 2;
+ if (Size > 2 && Data[2] == '!') {
+ Sink = Storage; // 'sign change'.
+ }
+ }
+ }
+ return 0;
+}
diff --git a/test/fuzzer/ImplicitSignedIntegerTruncationOrSignChangeTest.cpp b/test/fuzzer/ImplicitSignedIntegerTruncationOrSignChangeTest.cpp
new file mode 100644
index 000000000000..6e65f5442dcf
--- /dev/null
+++ b/test/fuzzer/ImplicitSignedIntegerTruncationOrSignChangeTest.cpp
@@ -0,0 +1,27 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Test for implicit-signed-integer-truncation-or-sign-change.
+#include <assert.h>
+#include <climits>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <iostream>
+
+static volatile int8_t Sink;
+static volatile uint32_t Storage = (uint32_t)-1;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ assert(Data);
+ if (Size > 0 && Data[0] == 'H') {
+ Sink = 1;
+ if (Size > 1 && Data[1] == 'i') {
+ Sink = 2;
+ if (Size > 2 && Data[2] == '!') {
+ Sink = Storage; // 'conversion'.
+ }
+ }
+ }
+ return 0;
+}
diff --git a/test/fuzzer/ImplicitIntegerTruncationTest.cpp b/test/fuzzer/ImplicitSignedIntegerTruncationTest.cpp
index cb935da0c13e..9a17802e2a5f 100644
--- a/test/fuzzer/ImplicitIntegerTruncationTest.cpp
+++ b/test/fuzzer/ImplicitSignedIntegerTruncationTest.cpp
@@ -9,8 +9,8 @@
#include <cstdlib>
#include <iostream>
-static volatile int Sink;
-static unsigned char Large = UINT8_MAX;
+static volatile int32_t Sink;
+static uint8_t Large = UINT8_MAX;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
assert(Data);
diff --git a/test/fuzzer/ImplicitUnsignedIntegerTruncationTest.cpp b/test/fuzzer/ImplicitUnsignedIntegerTruncationTest.cpp
new file mode 100644
index 000000000000..c0bf40ab08f9
--- /dev/null
+++ b/test/fuzzer/ImplicitUnsignedIntegerTruncationTest.cpp
@@ -0,0 +1,27 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Test for unsigned-integer-overflow.
+#include <assert.h>
+#include <climits>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <iostream>
+
+static volatile int32_t Sink;
+static uint8_t Large = UINT8_MAX;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ assert(Data);
+ if (Size > 0 && Data[0] == 'H') {
+ Sink = 1;
+ if (Size > 1 && Data[1] == 'i') {
+ Sink = 2;
+ if (Size > 2 && Data[2] == '!') {
+ Large = (unsigned int)Large + 1U; // 'char overflow'.
+ }
+ }
+ }
+ return 0;
+}
diff --git a/test/fuzzer/InitializeTest.cpp b/test/fuzzer/InitializeTest.cpp
index a93c2a525088..5022c9efa640 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/PrintUnstableStatsTest.cpp b/test/fuzzer/PrintUnstableStatsTest.cpp
deleted file mode 100644
index 078eb4c3d971..000000000000
--- a/test/fuzzer/PrintUnstableStatsTest.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-#include <assert.h>
-#include <cstdint>
-#include <cstdio>
-#include <cstdlib>
-
-int x = 0;
-bool skip0 = false;
-bool skip1 = false;
-bool skip2 = false;
-
-__attribute__((noinline)) void det0() { x++; }
-__attribute__((noinline)) void det1() { x++; }
-__attribute__((noinline)) void det2() { x++; }
-__attribute__((noinline)) void det3() { x++; }
-__attribute__((noinline)) void det4() { x++; }
-
-__attribute__((noinline)) void ini0() { x++; }
-__attribute__((noinline)) void ini1() { x++; }
-__attribute__((noinline)) void ini2() { x++; }
-
-__attribute__((noinline)) void t0() { x++; }
-__attribute__((noinline)) void t1() { x++; }
-__attribute__((noinline)) void t2() { x++; }
-__attribute__((noinline)) void t3() { x++; }
-__attribute__((noinline)) void t4() { x++; }
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
- if (Size == 1 && Data[0] == 'A' && !skip0) {
- skip0 = true;
- ini0();
- }
- if (Size == 1 && Data[0] == 'B' && !skip1) {
- skip1 = true;
- ini1();
- }
- if (Size == 1 && Data[0] == 'C' && !skip2) {
- skip2 = true;
- ini2();
- }
-
- det0();
- det1();
- int a = rand();
- det2();
-
- switch (a % 5) {
- case 0:
- t0();
- break;
- case 1:
- t1();
- break;
- case 2:
- t2();
- break;
- case 3:
- t3();
- break;
- case 4:
- t4();
- break;
- default:
- assert(false);
- }
-
- det3();
- det4();
- return 0;
-}
diff --git a/test/fuzzer/ReadBinaryTest.cpp b/test/fuzzer/ReadBinaryTest.cpp
new file mode 100644
index 000000000000..de7a40036981
--- /dev/null
+++ b/test/fuzzer/ReadBinaryTest.cpp
@@ -0,0 +1,18 @@
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+
+// Simple test for a fuzzer. Tests that fuzzer can read a file containing
+// carriage returns.
+#include <cstddef>
+#include <cstdint>
+#include <iostream>
+#include <string>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+ std::string InputStr(reinterpret_cast<const char*>(Data), Size);
+ std::string MagicStr("Hello\r\nWorld\r\n");
+ if (InputStr == MagicStr) {
+ std::cout << "BINGO!";
+ }
+ return 0;
+}
diff --git a/test/fuzzer/SymbolizeDeadlock.cpp b/test/fuzzer/SymbolizeDeadlock.cpp
index 5be1be804bce..b9ece38b2303 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 cddb683e6dec..2f5641daf724 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 d3d739d3b977..5e3007e5427e 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/counters.test b/test/fuzzer/counters.test
index f75d3a03783f..8f461c6e1bb3 100644
--- a/test/fuzzer/counters.test
+++ b/test/fuzzer/counters.test
@@ -1,5 +1,4 @@
-XFAIL: ios
-UNSUPPORTED: aarch64
+UNSUPPORTED: aarch64, ios
RUN: %cpp_compiler %S/CounterTest.cpp -o %t-CounterTest
RUN: not %run %t-CounterTest -max_len=6 -seed=1 -timeout=15 2>&1 | FileCheck %s --check-prefix=COUNTERS
diff --git a/test/fuzzer/coverage.test b/test/fuzzer/coverage.test
index 3b2341f21f69..ff7a436e3213 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/dead-stripping.test b/test/fuzzer/dead-stripping.test
new file mode 100644
index 000000000000..85445ea9f21b
--- /dev/null
+++ b/test/fuzzer/dead-stripping.test
@@ -0,0 +1,23 @@
+REQUIRES: darwin
+
+No dead_strip. Unused code is not removed.
+RUN: %cpp_compiler %S/GcSectionsTest.cpp -o %t
+RUN: nm %t | grep UnusedFunctionShouldBeRemovedByLinker | count 1
+RUN: %run %t -runs=0 2>&1 | FileCheck %s
+
+With dead_strip. Unused code is not removed.
+RUN: %cpp_compiler %S/GcSectionsTest.cpp -o %t -ffunction-sections -Wl,-dead_strip
+RUN: nm %t | grep UnusedFunctionShouldBeRemovedByLinker | count 1
+RUN: %run %t -runs=0 2>&1 | FileCheck %s
+
+With dead_strip, with trace-pc. Unused code is removed.
+RUN: %cpp_compiler %S/GcSectionsTest.cpp -o %t -ffunction-sections -fsanitize-coverage=0 -fsanitize-coverage=trace-pc -Wl,-dead_strip
+RUN: nm %t | not grep UnusedFunctionShouldBeRemovedByLinker
+RUN: %run %t -runs=0 2>&1 | FileCheck %s
+
+With dead_strip, with pc-table. Unused code is not removed.
+RUN: %cpp_compiler %S/GcSectionsTest.cpp -o %t -ffunction-sections -fsanitize-coverage=0 -fsanitize-coverage=trace-pc-guard,pc-table -Wl,-dead_strip
+RUN: nm %t | grep UnusedFunctionShouldBeRemovedByLinker | count 1
+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/dso.test b/test/fuzzer/dso.test
index fc1fe23818f0..60ef8a6ac832 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 41e193824de6..803a4fbb8a05 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 ad0fa0a7ce4e..c08c01410e28 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-alignment-assumption.test b/test/fuzzer/fuzzer-alignment-assumption.test
new file mode 100644
index 000000000000..6db77e19ccc8
--- /dev/null
+++ b/test/fuzzer/fuzzer-alignment-assumption.test
@@ -0,0 +1,7 @@
+RUN: rm -f %t-AlignmentAssumptionTest-Ubsan
+RUN: %cpp_compiler -fsanitize=alignment -fno-sanitize-recover=all %S/AlignmentAssumptionTest.cpp -o %t-AlignmentAssumptionTest-Ubsan
+RUN: not %run %t-AlignmentAssumptionTest-Ubsan 2>&1 | FileCheck %s
+CHECK: AlignmentAssumptionTest.cpp:22:39: runtime error: assumption of 32768 byte alignment for pointer of type 'const {{.*}} *' (aka 'const unsigned char *') failed
+CHECK: 0x{{.*}}: note: address is {{.*}} aligned, misalignment offset is {{.*}} byte
+
+CHECK: Test unit written to ./crash-
diff --git a/test/fuzzer/fuzzer-implicit-integer-sign-change.test b/test/fuzzer/fuzzer-implicit-integer-sign-change.test
new file mode 100644
index 000000000000..7524f6cc4e5e
--- /dev/null
+++ b/test/fuzzer/fuzzer-implicit-integer-sign-change.test
@@ -0,0 +1,5 @@
+RUN: rm -f %t-ImplicitIntegerSignChangeTest-Ubsan
+RUN: %cpp_compiler -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=all %S/ImplicitIntegerSignChangeTest.cpp -o %t-ImplicitIntegerSignChangeTest-Ubsan
+RUN: not %run %t-ImplicitIntegerSignChangeTest-Ubsan 2>&1 | FileCheck %s
+CHECK: ImplicitIntegerSignChangeTest.cpp:22:16: runtime error: implicit conversion from type 'int32_t' (aka 'int') of value -1 (32-bit, signed) to type 'uint32_t' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)
+CHECK: Test unit written to ./crash-
diff --git a/test/fuzzer/fuzzer-implicit-integer-truncation.test b/test/fuzzer/fuzzer-implicit-integer-truncation.test
deleted file mode 100644
index 212559bdca3c..000000000000
--- a/test/fuzzer/fuzzer-implicit-integer-truncation.test
+++ /dev/null
@@ -1,5 +0,0 @@
-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: Test unit written to ./crash-
diff --git a/test/fuzzer/fuzzer-implicit-signed-integer-truncation-or-sign-change.test b/test/fuzzer/fuzzer-implicit-signed-integer-truncation-or-sign-change.test
new file mode 100644
index 000000000000..532b36a03508
--- /dev/null
+++ b/test/fuzzer/fuzzer-implicit-signed-integer-truncation-or-sign-change.test
@@ -0,0 +1,5 @@
+RUN: rm -f %t-ImplicitSignedIntegerTruncationOrSignChangeTest-Ubsan
+RUN: %cpp_compiler -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=all %S/ImplicitSignedIntegerTruncationOrSignChangeTest.cpp -o %t-ImplicitSignedIntegerTruncationOrSignChangeTest-Ubsan
+RUN: not %run %t-ImplicitSignedIntegerTruncationOrSignChangeTest-Ubsan 2>&1 | FileCheck %s
+CHECK: ImplicitSignedIntegerTruncationOrSignChangeTest.cpp:22:16: runtime error: implicit conversion from type 'uint32_t' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type 'int8_t' (aka 'signed char') changed the value to -1 (8-bit, signed)
+CHECK: Test unit written to ./crash-
diff --git a/test/fuzzer/fuzzer-implicit-signed-integer-truncation.test b/test/fuzzer/fuzzer-implicit-signed-integer-truncation.test
new file mode 100644
index 000000000000..d41625d3aede
--- /dev/null
+++ b/test/fuzzer/fuzzer-implicit-signed-integer-truncation.test
@@ -0,0 +1,5 @@
+RUN: rm -f %t-ImplicitSignedIntegerTruncationTest-Ubsan
+RUN: %cpp_compiler -fsanitize=implicit-signed-integer-truncation -fno-sanitize-recover=all %S/ImplicitSignedIntegerTruncationTest.cpp -o %t-ImplicitSignedIntegerTruncationTest-Ubsan
+RUN: not %run %t-ImplicitSignedIntegerTruncationTest-Ubsan 2>&1 | FileCheck %s
+CHECK: ImplicitSignedIntegerTruncationTest.cpp:22:17: 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)
+CHECK: Test unit written to ./crash-
diff --git a/test/fuzzer/fuzzer-implicit-unsigned-integer-truncation.test b/test/fuzzer/fuzzer-implicit-unsigned-integer-truncation.test
new file mode 100644
index 000000000000..e62a01e9eb24
--- /dev/null
+++ b/test/fuzzer/fuzzer-implicit-unsigned-integer-truncation.test
@@ -0,0 +1,5 @@
+RUN: rm -f %t-ImplicitUnsignedIntegerTruncationTest-Ubsan
+RUN: %cpp_compiler -fsanitize=implicit-unsigned-integer-truncation -fno-sanitize-recover=all %S/ImplicitUnsignedIntegerTruncationTest.cpp -o %t-ImplicitUnsignedIntegerTruncationTest-Ubsan
+RUN: not %run %t-ImplicitUnsignedIntegerTruncationTest-Ubsan 2>&1 | FileCheck %s
+CHECK: ImplicitUnsignedIntegerTruncationTest.cpp:22:17: runtime error: implicit conversion from type 'unsigned int' of value 256 (32-bit, unsigned) to type 'uint8_t' (aka '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 95743a818d1f..000000000000
--- a/test/fuzzer/fuzzer-mutationstats.test
+++ /dev/null
@@ -1,5 +0,0 @@
-RUN: %cpp_compiler %S/SimpleTest.cpp -o %t-MutationStatsTest
-RUN: not %run %t-MutationStatsTest -print_mutation_stats=1 2>&1 | FileCheck %s
-
-# Ensures there are some non-zero values in the usefulness percentages printed.
-CHECK: stat::mutation_usefulness: {{[0-9]+\.[0-9]+}}
diff --git a/test/fuzzer/fuzzer-oom.test b/test/fuzzer/fuzzer-oom.test
index e82fb47c5bed..9bc451c50ee9 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
+UNSUPPORTED: aarch64, ios
+# 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 b8abfbbdf17b..e915c4cc9eb0 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
deleted file mode 100644
index 798ee2dc042f..000000000000
--- a/test/fuzzer/handle-unstable.test
+++ /dev/null
@@ -1,42 +0,0 @@
-# Tests -handle_unstable
-UNSUPPORTED: aarch64
-
-RUN: %cpp_compiler %S/PrintUnstableStatsTest.cpp -o %t-HandleUnstableTest
-
-; Normal
-RUN: %run %t-HandleUnstableTest -print_coverage=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=NORMAL
-NORMAL-DAG: det0()
-NORMAL-DAG: det1()
-NORMAL-DAG: det2()
-NORMAL-DAG: det3()
-NORMAL-DAG: det4()
-NORMAL-DAG: ini0()
-NORMAL-DAG: ini1()
-NORMAL-DAG: ini2()
-NORMAL-DAG: t0()
-NORMAL-DAG: t1()
-NORMAL-DAG: t2()
-NORMAL-DAG: t3()
-NORMAL-DAG: t4()
-
-; MinUnstable
-RUN: %run %t-HandleUnstableTest -print_coverage=1 -handle_unstable=1 -runs=100000 2>&1 | FileCheck %s --check-prefix=MIN
-MIN-NOT: ini0()
-MIN-NOT: ini1()
-MIN-NOT: ini2()
-MIN: det0()
-MIN: det1()
-MIN: det2()
-MIN: det3()
-MIN: det4()
-
-; ZeroUnstable
-RUN: %run %t-HandleUnstableTest -print_coverage=1 -handle_unstable=2 -runs=1 2>&1 | FileCheck %s --check-prefix=ZERO
-ZERO-NOT: ini0()
-ZERO-NOT: ini1()
-ZERO-NOT: ini2()
-ZERO: det0()
-ZERO: det1()
-ZERO: det2()
-ZERO: det3()
-ZERO: det4()
diff --git a/test/fuzzer/lit.cfg b/test/fuzzer/lit.cfg
index 8a44860d4a5d..608991c0764f 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 64b747116a9f..60b2a6a627ca 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 db0a48b5481e..883b7b6be97b 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 a03e5440a8b8..44448ca29e63 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 de44b8747e04..dcab67bfde32 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 3c528f707666..cba88eed12e4 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 f159a79f4838..d576cc12b131 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 31eb5990da33..e9926cab48e8 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/only-some-bytes.test b/test/fuzzer/only-some-bytes.test
index fbfef14c7850..861718384080 100644
--- a/test/fuzzer/only-some-bytes.test
+++ b/test/fuzzer/only-some-bytes.test
@@ -34,5 +34,5 @@ HAVE_DFT: INFO: 1/{{.*}} inputs have the Data Flow Trace
# Collect DFT, then use it.
RUN: rm -rf %t/C && mkdir %t/C && cp %t/IN/* %t/C
RUN: rm -rf %t/C_DFT && %libfuzzer_src/scripts/collect_data_flow.py %t-DFT %t/C %t/C_DFT > /dev/null 2>&1
-RUN: not %t-Fuzz -focus_function=f0 -data_flow_trace=%t/C_DFT -seed=1 -runs=1000000 -use_value_profile=3 %t/C 2> %t/log
+RUN: not %t-Fuzz -focus_function=f0 -data_flow_trace=%t/C_DFT -seed=1 -runs=1000000 -use_value_profile=1 %t/C 2> %t/log
RUN: grep BINGO %t/log
diff --git a/test/fuzzer/print_unstable_stats.test b/test/fuzzer/print_unstable_stats.test
deleted file mode 100644
index bba99aecc838..000000000000
--- a/test/fuzzer/print_unstable_stats.test
+++ /dev/null
@@ -1,3 +0,0 @@
-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
-LONG: stat::stability_rate: 27.59
diff --git a/test/fuzzer/read-binary.test b/test/fuzzer/read-binary.test
new file mode 100644
index 000000000000..c80858e81134
--- /dev/null
+++ b/test/fuzzer/read-binary.test
@@ -0,0 +1,7 @@
+# Test that libFuzzer reads files properly.
+
+# Account for the fact that echo will add a trailing newline.
+RUN: echo -e "Hello\r\nWorld\r" > %t-testcase
+RUN: %cpp_compiler %S/ReadBinaryTest.cpp -o %t-fuzzer
+RUN: %run %t-fuzzer %t-testcase | FileCheck %s
+CHECK: BINGO!
diff --git a/test/fuzzer/shrink.test b/test/fuzzer/shrink.test
index 5abbcc90b8c0..78386ffaf092 100644
--- a/test/fuzzer/shrink.test
+++ b/test/fuzzer/shrink.test
@@ -1,6 +1,6 @@
RUN: %cpp_compiler %S/ShrinkControlFlowTest.cpp -o %t-ShrinkControlFlowTest
RUN: %cpp_compiler %S/ShrinkValueProfileTest.cpp -o %t-ShrinkValueProfileTest
-RUN: %run %t-ShrinkControlFlowTest -seed=1 -exit_on_item=0eb8e4ed029b774d80f2b66408203801cb982a60 -runs=1000000 -shrink=1 -reduce_inputs=0 2>&1 | FileCheck %s --check-prefix=SHRINK1
+RUN: %run %t-ShrinkControlFlowTest -seed=1 -exit_on_item=0eb8e4ed029b774d80f2b66408203801cb982a60 -runs=2000000 -shrink=1 -reduce_inputs=0 2>&1 | FileCheck %s --check-prefix=SHRINK1
# Limit max_len to run this negative test faster.
RUN: %run %t-ShrinkControlFlowTest -seed=1 -exit_on_item=0eb8e4ed029b774d80f2b66408203801cb982a60 -runs=1000000 -shrink=0 -reduce_inputs=0 -max_len=64 2>&1 | FileCheck %s --check-prefix=SHRINK0
RUN: %run %t-ShrinkValueProfileTest -seed=1 -exit_on_item=aea2e3923af219a8956f626558ef32f30a914ebc -runs=100000 -shrink=1 -reduce_inputs=0 -use_value_profile=1 2>&1 | FileCheck %s --check-prefix=SHRINK1_VP
diff --git a/test/fuzzer/sigusr.test b/test/fuzzer/sigusr.test
index 0b3ddc72832d..fa477a76eea1 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 8f972d61f5c6..f38005c1d2f6 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 193df01ddeff..c7b4632140cb 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 076866c50940..7cf4c0a68866 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 b927422d10ff..8f6ffe99cd65 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, ios
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 4bf119fcb3df..5935ed6d1325 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 58ba18b9001e..fe715925f801 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 05bc3f435912..e5ac29f81c43 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 59cc7c2f9552..38f211241b0f 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 607b81cd527f..b6baf13200d4 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 57c844e92261..7d68b8811cf0 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 e55f1e4a853a..7515e3651f01 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 647121f22820..9b7a244d73c2 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 b60b97f86f3e..98488df0a552 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 cc3d4944c0bf..a71682d79404 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/fuzzer/windows-opt-ref.test b/test/fuzzer/windows-opt-ref.test
new file mode 100644
index 000000000000..1f3386d13478
--- /dev/null
+++ b/test/fuzzer/windows-opt-ref.test
@@ -0,0 +1,9 @@
+REQUIRES: windows
+// Verify that the linker eliminating unreferenced functions (/OPT:REF) does not
+// strip sancov module constructor.
+RUN: %cpp_compiler %S/SimpleCmpTest.cpp -o %t-SimpleCmpTest /link /OPT:REF
+
+RUN: not %run %t-SimpleCmpTest -seed=1 -runs=100000000 2>&1 | FileCheck %s
+
+CHECK-NOT: ERROR: no interesting inputs were found. Is the code instrumented for coverage? Exiting.
+CHECK: BINGO
diff --git a/test/hwasan/CMakeLists.txt b/test/hwasan/CMakeLists.txt
index 972c73250cf3..3e397ac067f2 100644
--- a/test/hwasan/CMakeLists.txt
+++ b/test/hwasan/CMakeLists.txt
@@ -11,6 +11,9 @@ foreach(arch ${HWASAN_TEST_ARCH})
string(TOUPPER ${arch} ARCH_UPPER_CASE)
set(CONFIG_NAME ${ARCH_UPPER_CASE})
+ # FIXME: Set this.
+ set(HWASAN_ANDROID_FILES_TO_PUSH [])
+
configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg)
diff --git a/test/hwasan/TestCases/Posix/system-allocator-fallback.cc b/test/hwasan/TestCases/Posix/system-allocator-fallback.cc
new file mode 100644
index 000000000000..8678d906daec
--- /dev/null
+++ b/test/hwasan/TestCases/Posix/system-allocator-fallback.cc
@@ -0,0 +1,54 @@
+// RUN: %clangxx %s -o %t -ldl
+// RUN: %clangxx_hwasan -shared %s -o %t.so -DSHARED_LIB -shared-libsan -Wl,-rpath,%compiler_rt_libdir
+// RUN: %env_hwasan_opts=disable_allocator_tagging=0 %run %t
+
+// The dynamic loader on Android O appears to have a bug where it crashes when
+// dlopening DF_1_GLOBAL libraries.
+// REQUIRES: android-28
+
+#include <stddef.h>
+
+// Test that allocations made by the system allocator can be realloc'd and freed
+// by the hwasan allocator.
+
+typedef void run_test_fn(void *(*system_malloc)(size_t size));
+
+#ifdef SHARED_LIB
+
+// Call the __sanitizer_ versions of these functions so that the test
+// doesn't require the Android dynamic loader.
+extern "C" void *__sanitizer_realloc(void *ptr, size_t size);
+extern "C" void __sanitizer_free(void *ptr);
+
+extern "C" run_test_fn run_test;
+void run_test(void *(*system_malloc)(size_t size)) {
+ void *mem = system_malloc(64);
+ mem = __sanitizer_realloc(mem, 128);
+ __sanitizer_free(mem);
+}
+
+#else
+
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <string>
+
+int main(int argc, char **argv) {
+ std::string path = argv[0];
+ path += ".so";
+ void *lib = dlopen(path.c_str(), RTLD_NOW);
+ if (!lib) {
+ printf("error in dlopen(): %s\n", dlerror());
+ return 1;
+ }
+
+ auto run_test = reinterpret_cast<run_test_fn *>(dlsym(lib, "run_test"));
+ if (!run_test) {
+ printf("failed dlsym\n");
+ return 1;
+ }
+
+ run_test(malloc);
+}
+
+#endif
diff --git a/test/hwasan/TestCases/abort-message-android.cc b/test/hwasan/TestCases/abort-message-android.cc
new file mode 100644
index 000000000000..f89b929d454d
--- /dev/null
+++ b/test/hwasan/TestCases/abort-message-android.cc
@@ -0,0 +1,28 @@
+// RUN: %clangxx_hwasan -DERR=1 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// RUN: %clangxx_hwasan -DERR=2 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// REQUIRES: android
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <sanitizer/hwasan_interface.h>
+
+__attribute__((no_sanitize("hwaddress")))
+extern "C" void android_set_abort_message(const char *msg) {
+ fprintf(stderr, "== abort message start\n%s\n== abort message end\n", msg);
+}
+
+int main() {
+ __hwasan_enable_allocator_tagging();
+ char *volatile p = (char *)malloc(16);
+ if (ERR==1) {
+ p[16] = 1;
+ } else {
+ free(p);
+ free(p);
+ }
+ // CHECK: ERROR: HWAddressSanitizer:
+ // CHECK: == abort message start
+ // CHECK: ERROR: HWAddressSanitizer:
+ // CHECK: == abort message end
+}
diff --git a/test/hwasan/TestCases/cfi.cc b/test/hwasan/TestCases/cfi.cc
new file mode 100644
index 000000000000..457e29659e77
--- /dev/null
+++ b/test/hwasan/TestCases/cfi.cc
@@ -0,0 +1,18 @@
+// RUN: %clang_hwasan -fsanitize=cfi -fno-sanitize-trap=cfi -flto -fvisibility=hidden -fuse-ld=lld %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// REQUIRES: android
+
+// Smoke test for CFI + HWASAN.
+
+struct A {
+ virtual void f();
+};
+
+void A::f() {}
+
+int main() {
+ // CHECK: control flow integrity check for type {{.*}} failed during cast to unrelated type
+ A *a = reinterpret_cast<A *>(reinterpret_cast<void *>(&main));
+ (void)a;
+}
diff --git a/test/hwasan/TestCases/deep-recursion.c b/test/hwasan/TestCases/deep-recursion.c
new file mode 100644
index 000000000000..2fe77a7bdaaf
--- /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: Previously allocated frames
+// D1: in OOB
+// D1-NOT: in FUNC
+// D1: Memory tags around the buggy address
+
+// D2: Previously allocated frames
+// D2: in OOB
+// D2: in FUNC1
+// D2-NOT: in FUNC
+// D2: Memory tags around the buggy address
+
+// D3: Previously allocated frames
+// D3: in OOB
+// D3: in FUNC1
+// D3: in FUNC2
+// D3-NOT: in FUNC
+// D3: Memory tags around the buggy address
+
+// D5: Previously 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: Previously 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 000000000000..e97aae6edb4c
--- /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 000000000000..9f605b3208d6
--- /dev/null
+++ b/test/hwasan/TestCases/heap-buffer-overflow.c
@@ -0,0 +1,60 @@
+// RUN: %clang_hwasan %s -o %t
+// RUN: not %run %t 40 2>&1 | FileCheck %s --check-prefix=CHECK40-LEFT
+// RUN: %env_hwasan_opts=malloc_align_right=2 not %run %t 40 2>&1 | FileCheck %s --check-prefix=CHECK40-RIGHT
+// RUN: not %run %t 80 2>&1 | FileCheck %s --check-prefix=CHECK80-LEFT
+// RUN: %env_hwasan_opts=malloc_align_right=2 not %run %t 80 2>&1 | FileCheck %s --check-prefix=CHECK80-RIGHT
+// 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
+
+// Test OOB within the granule.
+// Misses the bug when malloc is left-aligned, catches it otherwise.
+// RUN: %run %t 31
+// RUN: %env_hwasan_opts=malloc_align_right=2 not %run %t 31 2>&1 | FileCheck %s --check-prefix=CHECK31
+
+// RUN: %run %t 30 20
+// RUN: %env_hwasan_opts=malloc_align_right=9 not %run %t 30 20 2>&1 | FileCheck %s --check-prefix=CHECK20-RIGHT8
+
+// RUN: %env_hwasan_opts=malloc_align_right=42 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-WRONG-FLAG
+
+// REQUIRES: stable-runtime
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sanitizer/hwasan_interface.h>
+
+static volatile char sink;
+
+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);
+ fprintf(stderr, "base: %p access: %p\n", x, &x[offset]);
+ sink = x[offset];
+
+// CHECK40-LEFT: allocated heap chunk; size: 32 offset: 8
+// CHECK40-LEFT: is located 10 bytes to the right of 30-byte region
+// CHECK40-RIGHT: allocated heap chunk; size: 32 offset:
+// CHECK40-RIGHT: is located 10 bytes to the right of 30-byte region
+//
+// CHECK80-LEFT: allocated heap chunk; size: 32 offset: 16
+// CHECK80-LEFT: is located 50 bytes to the right of 30-byte region
+// CHECK80-RIGHT: allocated heap chunk; size: 32 offset:
+// CHECK80-RIGHT: 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 a large allocated heap chunk; size: 1003520 offset: -30
+// CHECKMm30: is located 30 bytes to the left of 1000000-byte region
+//
+// CHECKM: is a large allocated heap chunk; size: 1003520 offset: 1000000
+// CHECKM: is located 0 bytes to the right of 1000000-byte region
+//
+// CHECK31: is located 1 bytes to the right of 30-byte region
+//
+// CHECK20-RIGHT8: is located 10 bytes to the right of 20-byte region [0x{{.*}}8,0x{{.*}}c)
+//
+// CHECK-WRONG-FLAG: ERROR: unsupported value of malloc_align_right flag: 42
+ 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 000000000000..fa6330bbcccd
--- /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 000000000000..8d847b54b275
--- /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 000000000000..199464b9c300
--- /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 000000000000..b8513b7e2b73
--- /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 000000000000..3a79cb37b608
--- /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/mem-intrinsics-zero-size.c b/test/hwasan/TestCases/mem-intrinsics-zero-size.c
new file mode 100644
index 000000000000..bcb8e0771f1e
--- /dev/null
+++ b/test/hwasan/TestCases/mem-intrinsics-zero-size.c
@@ -0,0 +1,10 @@
+// RUN: %clang_hwasan %s -o %t && %run %t
+
+#include <string.h>
+
+int main() {
+ char a[1];
+ memset(a, 0, 0);
+ memmove(a, a, 0);
+ memcpy(a, a, 0);
+}
diff --git a/test/hwasan/TestCases/mem-intrinsics.c b/test/hwasan/TestCases/mem-intrinsics.c
new file mode 100644
index 000000000000..164bc6d93335
--- /dev/null
+++ b/test/hwasan/TestCases/mem-intrinsics.c
@@ -0,0 +1,37 @@
+// RUN: %clang_hwasan %s -DTEST_NO=1 -mllvm -hwasan-instrument-mem-intrinsics -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=WRITE
+// RUN: %clang_hwasan %s -DTEST_NO=2 -mllvm -hwasan-instrument-mem-intrinsics -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=READ
+// RUN: %clang_hwasan %s -DTEST_NO=3 -mllvm -hwasan-instrument-mem-intrinsics -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=WRITE
+// RUN: %clang_hwasan %s -DTEST_NO=2 -mllvm -hwasan-instrument-mem-intrinsics -o %t && not %env_hwasan_opts=halt_on_error=0 %run %t 2>&1 | FileCheck %s --check-prefix=RECOVER
+
+// REQUIRES: stable-runtime
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int main() {
+ char Q[16];
+ char P[16];
+#if TEST_NO == 1
+ memset(Q, 0, 32);
+#elif TEST_NO == 2
+ memmove(Q, Q + 16, 16);
+#elif TEST_NO == 3
+ memcpy(Q, P, 32);
+#endif
+ write(STDOUT_FILENO, "recovered\n", 10);
+ // WRITE: ERROR: HWAddressSanitizer: tag-mismatch on address
+ // WRITE: WRITE {{.*}} tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem)
+ // WRITE: Memory tags around the buggy address (one tag corresponds to 16 bytes):
+ // WRITE: =>{{.*}}[[MEM_TAG]]
+ // WRITE-NOT: recovered
+
+ // READ: ERROR: HWAddressSanitizer: tag-mismatch on address
+ // READ: READ {{.*}} tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem)
+ // READ: Memory tags around the buggy address (one tag corresponds to 16 bytes):
+ // READ: =>{{.*}}[[MEM_TAG]]
+ // READ-NOT: recovered
+
+ // RECOVER: recovered
+ return 0;
+}
diff --git a/test/hwasan/TestCases/new-test.cc b/test/hwasan/TestCases/new-test.cc
new file mode 100644
index 000000000000..3b1991e4deaa
--- /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 000000000000..5a057928ba75
--- /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 000000000000..df9d534f32d4
--- /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 000000000000..937e20c6ad19
--- /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/random-align-right.c b/test/hwasan/TestCases/random-align-right.c
new file mode 100644
index 000000000000..8c524ef4784d
--- /dev/null
+++ b/test/hwasan/TestCases/random-align-right.c
@@ -0,0 +1,35 @@
+// Tests malloc_align_right=1 and 8 (randomly aligning right).
+// RUN: %clang_hwasan %s -o %t
+//
+// RUN: %run %t
+// RUN: %env_hwasan_opts=malloc_align_right=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1
+// RUN: %env_hwasan_opts=malloc_align_right=8 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK8
+
+// REQUIRES: stable-runtime
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sanitizer/hwasan_interface.h>
+
+static volatile void *sink;
+
+int main(int argc, char **argv) {
+ __hwasan_enable_allocator_tagging();
+
+ // Perform 1000 buffer overflows within the 16-byte granule,
+ // so that random right-alignment has a very high chance of
+ // catching at least one of them.
+ for (int i = 0; i < 1000; i++) {
+ char *p = (char*)malloc(20);
+ sink = p;
+ fprintf(stderr, "[%d] p: %p; accessing p[20]:\n", i, p);
+ p[20 * argc] = 0; // requires malloc_align_right=1 to catch
+ fprintf(stderr, "[%d] p: %p; accessing p[30]:\n", i, p);
+ p[30 * argc] = 0; // requires malloc_align_right={1,8} to catch
+// CHECK1: accessing p[20]
+// CHECK1-NEXT: HWAddressSanitizer: tag-mismatch
+// CHECK8: accessing p[30]:
+// CHECK8-NEXT: HWAddressSanitizer: tag-mismatch
+ }
+}
+
diff --git a/test/hwasan/TestCases/realloc-after-free.c b/test/hwasan/TestCases/realloc-after-free.c
new file mode 100644
index 000000000000..c4bc48c9ccda
--- /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 000000000000..838790242426
--- /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 000000000000..6787d57769f4
--- /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 000000000000..66ac9641e109
--- /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 000000000000..52217de746e3
--- /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 000000000000..0aefd40bebb1
--- /dev/null
+++ b/test/hwasan/TestCases/stack-history-length.c
@@ -0,0 +1,36 @@
+// RUN: %clang_hwasan -O1 %s -o %t
+// 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(int argc, char **argv) {
+ int X = argc == 2 ? atoi(argv[1]) : 10;
+ // 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: Previously allocated frames
+// YES: OOB
+// YES: FUNC
+// YES: FUNC0
+
+// NO: Previously 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 000000000000..567af334e4bc
--- /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 60b9a6295005..000000000000
--- 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.c b/test/hwasan/TestCases/stack-uar.c
new file mode 100644
index 000000000000..863a84017ee9
--- /dev/null
+++ b/test/hwasan/TestCases/stack-uar.c
@@ -0,0 +1,41 @@
+// Tests use-after-return detection and reporting.
+// RUN: %clang_hwasan -O0 -fno-discard-value-names %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+// REQUIRES: stable-runtime
+
+void USE(void *x) { // pretend_to_do_something(void *x)
+ __asm__ __volatile__("" : : "r" (x) : "memory");
+}
+
+__attribute__((noinline))
+char *buggy() {
+ char zzz[0x1000];
+ char *volatile p = zzz;
+ return p;
+}
+
+__attribute__((noinline)) void Unrelated1() { int A[2]; USE(&A[0]); }
+__attribute__((noinline)) void Unrelated2() { int BB[3]; USE(&BB[0]); }
+__attribute__((noinline)) void Unrelated3() { int CCC[4]; USE(&CCC[0]); }
+
+int main() {
+ char *p = buggy();
+ Unrelated1();
+ Unrelated2();
+ Unrelated3();
+ return *p;
+ // CHECK: READ of size 1 at
+ // CHECK: #0 {{.*}} in main{{.*}}stack-uar.c:[[@LINE-2]]
+ // CHECK: is located in stack of thread
+ // CHECK: Previously allocated frames:
+ // CHECK: Unrelated3
+ // CHECK: 16 CCC
+ // CHECK: Unrelated2
+ // CHECK: 12 BB
+ // CHECK: Unrelated1
+ // CHECK: 8 A
+ // CHECK: buggy
+ // CHECK: 4096 zzz
+
+ // CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in main
+}
diff --git a/test/hwasan/TestCases/stack-uar.cc b/test/hwasan/TestCases/stack-uar.cc
deleted file mode 100644
index e99dcceed533..000000000000
--- a/test/hwasan/TestCases/stack-uar.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// RUN: %clangxx_hwasan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
-
-// REQUIRES: stable-runtime
-
-#include <stdlib.h>
-#include <sanitizer/hwasan_interface.h>
-
-__attribute__((noinline))
-char *f() {
- char z[0x1000];
- char *volatile p = z;
- return p;
-}
-
-int main() {
- return *f();
- // CHECK: READ of size 1 at
- // CHECK: #0 {{.*}} in main{{.*}}stack-uar.cc:16
-
- // CHECK: HWAddressSanitizer can not describe address in more detail.
-
- // CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in main
-}
diff --git a/test/hwasan/TestCases/tail-magic.c b/test/hwasan/TestCases/tail-magic.c
new file mode 100644
index 000000000000..95c5ada08a36
--- /dev/null
+++ b/test/hwasan/TestCases/tail-magic.c
@@ -0,0 +1,28 @@
+// Tests free_checks_tail_magic=1.
+// RUN: %clang_hwasan %s -o %t
+// RUN: %env_hwasan_opts=free_checks_tail_magic=0 %run %t
+// RUN: %env_hwasan_opts=free_checks_tail_magic=1 not %run %t 2>&1 | FileCheck %s
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+// REQUIRES: stable-runtime
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sanitizer/hwasan_interface.h>
+
+static volatile void *sink;
+
+int main(int argc, char **argv) {
+ __hwasan_enable_allocator_tagging();
+
+ char *p = (char*)malloc(20);
+ sink = p;
+ p[20] = 0x42;
+ p[24] = 0x66;
+ free(p);
+// CHECK: ERROR: HWAddressSanitizer: alocation-tail-overwritten; heap object [{{.*}}) of size 20
+// CHECK: in main {{.*}}tail-magic.c:[[@LINE-2]]
+// CHECK: allocated here:
+// CHECK: in main {{.*}}tail-magic.c:[[@LINE-8]]
+// CHECK: Tail contains: .. .. .. .. 42 {{.. .. ..}} 66
+}
diff --git a/test/hwasan/TestCases/thread-uaf.c b/test/hwasan/TestCases/thread-uaf.c
new file mode 100644
index 000000000000..f091167e3ced
--- /dev/null
+++ b/test/hwasan/TestCases/thread-uaf.c
@@ -0,0 +1,58 @@
+// 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
+ // CHECK-DAG: Thread: T0 0x
+ // CHECK-DAG: Thread: T1 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 000000000000..25aae5256c41
--- /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 b9f6060112c1..03a1771c1b8f 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,25 @@ 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]]
+ // Offset is 5 or 11 depending on left/right alignment.
+ // CHECK: is a small unallocated heap chunk; size: 32 offset: {{5|11}}
+ // CHECK: is located 5 bytes inside of 10-byte region
+ //
+ // 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-14]]
// 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-19]]
+ // 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/hwasan/lit.cfg b/test/hwasan/lit.cfg
index 3ebba51d05e4..66008a632bcf 100644
--- a/test/hwasan/lit.cfg
+++ b/test/hwasan/lit.cfg
@@ -9,14 +9,18 @@ config.name = 'HWAddressSanitizer' + getattr(config, 'name_suffix', 'default')
config.test_source_root = os.path.dirname(__file__)
# Setup default compiler flags used with -fsanitize=memory option.
-clang_hwasan_cflags = ["-fsanitize=hwaddress", "-mllvm", "-hwasan-generate-tags-with-calls", config.target_cflags] + config.debug_info_flags
+clang_cflags = [config.target_cflags] + config.debug_info_flags
+clang_cxxflags = config.cxx_mode_flags + clang_cflags
+clang_hwasan_cflags = ["-fsanitize=hwaddress", "-mllvm", "-hwasan-generate-tags-with-calls"] + clang_cflags
clang_hwasan_cxxflags = config.cxx_mode_flags + clang_hwasan_cflags
def build_invocation(compile_flags):
return " " + " ".join([config.clang] + compile_flags) + " "
+config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) )
config.substitutions.append( ("%clang_hwasan ", build_invocation(clang_hwasan_cflags)) )
config.substitutions.append( ("%clangxx_hwasan ", build_invocation(clang_hwasan_cxxflags)) )
+config.substitutions.append( ("%compiler_rt_libdir", config.compiler_rt_libdir) )
default_hwasan_opts_str = ':'.join(['disable_allocator_tagging=1', 'random_tags=0'] + config.default_sanitizer_opts)
if default_hwasan_opts_str:
diff --git a/test/hwasan/lit.site.cfg.in b/test/hwasan/lit.site.cfg.in
index 7453d1b74dc0..e95ea92cd2d9 100644
--- a/test/hwasan/lit.site.cfg.in
+++ b/test/hwasan/lit.site.cfg.in
@@ -4,6 +4,7 @@
config.name_suffix = "@HWASAN_TEST_CONFIG_SUFFIX@"
config.target_cflags = "@HWASAN_TEST_TARGET_CFLAGS@"
config.target_arch = "@HWASAN_TEST_TARGET_ARCH@"
+config.android_files_to_push = @HWASAN_ANDROID_FILES_TO_PUSH@
# 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/lit.common.cfg b/test/lit.common.cfg
index 5274b49c654f..f06658ff2d27 100644
--- a/test/lit.common.cfg
+++ b/test/lit.common.cfg
@@ -55,10 +55,12 @@ config.available_features.add(compiler_id)
if config.asan_shadow_scale != '':
config.target_cflags += " -mllvm -asan-mapping-scale=" + config.asan_shadow_scale
-# BFD linker in 64-bit android toolchains fails to find libm.so, which is a
-# transitive shared library dependency (via asan runtime).
+# BFD linker in 64-bit android toolchains fails to find libc++_shared.so, which
+# is a transitive shared library dependency (via asan runtime).
if config.android:
- config.target_cflags += " -pie -fuse-ld=gold -Wl,--enable-new-dtags"
+ # Prepend the flag so that it can be overridden.
+ config.target_cflags = "-pie -fuse-ld=gold " + config.target_cflags
+ config.cxx_mode_flags.append('-stdlib=libstdc++')
# Clear some environment variables that might affect Clang.
possibly_dangerous_env_vars = ['ASAN_OPTIONS', 'DFSAN_OPTIONS', 'LSAN_OPTIONS',
@@ -106,6 +108,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 +133,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 +154,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 +175,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.
@@ -252,9 +276,14 @@ else:
config.substitutions.append( ("%darwin_min_target_with_full_runtime_arc_support", "") )
if config.android:
+ env = os.environ.copy()
+ if config.android_serial:
+ env['ANDROID_SERIAL'] = config.android_serial
+ config.environment['ANDROID_SERIAL'] = config.android_serial
+
adb = os.environ.get('ADB', 'adb')
try:
- android_api_level_str = subprocess.check_output([adb, "shell", "getprop", "ro.build.version.sdk"]).rstrip()
+ android_api_level_str = subprocess.check_output([adb, "shell", "getprop", "ro.build.version.sdk"], env=env).rstrip()
except (subprocess.CalledProcessError, OSError):
lit_config.fatal("Failed to read ro.build.version.sdk (using '%s' as adb)" % adb)
try:
@@ -263,6 +292,29 @@ if config.android:
lit_config.fatal("Failed to read ro.build.version.sdk (using '%s' as adb): got '%s'" % (adb, android_api_level_str))
if android_api_level >= 26:
config.available_features.add('android-26')
+ if android_api_level >= 28:
+ config.available_features.add('android-28')
+
+ # Prepare the device.
+ android_tmpdir = '/data/local/tmp/Output'
+ subprocess.check_call([adb, "shell", "mkdir", "-p", android_tmpdir], env=env)
+ for file in config.android_files_to_push:
+ subprocess.check_call([adb, "push", file, android_tmpdir], env=env)
+
+if config.host_os == 'Linux':
+ # detect whether we are using glibc, and which version
+ # NB: 'ldd' is just one of the tools commonly installed as part of glibc
+ ldd_ver_cmd = subprocess.Popen(['ldd', '--version'],
+ stdout=subprocess.PIPE,
+ env={'LANG': 'C'})
+ sout, _ = ldd_ver_cmd.communicate()
+ ver_line = sout.splitlines()[0]
+ if ver_line.startswith(b"ldd "):
+ from distutils.version import LooseVersion
+ ver = LooseVersion(ver_line.split()[-1].decode())
+ # 2.27 introduced some incompatibilities
+ if ver >= LooseVersion("2.27"):
+ config.available_features.add("glibc-2.27")
sancovcc_path = os.path.join(config.llvm_tools_dir, "sancov")
if os.path.exists(sancovcc_path):
@@ -295,7 +347,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:
@@ -319,6 +371,9 @@ if config.lto_supported:
if config.use_newpm:
config.lto_flags += ["-fexperimental-new-pass-manager"]
+if config.have_rpc_xdr_h:
+ config.available_features.add('sunrpc')
+
# Ask llvm-config about assertion mode.
try:
llvm_config_cmd = subprocess.Popen(
diff --git a/test/lit.common.configured.in b/test/lit.common.configured.in
index 63d55bfdea81..4994ca69b18e 100644
--- a/test/lit.common.configured.in
+++ b/test/lit.common.configured.in
@@ -36,6 +36,9 @@ set_default("use_thinlto", False)
set_default("use_lto", config.use_thinlto)
set_default("use_newpm", False)
set_default("android", @ANDROID_PYBOOL@)
+set_default("android_serial", "@ANDROID_SERIAL_FOR_TESTING@")
+set_default("android_files_to_push", [])
+set_default("have_rpc_xdr_h", @HAVE_RPC_XDR_H@)
config.available_features.add('target-is-%s' % config.target_arch)
if config.enable_per_target_runtime_dir:
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 000000000000..d7427ce3ed04
--- /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 221c5d249d77..000000000000
--- 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/lsan/TestCases/Linux/use_tls_dynamic.cc b/test/lsan/TestCases/Linux/use_tls_dynamic.cc
index 4d70a46f8183..8093d6f0e68f 100644
--- a/test/lsan/TestCases/Linux/use_tls_dynamic.cc
+++ b/test/lsan/TestCases/Linux/use_tls_dynamic.cc
@@ -1,4 +1,9 @@
// Test that dynamically allocated TLS space is included in the root set.
+
+// This is known to be broken with glibc-2.27+
+// https://bugs.llvm.org/show_bug.cgi?id=37804
+// XFAIL: glibc-2.27
+
// RUN: LSAN_BASE="report_objects=1:use_stacks=0:use_registers=0:use_ld_allocations=0"
// RUN: %clangxx %s -DBUILD_DSO -fPIC -shared -o %t-so.so
// RUN: %clangxx_lsan %s -o %t
diff --git a/test/msan/Linux/reexec_unlimited_stack.cc b/test/msan/Linux/reexec_unlimited_stack.cc
new file mode 100644
index 000000000000..61492ec34533
--- /dev/null
+++ b/test/msan/Linux/reexec_unlimited_stack.cc
@@ -0,0 +1,23 @@
+// MSAN re-execs on unlimited stacks. We use that to verify ReExec() uses the
+// right path.
+// RUN: %clangxx_msan -O0 %s -o %t && ulimit -s unlimited && %run %t | FileCheck %s
+
+#include <stdio.h>
+
+#if !defined(__GLIBC_PREREQ)
+#define __GLIBC_PREREQ(a, b) 0
+#endif
+
+#if __GLIBC_PREREQ(2, 16)
+#include <sys/auxv.h>
+#endif
+
+int main() {
+#if __GLIBC_PREREQ(2, 16)
+ // Make sure AT_EXECFN didn't get overwritten by re-exec.
+ puts(reinterpret_cast<const char *>(getauxval(AT_EXECFN)));
+#else
+ puts("No getauxval");
+#endif
+ // CHECK-NOT: /proc/self/exe
+}
diff --git a/test/msan/Linux/sunrpc.cc b/test/msan/Linux/sunrpc.cc
index c92ad632c095..edf49c2a5cee 100644
--- a/test/msan/Linux/sunrpc.cc
+++ b/test/msan/Linux/sunrpc.cc
@@ -1,3 +1,5 @@
+// REQUIRES: sunrpc
+
// RUN: %clangxx_msan -g -O0 -DTYPE=int -DFN=xdr_int %s -o %t && \
// RUN: %run %t 2>&1
// RUN: %clangxx_msan -g -O0 -DTYPE=int -DFN=xdr_int -DUNINIT=1 %s -o %t && \
diff --git a/test/msan/Linux/sunrpc_bytes.cc b/test/msan/Linux/sunrpc_bytes.cc
index 477637af2b63..7eb47e178492 100644
--- a/test/msan/Linux/sunrpc_bytes.cc
+++ b/test/msan/Linux/sunrpc_bytes.cc
@@ -1,3 +1,5 @@
+// REQUIRES: sunrpc
+
// RUN: %clangxx_msan -g -O0 %s -o %t && \
// RUN: %run %t 2>&1
// RUN: %clangxx_msan -g -O0 -DUNINIT=1 %s -o %t && \
diff --git a/test/msan/Linux/sunrpc_string.cc b/test/msan/Linux/sunrpc_string.cc
index 350222f5cc1e..723b85592261 100644
--- a/test/msan/Linux/sunrpc_string.cc
+++ b/test/msan/Linux/sunrpc_string.cc
@@ -1,3 +1,5 @@
+// REQUIRES: sunrpc
+
// RUN: %clangxx_msan -g -O0 %s -o %t && \
// RUN: %run %t 2>&1
// RUN: %clangxx_msan -g -O0 -DUNINIT=1 %s -o %t && \
diff --git a/test/msan/chained_origin_with_signals.cc b/test/msan/chained_origin_with_signals.cc
index 43dbdcca76a9..9c071569a2ba 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 b9021e0da1af..bc1fe609a850 100644
--- a/test/msan/dtls_test.c
+++ b/test/msan/dtls_test.c
@@ -8,6 +8,14 @@
XFAIL: FreeBSD
UNSUPPORTED: powerpc
+
+ // Reports use-of-uninitialized-value, not analyzed
+ XFAIL: netbsd
+
+ // This is known to be broken with glibc-2.27+
+ // https://bugs.llvm.org/show_bug.cgi?id=37804
+ XFAIL: glibc-2.27
+
*/
#ifndef BUILD_SO
diff --git a/test/msan/fork.cc b/test/msan/fork.cc
index e4dc5490887c..87d71f87efe6 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 eaab63384582..794f34535358 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 550d04d0812b..e08673d5cfea 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/pthread_getname_np.cc b/test/msan/pthread_getname_np.cc
index e19b652a73c9..4827b3a00643 100644
--- a/test/msan/pthread_getname_np.cc
+++ b/test/msan/pthread_getname_np.cc
@@ -1,7 +1,7 @@
// RUN: %clangxx_msan -std=c++11 -O0 %s -o %t && %run %t
// The main goal is getting the pthread name back and
// FreeBSD based do not support this feature
-// UNSUPPORTED: android, netbsd, freebsd
+// UNSUPPORTED: android, freebsd
// Regression test for a deadlock in pthread_getattr_np
@@ -32,7 +32,11 @@ int main(void) {
assert(!res);
const char *kMyThreadName = "my-thread-name";
+#if defined(__NetBSD__)
+ res = pthread_setname_np(t, "%s", (void *)kMyThreadName);
+#else
res = pthread_setname_np(t, kMyThreadName);
+#endif
assert(!res);
char buf[100];
diff --git a/test/msan/signal_stress_test.cc b/test/msan/signal_stress_test.cc
index 5bc6f59213b1..dfbc580faf2c 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/strndup.cc b/test/msan/strndup.cc
index d4b9af1a9a6e..07bdd9f8f5fb 100644
--- a/test/msan/strndup.cc
+++ b/test/msan/strndup.cc
@@ -4,7 +4,7 @@
// When built as C on Linux, strndup is transformed to __strndup.
// RUN: %clangxx_msan -O3 -xc %s -o %t && not %run %t 2>&1 | FileCheck --check-prefix=ON %s
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
#include <assert.h>
#include <stdlib.h>
diff --git a/test/msan/tls_reuse.cc b/test/msan/tls_reuse.cc
index 9c2ee975cb57..78a328fa3ce0 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 000000000000..e1a7b1266165
--- /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-dlopen-dlclose-main.c.gcov b/test/profile/Inputs/instrprof-dlopen-dlclose-main.c.gcov
index acb2076fd763..2d538f63eb46 100644
--- a/test/profile/Inputs/instrprof-dlopen-dlclose-main.c.gcov
+++ b/test/profile/Inputs/instrprof-dlopen-dlclose-main.c.gcov
@@ -7,7 +7,7 @@
// CHECK-NEXT: -: 2:#include <stdio.h>
// CHECK-NEXT: -: 3:#include <stdlib.h>
// CHECK-NEXT: -: 4:
-// CHECK-NEXT: -: 5:int main(int argc, char *argv[]) {
+// CHECK-NEXT: 1: 5:int main(int argc, char *argv[]) {
// CHECK-NEXT: 1: 6: dlerror();
// CHECK-NEXT: 1: 7: void *f1_handle = dlopen("func.shared", RTLD_LAZY | RTLD_GLOBAL);
// CHECK-NEXT: 1: 8: if (f1_handle == NULL) {
diff --git a/test/profile/Inputs/instrprof-dlopen-dlclose-main_three-libs.c.gcov b/test/profile/Inputs/instrprof-dlopen-dlclose-main_three-libs.c.gcov
index 97eef4c3b905..f1dd1757144f 100644
--- a/test/profile/Inputs/instrprof-dlopen-dlclose-main_three-libs.c.gcov
+++ b/test/profile/Inputs/instrprof-dlopen-dlclose-main_three-libs.c.gcov
@@ -7,7 +7,7 @@
// CHECK-NEXT: -: 2:#include <stdio.h>
// CHECK-NEXT: -: 3:#include <stdlib.h>
// CHECK-NEXT: -: 4:
-// CHECK-NEXT: -: 5:int main(int argc, char *argv[]) {
+// CHECK-NEXT: 1: 5:int main(int argc, char *argv[]) {
// CHECK-NEXT: 1: 6: dlerror();
// CHECK-NEXT: 1: 7: void *f1_handle = dlopen("func.shared", RTLD_LAZY | RTLD_GLOBAL);
// CHECK-NEXT: 1: 8: if (f1_handle == NULL) {
diff --git a/test/profile/Inputs/instrprof-gcov-__gcov_flush-multiple.c b/test/profile/Inputs/instrprof-gcov-__gcov_flush-multiple.c
new file mode 100644
index 000000000000..dadd8959991a
--- /dev/null
+++ b/test/profile/Inputs/instrprof-gcov-__gcov_flush-multiple.c
@@ -0,0 +1,16 @@
+int main(void) {
+ __gcov_flush();
+
+ if (remove("instrprof-gcov-__gcov_flush-multiple.gcda") != 0) {
+ return 1;
+ }
+
+ __gcov_flush();
+ __gcov_flush();
+
+ if (remove("instrprof-gcov-__gcov_flush-multiple.gcda") != 0) {
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/test/profile/Inputs/instrprof-gcov-__gcov_flush-multiple.c.gcov b/test/profile/Inputs/instrprof-gcov-__gcov_flush-multiple.c.gcov
new file mode 100644
index 000000000000..f2141229bd4f
--- /dev/null
+++ b/test/profile/Inputs/instrprof-gcov-__gcov_flush-multiple.c.gcov
@@ -0,0 +1,21 @@
+// CHECK: -: 0:Source:{{.*}}Inputs/instrprof-gcov-__gcov_flush-multiple.c
+// CHECK-NEXT: -: 0:Graph:instrprof-gcov-__gcov_flush-multiple.gcno
+// CHECK-NEXT: -: 0:Data:instrprof-gcov-__gcov_flush-multiple.gcda
+// CHECK-NEXT: -: 0:Runs:1
+// CHECK-NEXT: -: 0:Programs:1
+// CHECK-NEXT: #####: 1:int main(void) {
+// CHECK-NEXT: #####: 2: __gcov_flush();
+// CHECK-NEXT: -: 3:
+// CHECK-NEXT: #####: 4: if (remove("instrprof-gcov-__gcov_flush-multiple.gcda") != 0) {
+// CHECK-NEXT: #####: 5: return 1;
+// CHECK-NEXT: -: 6: }
+// CHECK-NEXT: -: 7:
+// CHECK-NEXT: #####: 8: __gcov_flush();
+// CHECK-NEXT: #####: 9: __gcov_flush();
+// CHECK-NEXT: -: 10:
+// CHECK-NEXT: #####: 11: if (remove("instrprof-gcov-__gcov_flush-multiple.gcda") != 0) {
+// CHECK-NEXT: #####: 12: return 1;
+// CHECK-NEXT: -: 13: }
+// CHECK-NEXT: -: 14:
+// CHECK-NEXT: 1: 15: return 0;
+// CHECK-NEXT: 1: 16:}
diff --git a/test/profile/Inputs/instrprof-gcov-exceptions.cpp.gcov b/test/profile/Inputs/instrprof-gcov-exceptions.cpp.gcov
index 7caf50806020..f8e38297475d 100644
--- a/test/profile/Inputs/instrprof-gcov-exceptions.cpp.gcov
+++ b/test/profile/Inputs/instrprof-gcov-exceptions.cpp.gcov
@@ -5,10 +5,10 @@
// CHECK-NEXT: -: 0:Programs:1
// CHECK-NEXT: -: 1:#include <string>
// CHECK-NEXT: -: 2:
-// CHECK-NEXT: -: 3:void asd(std::string i) {
-// CHECK-NEXT: 2: 4:}
+// CHECK-NEXT: 1: 3:void asd(std::string i) {
+// CHECK-NEXT: 1: 4:}
// CHECK-NEXT: -: 5:
-// CHECK-NEXT: -: 6:int main(void)
+// CHECK-NEXT: 1: 6:int main(void)
// CHECK-NEXT: -: 7:{
// CHECK-NEXT: 1: 8: asd("22");
// CHECK-NEXT: -: 9:
diff --git a/test/profile/Inputs/instrprof-gcov-execlp.c b/test/profile/Inputs/instrprof-gcov-execlp.c
new file mode 100644
index 000000000000..5213a5b80360
--- /dev/null
+++ b/test/profile/Inputs/instrprof-gcov-execlp.c
@@ -0,0 +1,15 @@
+#include <unistd.h>
+
+void func1() {}
+void func2() {}
+
+int main(void)
+{
+ func1();
+
+ execlp("ls", "-l", "-h", (char*)0);
+
+ func2();
+
+ return 0;
+}
diff --git a/test/profile/Inputs/instrprof-gcov-execlp.c.gcov b/test/profile/Inputs/instrprof-gcov-execlp.c.gcov
new file mode 100644
index 000000000000..7542f6ff73b2
--- /dev/null
+++ b/test/profile/Inputs/instrprof-gcov-execlp.c.gcov
@@ -0,0 +1,23 @@
+//CHECK: -: 0:Source:{{.*}}Inputs/instrprof-gcov-execlp.c
+//CHECK-NEXT: -: 0:Graph:instrprof-gcov-execlp.gcno
+//CHECK-NEXT: -: 0:Data:instrprof-gcov-execlp.gcda
+//CHECK-NEXT: -: 0:Runs:1
+//CHECK-NEXT: -: 0:Programs:1
+//CHECK-NEXT: -: 1:#include <unistd.h>
+//CHECK-NEXT: -: 2:
+//CHECK-NEXT:function func1 called 1 returned 100% blocks executed 100%
+//CHECK-NEXT: 1: 3:void func1() {}
+//CHECK-NEXT:function func2 called 0 returned 0% blocks executed 0%
+//CHECK-NEXT: #####: 4:void func2() {}
+//CHECK-NEXT: -: 5:
+//CHECK-NEXT:function main called 1 returned 0% blocks executed 33%
+//CHECK-NEXT: 1: 6:int main(void)
+//CHECK-NEXT: -: 7:{
+//CHECK-NEXT: 1: 8: func1();
+//CHECK-NEXT: -: 9:
+//CHECK-NEXT: 1: 10: execlp("ls", "-l", "-h", (char*)0);
+//CHECK-NEXT: -: 11:
+//CHECK-NEXT: #####: 12: func2();
+//CHECK-NEXT: -: 13:
+//CHECK-NEXT: #####: 14: return 0;
+//CHECK-NEXT: -: 15:}
diff --git a/test/profile/Inputs/instrprof-gcov-execvp.c b/test/profile/Inputs/instrprof-gcov-execvp.c
new file mode 100644
index 000000000000..f2e411c2a42e
--- /dev/null
+++ b/test/profile/Inputs/instrprof-gcov-execvp.c
@@ -0,0 +1,17 @@
+#include <unistd.h>
+
+void func1() {}
+void func2() {}
+
+int main(void)
+{
+ char *const args[] = {"-l", "-h", (char*)0};
+
+ func1();
+
+ execvp("ls", args);
+
+ func2();
+
+ return 0;
+}
diff --git a/test/profile/Inputs/instrprof-gcov-execvp.c.gcov b/test/profile/Inputs/instrprof-gcov-execvp.c.gcov
new file mode 100644
index 000000000000..37cd1e59ab38
--- /dev/null
+++ b/test/profile/Inputs/instrprof-gcov-execvp.c.gcov
@@ -0,0 +1,25 @@
+//CHECK: -: 0:Source:{{.*}}Inputs/instrprof-gcov-execvp.c
+//CHECK-NEXT: -: 0:Graph:instrprof-gcov-execvp.gcno
+//CHECK-NEXT: -: 0:Data:instrprof-gcov-execvp.gcda
+//CHECK-NEXT: -: 0:Runs:1
+//CHECK-NEXT: -: 0:Programs:1
+//CHECK-NEXT: -: 1:#include <unistd.h>
+//CHECK-NEXT: -: 2:
+//CHECK-NEXT:function func1 called 1 returned 100% blocks executed 100%
+//CHECK-NEXT: 1: 3:void func1() {}
+//CHECK-NEXT:function func2 called 0 returned 0% blocks executed 0%
+//CHECK-NEXT: #####: 4:void func2() {}
+//CHECK-NEXT: -: 5:
+//CHECK-NEXT:function main called 1 returned 0% blocks executed 33%
+//CHECK-NEXT: 1: 6:int main(void)
+//CHECK-NEXT: -: 7:{
+//CHECK-NEXT: 1: 8: char *const args[] = {"-l", "-h", (char*)0};
+//CHECK-NEXT: -: 9:
+//CHECK-NEXT: 1: 10: func1();
+//CHECK-NEXT: -: 11:
+//CHECK-NEXT: 1: 12: execvp("ls", args);
+//CHECK-NEXT: -: 13:
+//CHECK-NEXT: #####: 14: func2();
+//CHECK-NEXT: -: 15:
+//CHECK-NEXT: #####: 16: return 0;
+//CHECK-NEXT: -: 17:}
diff --git a/test/profile/Inputs/instrprof-gcov-fork.c.gcov b/test/profile/Inputs/instrprof-gcov-fork.c.gcov
index 9591f62ed602..c667c7b9e97e 100644
--- a/test/profile/Inputs/instrprof-gcov-fork.c.gcov
+++ b/test/profile/Inputs/instrprof-gcov-fork.c.gcov
@@ -10,8 +10,8 @@
// CHECK-NEXT:function func2 called 2 returned 100% blocks executed 100%
// CHECK-NEXT: 2: 4:void func2() {}
// CHECK-NEXT: -: 5:
-// CHECK-NEXT:function main called 1 returned 100% blocks executed 100%
-// CHECK-NEXT: -: 6:int main(void)
+// CHECK-NEXT:function main called 1 returned 200% blocks executed 100%
+// CHECK-NEXT: 1: 6:int main(void)
// CHECK-NEXT: -: 7:{
// CHECK-NEXT: 1: 8: func1();
// CHECK-NEXT: -: 9:
diff --git a/test/profile/Inputs/instrprof-gcov-multiple-bbs-single-line.c.gcov b/test/profile/Inputs/instrprof-gcov-multiple-bbs-single-line.c.gcov
index 92532af30674..4bc1c1cd50f7 100644
--- a/test/profile/Inputs/instrprof-gcov-multiple-bbs-single-line.c.gcov
+++ b/test/profile/Inputs/instrprof-gcov-multiple-bbs-single-line.c.gcov
@@ -4,7 +4,7 @@
// CHECK-NEXT: -: 0:Runs:1
// CHECK-NEXT: -: 0:Programs:1
// CHECK-NEXT:function main called 1 returned 100% blocks executed 80%
-// CHECK-NEXT: -: 1:int main(void)
+// CHECK-NEXT: 1: 1:int main(void)
// CHECK-NEXT: -: 2:{
// CHECK-NEXT: -: 3: int var;
// CHECK-NEXT: -: 4:
diff --git a/test/profile/Inputs/instrprof-gcov-one-line-function.c b/test/profile/Inputs/instrprof-gcov-one-line-function.c
new file mode 100644
index 000000000000..ee1582351969
--- /dev/null
+++ b/test/profile/Inputs/instrprof-gcov-one-line-function.c
@@ -0,0 +1,11 @@
+void foo() { }
+
+void bar() { }
+
+int main(void) {
+ foo();
+
+ bar();
+
+ return 0;
+}
diff --git a/test/profile/Inputs/instrprof-gcov-one-line-function.c.gcov b/test/profile/Inputs/instrprof-gcov-one-line-function.c.gcov
new file mode 100644
index 000000000000..a91b20fdc9a3
--- /dev/null
+++ b/test/profile/Inputs/instrprof-gcov-one-line-function.c.gcov
@@ -0,0 +1,16 @@
+// CHECK: -: 0:Source:{{.*}}Inputs/instrprof-gcov-one-line-function.c
+// CHECK-NEXT: -: 0:Graph:instrprof-gcov-one-line-function.gcno
+// CHECK-NEXT: -: 0:Data:instrprof-gcov-one-line-function.gcda
+// CHECK-NEXT: -: 0:Runs:1
+// CHECK-NEXT: -: 0:Programs:1
+// CHECK-NEXT: 1: 1:void foo() { }
+// CHECK-NEXT: -: 2:
+// CHECK-NEXT: 1: 3:void bar() { }
+// CHECK-NEXT: -: 4:
+// CHECK-NEXT: 1: 5:int main(void) {
+// CHECK-NEXT: 1: 6: foo();
+// CHECK-NEXT: -: 7:
+// CHECK-NEXT: 1: 8: bar();
+// CHECK-NEXT: -: 9:
+// CHECK-NEXT: 1: 10: return 0;
+// CHECK-NEXT: -: 11:}
diff --git a/test/profile/Inputs/instrprof-gcov-switch1.c.gcov b/test/profile/Inputs/instrprof-gcov-switch1.c.gcov
index 7d136dc98ec3..6e9c5228b938 100644
--- a/test/profile/Inputs/instrprof-gcov-switch1.c.gcov
+++ b/test/profile/Inputs/instrprof-gcov-switch1.c.gcov
@@ -3,11 +3,11 @@
// CHECK-NEXT: -: 0:Data:instrprof-gcov-switch1.gcda
// CHECK-NEXT: -: 0:Runs:1
// CHECK-NEXT: -: 0:Programs:1
-// CHECK-NEXT: -: 1:int main(void)
+// CHECK-NEXT: 1: 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 67f408606a39..47d7f1d7cb41 100644
--- a/test/profile/Inputs/instrprof-gcov-switch2.c.gcov
+++ b/test/profile/Inputs/instrprof-gcov-switch2.c.gcov
@@ -3,11 +3,11 @@
// CHECK-NEXT: -: 0:Data:instrprof-gcov-switch2.gcda
// CHECK-NEXT: -: 0:Runs:1
// CHECK-NEXT: -: 0:Programs:1
-// CHECK-NEXT: -: 1:int main(void)
+// CHECK-NEXT: 1: 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-lib.c.gcov b/test/profile/Inputs/instrprof-shared-lib.c.gcov
index fbc43d5f7d50..620a85257577 100644
--- a/test/profile/Inputs/instrprof-shared-lib.c.gcov
+++ b/test/profile/Inputs/instrprof-shared-lib.c.gcov
@@ -6,7 +6,7 @@
// CHECK-NEXT: -: 1:int g1 = 0;
// CHECK-NEXT: -: 2:int g2 = 1;
// CHECK-NEXT: -: 3:
-// CHECK-NEXT: -: 4:void foo(int n) {
+// CHECK-NEXT: 1: 4:void foo(int n) {
// CHECK-NEXT: 1: 5: if (n % 5 == 0)
// CHECK-NEXT: #####: 6: g1++;
// CHECK-NEXT: -: 7: else
diff --git a/test/profile/Inputs/instrprof-shared-lib_called-twice.c.gcov b/test/profile/Inputs/instrprof-shared-lib_called-twice.c.gcov
index 779c885d862d..39b32b8c097a 100644
--- a/test/profile/Inputs/instrprof-shared-lib_called-twice.c.gcov
+++ b/test/profile/Inputs/instrprof-shared-lib_called-twice.c.gcov
@@ -6,7 +6,7 @@
// CHECK-NEXT: -: 1:int g1 = 0;
// CHECK-NEXT: -: 2:int g2 = 1;
// CHECK-NEXT: -: 3:
-// CHECK-NEXT: -: 4:void foo(int n) {
+// CHECK-NEXT: 2: 4:void foo(int n) {
// CHECK-NEXT: 2: 5: if (n % 5 == 0)
// CHECK-NEXT: #####: 6: g1++;
// CHECK-NEXT: -: 7: else
diff --git a/test/profile/Inputs/instrprof-shared-lib_in-loop.c.gcov b/test/profile/Inputs/instrprof-shared-lib_in-loop.c.gcov
index 76503d91426e..0fc7ccbabab2 100644
--- a/test/profile/Inputs/instrprof-shared-lib_in-loop.c.gcov
+++ b/test/profile/Inputs/instrprof-shared-lib_in-loop.c.gcov
@@ -6,7 +6,7 @@
// CHECK-NEXT: -: 1:int g1 = 0;
// CHECK-NEXT: -: 2:int g2 = 1;
// CHECK-NEXT: -: 3:
-// CHECK-NEXT: -: 4:void foo(int n) {
+// CHECK-NEXT: 1000000: 4:void foo(int n) {
// CHECK-NEXT: 1000000: 5: if (n % 5 == 0)
// CHECK-NEXT: 360000: 6: g1++;
// CHECK-NEXT: -: 7: else
diff --git a/test/profile/Inputs/instrprof-shared-main-gcov-flush_no-writeout.c.gcov b/test/profile/Inputs/instrprof-shared-main-gcov-flush_no-writeout.c.gcov
index b2dfe2acde67..6027c64af61b 100644
--- a/test/profile/Inputs/instrprof-shared-main-gcov-flush_no-writeout.c.gcov
+++ b/test/profile/Inputs/instrprof-shared-main-gcov-flush_no-writeout.c.gcov
@@ -9,14 +9,14 @@
// CHECK-NEXT: -: 4:int bar1 = 0;
// CHECK-NEXT: -: 5:int bar2 = 1;
// CHECK-NEXT: -: 6:
-// CHECK-NEXT: -: 7:void bar(int n) {
+// CHECK-NEXT: 1: 7:void bar(int n) {
// CHECK-NEXT: 1: 8: if (n % 5 == 0)
// CHECK-NEXT: 1: 9: bar1++;
// CHECK-NEXT: -: 10: else
// CHECK-NEXT: #####: 11: bar2++;
// CHECK-NEXT: 1: 12:}
// CHECK-NEXT: -: 13:
-// CHECK-NEXT: -: 14:int main(int argc, char *argv[]) {
+// CHECK-NEXT: 1: 14:int main(int argc, char *argv[]) {
// CHECK-NEXT: -: 15:#ifdef SHARED_CALL_BEFORE_GCOV_FLUSH
// CHECK-NEXT: 1: 16: foo(1);
// CHECK-NEXT: -: 17:#endif
diff --git a/test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-after.c.gcov b/test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-after.c.gcov
index f70e34e52894..fba3f3fe2812 100644
--- a/test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-after.c.gcov
+++ b/test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-after.c.gcov
@@ -9,14 +9,14 @@
// CHECK-NEXT: -: 4:int bar1 = 0;
// CHECK-NEXT: -: 5:int bar2 = 1;
// CHECK-NEXT: -: 6:
-// CHECK-NEXT: -: 7:void bar(int n) {
+// CHECK-NEXT: 3: 7:void bar(int n) {
// CHECK-NEXT: 3: 8: if (n % 5 == 0)
// CHECK-NEXT: 3: 9: bar1++;
// CHECK-NEXT: -: 10: else
// CHECK-NEXT: #####: 11: bar2++;
// CHECK-NEXT: 3: 12:}
// CHECK-NEXT: -: 13:
-// CHECK-NEXT: -: 14:int main(int argc, char *argv[]) {
+// CHECK-NEXT: 1: 14:int main(int argc, char *argv[]) {
// CHECK-NEXT: -: 15:#ifdef SHARED_CALL_BEFORE_GCOV_FLUSH
// CHECK-NEXT: -: 16: foo(1);
// CHECK-NEXT: -: 17:#endif
diff --git a/test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-before-after.c.gcov b/test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-before-after.c.gcov
index b9ecff698722..86beda22a1ad 100644
--- a/test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-before-after.c.gcov
+++ b/test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-before-after.c.gcov
@@ -9,14 +9,14 @@
// CHECK-NEXT: -: 4:int bar1 = 0;
// CHECK-NEXT: -: 5:int bar2 = 1;
// CHECK-NEXT: -: 6:
-// CHECK-NEXT: -: 7:void bar(int n) {
+// CHECK-NEXT: 3: 7:void bar(int n) {
// CHECK-NEXT: 3: 8: if (n % 5 == 0)
// CHECK-NEXT: 3: 9: bar1++;
// CHECK-NEXT: -: 10: else
// CHECK-NEXT: #####: 11: bar2++;
// CHECK-NEXT: 3: 12:}
// CHECK-NEXT: -: 13:
-// CHECK-NEXT: -: 14:int main(int argc, char *argv[]) {
+// CHECK-NEXT: 1: 14:int main(int argc, char *argv[]) {
// CHECK-NEXT: -: 15:#ifdef SHARED_CALL_BEFORE_GCOV_FLUSH
// CHECK-NEXT: 1: 16: foo(1);
// CHECK-NEXT: -: 17:#endif
diff --git a/test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-before.c.gcov b/test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-before.c.gcov
index 7c9e0afa11b2..2e55741cc536 100644
--- a/test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-before.c.gcov
+++ b/test/profile/Inputs/instrprof-shared-main-gcov-flush_shared-call-before.c.gcov
@@ -9,14 +9,14 @@
// CHECK-NEXT: -: 4:int bar1 = 0;
// CHECK-NEXT: -: 5:int bar2 = 1;
// CHECK-NEXT: -: 6:
-// CHECK-NEXT: -: 7:void bar(int n) {
+// CHECK-NEXT: 3: 7:void bar(int n) {
// CHECK-NEXT: 3: 8: if (n % 5 == 0)
// CHECK-NEXT: 3: 9: bar1++;
// CHECK-NEXT: -: 10: else
// CHECK-NEXT: #####: 11: bar2++;
// CHECK-NEXT: 3: 12:}
// CHECK-NEXT: -: 13:
-// CHECK-NEXT: -: 14:int main(int argc, char *argv[]) {
+// CHECK-NEXT: 1: 14:int main(int argc, char *argv[]) {
// CHECK-NEXT: -: 15:#ifdef SHARED_CALL_BEFORE_GCOV_FLUSH
// CHECK-NEXT: 1: 16: foo(1);
// CHECK-NEXT: -: 17:#endif
diff --git a/test/profile/Inputs/instrprof-shared-main.c.gcov b/test/profile/Inputs/instrprof-shared-main.c.gcov
index 70be367507ff..05cd4e31d6eb 100644
--- a/test/profile/Inputs/instrprof-shared-main.c.gcov
+++ b/test/profile/Inputs/instrprof-shared-main.c.gcov
@@ -6,10 +6,10 @@
// CHECK-NEXT: -: 1:extern int g1, g2;
// CHECK-NEXT: -: 2:extern void foo(int n);
// CHECK-NEXT: -: 3:
-// CHECK-NEXT: -: 4:int main() {
+// CHECK-NEXT: 1: 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/Posix/instrprof-gcov-execlp.test b/test/profile/Posix/instrprof-gcov-execlp.test
new file mode 100644
index 000000000000..1d136ce97907
--- /dev/null
+++ b/test/profile/Posix/instrprof-gcov-execlp.test
@@ -0,0 +1,10 @@
+RUN: mkdir -p %t.d
+RUN: cd %t.d
+
+RUN: %clang --coverage -o %t %S/../Inputs/instrprof-gcov-execlp.c
+RUN: test -f instrprof-gcov-execlp.gcno
+
+RUN: rm -f instrprof-gcov-execlp.gcda
+RUN: %run %t
+RUN: llvm-cov gcov -b -c instrprof-gcov-execlp.gcda
+RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-gcov-execlp.c.gcov %S/../Inputs/instrprof-gcov-execlp.c.gcov
diff --git a/test/profile/Posix/instrprof-gcov-execvp.test b/test/profile/Posix/instrprof-gcov-execvp.test
new file mode 100644
index 000000000000..8e5cbc609509
--- /dev/null
+++ b/test/profile/Posix/instrprof-gcov-execvp.test
@@ -0,0 +1,10 @@
+RUN: mkdir -p %t.d
+RUN: cd %t.d
+
+RUN: %clang --coverage -o %t %S/../Inputs/instrprof-gcov-execvp.c
+RUN: test -f instrprof-gcov-execvp.gcno
+
+RUN: rm -f instrprof-gcov-execvp.gcda
+RUN: %run %t
+RUN: llvm-cov gcov -b -c instrprof-gcov-execvp.gcda
+RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-gcov-execvp.c.gcov %S/../Inputs/instrprof-gcov-execvp.c.gcov
diff --git a/test/profile/Posix/instrprof-gcov-fork.test b/test/profile/Posix/instrprof-gcov-fork.test
index 436d7e663faa..5a406fda4b67 100644
--- a/test/profile/Posix/instrprof-gcov-fork.test
+++ b/test/profile/Posix/instrprof-gcov-fork.test
@@ -1,4 +1,4 @@
-XFAIL: *
+UNSUPPORTED: linux
RUN: mkdir -p %t.d
RUN: cd %t.d
diff --git a/test/profile/instrprof-darwin-exports.c b/test/profile/instrprof-darwin-exports.c
index 6667cabdb2df..1ef36ae8b553 100644
--- a/test/profile/instrprof-darwin-exports.c
+++ b/test/profile/instrprof-darwin-exports.c
@@ -8,4 +8,20 @@
// RUN: %clang_profgen -Werror -fcoverage-mapping -Wl,-exported_symbols_list,%t.exports -o %t %s 2>&1 | tee -a %t.log
// RUN: cat %t.log | count 0
+// RUN: %clang -Werror -Wl,-exported_symbols_list,%t.exports --coverage -o %t.gcov %s | tee -a %t.gcov.log
+// RUN: cat %t.gcov.log | count 0
+
+// The default set of weak external symbols should match the set of symbols
+// exported by clang. See Darwin::addProfileRTLibs.
+
+// RUN: %clang_pgogen -Werror -o %t.default %s
+// RUN: nm -jUg %t.default | grep -v __mh_execute_header > %t.default.exports
+// RUN: nm -jUg %t > %t.clang.exports
+// RUN: diff %t.default.exports %t.clang.exports
+
+// RUN: %clang -Werror --coverage -o %t.gcov.default %s
+// RUN: nm -jUg %t.gcov | grep -v __mh_execute_header > %t.gcov.exports
+// RUN: nm -jUg %t.gcov.default | grep -v __mh_execute_header > %t.gcov.default.exports
+// RUN: diff %t.gcov.default.exports %t.gcov.exports
+
int main() {}
diff --git a/test/profile/instrprof-dlopen-dlclose-gcov.test b/test/profile/instrprof-dlopen-dlclose-gcov.test
index 0444fca2692c..36b5dbd262df 100644
--- a/test/profile/instrprof-dlopen-dlclose-gcov.test
+++ b/test/profile/instrprof-dlopen-dlclose-gcov.test
@@ -1,3 +1,6 @@
+# atexit(3) not supported in dlopen(3)ed+dlclose(3)d DSO
+XFAIL: netbsd
+
RUN: mkdir -p %t.d
RUN: cd %t.d
@@ -7,6 +10,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 +18,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 +31,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-__gcov_flush-multiple.test b/test/profile/instrprof-gcov-__gcov_flush-multiple.test
new file mode 100644
index 000000000000..fdc93c93e0b0
--- /dev/null
+++ b/test/profile/instrprof-gcov-__gcov_flush-multiple.test
@@ -0,0 +1,10 @@
+RUN: mkdir -p %t.d
+RUN: cd %t.d
+
+RUN: %clang --coverage -o %t %S/Inputs/instrprof-gcov-__gcov_flush-multiple.c
+RUN: test -f instrprof-gcov-__gcov_flush-multiple.gcno
+
+RUN: rm -f instrprof-gcov-__gcov_flush-multiple.gcda
+RUN: %run %t
+RUN: llvm-cov gcov instrprof-gcov-__gcov_flush-multiple.gcda
+RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-gcov-__gcov_flush-multiple.c.gcov %S/Inputs/instrprof-gcov-__gcov_flush-multiple.c.gcov
diff --git a/test/profile/instrprof-gcov-multiple-bbs-single-line.test b/test/profile/instrprof-gcov-multiple-bbs-single-line.test
index 8839455189ee..66b6429208a0 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-one-line-function.test b/test/profile/instrprof-gcov-one-line-function.test
new file mode 100644
index 000000000000..d67c21a863bf
--- /dev/null
+++ b/test/profile/instrprof-gcov-one-line-function.test
@@ -0,0 +1,9 @@
+RUN: mkdir -p %t.d
+RUN: cd %t.d
+
+RUN: %clang --coverage -o %t %S/Inputs/instrprof-gcov-one-line-function.c
+RUN: test -f instrprof-gcov-one-line-function.gcno
+RUN: rm -f instrprof-gcov-one-line-function.gcda
+RUN: %run %t
+RUN: llvm-cov gcov instrprof-gcov-one-line-function.gcda
+RUN: FileCheck --match-full-lines --strict-whitespace --input-file instrprof-gcov-one-line-function.c.gcov %S/Inputs/instrprof-gcov-one-line-function.c.gcov
diff --git a/test/profile/instrprof-gcov-two-objects.test b/test/profile/instrprof-gcov-two-objects.test
index a53d51dce8ed..4080caa7fdd9 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-merging.cpp b/test/profile/instrprof-merging.cpp
new file mode 100644
index 000000000000..06f05ce612aa
--- /dev/null
+++ b/test/profile/instrprof-merging.cpp
@@ -0,0 +1,53 @@
+// 1) Compile shared code into different object files and into an executable.
+
+// RUN: %clangxx_profgen -fcoverage-mapping %s -c -o %t.v1.o -D_VERSION_1
+// RUN: %clangxx_profgen -fcoverage-mapping %s -c -o %t.v2.o -D_VERSION_2
+// RUN: %clangxx_profgen -fcoverage-mapping %t.v1.o %t.v2.o -o %t.exe
+
+// 2) Collect profile data.
+
+// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t.exe
+// RUN: llvm-profdata merge %t.profraw -o %t.profdata
+
+// 3) Generate coverage reports from the different object files and the exe.
+
+// RUN: llvm-cov show %t.v1.o -instr-profile=%t.profdata | FileCheck %s -check-prefixes=V1,V1-ONLY
+// RUN: llvm-cov show %t.v2.o -instr-profile=%t.profdata | FileCheck %s -check-prefixes=V2,V2-ONLY
+// RUN: llvm-cov show %t.v1.o -object %t.v2.o -instr-profile=%t.profdata | FileCheck %s -check-prefixes=V1,V2
+// RUN: llvm-cov show %t.exe -instr-profile=%t.profdata | FileCheck %s -check-prefixes=V1,V2
+
+// 4) Verify that coverage reporting on the aggregate coverage mapping shows
+// hits for all code. (We used to arbitrarily pick a mapping from one binary
+// and prefer it over others.) When only limited coverage information is
+// available (just from one binary), don't try to guess any region counts.
+
+struct A {
+ A() {} // V1: [[@LINE]]{{ *}}|{{ *}}1
+ // V1-ONLY: [[@LINE+1]]{{ *}}|{{ *}}|
+ A(int) {} // V2-ONLY: [[@LINE-2]]{{ *}}|{{ *}}|
+ // V2: [[@LINE-1]]{{ *}}|{{ *}}1
+};
+
+#ifdef _VERSION_1
+
+void foo();
+
+void bar() {
+ A x; // V1: [[@LINE]]{{ *}}|{{ *}}1
+}
+
+int main() {
+ foo(); // V1: [[@LINE]]{{ *}}|{{ *}}1
+ bar();
+ return 0;
+}
+
+#endif // _VERSION_1
+
+#ifdef _VERSION_2
+
+void foo() {
+ A x{0}; // V2: [[@LINE]]{{ *}}|{{ *}}1
+}
+
+#endif // _VERSION_2
diff --git a/test/profile/instrprof-shared-gcov-flush.test b/test/profile/instrprof-shared-gcov-flush.test
index 50292b6336c4..542db0412437 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/profile/lit.cfg b/test/profile/lit.cfg
index 1cd2509672fd..7449650a623c 100644
--- a/test/profile/lit.cfg
+++ b/test/profile/lit.cfg
@@ -67,7 +67,7 @@ config.substitutions.append( ("%clangxx_profuse=", build_invocation(clang_cxxfla
config.substitutions.append( ("%clang_lto_profgen=", build_invocation(clang_cflags, True) + " -fprofile-instr-generate=") )
-if config.host_os not in ['Darwin', 'FreeBSD', 'Linux', 'SunOS']:
+if config.host_os not in ['Darwin', 'FreeBSD', 'Linux', 'NetBSD', 'SunOS']:
config.unsupported = True
if config.target_arch in ['armv7l']:
diff --git a/test/safestack/lit.cfg b/test/safestack/lit.cfg
index 10cd8a5a5135..f58ec45533d7 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 805366c9f1eb..55c5b42dcbb2 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 000000000000..2fe7c680ef6f
--- /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 416586ee13e7..1687c10a6ef2 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/CMakeLists.txt b/test/sanitizer_common/CMakeLists.txt
index 4e2c80390f71..23292e548207 100644
--- a/test/sanitizer_common/CMakeLists.txt
+++ b/test/sanitizer_common/CMakeLists.txt
@@ -16,6 +16,13 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT ANDROID)
list(APPEND SUPPORTED_TOOLS lsan)
endif()
+# FIXME(dliew): Remove this.
+# Temporary helper for https://reviews.llvm.org/D55740
+message(
+ STATUS
+ "sanitizer_common tests on \"${CMAKE_SYSTEM_NAME}\" will run against "
+ "\"${SUPPORTED_TOOLS}\"")
+
# Create a separate config for each tool we support.
foreach(tool ${SUPPORTED_TOOLS})
string(TOUPPER ${tool} tool_toupper)
diff --git a/test/sanitizer_common/TestCases/FreeBSD/capsicum.cc b/test/sanitizer_common/TestCases/FreeBSD/capsicum.cc
new file mode 100644
index 000000000000..1bfb6f8d945c
--- /dev/null
+++ b/test/sanitizer_common/TestCases/FreeBSD/capsicum.cc
@@ -0,0 +1,68 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <sys/capsicum.h>
+#include <sys/ioctl.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+void test_cap_ioctls() {
+ cap_rights_t rights;
+ unsigned long ncmds[] = {TIOCGETA, TIOCGWINSZ, FIODTYPE};
+ unsigned long rcmds = 0;
+ cap_rights_t *rptr = cap_rights_init(&rights, CAP_IOCTL, CAP_READ);
+ assert(rptr);
+
+ int rv = cap_rights_limit(STDIN_FILENO, &rights);
+ assert(rv == 0);
+ rv = cap_ioctls_limit(STDIN_FILENO, ncmds, 3);
+ assert(rv == 0);
+ ssize_t rz = cap_ioctls_get(STDIN_FILENO, &rcmds, 3);
+ assert(rz == 3);
+ printf("ioctls test: %ld commands authorized\n", rz);
+}
+
+void test_cap_rights() {
+ cap_rights_t rights, little, remove, grights;
+ cap_rights_t *rptr = cap_rights_init(&rights, CAP_IOCTL, CAP_READ);
+ assert(rptr);
+ cap_rights_t *gptr = cap_rights_init(&remove, CAP_IOCTL);
+ assert(gptr);
+ cap_rights_t *sptr = cap_rights_init(&little, CAP_READ);
+ assert(sptr);
+ bool hasit = cap_rights_contains(rptr, sptr);
+ assert(hasit == true);
+ cap_rights_t *pptr = cap_rights_remove(&rights, gptr);
+ hasit = cap_rights_contains(pptr, sptr);
+ assert(hasit == true);
+ cap_rights_t *aptr = cap_rights_merge(&rights, gptr);
+ assert(aptr);
+ bool correct = cap_rights_is_valid(&rights);
+ assert(correct == true);
+
+ int rv = cap_rights_limit(STDIN_FILENO, &rights);
+ assert(rv == 0);
+ rv = cap_rights_get(STDIN_FILENO, &grights);
+ assert(rv == 0);
+ assert(memcmp(&grights, &rights, sizeof(grights)) == 0);
+ cap_rights_t *iptr = cap_rights_set(&rights, CAP_IOCTL);
+ assert(iptr);
+ cap_rights_t *eptr = cap_rights_clear(&rights, CAP_READ);
+ assert(eptr);
+ hasit = cap_rights_is_set(&rights, CAP_IOCTL);
+ assert(hasit == true);
+ printf("rights test: %d\n", rv);
+}
+
+int main(void) {
+ test_cap_ioctls();
+
+ test_cap_rights();
+
+ // CHECK: ioctls test: {{.*}} commands authorized
+ // CHECK: rights test: {{.*}}
+}
diff --git a/test/sanitizer_common/TestCases/FreeBSD/fdevname.cc b/test/sanitizer_common/TestCases/FreeBSD/fdevname.cc
new file mode 100644
index 000000000000..252335e9afb4
--- /dev/null
+++ b/test/sanitizer_common/TestCases/FreeBSD/fdevname.cc
@@ -0,0 +1,44 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+void test_fdevname() {
+ int fd = open("/dev/null", O_RDONLY);
+ char *name;
+
+ printf("test_fdevname\n");
+ assert(fd != -1);
+ assert((name = fdevname(fd)));
+ close(fd);
+
+ printf("%s\n", name);
+}
+
+void test_fdevname_r() {
+ int fd = open("/dev/null", O_RDONLY);
+ char *name;
+ char buf[5];
+
+ printf("test_fdevname_r\n");
+ assert(fd != -1);
+ assert((name = fdevname_r(fd, buf, sizeof(buf))));
+ close(fd);
+
+ printf("%s\n", name);
+}
+
+int main(void) {
+ test_fdevname();
+ test_fdevname_r();
+ // CHECK: test_fdevname
+ // CHECK: null
+ // CHECK: test_fdevname_r
+ // CHECK: null
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/FreeBSD/lit.local.cfg b/test/sanitizer_common/TestCases/FreeBSD/lit.local.cfg
new file mode 100644
index 000000000000..6f2f4280f978
--- /dev/null
+++ b/test/sanitizer_common/TestCases/FreeBSD/lit.local.cfg
@@ -0,0 +1,9 @@
+def getRoot(config):
+ if not config.parent:
+ return config
+ return getRoot(config.parent)
+
+root = getRoot(config)
+
+if root.host_os not in ['FreeBSD']:
+ config.unsupported = True
diff --git a/test/sanitizer_common/TestCases/Linux/allow_user_segv.cc b/test/sanitizer_common/TestCases/Linux/allow_user_segv.cc
index e17de1853eb3..bd58f4bd9265 100644
--- a/test/sanitizer_common/TestCases/Linux/allow_user_segv.cc
+++ b/test/sanitizer_common/TestCases/Linux/allow_user_segv.cc
@@ -1,7 +1,6 @@
// Regression test for
// https://code.google.com/p/address-sanitizer/issues/detail?id=180
-// clang-format off
// RUN: %clangxx -O0 %s -o %t
// RUN: %env_tool_opts=handle_segv=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK0
@@ -15,7 +14,6 @@
// RUN: %env_tool_opts=handle_segv=0:allow_user_segv_handler=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK0
// RUN: %env_tool_opts=handle_segv=1:allow_user_segv_handler=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1
// RUN: %env_tool_opts=handle_segv=2:allow_user_segv_handler=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK2
-// clang-format on
// Flaky errors in debuggerd with "waitpid returned unexpected pid (0)" in logcat.
// UNSUPPORTED: android && i386-target-arch
diff --git a/test/sanitizer_common/TestCases/Linux/assert.cc b/test/sanitizer_common/TestCases/Linux/assert.cc
index 9c5263f8e776..2a73c508844b 100644
--- a/test/sanitizer_common/TestCases/Linux/assert.cc
+++ b/test/sanitizer_common/TestCases/Linux/assert.cc
@@ -1,11 +1,9 @@
// Test the handle_abort option.
-// clang-format off
// RUN: %clangxx %s -o %t
// RUN: not --crash %run %t 2>&1 | FileCheck --check-prefix=CHECK0 %s
// RUN: %env_tool_opts=handle_abort=0 not --crash %run %t 2>&1 | FileCheck --check-prefix=CHECK0 %s
// RUN: %env_tool_opts=handle_abort=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK1 %s
-// clang-format on
#include <assert.h>
#include <stdio.h>
diff --git a/test/sanitizer_common/TestCases/Linux/ill.cc b/test/sanitizer_common/TestCases/Linux/ill.cc
index 43f7a7830495..bbde13b5675b 100644
--- a/test/sanitizer_common/TestCases/Linux/ill.cc
+++ b/test/sanitizer_common/TestCases/Linux/ill.cc
@@ -1,11 +1,9 @@
// Test the handle_sigill option.
-// clang-format off
// RUN: %clangxx %s -o %t -O1
// RUN: not --crash %run %t 2>&1 | FileCheck --check-prefix=CHECK0 %s
// RUN: %env_tool_opts=handle_sigill=0 not --crash %run %t 2>&1 | FileCheck --check-prefix=CHECK0 %s
// RUN: %env_tool_opts=handle_sigill=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK1 %s
-// clang-format on
// FIXME: seems to fail on ARM
// REQUIRES: x86_64-target-arch
diff --git a/test/sanitizer_common/TestCases/Linux/mallopt.cc b/test/sanitizer_common/TestCases/Linux/mallopt.cc
new file mode 100644
index 000000000000..9ac3c5dc5978
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Linux/mallopt.cc
@@ -0,0 +1,10 @@
+// Check that mallopt does not return invalid values (ex. -1).
+// RUN: %clangxx -O2 %s -o %t && %run %t
+#include <assert.h>
+#include <malloc.h>
+
+int main() {
+ // Try a random mallopt option, possibly invalid.
+ int res = mallopt(-42, 0);
+ assert(res == 0 || res == 1);
+}
diff --git a/test/sanitizer_common/TestCases/Linux/signal_segv_handler.cc b/test/sanitizer_common/TestCases/Linux/signal_segv_handler.cc
index d6c3ecbff475..9802617c2d05 100644
--- a/test/sanitizer_common/TestCases/Linux/signal_segv_handler.cc
+++ b/test/sanitizer_common/TestCases/Linux/signal_segv_handler.cc
@@ -1,6 +1,4 @@
-// clang-format off
// RUN: %clangxx -O1 %s -o %t && TSAN_OPTIONS="flush_memory_ms=1 memory_limit_mb=1" %run %t 2>&1 | FileCheck %s
-// clang-format on
// JVM uses SEGV to preempt threads. All threads do a load from a known address
// periodically. When runtime needs to preempt threads, it unmaps the page.
diff --git a/test/sanitizer_common/TestCases/NetBSD/asysctl.cc b/test/sanitizer_common/TestCases/NetBSD/asysctl.cc
new file mode 100644
index 000000000000..acdfb17f28b5
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/asysctl.cc
@@ -0,0 +1,44 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <sys/sysctl.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void test_asysctl() {
+ int mib[] = {CTL_KERN, KERN_OSTYPE};
+ size_t len;
+ char *buf = (char *)asysctl(mib, __arraycount(mib), &len);
+ assert(buf);
+
+ printf("asysctl: '%s' size: '%zu'\n", buf, len);
+
+ free(buf);
+}
+
+void test_asysctlbyname() {
+ size_t len;
+ char *buf = (char *)asysctlbyname("kern.ostype", &len);
+ assert(buf);
+
+ printf("asysctlbyname: '%s' size: '%zu'\n", buf, len);
+
+ free(buf);
+}
+
+int main(void) {
+ printf("asysctl\n");
+
+ test_asysctl();
+ test_asysctlbyname();
+
+ return 0;
+
+ // CHECK: asysctl
+ // CHECK: asysctl: '{{.*}}' size: '{{.*}}'
+ // CHECK: asysctlbyname: '{{.*}}' size: '{{.*}}'
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/cdb.cc b/test/sanitizer_common/TestCases/NetBSD/cdb.cc
new file mode 100644
index 000000000000..065623b7d7d0
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/cdb.cc
@@ -0,0 +1,134 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <sys/param.h>
+
+#include <sys/types.h>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <cdbr.h>
+#include <cdbw.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static char *name;
+
+const char data1[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
+const char data2[] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};
+const char key1[] = {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27};
+const char key2[] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37};
+
+void test_cdbw() {
+ uint32_t idx;
+
+ struct cdbw *cdbw = cdbw_open();
+ assert(cdbw);
+
+ int rv = cdbw_put_data(cdbw, data1, __arraycount(data1), &idx);
+ assert(!rv);
+
+ rv = cdbw_put_key(cdbw, key1, __arraycount(key1), idx);
+ assert(!rv);
+
+ rv = cdbw_put(cdbw, key2, __arraycount(key2), data2, __arraycount(data2));
+ assert(!rv);
+
+ name = strdup("/tmp/temp.XXXXXX");
+ assert(name);
+
+ name = mktemp(name);
+ assert(name);
+
+ int fd = open(name, O_RDWR | O_CREAT, 0644);
+ assert(fd != -1);
+
+ cdbw_output(cdbw, fd, "TEST1", cdbw_stable_seeder);
+
+ cdbw_close(cdbw);
+
+ rv = close(fd);
+ assert(rv != -1);
+}
+
+void test_cdbr1() {
+ struct cdbr *cdbr = cdbr_open(name, CDBR_DEFAULT);
+ assert(cdbr);
+
+ uint32_t idx = cdbr_entries(cdbr);
+ assert(idx > 0);
+ printf("entries: %" PRIu32 "\n", idx);
+
+ const void *data;
+ size_t data_len;
+ int rv = cdbr_get(cdbr, idx - 1, &data, &data_len);
+ assert(rv == 0);
+
+ printf("data: ");
+ for (size_t i = 0; i < data_len; i++)
+ printf("%02" PRIx8, ((uint8_t *)data)[i]);
+ printf("\n");
+
+ rv = cdbr_find(cdbr, key1, __arraycount(key1), &data, &data_len);
+
+ printf("data: ");
+ for (size_t i = 0; i < data_len; i++)
+ printf("%02" PRIx8, ((uint8_t *)data)[i]);
+ printf("\n");
+
+ cdbr_close(cdbr);
+}
+
+#define COOKIE ((void *)1)
+
+static void cdbr_unmap(void *cookie, void *base, size_t sz) {
+ assert(cookie == COOKIE);
+ int rv = munmap(base, sz);
+ assert(rv != -1);
+}
+
+void test_cdbr2() {
+ struct stat sb;
+
+ int fd = open(name, O_RDONLY);
+ assert(fd != -1);
+
+ int rv = fstat(fd, &sb);
+ assert(rv != -1);
+
+ size_t sz = sb.st_size;
+ assert(sz < SSIZE_MAX);
+
+ void *base = mmap(NULL, sz, PROT_READ, MAP_FILE | MAP_SHARED, fd, 0);
+ assert(base != MAP_FAILED);
+
+ rv = close(fd);
+ assert(rv != -1);
+
+ struct cdbr *cdbr = cdbr_open_mem(base, sz, CDBR_DEFAULT, cdbr_unmap, COOKIE);
+ assert(cdbr);
+
+ printf("entries: %" PRIu32 "\n", cdbr_entries(cdbr));
+
+ cdbr_close(cdbr);
+}
+
+int main(void) {
+ printf("cdb\n");
+
+ test_cdbw();
+ test_cdbr1();
+ test_cdbr2();
+
+ // CHECK: cdb
+ // CHECK: entries: 2
+ // CHECK: data: 1011121314151617
+ // CHECK: data: 0001020304050607
+ // CHECK: entries: 2
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/fparseln.cc b/test/sanitizer_common/TestCases/NetBSD/fparseln.cc
new file mode 100644
index 000000000000..8a71d5fcdadc
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/fparseln.cc
@@ -0,0 +1,25 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(void) {
+ printf("fparseln\n");
+
+ FILE *fp = fopen("/etc/fstab", "r");
+ assert(fp);
+
+ int flags = FPARSELN_UNESCALL;
+ const char *delim = "\\\\#";
+ size_t lineno = 0, len;
+ char *line;
+ while ((line = fparseln(fp, &len, &lineno, delim, flags))) {
+ printf("lineno: %zu, length: %zu, line: %s\n", lineno, len, line);
+ free(line);
+ }
+
+ // CHECK: fparseln
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/funopen2.cc b/test/sanitizer_common/TestCases/NetBSD/funopen2.cc
new file mode 100644
index 000000000000..181ad03c991f
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/funopen2.cc
@@ -0,0 +1,110 @@
+// RUN: %clangxx -g %s -o %t && %run %t | FileCheck %s
+
+// CHECK: READ CALLED; len={{[0-9]*}}
+// CHECK-NEXT: READ: test
+// CHECK-NEXT: WRITE CALLED: test
+// CHECK-NEXT: READ CALLED; len={{[0-9]*}}
+// CHECK-NEXT: READ: test
+// CHECK-NEXT: WRITE CALLED: test
+// CHECK-NEXT: CLOSE CALLED
+// CHECK-NEXT: SEEK CALLED; off=100, whence=0
+// CHECK-NEXT: READ CALLED; len={{[0-9]*}}
+// CHECK-NEXT: READ: test
+// CHECK-NEXT: WRITE CALLED: test
+// CHECK-NEXT: FLUSH CALLED
+// CHECK-NEXT: WRITE CALLED: test
+// CHECK-NEXT: FLUSH CALLED
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int cookie_var;
+
+ssize_t f_read(void *cookie, void *buf, size_t len) {
+ assert(cookie == &cookie_var);
+ assert(len >= 6);
+ printf("READ CALLED; len=%zd\n", len);
+ return strlcpy((char*)buf, "test\n", len);
+}
+
+ssize_t f_write(void *cookie, const void *buf, size_t len) {
+ assert(cookie == &cookie_var);
+ char *data = strndup((char*)buf, len);
+ assert(data);
+ printf("WRITE CALLED: %s\n", data);
+ free(data);
+ return len;
+}
+
+off_t f_seek(void *cookie, off_t off, int whence) {
+ assert(cookie == &cookie_var);
+ assert(whence == SEEK_SET);
+ printf("SEEK CALLED; off=%d, whence=%d\n", (int)off, whence);
+ return off;
+}
+
+int f_flush(void *cookie) {
+ assert(cookie == &cookie_var);
+ printf("FLUSH CALLED\n");
+ return 0;
+}
+
+int f_close(void *cookie) {
+ assert(cookie == &cookie_var);
+ printf("CLOSE CALLED\n");
+ return 0;
+}
+
+int main(void) {
+ FILE *fp;
+ char buf[10];
+
+ // 1. read-only variant
+ fp = fropen2(&cookie_var, f_read);
+ assert(fp);
+ // verify that fileno() does not crash or report nonsense
+ assert(fileno(fp) == -1);
+ assert(fgets(buf, sizeof(buf), fp));
+ printf("READ: %s", buf);
+ assert(!fclose(fp));
+
+ // 2. write-only variant
+ fp = fwopen2(&cookie_var, f_write);
+ assert(fp);
+ assert(fileno(fp) == -1);
+ assert(fputs("test", fp) >= 0);
+ assert(!fclose(fp));
+
+ // 3. read+write+close
+ fp = funopen2(&cookie_var, f_read, f_write, NULL, NULL, f_close);
+ assert(fp);
+ assert(fileno(fp) == -1);
+ assert(fgets(buf, sizeof(buf), fp));
+ printf("READ: %s", buf);
+ assert(fputs("test", fp) >= 0);
+ assert(!fflush(fp));
+ assert(!fclose(fp));
+
+ // 4. read+seek
+ fp = funopen2(&cookie_var, f_read, NULL, f_seek, NULL, NULL);
+ assert(fp);
+ assert(fileno(fp) == -1);
+ assert(fseek(fp, 100, SEEK_SET) == 0);
+ assert(fgets(buf, sizeof(buf), fp));
+ printf("READ: %s", buf);
+ assert(!fclose(fp));
+
+ // 5. write+flush
+ fp = funopen2(&cookie_var, NULL, f_write, NULL, f_flush, NULL);
+ assert(fp);
+ assert(fileno(fp) == -1);
+ assert(fputs("test", fp) >= 0);
+ assert(!fflush(fp));
+ assert(fputs("test", fp) >= 0);
+ // NB: fclose() also implicitly calls flush
+ assert(!fclose(fp));
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/getgroupmembership.cc b/test/sanitizer_common/TestCases/NetBSD/getgroupmembership.cc
index ee27ad6cf365..025ca9052e6f 100644
--- a/test/sanitizer_common/TestCases/NetBSD/getgroupmembership.cc
+++ b/test/sanitizer_common/TestCases/NetBSD/getgroupmembership.cc
@@ -1,5 +1,7 @@
// RUN: %clangxx -O0 -g %s -o %t && %run %t
+// XFAIL: netbsd && msan
+
#include <stdlib.h>
#include <unistd.h>
#include <grp.h>
diff --git a/test/sanitizer_common/TestCases/NetBSD/getvfsstat.cc b/test/sanitizer_common/TestCases/NetBSD/getvfsstat.cc
new file mode 100644
index 000000000000..ea72e41ede0f
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/getvfsstat.cc
@@ -0,0 +1,36 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <sys/types.h>
+
+#include <sys/statvfs.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(void) {
+ printf("getvfsstat\n");
+
+ int rv = getvfsstat(NULL, 0, ST_WAIT);
+ assert(rv != -1);
+
+ size_t sz = rv * sizeof(struct statvfs);
+ struct statvfs *buf = (struct statvfs *)malloc(sz);
+ assert(buf);
+
+ rv = getvfsstat(buf, sz, ST_WAIT);
+ assert(rv != -1);
+
+ for (int i = 0; i < rv; i++) {
+ printf("Filesystem %d\n", i);
+ printf("\tfstypename=%s\n", buf[i].f_fstypename);
+ printf("\tmntonname=%s\n", buf[i].f_mntonname);
+ printf("\tmntfromname=%s\n", buf[i].f_mntfromname);
+ }
+
+ free(buf);
+
+ // CHECK: getvfsstat
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/md2.cc b/test/sanitizer_common/TestCases/NetBSD/md2.cc
new file mode 100644
index 000000000000..7738aecdf822
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/md2.cc
@@ -0,0 +1,114 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <endian.h>
+#include <md2.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void test1() {
+ MD2_CTX ctx;
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ uint8_t digest[MD2_DIGEST_LENGTH];
+
+ MD2Init(&ctx);
+ MD2Update(&ctx, entropy, __arraycount(entropy));
+ MD2Final(digest, &ctx);
+
+ printf("test1: '");
+ for (size_t i = 0; i < __arraycount(digest); i++)
+ printf("%02x", digest[i]);
+ printf("'\n");
+}
+
+void test2() {
+ MD2_CTX ctx;
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ char digest[MD2_DIGEST_STRING_LENGTH];
+
+ MD2Init(&ctx);
+ MD2Update(&ctx, entropy, __arraycount(entropy));
+ char *p = MD2End(&ctx, digest);
+ assert(p == digest);
+
+ printf("test2: '%s'\n", digest);
+}
+
+void test3() {
+ MD2_CTX ctx;
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+
+ MD2Init(&ctx);
+ MD2Update(&ctx, entropy, __arraycount(entropy));
+ char *p = MD2End(&ctx, NULL);
+ assert(strlen(p) == MD2_DIGEST_STRING_LENGTH - 1);
+
+ printf("test3: '%s'\n", p);
+
+ free(p);
+}
+
+void test4() {
+ char digest[MD2_DIGEST_STRING_LENGTH];
+
+ char *p = MD2File("/etc/fstab", digest);
+ assert(p == digest);
+
+ printf("test4: '%s'\n", p);
+}
+
+void test5() {
+ char *p = MD2File("/etc/fstab", NULL);
+ assert(strlen(p) == MD2_DIGEST_STRING_LENGTH - 1);
+
+ printf("test5: '%s'\n", p);
+
+ free(p);
+}
+
+void test6() {
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ char digest[MD2_DIGEST_STRING_LENGTH];
+
+ char *p = MD2Data(entropy, __arraycount(entropy), digest);
+ assert(p == digest);
+
+ printf("test6: '%s'\n", p);
+}
+
+void test7() {
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+
+ char *p = MD2Data(entropy, __arraycount(entropy), NULL);
+ assert(strlen(p) == MD2_DIGEST_STRING_LENGTH - 1);
+
+ printf("test7: '%s'\n", p);
+
+ free(p);
+}
+
+int main(void) {
+ printf("MD2\n");
+
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+ test7();
+
+ // CHECK: MD2
+ // CHECK: test1: 'e303e49b34f981c2740cdf809200d51b'
+ // CHECK: test2: 'e303e49b34f981c2740cdf809200d51b'
+ // CHECK: test3: 'e303e49b34f981c2740cdf809200d51b'
+ // CHECK: test4: '{{.*}}'
+ // CHECK: test5: '{{.*}}'
+ // CHECK: test6: 'e303e49b34f981c2740cdf809200d51b'
+ // CHECK: test7: 'e303e49b34f981c2740cdf809200d51b'
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/md4.cc b/test/sanitizer_common/TestCases/NetBSD/md4.cc
new file mode 100644
index 000000000000..a319e89a3519
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/md4.cc
@@ -0,0 +1,114 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <endian.h>
+#include <md4.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void test1() {
+ MD4_CTX ctx;
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ uint8_t digest[MD4_DIGEST_LENGTH];
+
+ MD4Init(&ctx);
+ MD4Update(&ctx, entropy, __arraycount(entropy));
+ MD4Final(digest, &ctx);
+
+ printf("test1: '");
+ for (size_t i = 0; i < __arraycount(digest); i++)
+ printf("%02x", digest[i]);
+ printf("'\n");
+}
+
+void test2() {
+ MD4_CTX ctx;
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ char digest[MD4_DIGEST_STRING_LENGTH];
+
+ MD4Init(&ctx);
+ MD4Update(&ctx, entropy, __arraycount(entropy));
+ char *p = MD4End(&ctx, digest);
+ assert(p == digest);
+
+ printf("test2: '%s'\n", digest);
+}
+
+void test3() {
+ MD4_CTX ctx;
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+
+ MD4Init(&ctx);
+ MD4Update(&ctx, entropy, __arraycount(entropy));
+ char *p = MD4End(&ctx, NULL);
+ assert(strlen(p) == MD4_DIGEST_STRING_LENGTH - 1);
+
+ printf("test3: '%s'\n", p);
+
+ free(p);
+}
+
+void test4() {
+ char digest[MD4_DIGEST_STRING_LENGTH];
+
+ char *p = MD4File("/etc/fstab", digest);
+ assert(p == digest);
+
+ printf("test4: '%s'\n", p);
+}
+
+void test5() {
+ char *p = MD4File("/etc/fstab", NULL);
+ assert(strlen(p) == MD4_DIGEST_STRING_LENGTH - 1);
+
+ printf("test5: '%s'\n", p);
+
+ free(p);
+}
+
+void test6() {
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ char digest[MD4_DIGEST_STRING_LENGTH];
+
+ char *p = MD4Data(entropy, __arraycount(entropy), digest);
+ assert(p == digest);
+
+ printf("test6: '%s'\n", p);
+}
+
+void test7() {
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+
+ char *p = MD4Data(entropy, __arraycount(entropy), NULL);
+ assert(strlen(p) == MD4_DIGEST_STRING_LENGTH - 1);
+
+ printf("test7: '%s'\n", p);
+
+ free(p);
+}
+
+int main(void) {
+ printf("MD4\n");
+
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+ test7();
+
+ // CHECK: MD4
+ // CHECK: test1: 'bf78fda2ca35eb7a026bfcdd3d17283d'
+ // CHECK: test2: 'bf78fda2ca35eb7a026bfcdd3d17283d'
+ // CHECK: test3: 'bf78fda2ca35eb7a026bfcdd3d17283d'
+ // CHECK: test4: '{{.*}}'
+ // CHECK: test5: '{{.*}}'
+ // CHECK: test6: 'bf78fda2ca35eb7a026bfcdd3d17283d'
+ // CHECK: test7: 'bf78fda2ca35eb7a026bfcdd3d17283d'
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/md5.cc b/test/sanitizer_common/TestCases/NetBSD/md5.cc
new file mode 100644
index 000000000000..aee21681800d
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/md5.cc
@@ -0,0 +1,114 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <endian.h>
+#include <md5.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void test1() {
+ MD5_CTX ctx;
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ uint8_t digest[MD5_DIGEST_LENGTH];
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, entropy, __arraycount(entropy));
+ MD5Final(digest, &ctx);
+
+ printf("test1: '");
+ for (size_t i = 0; i < __arraycount(digest); i++)
+ printf("%02x", digest[i]);
+ printf("'\n");
+}
+
+void test2() {
+ MD5_CTX ctx;
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ char digest[MD5_DIGEST_STRING_LENGTH];
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, entropy, __arraycount(entropy));
+ char *p = MD5End(&ctx, digest);
+ assert(p);
+
+ printf("test2: '%s'\n", digest);
+}
+
+void test3() {
+ MD5_CTX ctx;
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, entropy, __arraycount(entropy));
+ char *p = MD5End(&ctx, NULL);
+ assert(strlen(p) == MD5_DIGEST_STRING_LENGTH - 1);
+
+ printf("test3: '%s'\n", p);
+
+ free(p);
+}
+
+void test4() {
+ char digest[MD5_DIGEST_STRING_LENGTH];
+
+ char *p = MD5File("/etc/fstab", digest);
+ assert(p == digest);
+
+ printf("test4: '%s'\n", p);
+}
+
+void test5() {
+ char *p = MD5File("/etc/fstab", NULL);
+ assert(strlen(p) == MD5_DIGEST_STRING_LENGTH - 1);
+
+ printf("test5: '%s'\n", p);
+
+ free(p);
+}
+
+void test6() {
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ char digest[MD5_DIGEST_STRING_LENGTH];
+
+ char *p = MD5Data(entropy, __arraycount(entropy), digest);
+ assert(p == digest);
+
+ printf("test6: '%s'\n", p);
+}
+
+void test7() {
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+
+ char *p = MD5Data(entropy, __arraycount(entropy), NULL);
+ assert(strlen(p) == MD5_DIGEST_STRING_LENGTH - 1);
+
+ printf("test7: '%s'\n", p);
+
+ free(p);
+}
+
+int main(void) {
+ printf("MD5\n");
+
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+ test7();
+
+ // CHECK: MD5
+ // CHECK: test1: '86e65b1ef4a830af347ac05ab4f0e999'
+ // CHECK: test2: '86e65b1ef4a830af347ac05ab4f0e999'
+ // CHECK: test3: '86e65b1ef4a830af347ac05ab4f0e999'
+ // CHECK: test4: '{{.*}}'
+ // CHECK: test5: '{{.*}}'
+ // CHECK: test6: '86e65b1ef4a830af347ac05ab4f0e999'
+ // CHECK: test7: '86e65b1ef4a830af347ac05ab4f0e999'
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/mi_vector_hash.cc b/test/sanitizer_common/TestCases/NetBSD/mi_vector_hash.cc
new file mode 100644
index 000000000000..1f6c14848884
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/mi_vector_hash.cc
@@ -0,0 +1,19 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(void) {
+ unsigned char key[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99};
+ uint32_t hashes[3];
+ mi_vector_hash(key, __arraycount(key), 0, hashes);
+ for (size_t i = 0; i < __arraycount(hashes); i++)
+ printf("hashes[%zu]='%" PRIx32 "'\n", i, hashes[i]);
+
+ // CHECK: hashes[0]='{{.*}}'
+ // CHECK: hashes[1]='{{.*}}'
+ // CHECK: hashes[2]='{{.*}}'
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/rmd160.cc b/test/sanitizer_common/TestCases/NetBSD/rmd160.cc
new file mode 100644
index 000000000000..5b9ff0cb2e11
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/rmd160.cc
@@ -0,0 +1,133 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <assert.h>
+#include <rmd160.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void test1() {
+ RMD160_CTX ctx;
+ uint8_t entropy[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+ uint8_t digest[RMD160_DIGEST_LENGTH];
+
+ RMD160Init(&ctx);
+ RMD160Update(&ctx, entropy, __arraycount(entropy));
+ RMD160Final(digest, &ctx);
+
+ printf("test1: '");
+ for (size_t i = 0; i < __arraycount(digest); i++)
+ printf("%02x", digest[i]);
+ printf("'\n");
+}
+
+void test2() {
+ RMD160_CTX ctx;
+ uint8_t entropy[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+ char digest[RMD160_DIGEST_STRING_LENGTH];
+
+ RMD160Init(&ctx);
+ RMD160Update(&ctx, entropy, __arraycount(entropy));
+ char *p = RMD160End(&ctx, digest);
+ assert(p == digest);
+
+ printf("test2: '%s'\n", digest);
+}
+
+void test3() {
+ RMD160_CTX ctx;
+ uint8_t entropy[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+
+ RMD160Init(&ctx);
+ RMD160Update(&ctx, entropy, __arraycount(entropy));
+ char *p = RMD160End(&ctx, NULL);
+ assert(strlen(p) == RMD160_DIGEST_STRING_LENGTH - 1);
+
+ printf("test3: '%s'\n", p);
+
+ free(p);
+}
+
+void test4() {
+ char digest[RMD160_DIGEST_STRING_LENGTH];
+
+ char *p = RMD160File("/etc/fstab", digest);
+ assert(p == digest);
+
+ printf("test4: '%s'\n", p);
+}
+
+void test5() {
+ char *p = RMD160File("/etc/fstab", NULL);
+ assert(strlen(p) == RMD160_DIGEST_STRING_LENGTH - 1);
+
+ printf("test5: '%s'\n", p);
+
+ free(p);
+}
+
+void test6() {
+ char digest[RMD160_DIGEST_STRING_LENGTH];
+
+ char *p = RMD160FileChunk("/etc/fstab", digest, 10, 20);
+ assert(p == digest);
+
+ printf("test6: '%s'\n", p);
+}
+
+void test7() {
+ char *p = RMD160FileChunk("/etc/fstab", NULL, 10, 20);
+ assert(strlen(p) == RMD160_DIGEST_STRING_LENGTH - 1);
+
+ printf("test7: '%s'\n", p);
+
+ free(p);
+}
+
+void test8() {
+ uint8_t entropy[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+ char digest[RMD160_DIGEST_STRING_LENGTH];
+
+ char *p = RMD160Data(entropy, __arraycount(entropy), digest);
+ assert(p == digest);
+
+ printf("test8: '%s'\n", p);
+}
+
+void test9() {
+ uint8_t entropy[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+
+ char *p = RMD160Data(entropy, __arraycount(entropy), NULL);
+ assert(strlen(p) == RMD160_DIGEST_STRING_LENGTH - 1);
+
+ printf("test9: '%s'\n", p);
+
+ free(p);
+}
+
+int main(void) {
+ printf("RMD160\n");
+
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+ test7();
+ test8();
+ test9();
+
+ // CHECK: RMD160
+ // CHECK: test1: '2787e5a006365df6e8e799315b669dc34866783c'
+ // CHECK: test2: '2787e5a006365df6e8e799315b669dc34866783c'
+ // CHECK: test3: '2787e5a006365df6e8e799315b669dc34866783c'
+ // CHECK: test4: '{{.*}}'
+ // CHECK: test5: '{{.*}}'
+ // CHECK: test6: '{{.*}}'
+ // CHECK: test7: '{{.*}}'
+ // CHECK: test8: '2787e5a006365df6e8e799315b669dc34866783c'
+ // CHECK: test9: '2787e5a006365df6e8e799315b669dc34866783c'
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/sha1.cc b/test/sanitizer_common/TestCases/NetBSD/sha1.cc
new file mode 100644
index 000000000000..ee5060a6601d
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/sha1.cc
@@ -0,0 +1,171 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <assert.h>
+#include <sha1.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void test1() {
+ SHA1_CTX ctx;
+ uint8_t entropy[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+ uint8_t digest[SHA1_DIGEST_LENGTH];
+
+ SHA1Init(&ctx);
+ SHA1Update(&ctx, entropy, __arraycount(entropy));
+ SHA1Final(digest, &ctx);
+
+ printf("test1: '");
+ for (size_t i = 0; i < __arraycount(digest); i++)
+ printf("%02x", digest[i]);
+ printf("'\n");
+}
+
+void local_SHA1Update(SHA1_CTX *context, const uint8_t *data, unsigned int len)
+{
+ unsigned int a, b;
+
+ b = context->count[0];
+ context->count[0] += len << 3;
+ if (context->count[0] < b)
+ context->count[1] += (len >> 29) + 1;
+ b = (b >> 3) & 63;
+ if ((b + len) > 63) {
+ memcpy(&context->buffer[b], data, (a = 64 - b));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; a + 63 < len; a += 64)
+ SHA1Transform(context->state, &data[a]);
+ b = 0;
+ } else {
+ a = 0;
+ }
+ memcpy(&context->buffer[b], &data[a], len - a);
+}
+
+void test2() {
+ SHA1_CTX ctx;
+ uint8_t entropy[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+ uint8_t digest[SHA1_DIGEST_LENGTH];
+
+ SHA1Init(&ctx);
+ local_SHA1Update(&ctx, entropy, __arraycount(entropy));
+ SHA1Final(digest, &ctx);
+
+ printf("test2: '");
+ for (size_t i = 0; i < __arraycount(digest); i++)
+ printf("%02x", digest[i]);
+ printf("'\n");
+}
+
+void test3() {
+ SHA1_CTX ctx;
+ uint8_t entropy[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+ char digest[SHA1_DIGEST_STRING_LENGTH];
+
+ SHA1Init(&ctx);
+ SHA1Update(&ctx, entropy, __arraycount(entropy));
+ char *p = SHA1End(&ctx, digest);
+ assert(p == digest);
+
+ printf("test3: '%s'\n", digest);
+}
+
+void test4() {
+ SHA1_CTX ctx;
+ uint8_t entropy[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+
+ SHA1Init(&ctx);
+ SHA1Update(&ctx, entropy, __arraycount(entropy));
+ char *p = SHA1End(&ctx, NULL);
+ assert(strlen(p) == SHA1_DIGEST_STRING_LENGTH - 1);
+
+ printf("test4: '%s'\n", p);
+
+ free(p);
+}
+
+void test5() {
+ char digest[SHA1_DIGEST_STRING_LENGTH];
+
+ char *p = SHA1File("/etc/fstab", digest);
+ assert(p == digest);
+
+ printf("test5: '%s'\n", p);
+}
+
+void test6() {
+ char *p = SHA1File("/etc/fstab", NULL);
+ assert(strlen(p) == SHA1_DIGEST_STRING_LENGTH - 1);
+
+ printf("test6: '%s'\n", p);
+
+ free(p);
+}
+
+void test7() {
+ char digest[SHA1_DIGEST_STRING_LENGTH];
+
+ char *p = SHA1FileChunk("/etc/fstab", digest, 10, 20);
+ assert(p == digest);
+
+ printf("test7: '%s'\n", p);
+}
+
+void test8() {
+ char *p = SHA1FileChunk("/etc/fstab", NULL, 10, 20);
+ assert(strlen(p) == SHA1_DIGEST_STRING_LENGTH - 1);
+
+ printf("test8: '%s'\n", p);
+
+ free(p);
+}
+
+void test9() {
+ uint8_t entropy[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+ char digest[SHA1_DIGEST_STRING_LENGTH];
+
+ char *p = SHA1Data(entropy, __arraycount(entropy), digest);
+ assert(p == digest);
+
+ printf("test9: '%s'\n", p);
+}
+
+void test10() {
+ uint8_t entropy[] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 };
+
+ char *p = SHA1Data(entropy, __arraycount(entropy), NULL);
+ assert(strlen(p) == SHA1_DIGEST_STRING_LENGTH - 1);
+
+ printf("test10: '%s'\n", p);
+
+ free(p);
+}
+
+int main(void) {
+ printf("SHA1\n");
+
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+ test7();
+ test8();
+ test9();
+ test10();
+
+ // CHECK: SHA1
+ // CHECK: test1: '57d1b759bf3d1811135748cb0328c73b51fa6f57'
+ // CHECK: test2: '57d1b759bf3d1811135748cb0328c73b51fa6f57'
+ // CHECK: test3: '57d1b759bf3d1811135748cb0328c73b51fa6f57'
+ // CHECK: test4: '57d1b759bf3d1811135748cb0328c73b51fa6f57'
+ // CHECK: test5: '{{.*}}'
+ // CHECK: test6: '{{.*}}'
+ // CHECK: test7: '{{.*}}'
+ // CHECK: test8: '{{.*}}'
+ // CHECK: test9: '57d1b759bf3d1811135748cb0328c73b51fa6f57'
+ // CHECK: test10: '57d1b759bf3d1811135748cb0328c73b51fa6f57'
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/sha2.cc b/test/sanitizer_common/TestCases/NetBSD/sha2.cc
new file mode 100644
index 000000000000..e905e3b610fd
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/sha2.cc
@@ -0,0 +1,206 @@
+// RUN: %clangxx -O0 -g %s -DSHASIZE=224 -o %t && %run %t 2>&1 | FileCheck %s -check-prefix=CHECK-224
+// RUN: %clangxx -O0 -g %s -DSHASIZE=256 -o %t && %run %t 2>&1 | FileCheck %s -check-prefix=CHECK-256
+// RUN: %clangxx -O0 -g %s -DSHASIZE=384 -o %t && %run %t 2>&1 | FileCheck %s -check-prefix=CHECK-384
+// RUN: %clangxx -O0 -g %s -DSHASIZE=512 -o %t && %run %t 2>&1 | FileCheck %s -check-prefix=CHECK-512
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <endian.h>
+#include <sha2.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef SHASIZE
+#error SHASIZE must be defined
+#endif
+
+#define _SHA_CTX(x) SHA##x##_CTX
+#define SHA_CTX(x) _SHA_CTX(x)
+
+#define _SHA_DIGEST_LENGTH(x) SHA##x##_DIGEST_LENGTH
+#define SHA_DIGEST_LENGTH(x) _SHA_DIGEST_LENGTH(x)
+
+#define _SHA_DIGEST_STRING_LENGTH(x) SHA##x##_DIGEST_STRING_LENGTH
+#define SHA_DIGEST_STRING_LENGTH(x) _SHA_DIGEST_STRING_LENGTH(x)
+
+#define _SHA_Init(x) SHA##x##_Init
+#define SHA_Init(x) _SHA_Init(x)
+
+#define _SHA_Update(x) SHA##x##_Update
+#define SHA_Update(x) _SHA_Update(x)
+
+#define _SHA_Final(x) SHA##x##_Final
+#define SHA_Final(x) _SHA_Final(x)
+
+#define _SHA_End(x) SHA##x##_End
+#define SHA_End(x) _SHA_End(x)
+
+#define _SHA_File(x) SHA##x##_File
+#define SHA_File(x) _SHA_File(x)
+
+#define _SHA_FileChunk(x) SHA##x##_FileChunk
+#define SHA_FileChunk(x) _SHA_FileChunk(x)
+
+#define _SHA_Data(x) SHA##x##_Data
+#define SHA_Data(x) _SHA_Data(x)
+
+void test1() {
+ SHA_CTX(SHASIZE) ctx;
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ uint8_t digest[SHA_DIGEST_LENGTH(SHASIZE)];
+
+ SHA_Init(SHASIZE)(&ctx);
+ SHA_Update(SHASIZE)(&ctx, entropy, __arraycount(entropy));
+ SHA_Final(SHASIZE)(digest, &ctx);
+
+ printf("test1: '");
+ for (size_t i = 0; i < __arraycount(digest); i++)
+ printf("%02x", digest[i]);
+ printf("'\n");
+}
+
+void test2() {
+ SHA_CTX(SHASIZE) ctx;
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ char digest[SHA_DIGEST_STRING_LENGTH(SHASIZE)];
+
+ SHA_Init(SHASIZE)(&ctx);
+ SHA_Update(SHASIZE)(&ctx, entropy, __arraycount(entropy));
+ char *p = SHA_End(SHASIZE)(&ctx, digest);
+ assert(p == digest);
+
+ printf("test2: '%s'\n", digest);
+}
+
+void test3() {
+ SHA_CTX(SHASIZE) ctx;
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+
+ SHA_Init(SHASIZE)(&ctx);
+ SHA_Update(SHASIZE)(&ctx, entropy, __arraycount(entropy));
+ char *p = SHA_End(SHASIZE)(&ctx, NULL);
+ assert(strlen(p) == SHA_DIGEST_STRING_LENGTH(SHASIZE) - 1);
+
+ printf("test3: '%s'\n", p);
+
+ free(p);
+}
+
+void test4() {
+ char digest[SHA_DIGEST_STRING_LENGTH(SHASIZE)];
+
+ char *p = SHA_File(SHASIZE)("/etc/fstab", digest);
+ assert(p == digest);
+
+ printf("test4: '%s'\n", p);
+}
+
+void test5() {
+ char *p = SHA_File(SHASIZE)("/etc/fstab", NULL);
+ assert(strlen(p) == SHA_DIGEST_STRING_LENGTH(SHASIZE) - 1);
+
+ printf("test5: '%s'\n", p);
+
+ free(p);
+}
+
+void test6() {
+ char digest[SHA_DIGEST_STRING_LENGTH(SHASIZE)];
+
+ char *p = SHA_FileChunk(SHASIZE)("/etc/fstab", digest, 10, 20);
+ assert(p == digest);
+
+ printf("test6: '%s'\n", p);
+}
+
+void test7() {
+ char *p = SHA_FileChunk(SHASIZE)("/etc/fstab", NULL, 10, 20);
+ assert(strlen(p) == SHA_DIGEST_STRING_LENGTH(SHASIZE) - 1);
+
+ printf("test7: '%s'\n", p);
+
+ free(p);
+}
+
+void test8() {
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+ char digest[SHA_DIGEST_STRING_LENGTH(SHASIZE)];
+
+ char *p = SHA_Data(SHASIZE)(entropy, __arraycount(entropy), digest);
+ assert(p == digest);
+
+ printf("test8: '%s'\n", p);
+}
+
+void test9() {
+ uint8_t entropy[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
+
+ char *p = SHA_Data(SHASIZE)(entropy, __arraycount(entropy), NULL);
+ assert(strlen(p) == SHA_DIGEST_STRING_LENGTH(SHASIZE) - 1);
+
+ printf("test9: '%s'\n", p);
+
+ free(p);
+}
+
+int main(void) {
+ printf("SHA" ___STRING(SHASIZE) "\n");
+
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+ test7();
+ test8();
+ test9();
+
+ // CHECK-224: SHA224
+ // CHECK-224: test1: '760dfb93100a6bf5996c90f678e529dc945bb2f74a211eedcf0f3a48'
+ // CHECK-224: test2: '760dfb93100a6bf5996c90f678e529dc945bb2f74a211eedcf0f3a48'
+ // CHECK-224: test3: '760dfb93100a6bf5996c90f678e529dc945bb2f74a211eedcf0f3a48'
+ // CHECK-224: test4: '{{.*}}'
+ // CHECK-224: test5: '{{.*}}'
+ // CHECK-224: test6: '{{.*}}'
+ // CHECK-224: test7: '{{.*}}'
+ // CHECK-224: test8: '760dfb93100a6bf5996c90f678e529dc945bb2f74a211eedcf0f3a48'
+ // CHECK-224: test9: '760dfb93100a6bf5996c90f678e529dc945bb2f74a211eedcf0f3a48'
+
+ // CHECK-256: SHA256
+ // CHECK-256: test1: 'bb000ddd92a0a2a346f0b531f278af06e370f86932ccafccc892d68d350f80f8'
+ // CHECK-256: test2: 'bb000ddd92a0a2a346f0b531f278af06e370f86932ccafccc892d68d350f80f8'
+ // CHECK-256: test3: 'bb000ddd92a0a2a346f0b531f278af06e370f86932ccafccc892d68d350f80f8'
+ // CHECK-256: test4: '{{.*}}'
+ // CHECK-256: test5: '{{.*}}'
+ // CHECK-256: test6: '{{.*}}'
+ // CHECK-256: test7: '{{.*}}'
+ // CHECK-256: test8: 'bb000ddd92a0a2a346f0b531f278af06e370f86932ccafccc892d68d350f80f8'
+ // CHECK-256: test9: 'bb000ddd92a0a2a346f0b531f278af06e370f86932ccafccc892d68d350f80f8'
+
+ // CHECK-384: SHA384
+ // CHECK-384: test1: 'f450c023b168ebd56ff916ca9b1f1f0010b8c592d28205cc91fa3056f629eed108e8bac864f01ca37a3edee596739e12'
+ // CHECK-384: test2: 'f450c023b168ebd56ff916ca9b1f1f0010b8c592d28205cc91fa3056f629eed108e8bac864f01ca37a3edee596739e12'
+ // CHECK-384: test3: 'f450c023b168ebd56ff916ca9b1f1f0010b8c592d28205cc91fa3056f629eed108e8bac864f01ca37a3edee596739e12'
+ // CHECK-384: test4: '{{.*}}'
+ // CHECK-384: test5: '{{.*}}'
+ // CHECK-384: test6: '{{.*}}'
+ // CHECK-384: test7: '{{.*}}'
+ // CHECK-384: test8: 'f450c023b168ebd56ff916ca9b1f1f0010b8c592d28205cc91fa3056f629eed108e8bac864f01ca37a3edee596739e12'
+ // CHECK-384: test9: 'f450c023b168ebd56ff916ca9b1f1f0010b8c592d28205cc91fa3056f629eed108e8bac864f01ca37a3edee596739e12'
+
+ // CHECK-512: SHA512
+ // CHECK-512: test1: '0e3f68731c0e2a6a4eab5d713c9a80dc78086b5fa7d2b5ab127277958e68d1b1dee1882b083b0106cd4319de42c0c8f452871364f5baa8a6379690612c6b844e'
+ // CHECK-512: test2: '0e3f68731c0e2a6a4eab5d713c9a80dc78086b5fa7d2b5ab127277958e68d1b1dee1882b083b0106cd4319de42c0c8f452871364f5baa8a6379690612c6b844e'
+ // CHECK-512: test3: '0e3f68731c0e2a6a4eab5d713c9a80dc78086b5fa7d2b5ab127277958e68d1b1dee1882b083b0106cd4319de42c0c8f452871364f5baa8a6379690612c6b844e'
+ // CHECK-512: test4: '{{.*}}'
+ // CHECK-512: test5: '{{.*}}'
+ // CHECK-512: test6: '{{.*}}'
+ // CHECK-512: test7: '{{.*}}'
+ // CHECK-512: test8: '0e3f68731c0e2a6a4eab5d713c9a80dc78086b5fa7d2b5ab127277958e68d1b1dee1882b083b0106cd4319de42c0c8f452871364f5baa8a6379690612c6b844e'
+ // CHECK-512: test9: '0e3f68731c0e2a6a4eab5d713c9a80dc78086b5fa7d2b5ab127277958e68d1b1dee1882b083b0106cd4319de42c0c8f452871364f5baa8a6379690612c6b844e'
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/statvfs1.cc b/test/sanitizer_common/TestCases/NetBSD/statvfs1.cc
new file mode 100644
index 000000000000..40dfca37bb58
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/statvfs1.cc
@@ -0,0 +1,58 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <sys/statvfs.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void test_statvfs1() {
+ printf("statvfs1\n");
+
+ struct statvfs buf;
+ int rv = statvfs1("/etc/fstab", &buf, ST_WAIT);
+ assert(rv != -1);
+
+ printf("fstypename='%s'\n", buf.f_fstypename);
+ printf("mntonname='%s'\n", buf.f_mntonname);
+ printf("mntfromname='%s'\n", buf.f_mntfromname);
+}
+
+void test_fstatvfs1() {
+ printf("fstatvfs1\n");
+
+ int fd = open("/etc/fstab", O_RDONLY);
+ assert(fd > 0);
+
+ struct statvfs buf;
+ int rv = fstatvfs1(fd, &buf, ST_WAIT);
+ assert(rv != -1);
+
+ printf("fstypename='%s'\n", buf.f_fstypename);
+ printf("mntonname='%s'\n", buf.f_mntonname);
+ printf("mntfromname='%s'\n", buf.f_mntfromname);
+
+ rv = close(fd);
+ assert(rv != -1);
+}
+
+int main(void) {
+ test_statvfs1();
+ test_fstatvfs1();
+
+ // CHECK: statvfs1
+ // CHECK: fstypename='{{.*}}'
+ // CHECK: mntonname='{{.*}}'
+ // CHECK: mntfromname='{{.*}}'
+ // CHECK: fstatvfs1
+ // CHECK: fstypename='{{.*}}'
+ // CHECK: mntonname='{{.*}}'
+ // CHECK: mntfromname='{{.*}}'
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/strtoi.cc b/test/sanitizer_common/TestCases/NetBSD/strtoi.cc
new file mode 100644
index 000000000000..4d0d8b3aebf0
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/strtoi.cc
@@ -0,0 +1,43 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <inttypes.h>
+#include <stdio.h>
+
+void test_strtoi(const char *nptr, int base, intmax_t lo, intmax_t hi) {
+ char *p;
+ int status;
+ intmax_t i = strtoi(nptr, &p, base, lo, hi, &status);
+ printf("strtoi: conversion of '%s' to a number %s, using %jd, p=%#" PRIx8
+ "\n",
+ nptr, status ? "failed" : "successful", i, *p);
+}
+
+void test_strtou(const char *nptr, int base, intmax_t lo, intmax_t hi) {
+ char *p;
+ int status;
+ uintmax_t i = strtou(nptr, &p, base, lo, hi, &status);
+ printf("strtou: conversion of '%s' to a number %s, using %ju, p=%#" PRIx8
+ "\n",
+ nptr, status ? "failed" : "successful", i, *p);
+}
+
+int main(void) {
+ printf("strtoi\n");
+
+ test_strtoi("100", 0, 1, 100);
+ test_strtoi("100", 0, 1, 10);
+ test_strtoi("100xyz", 0, 1, 100);
+ test_strtou("100", 0, 1, 100);
+ test_strtou("100", 0, 1, 10);
+ test_strtou("100xyz", 0, 1, 100);
+
+ // CHECK: strtoi
+ // CHECK: strtoi: conversion of '100' to a number successful, using 100, p=0
+ // CHECK: strtoi: conversion of '100' to a number failed, using 10, p=0
+ // CHECK: strtoi: conversion of '100xyz' to a number failed, using 100, p=0x78
+ // CHECK: strtou: conversion of '100' to a number successful, using 100, p=0
+ // CHECK: strtou: conversion of '100' to a number failed, using 10, p=0
+ // CHECK: strtou: conversion of '100xyz' to a number failed, using 100, p=0x78
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/NetBSD/sysctlgetmibinfo.cc b/test/sanitizer_common/TestCases/NetBSD/sysctlgetmibinfo.cc
new file mode 100644
index 000000000000..d81c1567fc44
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/sysctlgetmibinfo.cc
@@ -0,0 +1,36 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <sys/sysctl.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+void test_sysctlgetmibinfo() {
+ int mib[CTL_MAXNAME];
+ unsigned int mib_len = __arraycount(mib);
+ int rv = sysctlgetmibinfo("kern.ostype", &mib[0], &mib_len, NULL, NULL, NULL,
+ SYSCTL_VERSION);
+ assert(!rv);
+
+ char buf[100];
+ size_t len = sizeof(buf);
+ rv = sysctl(mib, mib_len, buf, &len, NULL, 0);
+ assert(!rv);
+
+ printf("sysctlgetmibinfo: '%s' size: '%zu'\n", buf, len);
+}
+
+int main(void) {
+ printf("sysctlgetmibinfo\n");
+
+ test_sysctlgetmibinfo();
+
+ return 0;
+
+ // CHECK: sysctlgetmibinfo
+ // CHECK: sysctlgetmibinfo: '{{.*}}' size: '{{.*}}'
+}
diff --git a/test/sanitizer_common/TestCases/Posix/arc4random.cc b/test/sanitizer_common/TestCases/Posix/arc4random.cc
new file mode 100644
index 000000000000..0a983b58f59f
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/arc4random.cc
@@ -0,0 +1,71 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+//
+// UNSUPPORTED: linux, darwin, solaris
+
+#include <cstdlib>
+#include <ctime>
+#include <cstdio>
+#include <inttypes.h>
+
+void print_buf(unsigned char *buf, size_t buflen) {
+ printf("buf '");
+ for (auto i = 0; i < buflen; i ++)
+ printf("%" PRIx8, buf[i]);
+ printf("'\n");
+}
+
+void test_seed() {
+#ifdef __NetBSD__
+ time_t now = ::time(nullptr);
+ arc4random_addrandom((unsigned char *)&now, sizeof(now));
+#endif
+}
+
+void test_arc4random() {
+ printf("test_arc4random\n");
+ auto i = arc4random();
+ print_buf((unsigned char *)&i, sizeof(i));
+}
+
+void test_arc4random_uniform() {
+ printf("test_arc4random_uniform\n");
+ auto i = arc4random_uniform(1024);
+ print_buf((unsigned char *)&i, sizeof(i));
+}
+
+void test_arc4random_buf10() {
+ printf("test_arc4random_buf10\n");
+ char buf[10];
+#ifdef __NetBSD__
+ arc4random_stir();
+#endif
+ arc4random_buf(buf, sizeof(buf));
+ print_buf((unsigned char *)buf, sizeof(buf));
+}
+
+void test_arc4random_buf256() {
+ printf("test_arc4random_buf256\n");
+ char buf[256];
+#ifdef __NetBSD__
+ arc4random_stir();
+#endif
+ arc4random_buf(buf, sizeof(buf));
+ print_buf((unsigned char *)buf, sizeof(buf));
+}
+
+int main(void) {
+ test_seed();
+ test_arc4random();
+ test_arc4random_uniform();
+ test_arc4random_buf10();
+ test_arc4random_buf256();
+ return 0;
+ // CHECK: test_arc4random
+ // CHECK: buf '{{.*}}'
+ // CHECK: test_arc4random_uniform
+ // CHECK: buf '{{.*}}'
+ // CHECK: test_arc4random_buf10
+ // CHECK: buf '{{.*}}'
+ // CHECK: test_arc4random_buf256
+ // CHECK: buf '{{.*}}'
+}
diff --git a/test/sanitizer_common/TestCases/Posix/dedup_token_length_test.cc b/test/sanitizer_common/TestCases/Posix/dedup_token_length_test.cc
index d9a1bc66082c..94c50be169b4 100644
--- a/test/sanitizer_common/TestCases/Posix/dedup_token_length_test.cc
+++ b/test/sanitizer_common/TestCases/Posix/dedup_token_length_test.cc
@@ -8,6 +8,8 @@
// REQUIRES: stable-runtime
+// XFAIL: netbsd && !asan
+
volatile int *null = 0;
namespace Xyz {
diff --git a/test/sanitizer_common/TestCases/Posix/devname.cc b/test/sanitizer_common/TestCases/Posix/devname.cc
index da4bb8853a12..1495f7d9d518 100644
--- a/test/sanitizer_common/TestCases/Posix/devname.cc
+++ b/test/sanitizer_common/TestCases/Posix/devname.cc
@@ -1,6 +1,7 @@
// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
// UNSUPPORTED: linux, solaris
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
@@ -9,11 +10,8 @@ int main(void) {
struct stat st;
char *name;
- if (stat("/dev/null", &st))
- exit(1);
-
- if (!(name = devname(st.st_rdev, S_ISCHR(st.st_mode) ? S_IFCHR : S_IFBLK)))
- exit(1);
+ assert(!stat("/dev/null", &st));
+ assert((name = devname(st.st_rdev, S_ISCHR(st.st_mode) ? S_IFCHR : S_IFBLK)));
printf("%s\n", name);
diff --git a/test/sanitizer_common/TestCases/Posix/devname_r.cc b/test/sanitizer_common/TestCases/Posix/devname_r.cc
index 826b7c92ef2f..ae10a766271e 100644
--- a/test/sanitizer_common/TestCases/Posix/devname_r.cc
+++ b/test/sanitizer_common/TestCases/Posix/devname_r.cc
@@ -4,6 +4,7 @@
#include <sys/cdefs.h>
#include <sys/stat.h>
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
@@ -12,13 +13,15 @@ int main(void) {
char name[100];
mode_t type;
- if (stat("/dev/null", &st))
- exit(1);
+ assert(!stat("/dev/null", &st));
type = S_ISCHR(st.st_mode) ? S_IFCHR : S_IFBLK;
- if (!devname_r(st.st_rdev, type, name, sizeof(name)))
- exit(1);
+#if defined(__NetBSD__)
+ assert(!devname_r(st.st_rdev, type, name, sizeof(name)));
+#else
+ assert(devname_r(st.st_rdev, type, name, sizeof(name)));
+#endif
printf("%s\n", name);
diff --git a/test/sanitizer_common/TestCases/Posix/dump_instruction_bytes.cc b/test/sanitizer_common/TestCases/Posix/dump_instruction_bytes.cc
index 87e797a00ae1..f42802a34582 100644
--- a/test/sanitizer_common/TestCases/Posix/dump_instruction_bytes.cc
+++ b/test/sanitizer_common/TestCases/Posix/dump_instruction_bytes.cc
@@ -1,11 +1,9 @@
// Check that sanitizer prints the faulting instruction bytes on
// dump_instruction_bytes=1
-// clang-format off
// RUN: %clangxx %s -o %t
// RUN: %env_tool_opts=dump_instruction_bytes=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-DUMP
// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NODUMP
-// clang-format on
// REQUIRES: x86-target-arch
diff --git a/test/sanitizer_common/TestCases/Posix/feof_fileno_ferror.cc b/test/sanitizer_common/TestCases/Posix/feof_fileno_ferror.cc
new file mode 100644
index 000000000000..cfcf0e3970a4
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/feof_fileno_ferror.cc
@@ -0,0 +1,41 @@
+// RUN: %clangxx -g %s -o %t && %run %t
+
+#include <assert.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int main(int argc, char **argv) {
+ FILE *fp = fopen(argv[0], "r");
+ assert(fp);
+
+ // file should be good upon opening
+ assert(!feof(fp) && !ferror(fp));
+
+ // read until EOF
+ char buf[BUFSIZ];
+ while (fread(buf, 1, sizeof buf, fp) != 0) {}
+ assert(feof(fp));
+
+ // clear EOF
+ clearerr(fp);
+ assert(!feof(fp) && !ferror(fp));
+
+ // get file descriptor
+ int fd = fileno(fp);
+ assert(fd != -1);
+
+ // break the file by closing underlying descriptor
+ assert(close(fd) != -1);
+
+ // verify that an error is signalled
+ assert(fread(buf, 1, sizeof buf, fp) == 0);
+ assert(ferror(fp));
+
+ // clear error
+ clearerr(fp);
+ assert(!feof(fp) && !ferror(fp));
+
+ // fclose() will return EBADF because of closed fd
+ assert(fclose(fp) == -1);
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/fgetc_ungetc_getc.cc b/test/sanitizer_common/TestCases/Posix/fgetc_ungetc_getc.cc
new file mode 100644
index 000000000000..f895cf194ce7
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/fgetc_ungetc_getc.cc
@@ -0,0 +1,19 @@
+// RUN: %clangxx -g %s -o %t && %run %t
+
+#include <assert.h>
+#include <stdio.h>
+
+int main(int argc, char **argv) {
+ FILE *fp = fopen(argv[0], "r");
+ assert(fp);
+
+ // the file should be at least one character long, always
+ assert(fgetc(fp) != EOF);
+ // POSIX guarantees being able to ungetc() at least one character
+ assert(ungetc('X', fp) != EOF);
+ // check whether ungetc() worked
+ assert(getc(fp) == 'X');
+
+ assert(!fclose(fp));
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/fgetln.cc b/test/sanitizer_common/TestCases/Posix/fgetln.cc
index e98cf449a272..b1b4665389a3 100644
--- a/test/sanitizer_common/TestCases/Posix/fgetln.cc
+++ b/test/sanitizer_common/TestCases/Posix/fgetln.cc
@@ -1,24 +1,20 @@
// RUN: %clangxx -O0 -g %s -o %t && %run %t
// UNSUPPORTED: linux
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
- FILE *fp;
- size_t len;
- char *s;
-
- fp = fopen("/etc/hosts", "r");
- if (!fp)
- exit(1);
+ FILE *fp = fopen("/etc/hosts", "r");
+ assert(fp);
- s = fgetln(fp, &len);
+ size_t len;
+ char *s = fgetln(fp, &len);
printf("%.*s\n", (int)len, s);
- if (fclose(fp) == EOF)
- exit(1);
+ assert(!fclose(fp));
return 0;
}
diff --git a/test/sanitizer_common/TestCases/Posix/fgets.cc b/test/sanitizer_common/TestCases/Posix/fgets.cc
index 8dde5cd1a84f..6a639f8047db 100644
--- a/test/sanitizer_common/TestCases/Posix/fgets.cc
+++ b/test/sanitizer_common/TestCases/Posix/fgets.cc
@@ -1,20 +1,16 @@
// RUN: %clangxx -g %s -o %t && %run %t
+#include <assert.h>
#include <stdio.h>
int main(int argc, char **argv) {
- FILE *fp;
- char buf[2];
- char *s;
-
- fp = fopen(argv[0], "r");
- if (!fp)
- return 1;
+ FILE *fp = fopen(argv[0], "r");
+ assert(fp);
- s = fgets(buf, sizeof(buf), fp);
- if (!s)
- return 2;
+ char buf[2];
+ char *s = fgets(buf, sizeof(buf), fp);
+ assert(s);
- fclose(fp);
+ assert(!fclose(fp));
return 0;
}
diff --git a/test/sanitizer_common/TestCases/Posix/fputc_putc_putchar.cc b/test/sanitizer_common/TestCases/Posix/fputc_putc_putchar.cc
new file mode 100644
index 000000000000..7e786cd9ef37
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/fputc_putc_putchar.cc
@@ -0,0 +1,13 @@
+// RUN: %clangxx -g %s -o %t && %run %t | FileCheck %s
+// CHECK: abc
+
+#include <assert.h>
+#include <stdio.h>
+
+int main(void) {
+ assert(fputc('a', stdout) != EOF);
+ assert(putc('b', stdout) != EOF);
+ assert(putchar('c') != EOF);
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/fputs_puts.cc b/test/sanitizer_common/TestCases/Posix/fputs_puts.cc
index 8e8f7d384e8c..21bb93aa4310 100644
--- a/test/sanitizer_common/TestCases/Posix/fputs_puts.cc
+++ b/test/sanitizer_common/TestCases/Posix/fputs_puts.cc
@@ -1,18 +1,12 @@
// RUN: %clangxx -g %s -o %t && %run %t | FileCheck %s
// CHECK: {{^foobar$}}
+#include <assert.h>
#include <stdio.h>
int main(void) {
- int r;
-
- r = fputs("foo", stdout);
- if (r < 0)
- return 1;
-
- r = puts("bar");
- if (r < 0)
- return 1;
+ assert(fputs("foo", stdout) >= 0);
+ assert(puts("bar") >= 0);
return 0;
}
diff --git a/test/sanitizer_common/TestCases/Posix/fseek.cc b/test/sanitizer_common/TestCases/Posix/fseek.cc
new file mode 100644
index 000000000000..26f3b849f4c4
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/fseek.cc
@@ -0,0 +1,53 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+//
+// UNSUPPORTED: linux, darwin, solaris
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+
+int main(void) {
+ printf("fseek\n");
+
+ FILE *fp = fopen("/etc/fstab", "r");
+ assert(fp);
+
+ int rv = fseek(fp, 10, SEEK_SET);
+ assert(!rv);
+
+ printf("position: %ld\n", ftell(fp));
+
+ rewind(fp);
+
+ printf("position: %ld\n", ftell(fp));
+
+ rv = fseeko(fp, 15, SEEK_SET);
+ assert(!rv);
+
+ printf("position: %" PRIuMAX "\n", (uintmax_t)ftello(fp));
+
+ fpos_t pos;
+ rv = fgetpos(fp, &pos);
+ assert(!rv);
+
+ rewind(fp);
+
+ printf("position: %" PRIuMAX "\n", (uintmax_t)ftello(fp));
+
+ rv = fsetpos(fp, &pos);
+ assert(!rv);
+
+ printf("position: %" PRIuMAX "\n", (uintmax_t)ftello(fp));
+
+ rv = fclose(fp);
+ assert(!rv);
+
+ // CHECK: fseek
+ // CHECK: position: 10
+ // CHECK: position: 0
+ // CHECK: position: 15
+ // CHECK: position: 0
+ // CHECK: position: 15
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/fts.cc b/test/sanitizer_common/TestCases/Posix/fts.cc
new file mode 100644
index 000000000000..79c41f7de674
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/fts.cc
@@ -0,0 +1,42 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+//
+// UNSUPPORTED: linux, darwin, solaris
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main() {
+ char *const paths[] = {(char *)"/etc", 0};
+ FTS *ftsp = fts_open(paths, FTS_LOGICAL, NULL);
+ assert(ftsp);
+
+ FTSENT *chp = fts_children(ftsp, 0);
+ assert(chp);
+
+ size_t n = 0;
+ for (FTSENT *p = fts_read(ftsp); p; p = fts_read(ftsp)) {
+ /* Skip recursively subdirectories */
+ if (p->fts_info == FTS_D && p->fts_level != FTS_ROOTLEVEL) /* pre-order */
+ fts_set(ftsp, p, FTS_SKIP);
+ else if (p->fts_info == FTS_DP) /* post-order */
+ continue;
+ else if (p->fts_info == FTS_F) /* regular file */
+ n++;
+ }
+
+ int rv = fts_close(ftsp);
+ assert(!rv);
+
+ printf("Number of files in /etc: '%zu'\n", n);
+
+ return EXIT_SUCCESS;
+
+ // CHECK: Number of files in /etc: '{{.*}}'
+}
diff --git a/test/sanitizer_common/TestCases/Posix/funopen.cc b/test/sanitizer_common/TestCases/Posix/funopen.cc
new file mode 100644
index 000000000000..7d3192488ec5
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/funopen.cc
@@ -0,0 +1,91 @@
+// RUN: %clangxx -g %s -o %t && %run %t | FileCheck %s
+
+// CHECK: READ CALLED; len={{[0-9]*}}
+// CHECK-NEXT: READ: test
+// CHECK-NEXT: WRITE CALLED: test
+// CHECK-NEXT: READ CALLED; len={{[0-9]*}}
+// CHECK-NEXT: READ: test
+// CHECK-NEXT: WRITE CALLED: test
+// CHECK-NEXT: CLOSE CALLED
+// CHECK-NEXT: SEEK CALLED; off=100, whence=0
+// CHECK-NEXT: READ CALLED; len={{[0-9]*}}
+// CHECK-NEXT: READ: test
+//
+// UNSUPPORTED: linux, darwin, solaris
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int cookie_var;
+
+int f_read(void *cookie, char *buf, int len) {
+ assert(cookie == &cookie_var);
+ assert(len >= 6);
+ printf("READ CALLED; len=%d\n", len);
+ return strlcpy(buf, "test\n", len);
+}
+
+int f_write(void *cookie, const char *buf, int len) {
+ assert(cookie == &cookie_var);
+ char *data = strndup(buf, len);
+ assert(data);
+ printf("WRITE CALLED: %s\n", data);
+ free(data);
+ return len;
+}
+
+off_t f_seek(void *cookie, off_t off, int whence) {
+ assert(cookie == &cookie_var);
+ assert(whence == SEEK_SET);
+ printf("SEEK CALLED; off=%d, whence=%d\n", (int)off, whence);
+ return off;
+}
+
+int f_close(void *cookie) {
+ assert(cookie == &cookie_var);
+ printf("CLOSE CALLED\n");
+ return 0;
+}
+
+int main(void) {
+ FILE *fp;
+ char buf[10];
+
+ // 1. read-only variant
+ fp = fropen(&cookie_var, f_read);
+ assert(fp);
+ // verify that fileno() does not crash or report nonsense
+ assert(fileno(fp) == -1);
+ assert(fgets(buf, sizeof(buf), fp));
+ printf("READ: %s", buf);
+ assert(!fclose(fp));
+
+ // 2. write-only variant
+ fp = fwopen(&cookie_var, f_write);
+ assert(fp);
+ assert(fileno(fp) == -1);
+ assert(fputs("test", fp) >= 0);
+ assert(!fclose(fp));
+
+ // 3. read+write+close
+ fp = funopen(&cookie_var, f_read, f_write, NULL, f_close);
+ assert(fp);
+ assert(fileno(fp) == -1);
+ assert(fgets(buf, sizeof(buf), fp));
+ printf("READ: %s", buf);
+ assert(fputs("test", fp) >= 0);
+ assert(!fclose(fp));
+
+ // 4. read+seek
+ fp = funopen(&cookie_var, f_read, NULL, f_seek, NULL);
+ assert(fp);
+ assert(fileno(fp) == -1);
+ assert(fseek(fp, 100, SEEK_SET) == 0);
+ assert(fgets(buf, sizeof(buf), fp));
+ printf("READ: %s", buf);
+ assert(!fclose(fp));
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/getc_unlocked.cc b/test/sanitizer_common/TestCases/Posix/getc_unlocked.cc
new file mode 100644
index 000000000000..c4257d130428
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/getc_unlocked.cc
@@ -0,0 +1,20 @@
+// RUN: %clangxx -g %s -o %t && %run %t
+
+#include <assert.h>
+#include <stdio.h>
+
+int main(int argc, char **argv) {
+ FILE *fp = fopen(argv[0], "r");
+ assert(fp);
+
+ // the file should be at least one character long, always
+ assert(getc_unlocked(fp) != EOF);
+ // POSIX guarantees being able to ungetc() at least one character
+ // NB: ungetc_unlocked is apparently not present
+ assert(ungetc('X', fp) != EOF);
+ // check whether ungetc() works with getc_unlocked()
+ assert(getc_unlocked(fp) == 'X');
+
+ assert(!fclose(fp));
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/getfsent.cc b/test/sanitizer_common/TestCases/Posix/getfsent.cc
new file mode 100644
index 000000000000..687a7a7d4879
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/getfsent.cc
@@ -0,0 +1,36 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+//
+// UNSUPPORTED: linux, darwin, solaris
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <fstab.h>
+
+int main(void) {
+ printf("getfsent\n");
+
+ setfsent();
+ struct fstab *fentry = getfsent();
+
+ assert(fentry);
+
+ setfsent();
+ struct fstab *pentry = getfsspec(fentry->fs_spec);
+ assert(pentry);
+ setfsent();
+ struct fstab *wentry = getfsfile(fentry->fs_file);
+ assert(wentry);
+ assert(!memcmp(fentry, wentry, sizeof(*wentry)));
+ assert(!memcmp(pentry, wentry, sizeof(*pentry)));
+
+ printf("First entry: device block '%s', mounted with '%s'\n",
+ fentry->fs_spec, fentry->fs_mntops);
+
+ endfsent();
+
+ return 0;
+ // CHECK: getfsent
+ // CHECK: First entry: device block '{{.*}}', mounted with '{{.*}}'
+}
diff --git a/test/sanitizer_common/TestCases/Posix/getmntinfo.cc b/test/sanitizer_common/TestCases/Posix/getmntinfo.cc
new file mode 100644
index 000000000000..26c065d4dd1f
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/getmntinfo.cc
@@ -0,0 +1,35 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+//
+// UNSUPPORTED: linux, solaris
+
+#include <sys/types.h>
+
+#if defined(__NetBSD__)
+#include <sys/statvfs.h>
+#else
+#include <sys/mount.h>
+#endif
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(void) {
+ printf("getmntinfo\n");
+
+#if defined(__NetBSD__)
+ struct statvfs *fss;
+#else
+ struct statfs *fss;
+#endif
+ int nfss = getmntinfo(&fss, MNT_NOWAIT);
+ if (nfss <= 0)
+ errx(1, "getmntinfo");
+
+ for (int i = 0; i < nfss; i++)
+ printf("%d: %s\n", i, fss[i].f_fstypename);
+
+ // CHECK: getmntinfo
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/getusershell.cc b/test/sanitizer_common/TestCases/Posix/getusershell.cc
new file mode 100644
index 000000000000..c00d75f11211
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/getusershell.cc
@@ -0,0 +1,23 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// UNSUPPORTED: android
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int main(void) {
+ printf("getusershell\n");
+
+ setusershell();
+ char *fentry = getusershell();
+
+ printf("First entry: '%s'\n", fentry);
+
+ endusershell();
+
+ return 0;
+ // CHECK: getusershell
+ // CHECK: First entry: '{{.*}}'
+}
diff --git a/test/sanitizer_common/TestCases/Posix/lstat.cc b/test/sanitizer_common/TestCases/Posix/lstat.cc
index 37237d82102c..01c2ea83d9c1 100644
--- a/test/sanitizer_common/TestCases/Posix/lstat.cc
+++ b/test/sanitizer_common/TestCases/Posix/lstat.cc
@@ -1,16 +1,14 @@
// RUN: %clangxx -O0 -g %s -o %t && %run %t
+#include <assert.h>
#include <stdlib.h>
#include <sys/stat.h>
int main(void) {
struct stat st;
- if (lstat("/dev/null", &st))
- exit(1);
-
- if (!S_ISCHR(st.st_mode))
- exit(1);
+ assert(!lstat("/dev/null", &st));
+ assert(S_ISCHR(st.st_mode));
return 0;
}
diff --git a/test/sanitizer_common/TestCases/Posix/nl_langinfo.cc b/test/sanitizer_common/TestCases/Posix/nl_langinfo.cc
new file mode 100644
index 000000000000..b8123542fb8f
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/nl_langinfo.cc
@@ -0,0 +1,20 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+//
+// UNSUPPORTED: linux, darwin, solaris
+
+#include <langinfo.h>
+
+#include <stdio.h>
+
+int main(void) {
+ printf("nl_langinfo\n");
+
+ char *info = nl_langinfo(DAY_1);
+
+ printf("DAY_1='%s'\n", info);
+
+ // CHECK: nl_langinfo
+ // CHECK: DAY_1='{{.*}}'
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/popen.cc b/test/sanitizer_common/TestCases/Posix/popen.cc
new file mode 100644
index 000000000000..6bf6255a697a
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/popen.cc
@@ -0,0 +1,23 @@
+// RUN: %clangxx -g %s -o %t && %run %t | FileCheck %s
+// CHECK: 1
+// CHECK-NEXT: 2
+
+#include <assert.h>
+#include <stdio.h>
+
+int main(void) {
+ // use a tool that produces different output than input to verify
+ // that everything worked correctly
+ FILE *fp = popen("sort", "w");
+ assert(fp);
+
+ // verify that fileno() returns a meaningful descriptor (needed
+ // for the implementation of TSan)
+ assert(fileno(fp) != -1);
+
+ assert(fputs("2\n", fp) >= 0);
+ assert(fputs("1\n", fp) >= 0);
+ assert(pclose(fp) == 0);
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/putc_putchar_unlocked.cc b/test/sanitizer_common/TestCases/Posix/putc_putchar_unlocked.cc
new file mode 100644
index 000000000000..12c35c220d97
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/putc_putchar_unlocked.cc
@@ -0,0 +1,12 @@
+// RUN: %clangxx -g %s -o %t && %run %t | FileCheck %s
+// CHECK: bc
+
+#include <assert.h>
+#include <stdio.h>
+
+int main(void) {
+ assert(putc_unlocked('b', stdout) != EOF);
+ assert(putchar_unlocked('c') != EOF);
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/readlinkat.c b/test/sanitizer_common/TestCases/Posix/readlinkat.c
index 0afb5efe6b5f..227c6da54afb 100644
--- a/test/sanitizer_common/TestCases/Posix/readlinkat.c
+++ b/test/sanitizer_common/TestCases/Posix/readlinkat.c
@@ -1,5 +1,7 @@
// RUN: %clang -O0 %s -o %t && %run %t
+// XFAIL: i386-netbsd && asan
+
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
diff --git a/test/sanitizer_common/TestCases/Posix/regex.cc b/test/sanitizer_common/TestCases/Posix/regex.cc
new file mode 100644
index 000000000000..3727f01325f8
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/regex.cc
@@ -0,0 +1,71 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+//
+// UNSUPPORTED: darwin, solaris
+
+#include <assert.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef __arraycount
+#define __arraycount(a) ((sizeof(a) / sizeof(a[0])))
+#endif
+
+void test_matched(const regex_t *preg, const char *string) {
+ int rv = regexec(preg, string, 0, NULL, 0);
+ if (!rv)
+ printf("%s: matched\n", string);
+ else if (rv == REG_NOMATCH)
+ printf("%s: not-matched\n", string);
+ else
+ abort();
+}
+
+void test_print_matches(const regex_t *preg, const char *string) {
+ regmatch_t rm[10];
+ int rv = regexec(preg, string, __arraycount(rm), rm, 0);
+ if (!rv) {
+ for (size_t i = 0; i < __arraycount(rm); i++) {
+ // This condition shall be simplified, but verify that the data fields
+ // are accessible.
+ if (rm[i].rm_so == -1 && rm[i].rm_eo == -1)
+ continue;
+ printf("matched[%zu]='%.*s'\n", i, (int)(rm[i].rm_eo - rm[i].rm_so),
+ string + rm[i].rm_so);
+ }
+ } else if (rv == REG_NOMATCH)
+ printf("%s: not-matched\n", string);
+ else
+ abort();
+}
+
+int main(void) {
+ printf("regex\n");
+
+ regex_t regex;
+ int rv = regcomp(&regex, "[[:upper:]]\\([[:upper:]]\\)", 0);
+ assert(!rv);
+
+ test_matched(&regex, "abc");
+ test_matched(&regex, "ABC");
+
+ test_print_matches(&regex, "ABC");
+
+ regfree(&regex);
+
+ rv = regcomp(&regex, "[[:upp:]]", 0);
+ assert(rv);
+
+ char errbuf[1024];
+ regerror(rv, &regex, errbuf, sizeof errbuf);
+ printf("error: %s\n", errbuf);
+
+ // CHECK: regex
+ // CHECK: abc: not-matched
+ // CHECK: ABC: matched
+ // CHECK: matched[0]='AB'
+ // CHECK: matched[1]='B'
+ // CHECK: error:{{.*}}
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/setvbuf.cc b/test/sanitizer_common/TestCases/Posix/setvbuf.cc
new file mode 100644
index 000000000000..bc29ba45d89a
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/setvbuf.cc
@@ -0,0 +1,81 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// UNSUPPORTED: solaris
+
+#include <stdio.h>
+
+void print_something() {
+ for (size_t i = 0; i < 10 * BUFSIZ; i++)
+ printf("Hello world %zu\n", i);
+}
+
+void print_one_byte(char *buf) {
+ printf("First byte is %c\n", buf[0]);
+}
+
+void test_setbuf() {
+ char buf[BUFSIZ];
+
+ setbuf(stdout, NULL);
+
+ print_something();
+
+ setbuf(stdout, buf);
+
+ print_something();
+
+ print_one_byte(buf);
+}
+
+void test_setbuffer() {
+ char buf[BUFSIZ];
+
+ setbuffer(stdout, NULL, 0);
+
+ print_something();
+
+ setbuffer(stdout, buf, BUFSIZ);
+
+ print_something();
+
+ print_one_byte(buf);
+}
+
+void test_setlinebuf() {
+ setlinebuf(stdout);
+
+ print_something();
+}
+
+void test_setvbuf() {
+ char buf[BUFSIZ];
+
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ print_something();
+
+ setvbuf(stdout, buf, _IOLBF, BUFSIZ);
+
+ print_something();
+
+ print_one_byte(buf);
+
+ setvbuf(stdout, buf, _IOFBF, BUFSIZ);
+
+ print_something();
+
+ print_one_byte(buf);
+}
+
+int main(void) {
+ printf("setvbuf\n");
+
+ test_setbuf();
+ test_setbuffer();
+ test_setlinebuf();
+ test_setvbuf();
+
+ // CHECK: setvbuf
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/sl_add.cc b/test/sanitizer_common/TestCases/Posix/sl_add.cc
new file mode 100644
index 000000000000..4da70c7888f6
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/sl_add.cc
@@ -0,0 +1,26 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+//
+// UNSUPPORTED: linux, darwin, solaris
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stringlist.h>
+
+int main(void) {
+ printf("sl_add\n");
+
+ StringList *sl = sl_init();
+ assert(sl);
+ char *p = strdup("entry");
+ assert(!sl_add(sl, p));
+ char *entry = sl_find(sl, "entry");
+ assert(!strcmp(entry, p));
+ printf("Found '%s'\n", entry);
+ sl_free(sl, 1);
+
+ return 0;
+ // CHECK: sl_add
+ // CHECK: Found '{{.*}}'
+}
diff --git a/test/sanitizer_common/TestCases/Posix/strtonum.cc b/test/sanitizer_common/TestCases/Posix/strtonum.cc
new file mode 100644
index 000000000000..22346b2e111c
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/strtonum.cc
@@ -0,0 +1,54 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+//
+// UNSUPPORTED: linux, darwin, solaris
+
+#define _OPENBSD_SOURCE
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(void) {
+ const char *errstr;
+
+ printf("strtonum\n");
+
+ long long l = strtonum("100", 1, 100, &errstr);
+ assert(!errstr);
+ printf("%lld\n", l);
+
+ l = strtonum("200", 1, 100, &errstr);
+ assert(errstr);
+ printf("%s\n", errstr);
+
+ l = strtonum("300", 1000, 1001, &errstr);
+ assert(errstr);
+ printf("%s\n", errstr);
+
+ l = strtonum("abc", 1000, 1001, &errstr);
+ assert(errstr);
+ printf("%s\n", errstr);
+
+ l = strtonum("1000", 1001, 1000, &errstr);
+ assert(errstr);
+ printf("%s\n", errstr);
+
+ l = strtonum("1000abc", 1000, 1001, &errstr);
+ assert(errstr);
+ printf("%s\n", errstr);
+
+ l = strtonum("1000.0", 1000, 1001, &errstr);
+ assert(errstr);
+ printf("%s\n", errstr);
+
+ // CHECK: strtonum
+ // CHECK: 100
+ // CHECK: too large
+ // CHECK: too small
+ // CHECK: invalid
+ // CHECK: invalid
+ // CHECK: invalid
+ // CHECK: invalid
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/sysctl.cc b/test/sanitizer_common/TestCases/Posix/sysctl.cc
new file mode 100644
index 000000000000..2cb8764f34b0
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/sysctl.cc
@@ -0,0 +1,64 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+//
+// UNSUPPORTED: linux, solaris
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <sys/sysctl.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef __arraycount
+#define __arraycount(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
+void test_sysctl() {
+ char buf[100];
+ size_t len = sizeof(buf);
+ int mib[] = {CTL_KERN, KERN_OSTYPE};
+ int rv = sysctl(mib, __arraycount(mib), buf, &len, NULL, 0);
+ assert(!rv);
+
+ printf("sysctl: '%s' size: '%zu'\n", buf, len);
+}
+
+void test_sysctlbyname() {
+ char buf[100];
+ size_t len = sizeof(buf);
+ int rv = sysctlbyname("kern.ostype", buf, &len, NULL, 0);
+ assert(!rv);
+
+ printf("sysctlbyname: '%s' size: '%zu'\n", buf, len);
+}
+
+void test_sysctlnametomib() {
+ int mib[CTL_MAXNAME];
+ size_t mib_len = __arraycount(mib);
+ int rv = sysctlnametomib("kern.ostype", &mib[0], &mib_len);
+ assert(!rv);
+
+ char buf[100];
+ size_t len = sizeof(buf);
+ rv = sysctl(mib, mib_len, buf, &len, NULL, 0);
+ assert(!rv);
+
+ printf("sysctlnametomib: '%s' size: '%zu'\n", buf, len);
+}
+
+int main(void) {
+ printf("sysctl\n");
+
+ test_sysctl();
+ test_sysctlbyname();
+ test_sysctlnametomib();
+
+ // CHECK: sysctl
+ // CHECK: sysctl: '{{.*}}' size: '{{.*}}'
+ // CHECK: sysctlbyname: '{{.*}}' size: '{{.*}}'
+ // CHECK: sysctlnametomib: '{{.*}}' size: '{{.*}}'
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/Posix/vis.cc b/test/sanitizer_common/TestCases/Posix/vis.cc
new file mode 100644
index 000000000000..15f1bc949023
--- /dev/null
+++ b/test/sanitizer_common/TestCases/Posix/vis.cc
@@ -0,0 +1,247 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+//
+// UNSUPPORTED: linux, solaris, darwin
+
+#include <ctype.h>
+#include <err.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <vis.h>
+
+void test_vis() {
+ char visout[5];
+ int ch = toascii(0x1);
+ vis(visout, ch, VIS_SAFE | VIS_NOSLASH, 0);
+ printf("vis: %s\n", visout);
+}
+
+void test_nvis() {
+ char visout[5];
+ int ch = toascii(0x2);
+ nvis(visout, sizeof visout, ch, VIS_SAFE | VIS_NOSLASH, 0);
+ printf("nvis: %s\n", visout);
+}
+
+void test_strvis() {
+ char visout[5];
+ strvis(visout, "\3", VIS_SAFE | VIS_NOSLASH);
+ printf("strvis: %s\n", visout);
+}
+
+void test_stravis() {
+ char *visout;
+ stravis(&visout, "\4", VIS_SAFE | VIS_NOSLASH);
+ printf("stravis: %s\n", visout);
+ free(visout);
+}
+
+void test_strnvis() {
+ char visout[5];
+ strnvis(visout, sizeof visout, "\5", VIS_SAFE | VIS_NOSLASH);
+ printf("strnvis: %s\n", visout);
+}
+
+void test_strvisx() {
+ char visout[5];
+ char src[] = "\6";
+ strvisx(visout, src, sizeof src - 1 /* skip final \0 */,
+ VIS_SAFE | VIS_NOSLASH);
+ printf("strvisx: %s\n", visout);
+}
+
+void test_strnvisx() {
+ char visout[5];
+ char src[] = "\1";
+ strnvisx(visout, sizeof visout, src, sizeof src - 1 /* skip final \0 */,
+ VIS_SAFE | VIS_NOSLASH);
+ printf("strnvisx: %s\n", visout);
+}
+
+void test_strenvisx() {
+ char visout[5];
+ char src[] = "\2";
+ strenvisx(visout, sizeof visout, src, sizeof src - 1 /* skip final \0 */,
+ VIS_SAFE | VIS_NOSLASH, NULL);
+ printf("strenvisx: %s\n", visout);
+}
+
+void test_svis() {
+ char visout[5];
+ int ch = toascii(0x3);
+ svis(visout, ch, VIS_SAFE | VIS_NOSLASH, 0, "x");
+ printf("svis: %s\n", visout);
+}
+
+void test_snvis() {
+ char visout[5];
+ int ch = toascii(0x2);
+ snvis(visout, sizeof visout, ch, VIS_SAFE | VIS_NOSLASH, 0, "x");
+ printf("snvis: %s\n", visout);
+}
+
+void test_strsvis() {
+ char visout[5];
+ strsvis(visout, "\4", VIS_SAFE | VIS_NOSLASH, "x");
+ printf("strsvis: %s\n", visout);
+}
+
+void test_strsnvis() {
+ char visout[5];
+ strsnvis(visout, sizeof visout, "\5", VIS_SAFE | VIS_NOSLASH, "x");
+ printf("strsnvis: %s\n", visout);
+}
+
+void test_strsvisx() {
+ char visout[5];
+ char src[] = "\5";
+ strsvisx(visout, src, sizeof src - 1 /* skip final \0 */,
+ VIS_SAFE | VIS_NOSLASH, "x");
+ printf("strsvisx: %s\n", visout);
+}
+
+void test_strsnvisx() {
+ char visout[5];
+ char src[] = "\6";
+ strsnvisx(visout, sizeof visout, src, sizeof src - 1 /* skip final \0 */,
+ VIS_SAFE | VIS_NOSLASH, "x");
+ printf("strsnvisx: %s\n", visout);
+}
+
+void test_strsenvisx() {
+ char visout[5];
+ char src[] = "\1";
+ strsenvisx(visout, sizeof visout, src, sizeof src - 1 /* skip final \0 */,
+ VIS_SAFE | VIS_NOSLASH, "x", NULL);
+ printf("strsenvisx: %s\n", visout);
+}
+
+void test_unvis() {
+ char visout[5];
+ int ch = toascii(0x1);
+ vis(visout, ch, VIS_SAFE, 0);
+
+ int state = 0;
+ char out;
+ char *p = visout;
+ while ((ch = *(p++)) != '\0') {
+ again:
+ switch (unvis(&out, ch, &state, 0)) {
+ case 0:
+ case UNVIS_NOCHAR:
+ break;
+ case UNVIS_VALID:
+ printf("unvis: %" PRIx8 "\n", (unsigned char)out);
+ break;
+ case UNVIS_VALIDPUSH:
+ printf("unvis: %" PRIx8 "\n", (unsigned char)out);
+ goto again;
+ case UNVIS_SYNBAD:
+ errx(1, "Bad character sequence!");
+ }
+ }
+ if (unvis(&out, '\0', &state, UNVIS_END) == UNVIS_VALID)
+ printf("unvis: %" PRIx8 "\n", (unsigned char)out);
+}
+
+void test_strunvis() {
+ char visout[5];
+ int ch = toascii(0x2);
+ vis(visout, ch, VIS_SAFE, 0);
+
+ char p[5];
+ strunvis(p, visout);
+
+ char *pp = p;
+ while ((ch = *(pp++)) != '\0')
+ printf("strunvis: %" PRIx8 "\n", (unsigned char)ch);
+}
+
+void test_strnunvis() {
+ char visout[5];
+ int ch = toascii(0x3);
+ vis(visout, ch, VIS_SAFE, 0);
+
+ char p[5];
+ strnunvis(p, sizeof p, visout);
+
+ char *pp = p;
+ while ((ch = *(pp++)) != '\0')
+ printf("strnunvis: %" PRIx8 "\n", (unsigned char)ch);
+}
+
+void test_strunvisx() {
+ char visout[5];
+ int ch = toascii(0x4);
+ vis(visout, ch, VIS_SAFE, 0);
+
+ char p[5];
+ strunvisx(p, visout, VIS_SAFE);
+
+ char *pp = p;
+ while ((ch = *(pp++)) != '\0')
+ printf("strunvisx: %" PRIx8 "\n", (unsigned char)ch);
+}
+
+void test_strnunvisx() {
+ char visout[5];
+ int ch = toascii(0x5);
+ vis(visout, ch, VIS_SAFE, 0);
+
+ char p[5];
+ strnunvisx(p, sizeof p, visout, VIS_SAFE);
+
+ char *pp = p;
+ while ((ch = *(pp++)) != '\0')
+ printf("strnunvisx: %" PRIx8 "\n", (unsigned char)ch);
+}
+
+int main(void) {
+ printf("vis\n");
+
+ test_vis();
+ test_nvis();
+ test_strvis();
+ test_stravis();
+ test_strnvis();
+ test_strvisx();
+ test_strnvisx();
+ test_strenvisx();
+ test_svis();
+ test_snvis();
+ test_strsvis();
+ test_strsnvis();
+ test_strsvisx();
+ test_strsnvisx();
+ test_strsenvisx();
+ test_unvis();
+ test_strunvis();
+ test_strnunvis();
+ test_strunvisx();
+ test_strnunvisx();
+
+ // CHECK: vis
+ // CHECK: vis: ^A
+ // CHECK: nvis: ^B
+ // CHECK: strvis: ^C
+ // CHECK: stravis: ^D
+ // CHECK: strnvis: ^E
+ // CHECK: strvisx: ^F
+ // CHECK: strnvisx: ^A
+ // CHECK: strenvisx: ^B
+ // CHECK: svis: ^C
+ // CHECK: snvis: ^B
+ // CHECK: strsvis: ^D
+ // CHECK: strsnvis: ^E
+ // CHECK: strsvisx: ^E
+ // CHECK: strsnvisx: ^F
+ // CHECK: strsenvisx: ^A
+ // CHECK: unvis: 1
+ // CHECK: strunvis: 2
+ // CHECK: strnunvis: 3
+ // CHECK: strunvisx: 4
+ // CHECK: strnunvisx: 5
+
+ return 0;
+}
diff --git a/test/sanitizer_common/TestCases/allocator_returns_null.cc b/test/sanitizer_common/TestCases/allocator_returns_null.cc
index 9ecdfef9ffcd..f5d99174910f 100644
--- a/test/sanitizer_common/TestCases/allocator_returns_null.cc
+++ b/test/sanitizer_common/TestCases/allocator_returns_null.cc
@@ -36,7 +36,7 @@
// RUN: | FileCheck %s --check-prefix=CHECK-NULL
// TODO(alekseyshl): win32 is disabled due to failing errno tests, fix it there.
-// UNSUPPORTED: ubsan, win32
+// UNSUPPORTED: ubsan, windows-msvc
#include <assert.h>
#include <errno.h>
diff --git a/test/sanitizer_common/TestCases/corelimit.cc b/test/sanitizer_common/TestCases/corelimit.cc
index eb02afc01a1b..2378a4cfdced 100644
--- a/test/sanitizer_common/TestCases/corelimit.cc
+++ b/test/sanitizer_common/TestCases/corelimit.cc
@@ -10,7 +10,7 @@ int main() {
getrlimit(RLIMIT_CORE, &lim_core);
void *p;
if (sizeof(p) == 8) {
- assert(0 == lim_core.rlim_max);
+ assert(0 == lim_core.rlim_cur);
}
return 0;
}
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 0591d356f136..b313df87ce1d 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/Linux/hard_rss_limit_mb_test.cc b/test/sanitizer_common/TestCases/hard_rss_limit_mb_test.cc
index 3013a3c3fd7d..e01d416cb80f 100644
--- a/test/sanitizer_common/TestCases/Linux/hard_rss_limit_mb_test.cc
+++ b/test/sanitizer_common/TestCases/hard_rss_limit_mb_test.cc
@@ -16,8 +16,7 @@
// XFAIL: msan
// XFAIL: ubsan
-// https://github.com/google/sanitizers/issues/981
-// UNSUPPORTED: android-26
+// UNSUPPORTED: freebsd, solaris, darwin
#include <string.h>
#include <stdio.h>
diff --git a/test/sanitizer_common/TestCases/malloc_hook.cc b/test/sanitizer_common/TestCases/malloc_hook.cc
index 853bb66ac5c4..7579ca2c3f98 100644
--- a/test/sanitizer_common/TestCases/malloc_hook.cc
+++ b/test/sanitizer_common/TestCases/malloc_hook.cc
@@ -1,7 +1,7 @@
// RUN: %clangxx -O2 %s -o %t && %run %t 2>&1 | FileCheck %s
// Malloc/free hooks are not supported on Windows.
-// XFAIL: win32
+// XFAIL: windows-msvc
// XFAIL: ubsan
#include <stdlib.h>
diff --git a/test/sanitizer_common/TestCases/pthread_mutexattr_get.cc b/test/sanitizer_common/TestCases/pthread_mutexattr_get.cc
index 26060f395c11..742ae72c41b4 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 28e237802b21..daa994c81162 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 1adbf653bb76..5e01a803bff0 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/strcasestr.c b/test/sanitizer_common/TestCases/strcasestr.c
index 4de3cac7e253..04f8d744b7ab 100644
--- a/test/sanitizer_common/TestCases/strcasestr.c
+++ b/test/sanitizer_common/TestCases/strcasestr.c
@@ -1,7 +1,7 @@
// RUN: %clang %s -o %t && %run %t 2>&1
// There's no interceptor for strcasestr on Windows
-// XFAIL: win32
+// XFAIL: windows-msvc
#define _GNU_SOURCE
#include <assert.h>
diff --git a/test/sanitizer_common/TestCases/symbolize_pc.cc b/test/sanitizer_common/TestCases/symbolize_pc.cc
index 68a6733f4464..16a81f1bbd43 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 000000000000..9589a3af0765
--- /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 47b847f5329c..e1f633e87463 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", "APPLE_ASAN_INIT_FOR_DLOPEN"]:
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 1b347bf2bbac..75cc7b77e70e 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 000000000000..326e22f27373
--- /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 000000000000..87658d7f3e3a
--- /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/Darwin/ignore-noninstrumented.mm b/test/tsan/Darwin/ignore-noninstrumented.mm
index 668a76a462ce..88d39268f1b8 100644
--- a/test/tsan/Darwin/ignore-noninstrumented.mm
+++ b/test/tsan/Darwin/ignore-noninstrumented.mm
@@ -1,4 +1,7 @@
-// Check that ignore_noninstrumented_modules=1 suppresses races from system libraries on OS X.
+// Check that ignore_noninstrumented_modules=1 suppresses reporting races from
+// system libraries on OS X. There are currently false positives coming from
+// libxpc, libdispatch, CoreFoundation and others, because these libraries use
+// TSan-invisible atomics as synchronization.
// RUN: %clang_tsan %s -o %t -framework Foundation
diff --git a/test/tsan/Darwin/ignored-interceptors.mm b/test/tsan/Darwin/ignored-interceptors.mm
deleted file mode 100644
index b2e40f07d574..000000000000
--- a/test/tsan/Darwin/ignored-interceptors.mm
+++ /dev/null
@@ -1,55 +0,0 @@
-// Check that ignore_interceptors_accesses=1 suppresses reporting races from
-// system libraries on OS X. There are currently false positives coming from
-// libxpc, libdispatch, CoreFoundation and others, because these libraries use
-// TSan-invisible atomics as synchronization.
-
-// RUN: %clang_tsan %s -o %t -framework Foundation
-
-// Check that without the flag, there are false positives.
-// RUN: %env_tsan_opts=ignore_noninstrumented_modules=0 %deflake %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-RACE
-
-// With ignore_interceptors_accesses=1, no races are reported.
-// RUN: %env_tsan_opts=ignore_noninstrumented_modules=0:ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s
-
-// With ignore_interceptors_accesses=1, races in user's code are still reported.
-// RUN: %env_tsan_opts=ignore_noninstrumented_modules=0:ignore_interceptors_accesses=1 %deflake %run %t race 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-RACE
-
-#import <Foundation/Foundation.h>
-
-#import "../test.h"
-
-long global;
-
-void *Thread1(void *x) {
- barrier_wait(&barrier);
- global = 42;
- return NULL;
-}
-
-void *Thread2(void *x) {
- global = 43;
- barrier_wait(&barrier);
- return NULL;
-}
-
-int main(int argc, char *argv[]) {
- fprintf(stderr, "Hello world.\n");
-
- // NSUserDefaults uses XPC which triggers the false positive.
- NSDictionary *d = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
-
- if (argc > 1 && strcmp(argv[1], "race") == 0) {
- barrier_init(&barrier, 2);
- pthread_t t[2];
- pthread_create(&t[0], NULL, Thread1, NULL);
- pthread_create(&t[1], NULL, Thread2, NULL);
- pthread_join(t[0], NULL);
- pthread_join(t[1], NULL);
- }
-
- fprintf(stderr, "Done.\n");
-}
-
-// CHECK: Hello world.
-// CHECK-RACE: SUMMARY: ThreadSanitizer: data race
-// CHECK: Done.
diff --git a/test/tsan/Darwin/objc-synchronize-cycle-tagged.mm b/test/tsan/Darwin/objc-synchronize-cycle-tagged.mm
new file mode 100644
index 000000000000..5806e8af925e
--- /dev/null
+++ b/test/tsan/Darwin/objc-synchronize-cycle-tagged.mm
@@ -0,0 +1,42 @@
+// RUN: %clangxx_tsan %s -o %t -framework Foundation -fobjc-arc %darwin_min_target_with_full_runtime_arc_support
+// RUN: %run %t 6 2>&1 | FileCheck %s --check-prefix=SIX
+// RUN: not %run %t 7 2>&1 | FileCheck %s --check-prefix=SEVEN
+
+#import <Foundation/Foundation.h>
+
+static bool isTaggedPtr(id obj) {
+ uintptr_t ptr = (uintptr_t) obj;
+ return (ptr & 0x8000000000000001ull) != 0;
+}
+
+int main(int argc, char* argv[]) {
+ assert(argc == 2);
+ int arg = atoi(argv[1]);
+
+ @autoreleasepool {
+ NSObject* obj = [NSObject new];
+ NSObject* num1 = @7;
+ NSObject* num2 = [NSNumber numberWithInt:arg];
+
+ assert(!isTaggedPtr(obj));
+ assert(isTaggedPtr(num1) && isTaggedPtr(num2));
+
+ // obj -> num1 (includes num2)
+ @synchronized(obj) {
+ @synchronized(num1) {
+ }
+ }
+
+ // num2 -> obj1
+ @synchronized(num2) {
+ @synchronized(obj) {
+// SEVEN: ThreadSanitizer: lock-order-inversion (potential deadlock)
+ }
+ }
+ }
+
+ NSLog(@"PASS");
+// SIX-NOT: ThreadSanitizer
+// SIX: PASS
+ return 0;
+}
diff --git a/test/tsan/Darwin/objc-synchronize-cycle.mm b/test/tsan/Darwin/objc-synchronize-cycle.mm
new file mode 100644
index 000000000000..fb816311580b
--- /dev/null
+++ b/test/tsan/Darwin/objc-synchronize-cycle.mm
@@ -0,0 +1,31 @@
+// RUN: %clangxx_tsan %s -o %t -framework Foundation -fobjc-arc %darwin_min_target_with_full_runtime_arc_support
+// RUN: not %run %t 2>&1 | FileCheck %s
+// RUN: %env_tsan_opts=detect_deadlocks=1 not %run %t 2>&1 | FileCheck %s
+// RUN: %env_tsan_opts=detect_deadlocks=0 %run %t 2>&1 | FileCheck %s --check-prefix=DISABLED
+
+#import <Foundation/Foundation.h>
+
+int main() {
+ @autoreleasepool {
+ NSObject* obj1 = [NSObject new];
+ NSObject* obj2 = [NSObject new];
+
+ // obj1 -> obj2
+ @synchronized(obj1) {
+ @synchronized(obj2) {
+ }
+ }
+
+ // obj1 -> obj1
+ @synchronized(obj2) {
+ @synchronized(obj1) {
+// CHECK: ThreadSanitizer: lock-order-inversion (potential deadlock)
+ }
+ }
+ }
+
+ NSLog(@"PASS");
+// DISABLED-NOT: ThreadSanitizer
+// DISABLED: PASS
+ return 0;
+}
diff --git a/test/tsan/Darwin/objc-synchronize-nested-recursive.mm b/test/tsan/Darwin/objc-synchronize-nested-recursive.mm
new file mode 100644
index 000000000000..ab6643e5c54e
--- /dev/null
+++ b/test/tsan/Darwin/objc-synchronize-nested-recursive.mm
@@ -0,0 +1,35 @@
+// RUN: %clangxx_tsan %s -o %t -framework Foundation -fobjc-arc %darwin_min_target_with_full_runtime_arc_support
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#import <Foundation/Foundation.h>
+
+int main() {
+ @autoreleasepool {
+ NSObject* obj1 = [NSObject new];
+ NSObject* obj2 = [NSObject new];
+
+ @synchronized(obj1) {
+ @synchronized(obj1) {
+ NSLog(@"nested 1-1");
+// CHECK: nested 1-1
+ }
+ }
+
+ @synchronized(obj1) {
+ @synchronized(obj2) {
+ @synchronized(obj1) {
+ @synchronized(obj2) {
+ NSLog(@"nested 1-2-1-2");
+// CHECK: nested 1-2-1-2
+ }
+ }
+ }
+ }
+
+ }
+
+ NSLog(@"PASS");
+// CHECK-NOT: ThreadSanitizer
+// CHECK: PASS
+ return 0;
+}
diff --git a/test/tsan/Linux/thread_timedjoin.c b/test/tsan/Linux/thread_timedjoin.c
new file mode 100644
index 000000000000..1d3f1098ece7
--- /dev/null
+++ b/test/tsan/Linux/thread_timedjoin.c
@@ -0,0 +1,39 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#define _GNU_SOURCE
+#include "../test.h"
+#include <errno.h>
+
+int var;
+
+void *Thread(void *x) {
+ barrier_wait(&barrier);
+ var = 1;
+ return 0;
+}
+
+static void check(int res, int expect) {
+ if (res != expect) {
+ fprintf(stderr, "Unexpected result of pthread_timedjoin_np: %d\n", res);
+ exit(1);
+ }
+}
+
+int main() {
+ barrier_init(&barrier, 2);
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ check(pthread_timedjoin_np(t, 0, &ts), ETIMEDOUT);
+ barrier_wait(&barrier);
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += 10000;
+ check(pthread_timedjoin_np(t, 0, &ts), 0);
+ var = 2;
+ fprintf(stderr, "PASS\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
+// CHECK: PASS
diff --git a/test/tsan/Linux/thread_tryjoin.c b/test/tsan/Linux/thread_tryjoin.c
new file mode 100644
index 000000000000..675e1595528a
--- /dev/null
+++ b/test/tsan/Linux/thread_tryjoin.c
@@ -0,0 +1,41 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#define _GNU_SOURCE
+#include "../test.h"
+#include <errno.h>
+
+int var;
+
+void *Thread(void *x) {
+ barrier_wait(&barrier);
+ var = 1;
+ return 0;
+}
+
+static void check(int res) {
+ if (res != EBUSY) {
+ fprintf(stderr, "Unexpected result of pthread_tryjoin_np: %d\n", res);
+ exit(1);
+ }
+}
+
+int main() {
+ barrier_init(&barrier, 2);
+ pthread_t t;
+ pthread_create(&t, 0, Thread, 0);
+ check(pthread_tryjoin_np(t, 0));
+ barrier_wait(&barrier);
+ for (;;) {
+ int res = pthread_tryjoin_np(t, 0);
+ if (!res)
+ break;
+ check(res);
+ pthread_yield();
+ }
+ var = 2;
+ fprintf(stderr, "PASS\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer: data race
+// CHECK-NOT: WARNING: ThreadSanitizer: thread leak
+// CHECK: PASS
diff --git a/test/tsan/cxa_guard_acquire.cc b/test/tsan/cxa_guard_acquire.cc
new file mode 100644
index 000000000000..cdbe609003d4
--- /dev/null
+++ b/test/tsan/cxa_guard_acquire.cc
@@ -0,0 +1,25 @@
+// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+
+namespace __tsan {
+
+void OnPotentiallyBlockingRegionBegin() {
+ printf("Enter __cxa_guard_acquire\n");
+}
+
+void OnPotentiallyBlockingRegionEnd() { printf("Exit __cxa_guard_acquire\n"); }
+
+} // namespace __tsan
+
+int main(int argc, char **argv) {
+ // CHECK: Enter main
+ printf("Enter main\n");
+ // CHECK-NEXT: Enter __cxa_guard_acquire
+ // CHECK-NEXT: Exit __cxa_guard_acquire
+ static int s = argc;
+ (void)s;
+ // CHECK-NEXT: Exit main
+ printf("Exit main\n");
+ return 0;
+}
diff --git a/test/tsan/deadlock_detector_stress_test.cc b/test/tsan/deadlock_detector_stress_test.cc
index bbaaabbb3c14..f3d2fc71249d 100644
--- a/test/tsan/deadlock_detector_stress_test.cc
+++ b/test/tsan/deadlock_detector_stress_test.cc
@@ -1,12 +1,12 @@
// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadMutex
-// RUN: %env_tsan_opts=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOT-SECOND
-// RUN: %env_tsan_opts=detect_deadlocks=1:second_deadlock_stack=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SECOND
+// RUN: %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOT-SECOND
+// RUN: %env_tsan_opts=second_deadlock_stack=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SECOND
// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadSpinLock
-// RUN: %env_tsan_opts=detect_deadlocks=1 %deflake %run %t | FileCheck %s
+// RUN: %deflake %run %t | FileCheck %s
// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadRWLock
-// RUN: %env_tsan_opts=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-RD
+// RUN: %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-RD
// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadRecursiveMutex
-// RUN: %env_tsan_opts=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-REC
+// RUN: %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-REC
#include "test.h"
#undef NDEBUG
#include <assert.h>
diff --git a/test/tsan/dtls.c b/test/tsan/dtls.c
index 51697565f1f1..b00ca7691354 100644
--- a/test/tsan/dtls.c
+++ b/test/tsan/dtls.c
@@ -1,6 +1,7 @@
// RUN: %clang_tsan %s -o %t
// RUN: %clang_tsan %s -DBUILD_SO -fPIC -o %t-so.so -shared
// RUN: %run %t 2>&1 | FileCheck %s
+// XFAIL: netbsd
// Test that tsan cleans up dynamic TLS memory between reuse.
diff --git a/test/tsan/getline_nohang.cc b/test/tsan/getline_nohang.cc
index d103839b8bd0..027ba7bac26a 100644
--- a/test/tsan/getline_nohang.cc
+++ b/test/tsan/getline_nohang.cc
@@ -1,5 +1,8 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %run %t
+// Data race randomly triggered.
+// UNSUPPORTED: netbsd
+
// Make sure TSan doesn't deadlock on a file stream lock at program shutdown.
// See https://github.com/google/sanitizers/issues/454
#ifdef __FreeBSD__
diff --git a/test/tsan/ignore_lib5.cc b/test/tsan/ignore_lib5.cc
index d6c3f870e49b..43780daf7b68 100644
--- a/test/tsan/ignore_lib5.cc
+++ b/test/tsan/ignore_lib5.cc
@@ -18,6 +18,9 @@
// matched against 2 libraries: '/libignore_lib1.so' and '/libignore_lib1.so'
// This was caused by non-atomicity of reading of /proc/self/maps.
+// ReadProcMaps() on NetBSD does not handle >=1MB of memory layout information
+// UNSUPPORTED: netbsd
+
#ifndef LIB
#include <dlfcn.h>
@@ -78,4 +81,3 @@ int main(int argc, char **argv) {
// CHECK-WITHSUPP-NOT: WARNING: ThreadSanitizer: data race
// CHECK-WITHSUPP: OK
-
diff --git a/test/tsan/ignored-interceptors-mmap.cc b/test/tsan/ignored-interceptors-mmap.cc
index 796ea9323345..bb43250a659d 100644
--- a/test/tsan/ignored-interceptors-mmap.cc
+++ b/test/tsan/ignored-interceptors-mmap.cc
@@ -1,10 +1,12 @@
// RUN: %clangxx_tsan -O0 %s -o %t
-// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NORMAL
-// RUN: %env_tsan_opts=ignore_interceptors_accesses=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-IGNORE
-// XFAIL: freebsd
+// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-RACE
+// RUN: %run %t ignore 2>&1 | FileCheck %s --check-prefix=CHECK-IGNORE
+// XFAIL: freebsd,netbsd
-#include <errno.h>
#include <sys/mman.h>
+#include <string.h>
+#include <assert.h>
+#include <atomic>
#include "test.h"
@@ -15,48 +17,45 @@ void AnnotateIgnoreWritesBegin(const char *f, int l);
void AnnotateIgnoreWritesEnd(const char *f, int l);
}
-void *global_p;
+// Use atomic to ensure we do not have a race for the pointer value itself. We
+// only want to check races in the mmap'd memory to isolate the test that mmap
+// respects ignore annotations.
+std::atomic<int*> global_p;
-int mmap_and_ignore_reads_and_writes() {
+void mmap_ignored(bool ignore) {
const size_t kSize = sysconf(_SC_PAGESIZE);
- void *p = mmap(0, kSize, PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MAP_ANON, -1, 0);
- if (p == MAP_FAILED)
- return printf("mmap failed with %d\n", errno);
- munmap(p, kSize);
-
- void *new_p = mmap(p, kSize, PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MAP_ANON, -1, 0);
- if (p == MAP_FAILED || p != new_p)
- return printf("second mmap failed with %d\n", errno);
-
- AnnotateIgnoreWritesBegin(__FILE__, __LINE__);
- global_p = p;
- AnnotateIgnoreWritesEnd(__FILE__, __LINE__);
+
+ if (ignore) AnnotateIgnoreWritesBegin(__FILE__, __LINE__);
+ void *p = mmap(0, kSize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (ignore) AnnotateIgnoreWritesEnd(__FILE__, __LINE__);
+
+ // Use relaxed to retain the race between the mmap call and the memory write
+ global_p.store((int *)p, std::memory_order_relaxed);
barrier_wait(&barrier);
- return 0;
}
-void *Thread(void *a) {
+void *WriteToMemory(void *unused) {
barrier_wait(&barrier);
-
- ((int*)global_p)[1] = 10;
- printf("Read the zero value from mmapped memory %d\n", ((int*)global_p)[1]);
+ global_p[0] = 7;
return 0;
}
-int main() {
+// Create race between allocating (mmap) and writing memory
+int main(int argc, const char *argv[]) {
+ bool ignore = (argc > 1) && (strcmp(argv[1], "ignore") == 0);
+
barrier_init(&barrier, 2);
pthread_t t;
- pthread_create(&t, 0, Thread, 0);
- if (mmap_and_ignore_reads_and_writes())
- return 1;
+ pthread_create(&t, 0, WriteToMemory, 0);
+ mmap_ignored(ignore);
pthread_join(t, 0);
+
+ assert(global_p[0] == 7);
printf("OK\n");
return 0;
}
-// CHECK-NORMAL: WARNING: ThreadSanitizer: data race
-// CHECK-NORMAL: OK
-// CHECK-IGNORE_NOT: WARNING: ThreadSanitizer: data race
+// CHECK-RACE: WARNING: ThreadSanitizer: data race
+// CHECK-RACE: OK
+// CHECK-IGNORE-NOT: WARNING: ThreadSanitizer: data race
// CHECK-IGNORE: OK
diff --git a/test/tsan/large_malloc_meta.cc b/test/tsan/large_malloc_meta.cc
index e83004824a3a..0d0eec34b38d 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 764e954f2b8e..c8d258e804d7 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/tsan/mutex_cycle2.c b/test/tsan/mutex_cycle2.c
index 32659d4eec0d..0dc96d07cb96 100644
--- a/test/tsan/mutex_cycle2.c
+++ b/test/tsan/mutex_cycle2.c
@@ -1,5 +1,5 @@
// RUN: %clangxx_tsan %s -o %t
-// RUN: not %run %t 2>&1 | FileCheck %s
+// RUN: not %run %t 2>&1 | FileCheck %s
// RUN: %env_tsan_opts=detect_deadlocks=1 not %run %t 2>&1 | FileCheck %s
// RUN: %env_tsan_opts=detect_deadlocks=0 %run %t 2>&1 | FileCheck %s --check-prefix=DISABLED
// RUN: echo "deadlock:main" > %t.supp
diff --git a/test/tsan/mutex_lock_destroyed.cc b/test/tsan/mutex_lock_destroyed.cc
index 52d6be6210a6..581d8cce296e 100644
--- a/test/tsan/mutex_lock_destroyed.cc
+++ b/test/tsan/mutex_lock_destroyed.cc
@@ -2,6 +2,9 @@
// RUN: %deflake %run %t | FileCheck %s
// RUN: %deflake %run %t 1 | FileCheck %s
+// The pthread_mutex_lock interceptor assumes incompatible internals w/ NetBSD
+// XFAIL: netbsd
+
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/test/tsan/strerror_r.cc b/test/tsan/strerror_r.cc
index cfe8a18c1736..438f54914d6c 100644
--- a/test/tsan/strerror_r.cc
+++ b/test/tsan/strerror_r.cc
@@ -1,8 +1,7 @@
// RUN: %clangxx_tsan -O1 -DTEST_ERROR=ERANGE %s -o %t && %run %t 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-SYS %s
// RUN: %clangxx_tsan -O1 -DTEST_ERROR=-1 %s -o %t && not %run %t 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-USER %s
-// UNSUPPORTED: darwin
-// This test provokes a data race under FreeBSD
-// XFAIL: freebsd
+// This test is for GNU specific version of strerror_r()
+// UNSUPPORTED: darwin, netbsd, freebsd
#include "test.h"
@@ -14,8 +13,7 @@ char buffer[1000];
void *Thread(void *p) {
barrier_wait(&barrier);
- strerror_r(TEST_ERROR, buffer, sizeof(buffer));
- return buffer;
+ return strerror_r(TEST_ERROR, buffer, sizeof(buffer));
}
int main() {
diff --git a/test/tsan/sunrpc.cc b/test/tsan/sunrpc.cc
index 5cfb5344ec10..8e32d6d30436 100644
--- a/test/tsan/sunrpc.cc
+++ b/test/tsan/sunrpc.cc
@@ -1,3 +1,5 @@
+// REQUIRES: sunrpc
+
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
#include <pthread.h>
diff --git a/test/ubsan/CMakeLists.txt b/test/ubsan/CMakeLists.txt
index fa8b16b8093e..351bf3b829da 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-arithmetic-value-change.c b/test/ubsan/TestCases/ImplicitConversion/integer-arithmetic-value-change.c
new file mode 100644
index 000000000000..ef06fbf8398d
--- /dev/null
+++ b/test/ubsan/TestCases/ImplicitConversion/integer-arithmetic-value-change.c
@@ -0,0 +1,345 @@
+// RUN: %clang -x c -fsanitize=implicit-integer-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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-arithmetic-value-change %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.
+
+uint32_t convert_unsigned_int_to_unsigned_int(uint32_t x) {
+#line 100
+ return x;
+}
+
+uint8_t convert_unsigned_char_to_unsigned_char(uint8_t x) {
+#line 200
+ return x;
+}
+
+int32_t convert_signed_int_to_signed_int(int32_t x) {
+#line 300
+ return x;
+}
+
+int8_t convert_signed_char_to_signed_char(int8_t x) {
+#line 400
+ return x;
+}
+
+uint8_t convert_unsigned_int_to_unsigned_char(uint32_t x) {
+#line 500
+ return x;
+}
+
+uint32_t convert_unsigned_char_to_unsigned_int(uint8_t x) {
+#line 600
+ return x;
+}
+
+int32_t convert_unsigned_char_to_signed_int(uint8_t x) {
+#line 700
+ return x;
+}
+
+int32_t convert_signed_char_to_signed_int(int8_t x) {
+#line 800
+ return x;
+}
+
+int32_t convert_unsigned_int_to_signed_int(uint32_t x) {
+#line 900
+ return x;
+}
+
+uint32_t convert_signed_int_to_unsigned_int(int32_t x) {
+#line 1000
+ return x;
+}
+
+uint8_t convert_signed_int_to_unsigned_char(int32_t x) {
+#line 1100
+ return x;
+}
+
+uint8_t convert_signed_char_to_unsigned_char(int8_t x) {
+#line 1200
+ return x;
+}
+
+int8_t convert_unsigned_char_to_signed_char(uint8_t x) {
+#line 1300
+ return x;
+}
+
+uint32_t convert_signed_char_to_unsigned_int(int8_t x) {
+#line 1400
+ return x;
+}
+
+int8_t convert_unsigned_int_to_signed_char(uint32_t x) {
+#line 1500
+ return x;
+}
+
+int8_t convert_signed_int_to_signed_char(int32_t x) {
+#line 1600
+ return x;
+}
+
+#line 10111 // !!!
+
+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);
+ 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);
+// CHECK-V0: {{.*}}integer-arithmetic-value-change.c:900:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -1 (32-bit, signed)
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)UINT32_MAX);
+// CHECK-V0: {{.*}}integer-arithmetic-value-change.c:1000:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -1 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)
+ convert_signed_int_to_unsigned_char((int32_t)(uint32_t)UINT32_MAX);
+// CHECK-V0: {{.*}}integer-arithmetic-value-change.c:1100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -1 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)UINT8_MAX);
+// CHECK-V0: {{.*}}integer-arithmetic-value-change.c:1200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -1 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
+ convert_unsigned_char_to_signed_char((uint8_t)UINT8_MAX);
+// CHECK-V0: {{.*}}integer-arithmetic-value-change.c:1300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 255 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+ convert_signed_char_to_unsigned_int((int8_t)UINT8_MAX);
+// CHECK-V0: {{.*}}integer-arithmetic-value-change.c:1400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -1 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)
+ convert_unsigned_int_to_signed_char((uint32_t)UINT32_MAX);
+// CHECK-V0: {{.*}}integer-arithmetic-value-change.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+ 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);
+ 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);
+// CHECK-V1: {{.*}}integer-arithmetic-value-change.c:900:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -2147483648 (32-bit, signed)
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-arithmetic-value-change.c:1000:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 2147483648 (32-bit, unsigned)
+ convert_signed_int_to_unsigned_char((int32_t)(uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-arithmetic-value-change.c:1100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 0 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)INT8_MIN);
+// CHECK-V1: {{.*}}integer-arithmetic-value-change.c:1200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ convert_unsigned_char_to_signed_char((uint8_t)INT8_MIN);
+// CHECK-V1: {{.*}}integer-arithmetic-value-change.c:1300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 128 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ convert_signed_char_to_unsigned_int((int8_t)INT8_MIN);
+// CHECK-V1: {{.*}}integer-arithmetic-value-change.c:1400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967168 (32-bit, unsigned)
+ convert_unsigned_int_to_signed_char((uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-arithmetic-value-change.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka '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-arithmetic-value-change.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka '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);
+ 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-arithmetic-value-change.c:1100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 2147483647 (32-bit, signed) to type '{{.*}}' (aka '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-arithmetic-value-change.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483647 (32-bit, unsigned) to type '{{.*}}' (aka '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-arithmetic-value-change.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 2147483647 (32-bit, signed) to type '{{.*}}' (aka '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);
+// CHECK-V3: {{.*}}integer-arithmetic-value-change.c:1200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -1 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
+ convert_unsigned_char_to_signed_char((uint8_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-arithmetic-value-change.c:1300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 255 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+ convert_signed_char_to_unsigned_int((int8_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-arithmetic-value-change.c:1400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -1 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)
+ convert_unsigned_int_to_signed_char((uint32_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-arithmetic-value-change.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 255 (32-bit, unsigned) to type '{{.*}}' (aka '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-arithmetic-value-change.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 255 (32-bit, signed) to type '{{.*}}' (aka '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);
+// CHECK-V4: {{.*}}integer-arithmetic-value-change.c:1200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ convert_unsigned_char_to_signed_char((uint8_t)(uint8_t)INT8_MIN);
+// CHECK-V4: {{.*}}integer-arithmetic-value-change.c:1300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 128 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ convert_signed_char_to_unsigned_int((int8_t)(uint8_t)INT8_MIN);
+// CHECK-V4: {{.*}}integer-arithmetic-value-change.c:1400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967168 (32-bit, unsigned)
+ convert_unsigned_int_to_signed_char((uint32_t)(uint8_t)INT8_MIN);
+// CHECK-V4: {{.*}}integer-arithmetic-value-change.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 128 (32-bit, unsigned) to type '{{.*}}' (aka '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-arithmetic-value-change.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 128 (32-bit, signed) to type '{{.*}}' (aka '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)));
+ 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)));
+// CHECK-V5: {{.*}}integer-arithmetic-value-change.c:900:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967167 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -129 (32-bit, signed)
+ convert_signed_int_to_unsigned_int((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V5: {{.*}}integer-arithmetic-value-change.c:1000:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -129 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967167 (32-bit, unsigned)
+ convert_signed_int_to_unsigned_char((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V5: {{.*}}integer-arithmetic-value-change.c:1100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -129 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 127 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)(uint8_t)INT8_MIN);
+// CHECK-V5: {{.*}}integer-arithmetic-value-change.c:1200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ convert_unsigned_char_to_signed_char((uint8_t)(uint8_t)INT8_MIN);
+// CHECK-V5: {{.*}}integer-arithmetic-value-change.c:1300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 128 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ convert_signed_char_to_unsigned_int((int8_t)(uint8_t)INT8_MIN);
+// CHECK-V5: {{.*}}integer-arithmetic-value-change.c:1400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967168 (32-bit, unsigned)
+ convert_unsigned_int_to_signed_char((~((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V5: {{.*}}integer-arithmetic-value-change.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967167 (32-bit, unsigned) to type '{{.*}}' (aka '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-arithmetic-value-change.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -129 (32-bit, signed) to type '{{.*}}' (aka '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));
+ 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);
+// CHECK-V6: {{.*}}integer-arithmetic-value-change.c:900:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -2147483648 (32-bit, signed)
+ convert_signed_int_to_unsigned_int((int32_t)INT32_MIN);
+// CHECK-V6: {{.*}}integer-arithmetic-value-change.c:1000:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 2147483648 (32-bit, unsigned)
+ convert_signed_int_to_unsigned_char((int32_t)(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V6: {{.*}}integer-arithmetic-value-change.c:1100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483520 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)INT8_MIN);
+// CHECK-V6: {{.*}}integer-arithmetic-value-change.c:1200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ convert_unsigned_char_to_signed_char((uint8_t)INT8_MIN);
+// CHECK-V6: {{.*}}integer-arithmetic-value-change.c:1300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 128 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ convert_signed_char_to_unsigned_int((int8_t)INT8_MIN);
+// CHECK-V6: {{.*}}integer-arithmetic-value-change.c:1400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967168 (32-bit, unsigned)
+ convert_unsigned_int_to_signed_char((((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V6: {{.*}}integer-arithmetic-value-change.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483776 (32-bit, unsigned) to type '{{.*}}' (aka '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-arithmetic-value-change.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483520 (32-bit, signed) to type '{{.*}}' (aka '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)));
+ 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)INT32_MAX);
+ convert_signed_int_to_unsigned_char((int32_t)(~(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN))));
+// CHECK-V7: {{.*}}integer-arithmetic-value-change.c:1100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 2147483519 (32-bit, signed) to type '{{.*}}' (aka '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-arithmetic-value-change.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483519 (32-bit, unsigned) to type '{{.*}}' (aka '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-arithmetic-value-change.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 2147483519 (32-bit, signed) to type '{{.*}}' (aka '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-conversion.c b/test/ubsan/TestCases/ImplicitConversion/integer-conversion.c
new file mode 100644
index 000000000000..5c1530a63786
--- /dev/null
+++ b/test/ubsan/TestCases/ImplicitConversion/integer-conversion.c
@@ -0,0 +1,351 @@
+// 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.
+
+uint32_t convert_unsigned_int_to_unsigned_int(uint32_t x) {
+#line 100
+ return x;
+}
+
+uint8_t convert_unsigned_char_to_unsigned_char(uint8_t x) {
+#line 200
+ return x;
+}
+
+int32_t convert_signed_int_to_signed_int(int32_t x) {
+#line 300
+ return x;
+}
+
+int8_t convert_signed_char_to_signed_char(int8_t x) {
+#line 400
+ return x;
+}
+
+uint8_t convert_unsigned_int_to_unsigned_char(uint32_t x) {
+#line 500
+ return x;
+}
+
+uint32_t convert_unsigned_char_to_unsigned_int(uint8_t x) {
+#line 600
+ return x;
+}
+
+int32_t convert_unsigned_char_to_signed_int(uint8_t x) {
+#line 700
+ return x;
+}
+
+int32_t convert_signed_char_to_signed_int(int8_t x) {
+#line 800
+ return x;
+}
+
+int32_t convert_unsigned_int_to_signed_int(uint32_t x) {
+#line 900
+ return x;
+}
+
+uint32_t convert_signed_int_to_unsigned_int(int32_t x) {
+#line 1000
+ return x;
+}
+
+uint8_t convert_signed_int_to_unsigned_char(int32_t x) {
+#line 1100
+ return x;
+}
+
+uint8_t convert_signed_char_to_unsigned_char(int8_t x) {
+#line 1200
+ return x;
+}
+
+int8_t convert_unsigned_char_to_signed_char(uint8_t x) {
+#line 1300
+ return x;
+}
+
+uint32_t convert_signed_char_to_unsigned_int(int8_t x) {
+#line 1400
+ return x;
+}
+
+int8_t convert_unsigned_int_to_signed_char(uint32_t x) {
+#line 1500
+ return x;
+}
+
+int8_t convert_signed_int_to_signed_char(int32_t x) {
+#line 1600
+ return x;
+}
+
+#line 10111 // !!!
+
+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 '{{.*}}' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type '{{.*}}' (aka '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);
+// CHECK-V0: {{.*}}integer-conversion.c:900:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -1 (32-bit, signed)
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)UINT32_MAX);
+// CHECK-V0: {{.*}}integer-conversion.c:1000:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -1 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)
+ 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 '{{.*}}' (aka 'int') of value -1 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)UINT8_MAX);
+// CHECK-V0: {{.*}}integer-conversion.c:1200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -1 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
+ convert_unsigned_char_to_signed_char((uint8_t)UINT8_MAX);
+// CHECK-V0: {{.*}}integer-conversion.c:1300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 255 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+ convert_signed_char_to_unsigned_int((int8_t)UINT8_MAX);
+// CHECK-V0: {{.*}}integer-conversion.c:1400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -1 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)
+ convert_unsigned_int_to_signed_char((uint32_t)UINT32_MAX);
+// CHECK-V0: {{.*}}integer-conversion.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+ 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 '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka '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);
+// CHECK-V1: {{.*}}integer-conversion.c:900:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -2147483648 (32-bit, signed)
+ convert_signed_int_to_unsigned_int((int32_t)(uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-conversion.c:1000:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 2147483648 (32-bit, unsigned)
+ 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 '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 0 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)INT8_MIN);
+// CHECK-V1: {{.*}}integer-conversion.c:1200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ convert_unsigned_char_to_signed_char((uint8_t)INT8_MIN);
+// CHECK-V1: {{.*}}integer-conversion.c:1300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 128 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ convert_signed_char_to_unsigned_int((int8_t)INT8_MIN);
+// CHECK-V1: {{.*}}integer-conversion.c:1400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967168 (32-bit, unsigned)
+ convert_unsigned_int_to_signed_char((uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-conversion.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 2147483647 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value 2147483647 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 2147483647 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value 2147483647 (32-bit, signed) to type '{{.*}}' (aka '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);
+// CHECK-V3: {{.*}}integer-conversion.c:1200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -1 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
+ convert_unsigned_char_to_signed_char((uint8_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-conversion.c:1300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 255 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+ convert_signed_char_to_unsigned_int((int8_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-conversion.c:1400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -1 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)
+ convert_unsigned_int_to_signed_char((uint32_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-conversion.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 255 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value 255 (32-bit, signed) to type '{{.*}}' (aka '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);
+// CHECK-V4: {{.*}}integer-conversion.c:1200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ convert_unsigned_char_to_signed_char((uint8_t)(uint8_t)INT8_MIN);
+// CHECK-V4: {{.*}}integer-conversion.c:1300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 128 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ convert_signed_char_to_unsigned_int((int8_t)(uint8_t)INT8_MIN);
+// CHECK-V4: {{.*}}integer-conversion.c:1400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967168 (32-bit, unsigned)
+ 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 '{{.*}}' (aka 'unsigned int') of value 128 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value 128 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 4294967167 (32-bit, unsigned) to type '{{.*}}' (aka '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)));
+// CHECK-V5: {{.*}}integer-conversion.c:900:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967167 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -129 (32-bit, signed)
+ convert_signed_int_to_unsigned_int((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V5: {{.*}}integer-conversion.c:1000:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -129 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967167 (32-bit, unsigned)
+ 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 '{{.*}}' (aka 'int') of value -129 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 127 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)(uint8_t)INT8_MIN);
+// CHECK-V5: {{.*}}integer-conversion.c:1200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ convert_unsigned_char_to_signed_char((uint8_t)(uint8_t)INT8_MIN);
+// CHECK-V5: {{.*}}integer-conversion.c:1300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 128 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ convert_signed_char_to_unsigned_int((int8_t)(uint8_t)INT8_MIN);
+// CHECK-V5: {{.*}}integer-conversion.c:1400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967168 (32-bit, unsigned)
+ 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 '{{.*}}' (aka 'unsigned int') of value 4294967167 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value -129 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 2147483776 (32-bit, unsigned) to type '{{.*}}' (aka '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);
+// CHECK-V6: {{.*}}integer-conversion.c:900:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -2147483648 (32-bit, signed)
+ convert_signed_int_to_unsigned_int((int32_t)INT32_MIN);
+// CHECK-V6: {{.*}}integer-conversion.c:1000:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 2147483648 (32-bit, unsigned)
+ 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 '{{.*}}' (aka 'int') of value -2147483520 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ convert_signed_char_to_unsigned_char((int8_t)INT8_MIN);
+// CHECK-V6: {{.*}}integer-conversion.c:1200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ convert_unsigned_char_to_signed_char((uint8_t)INT8_MIN);
+// CHECK-V6: {{.*}}integer-conversion.c:1300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 128 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ convert_signed_char_to_unsigned_int((int8_t)INT8_MIN);
+// CHECK-V6: {{.*}}integer-conversion.c:1400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967168 (32-bit, unsigned)
+ 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 '{{.*}}' (aka 'unsigned int') of value 2147483776 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value -2147483520 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 2147483519 (32-bit, unsigned) to type '{{.*}}' (aka '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((int32_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 '{{.*}}' (aka 'int') of value 2147483519 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 2147483519 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value 2147483519 (32-bit, signed) to type '{{.*}}' (aka '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-sign-change-blacklist.c b/test/ubsan/TestCases/ImplicitConversion/integer-sign-change-blacklist.c
new file mode 100644
index 000000000000..a3a745e5fbf6
--- /dev/null
+++ b/test/ubsan/TestCases/ImplicitConversion/integer-sign-change-blacklist.c
@@ -0,0 +1,28 @@
+// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316
+// I'm not sure this is actually *that* issue, but this seems oddly similar to the other XFAIL'ed cases.
+// XFAIL: android
+// UNSUPPORTED: ios
+
+// RUN: %clang -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=implicit-integer-sign-change -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=implicit-integer-sign-change -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=implicit-integer-sign-change -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=implicit-integer-sign-change -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+
+// RUN: rm -f %tmp
+// RUN: echo "[implicit-integer-sign-change]" >> %tmp
+// RUN: echo "fun:implicitSignChange" >> %tmp
+// RUN: %clang -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=implicit-integer-sign-change -fsanitize-blacklist=%tmp -O0 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+// RUN: %clang -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=implicit-integer-sign-change -fsanitize-blacklist=%tmp -O1 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+// RUN: %clang -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=implicit-integer-sign-change -fsanitize-blacklist=%tmp -O2 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+// RUN: %clang -fsanitize=implicit-integer-sign-change -fno-sanitize-recover=implicit-integer-sign-change -fsanitize-blacklist=%tmp -O3 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+
+#include <stdint.h>
+
+int32_t implicitSignChange(uint32_t argc) {
+ return argc; // BOOM
+// CHECK: {{.*}}integer-sign-change-blacklist.c:[[@LINE-1]]:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -1 (32-bit, signed)
+}
+
+int main(int argc, char **argv) {
+ return implicitSignChange(~0U);
+}
diff --git a/test/ubsan/TestCases/ImplicitConversion/integer-truncation-summary.cpp b/test/ubsan/TestCases/ImplicitConversion/integer-sign-change-summary.cpp
index a92e01fb4ab6..7cdc4c5a1b97 100644
--- a/test/ubsan/TestCases/ImplicitConversion/integer-truncation-summary.cpp
+++ b/test/ubsan/TestCases/ImplicitConversion/integer-sign-change-summary.cpp
@@ -1,4 +1,4 @@
-// RUN: %clangxx -fsanitize=implicit-integer-truncation %s -o %t
+// RUN: %clangxx -fsanitize=implicit-integer-sign-change %s -o %t
// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOTYPE
// RUN: %env_ubsan_opts=report_error_type=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TYPE
// REQUIRES: !ubsan-standalone && !ubsan-standalone-static
@@ -6,8 +6,8 @@
#include <stdint.h>
int main() {
- uint8_t t0 = (~(uint32_t(0)));
+ int32_t t0 = (~(uint32_t(0)));
// CHECK-NOTYPE: SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]:16
- // CHECK-TYPE: SUMMARY: UndefinedBehaviorSanitizer: implicit-integer-truncation {{.*}}summary.cpp:[[@LINE-2]]:16
+ // CHECK-TYPE: SUMMARY: UndefinedBehaviorSanitizer: implicit-integer-sign-change {{.*}}summary.cpp:[[@LINE-2]]:16
return 0;
}
diff --git a/test/ubsan/TestCases/ImplicitConversion/integer-sign-change.c b/test/ubsan/TestCases/ImplicitConversion/integer-sign-change.c
new file mode 100644
index 000000000000..192c69ade1d5
--- /dev/null
+++ b/test/ubsan/TestCases/ImplicitConversion/integer-sign-change.c
@@ -0,0 +1,297 @@
+// RUN: %clang -x c -fsanitize=implicit-integer-sign-change %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-sign-change %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-sign-change %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-sign-change %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-sign-change %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-sign-change %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-sign-change %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-sign-change %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-sign-change %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-sign-change %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-sign-change %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-sign-change %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-sign-change %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-sign-change %s -DV6 -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V6
+
+#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.
+
+//============================================================================//
+// Half of the cases do not need the check. //
+//============================================================================//
+
+uint32_t negative0_convert_unsigned_int_to_unsigned_int(uint32_t x) {
+ return x;
+}
+
+uint8_t negative1_convert_unsigned_char_to_unsigned_char(uint8_t x) {
+ return x;
+}
+
+int32_t negative2_convert_signed_int_to_signed_int(int32_t x) {
+ return x;
+}
+
+int8_t negative3_convert_signed_char_to_signed_char(int8_t x) {
+ return x;
+}
+
+uint8_t negative4_convert_unsigned_int_to_unsigned_char(uint32_t x) {
+ return x;
+}
+
+uint32_t negative5_convert_unsigned_char_to_unsigned_int(uint8_t x) {
+ return x;
+}
+
+int32_t negative6_convert_unsigned_char_to_signed_int(uint8_t x) {
+ return x;
+}
+
+int32_t negative7_convert_signed_char_to_signed_int(int8_t x) {
+ return x;
+}
+
+void test_negatives() {
+ // No bits set.
+ negative0_convert_unsigned_int_to_unsigned_int(0);
+ negative1_convert_unsigned_char_to_unsigned_char(0);
+ negative2_convert_signed_int_to_signed_int(0);
+ negative3_convert_signed_char_to_signed_char(0);
+ negative4_convert_unsigned_int_to_unsigned_char(0);
+ negative5_convert_unsigned_char_to_unsigned_int(0);
+ negative6_convert_unsigned_char_to_signed_int(0);
+ negative7_convert_signed_char_to_signed_int(0);
+
+ // One lowest bit set.
+ negative0_convert_unsigned_int_to_unsigned_int(1);
+ negative1_convert_unsigned_char_to_unsigned_char(1);
+ negative2_convert_signed_int_to_signed_int(1);
+ negative3_convert_signed_char_to_signed_char(1);
+ negative4_convert_unsigned_int_to_unsigned_char(1);
+ negative5_convert_unsigned_char_to_unsigned_int(1);
+ negative6_convert_unsigned_char_to_signed_int(1);
+ negative7_convert_signed_char_to_signed_int(1);
+
+ // All source bits set.
+ negative0_convert_unsigned_int_to_unsigned_int((uint32_t)UINT32_MAX);
+ negative1_convert_unsigned_char_to_unsigned_char((uint8_t)UINT8_MAX);
+ negative2_convert_signed_int_to_signed_int((int32_t)INT32_MAX);
+ negative3_convert_signed_char_to_signed_char((int8_t)INT8_MAX);
+ negative4_convert_unsigned_int_to_unsigned_char((uint32_t)UINT32_MAX);
+ negative5_convert_unsigned_char_to_unsigned_int((uint8_t)UINT8_MAX);
+ negative6_convert_unsigned_char_to_signed_int((uint8_t)UINT8_MAX);
+ negative7_convert_signed_char_to_signed_int((int8_t)INT8_MAX);
+
+ // Source 'sign' bit set.
+ negative0_convert_unsigned_int_to_unsigned_int((uint32_t)INT32_MIN);
+ negative1_convert_unsigned_char_to_unsigned_char((uint8_t)INT8_MIN);
+ negative2_convert_signed_int_to_signed_int((int32_t)INT32_MIN);
+ negative3_convert_signed_char_to_signed_char((int8_t)INT8_MIN);
+ negative4_convert_unsigned_int_to_unsigned_char((uint32_t)INT32_MIN);
+ negative5_convert_unsigned_char_to_unsigned_int((uint8_t)INT8_MIN);
+ negative6_convert_unsigned_char_to_signed_int((uint8_t)INT8_MIN);
+ negative7_convert_signed_char_to_signed_int((int8_t)INT8_MIN);
+}
+
+//============================================================================//
+// The remaining 8 cases *do* need the check. //
+//============================================================================//
+
+int32_t positive0_convert_unsigned_int_to_signed_int(uint32_t x) {
+#line 100
+ return x;
+}
+
+uint32_t positive1_convert_signed_int_to_unsigned_int(int32_t x) {
+#line 200
+ return x;
+}
+
+uint8_t positive2_convert_signed_int_to_unsigned_char(int32_t x) {
+#line 300
+ return x;
+}
+
+uint8_t positive3_convert_signed_char_to_unsigned_char(int8_t x) {
+#line 400
+ return x;
+}
+
+int8_t positive4_convert_unsigned_char_to_signed_char(uint8_t x) {
+#line 500
+ return x;
+}
+
+uint32_t positive5_convert_signed_char_to_unsigned_int(int8_t x) {
+#line 600
+ return x;
+}
+
+int8_t positive6_convert_unsigned_int_to_signed_char(uint32_t x) {
+#line 700
+ return x;
+}
+
+int8_t positive7_convert_signed_int_to_signed_char(int32_t x) {
+#line 800
+ return x;
+}
+
+#line 1120 // !!!
+
+void test_positives() {
+ // No bits set.
+ positive0_convert_unsigned_int_to_signed_int(0);
+ positive1_convert_signed_int_to_unsigned_int(0);
+ positive2_convert_signed_int_to_unsigned_char(0);
+ positive3_convert_signed_char_to_unsigned_char(0);
+ positive4_convert_unsigned_char_to_signed_char(0);
+ positive5_convert_signed_char_to_unsigned_int(0);
+ positive6_convert_unsigned_int_to_signed_char(0);
+ positive7_convert_signed_int_to_signed_char(0);
+
+ // One lowest bit set.
+ positive0_convert_unsigned_int_to_signed_int(1);
+ positive1_convert_signed_int_to_unsigned_int(1);
+ positive2_convert_signed_int_to_unsigned_char(1);
+ positive3_convert_signed_char_to_unsigned_char(1);
+ positive4_convert_unsigned_char_to_signed_char(1);
+ positive5_convert_signed_char_to_unsigned_int(1);
+ positive6_convert_unsigned_int_to_signed_char(1);
+ positive7_convert_signed_int_to_signed_char(1);
+
+#if defined(V0)
+ // All source bits set.
+ positive0_convert_unsigned_int_to_signed_int((uint32_t)UINT32_MAX);
+// CHECK-V0: {{.*}}integer-sign-change.c:100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -1 (32-bit, signed)
+ positive1_convert_signed_int_to_unsigned_int((int32_t)UINT32_MAX);
+// CHECK-V0: {{.*}}integer-sign-change.c:200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -1 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)
+ positive2_convert_signed_int_to_unsigned_char((int32_t)UINT32_MAX);
+// CHECK-V0: {{.*}}integer-sign-change.c:300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -1 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
+ positive3_convert_signed_char_to_unsigned_char((int8_t)UINT8_MAX);
+// CHECK-V0: {{.*}}integer-sign-change.c:400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -1 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
+ positive4_convert_unsigned_char_to_signed_char((uint8_t)UINT8_MAX);
+// CHECK-V0: {{.*}}integer-sign-change.c:500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 255 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+ positive5_convert_signed_char_to_unsigned_int((int8_t)UINT8_MAX);
+// CHECK-V0: {{.*}}integer-sign-change.c:600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -1 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)
+ positive6_convert_unsigned_int_to_signed_char((uint32_t)UINT32_MAX);
+// CHECK-V0: {{.*}}integer-sign-change.c:700:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+ positive7_convert_signed_int_to_signed_char((int32_t)UINT32_MAX);
+#elif defined(V1)
+ // Source 'Sign' bit set.
+ positive0_convert_unsigned_int_to_signed_int((uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-sign-change.c:100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -2147483648 (32-bit, signed)
+ positive1_convert_signed_int_to_unsigned_int((int32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-sign-change.c:200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 2147483648 (32-bit, unsigned)
+ positive2_convert_signed_int_to_unsigned_char((int32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-sign-change.c:300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 0 (8-bit, unsigned)
+ positive3_convert_signed_char_to_unsigned_char((int8_t)INT8_MIN);
+// CHECK-V1: {{.*}}integer-sign-change.c:400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ positive4_convert_unsigned_char_to_signed_char((uint8_t)INT8_MIN);
+// CHECK-V1: {{.*}}integer-sign-change.c:500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 128 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ positive5_convert_signed_char_to_unsigned_int((int8_t)INT8_MIN);
+// CHECK-V1: {{.*}}integer-sign-change.c:600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967168 (32-bit, unsigned)
+ positive6_convert_unsigned_int_to_signed_char((uint32_t)INT32_MIN);
+ positive7_convert_signed_int_to_signed_char((int32_t)INT32_MIN);
+// CHECK-V1: {{.*}}integer-sign-change.c:800:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka 'signed char') changed the value to 0 (8-bit, signed)
+#elif defined(V2)
+ // All bits except the source 'Sign' bit are set.
+ positive0_convert_unsigned_int_to_signed_int((uint32_t)INT32_MAX);
+ positive1_convert_signed_int_to_unsigned_int((int32_t)INT32_MAX);
+ positive2_convert_signed_int_to_unsigned_char((int32_t)INT32_MAX);
+ positive3_convert_signed_char_to_unsigned_char((int8_t)INT8_MAX);
+ positive4_convert_unsigned_char_to_signed_char((uint8_t)INT8_MAX);
+ positive5_convert_signed_char_to_unsigned_int((int8_t)INT8_MAX);
+ positive6_convert_unsigned_int_to_signed_char((uint32_t)INT32_MAX);
+// CHECK-V2: {{.*}}integer-sign-change.c:700:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483647 (32-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+ positive7_convert_signed_int_to_signed_char((int32_t)INT32_MAX);
+// CHECK-V2: {{.*}}integer-sign-change.c:800:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 2147483647 (32-bit, signed) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+#elif defined(V3)
+ // All destination bits set.
+ positive0_convert_unsigned_int_to_signed_int((uint32_t)UINT32_MAX);
+// CHECK-V3: {{.*}}integer-sign-change.c:100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -1 (32-bit, signed)
+ positive1_convert_signed_int_to_unsigned_int((int32_t)UINT32_MAX);
+// CHECK-V3: {{.*}}integer-sign-change.c:200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -1 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)
+ positive2_convert_signed_int_to_unsigned_char((int32_t)UINT8_MAX);
+ positive3_convert_signed_char_to_unsigned_char((int8_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-sign-change.c:400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -1 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
+ positive4_convert_unsigned_char_to_signed_char((uint8_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-sign-change.c:500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 255 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+ positive5_convert_signed_char_to_unsigned_int((int8_t)UINT32_MAX);
+// CHECK-V3: {{.*}}integer-sign-change.c:600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -1 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967295 (32-bit, unsigned)
+ positive6_convert_unsigned_int_to_signed_char((uint32_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-sign-change.c:700:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 255 (32-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+ positive7_convert_signed_int_to_signed_char((int32_t)UINT8_MAX);
+// CHECK-V3: {{.*}}integer-sign-change.c:800:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 255 (32-bit, signed) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+#elif defined(V4)
+ // Destination 'sign' bit set.
+ positive0_convert_unsigned_int_to_signed_int((uint32_t)INT32_MIN);
+// CHECK-V4: {{.*}}integer-sign-change.c:100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -2147483648 (32-bit, signed)
+ positive1_convert_signed_int_to_unsigned_int((int32_t)INT32_MIN);
+// CHECK-V4: {{.*}}integer-sign-change.c:200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 2147483648 (32-bit, unsigned)
+ positive2_convert_signed_int_to_unsigned_char((int32_t)INT8_MIN);
+// CHECK-V4: {{.*}}integer-sign-change.c:300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -128 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ positive3_convert_signed_char_to_unsigned_char((int8_t)INT8_MIN);
+// CHECK-V4: {{.*}}integer-sign-change.c:400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ positive4_convert_unsigned_char_to_signed_char((uint8_t)INT8_MIN);
+// CHECK-V4: {{.*}}integer-sign-change.c:500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 128 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ positive5_convert_signed_char_to_unsigned_int((int8_t)INT32_MIN);
+ positive6_convert_unsigned_int_to_signed_char((uint32_t)INT8_MIN);
+// CHECK-V4: {{.*}}integer-sign-change.c:700:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967168 (32-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ positive7_convert_signed_int_to_signed_char((int32_t)INT8_MIN);
+#elif defined(V5)
+ // All bits except the destination 'sign' bit are set.
+ positive0_convert_unsigned_int_to_signed_int((uint32_t)INT32_MIN);
+// CHECK-V5: {{.*}}integer-sign-change.c:100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -2147483648 (32-bit, signed)
+ positive1_convert_signed_int_to_unsigned_int((int32_t)INT32_MIN);
+// CHECK-V5: {{.*}}integer-sign-change.c:200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 2147483648 (32-bit, unsigned)
+ positive2_convert_signed_int_to_unsigned_char((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
+// CHECK-V5: {{.*}}integer-sign-change.c:300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -129 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 127 (8-bit, unsigned)
+ positive3_convert_signed_char_to_unsigned_char((int8_t)(INT8_MIN));
+// CHECK-V5: {{.*}}integer-sign-change.c:400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ positive4_convert_unsigned_char_to_signed_char((uint8_t)(INT8_MIN));
+// CHECK-V5: {{.*}}integer-sign-change.c:500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 128 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ positive5_convert_signed_char_to_unsigned_int((int8_t)(INT32_MIN));
+ positive6_convert_unsigned_int_to_signed_char(~((uint32_t)(uint8_t)INT8_MIN));
+ positive7_convert_signed_int_to_signed_char((int32_t)(~((uint32_t)((uint8_t)INT8_MIN))));
+// CHECK-V5: {{.*}}integer-sign-change.c:800:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -129 (32-bit, signed) to type '{{.*}}' (aka 'signed char') changed the value to 127 (8-bit, signed)
+#elif defined(V6)
+ // Only the source and destination sign bits are set.
+ positive0_convert_unsigned_int_to_signed_int((uint32_t)INT32_MIN);
+// CHECK-V6: {{.*}}integer-sign-change.c:100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka 'int') changed the value to -2147483648 (32-bit, signed)
+ positive1_convert_signed_int_to_unsigned_int((int32_t)INT32_MIN);
+// CHECK-V6: {{.*}}integer-sign-change.c:200:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 2147483648 (32-bit, unsigned)
+ positive2_convert_signed_int_to_unsigned_char((int32_t)((uint32_t)INT32_MIN | (uint32_t)((uint8_t)INT8_MIN)));
+// CHECK-V6: {{.*}}integer-sign-change.c:300:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483520 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ positive3_convert_signed_char_to_unsigned_char((int8_t)INT8_MIN);
+// CHECK-V6: {{.*}}integer-sign-change.c:400:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 128 (8-bit, unsigned)
+ positive4_convert_unsigned_char_to_signed_char((uint8_t)INT8_MIN);
+// CHECK-V6: {{.*}}integer-sign-change.c:500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned char') of value 128 (8-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ positive5_convert_signed_char_to_unsigned_int((int8_t)INT8_MIN);
+// CHECK-V6: {{.*}}integer-sign-change.c:600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'signed char') of value -128 (8-bit, signed) to type '{{.*}}' (aka 'unsigned int') changed the value to 4294967168 (32-bit, unsigned)
+ positive6_convert_unsigned_int_to_signed_char((uint32_t)((uint32_t)INT32_MIN | (uint32_t)((uint8_t)INT8_MIN)));
+// CHECK-V6: {{.*}}integer-sign-change.c:700:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483776 (32-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -128 (8-bit, signed)
+ positive7_convert_signed_int_to_signed_char((int32_t)((uint32_t)INT32_MIN | (uint32_t)((uint8_t)INT8_MIN)));
+#else
+#error Some V* needs to be defined!
+#endif
+}
+
+// CHECK-NOT: implicit conversion
+
+int main() {
+ test_negatives();
+ test_positives();
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/ImplicitConversion/integer-truncation-blacklist.c b/test/ubsan/TestCases/ImplicitConversion/integer-truncation-blacklist.c
deleted file mode 100644
index 13d4dca4ff14..000000000000
--- a/test/ubsan/TestCases/ImplicitConversion/integer-truncation-blacklist.c
+++ /dev/null
@@ -1,20 +0,0 @@
-// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316
-// I'm not sure this is actually *that* issue, but this seems oddly similar to the other XFAIL'ed cases.
-// XFAIL: android
-// UNSUPPORTED: ios
-
-// 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
-
-unsigned char implicitTruncation(unsigned int argc) {
- return argc; // BOOM
-}
-
-int main(int argc, char **argv) {
- return implicitTruncation(~0U);
-}
diff --git a/test/ubsan/TestCases/ImplicitConversion/integer-truncation.c b/test/ubsan/TestCases/ImplicitConversion/integer-truncation.c
index 995eb7d0faf4..157564871e45 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
+uint32_t convert_unsigned_int_to_unsigned_int(uint32_t 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;
+uint8_t convert_unsigned_char_to_unsigned_char(uint8_t x) {
+#line 200
+ return x;
+}
- // Explicit and-ing of bits will silence it.
- uint8_t nc0 = (~((uint32_t)0)) & 255;
+int32_t convert_signed_int_to_signed_int(int32_t x) {
+#line 300
+ return x;
+}
- // Explicit casts
- uint8_t i0 = (uint8_t)(~((uint32_t)0));
+int8_t convert_signed_char_to_signed_char(int8_t 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
+uint8_t convert_unsigned_int_to_unsigned_char(uint32_t x) {
+#line 500
+ return x;
+}
+
+uint32_t convert_unsigned_char_to_unsigned_int(uint8_t x) {
+#line 600
+ return x;
+}
+
+int32_t convert_unsigned_char_to_signed_int(uint8_t x) {
+#line 700
+ return x;
+}
+
+int32_t convert_signed_char_to_signed_int(int8_t x) {
+#line 800
+ return x;
+}
+
+int32_t convert_unsigned_int_to_signed_int(uint32_t x) {
+#line 900
+ return x;
+}
+
+uint32_t convert_signed_int_to_unsigned_int(int32_t x) {
+#line 1000
+ return x;
+}
+
+uint8_t convert_signed_int_to_unsigned_char(int32_t x) {
+#line 1100
+ return x;
+}
+
+uint8_t convert_signed_char_to_unsigned_char(int8_t x) {
+#line 1200
+ return x;
+}
+
+int8_t convert_unsigned_char_to_signed_char(uint8_t x) {
+#line 1300
+ return x;
+}
+
+uint32_t convert_signed_char_to_unsigned_int(int8_t 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)
+int8_t convert_unsigned_int_to_signed_char(uint32_t x) {
+#line 1500
+ return x;
+}
+
+int8_t convert_signed_int_to_signed_char(int32_t 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 '{{.*}}' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value -1 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 2147483647 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value 2147483647 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 2147483647 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value 2147483647 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 255 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value 255 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 128 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value 128 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 4294967167 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value -129 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 4294967167 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value -129 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 2147483776 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value -2147483520 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 2147483776 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value -2147483520 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 2147483519 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value 2147483519 (32-bit, signed) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'unsigned int') of value 2147483519 (32-bit, unsigned) to type '{{.*}}' (aka '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 '{{.*}}' (aka 'int') of value 2147483519 (32-bit, signed) to type '{{.*}}' (aka '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/signed-integer-truncation-blacklist.c b/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-blacklist.c
new file mode 100644
index 000000000000..229f8326077a
--- /dev/null
+++ b/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-blacklist.c
@@ -0,0 +1,60 @@
+// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316
+// I'm not sure this is actually *that* issue, but this seems oddly similar to the other XFAIL'ed cases.
+// 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 "[integer]" >> %tmp
+// RUN: echo "fun:implicitUnsignedTruncation" >> %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
+
+// RUN: rm -f %tmp
+// RUN: echo "[implicit-conversion]" >> %tmp
+// RUN: echo "fun:implicitUnsignedTruncation" >> %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
+
+// RUN: rm -f %tmp
+// RUN: echo "[implicit-integer-truncation]" >> %tmp
+// RUN: echo "fun:implicitUnsignedTruncation" >> %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
+
+// RUN: rm -f %tmp
+// RUN: echo "[implicit-signed-integer-truncation]" >> %tmp
+// RUN: echo "fun:implicitUnsignedTruncation" >> %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
+
+// RUN: rm -f %tmp
+// RUN: echo "[implicit-unsigned-integer-truncation]" >> %tmp
+// RUN: echo "fun:implicitUnsignedTruncation" >> %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 | 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 | 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 | 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 | FileCheck %s
+
+#include <stdint.h>
+
+uint8_t implicitUnsignedTruncation(int32_t argc) {
+ return argc; // BOOM
+// CHECK: {{.*}}signed-integer-truncation-blacklist.c:[[@LINE-1]]:10: runtime error: implicit conversion from type '{{.*}} (aka 'int') of value -1 (32-bit, signed) to type '{{.*}}' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
+}
+
+int main(int argc, char **argv) {
+ return implicitUnsignedTruncation(-1);
+}
diff --git a/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-or-sign-change-blacklist.c b/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-or-sign-change-blacklist.c
new file mode 100644
index 000000000000..0e79df0b512c
--- /dev/null
+++ b/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-or-sign-change-blacklist.c
@@ -0,0 +1,58 @@
+// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316
+// I'm not sure this is actually *that* issue, but this seems oddly similar to the other XFAIL'ed cases.
+// XFAIL: android
+// UNSUPPORTED: ios
+
+// All of these don't actually silence it:
+
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+
+// RUN: rm -f %tmp
+// RUN: echo "[implicit-signed-integer-truncation]" >> %tmp
+// RUN: echo "fun:implicitConversion" >> %tmp
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+
+// RUN: rm -f %tmp
+// RUN: echo "[implicit-signed-integer-truncation,implicit-integer-sign-change]" >> %tmp
+// RUN: echo "fun:implicitConversion" >> %tmp
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
+
+
+// The only two way it works:
+
+// RUN: rm -f %tmp
+// RUN: echo "[implicit-integer-sign-change]" >> %tmp
+// RUN: echo "fun:implicitConversion" >> %tmp
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O0 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O1 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O2 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O3 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+
+// RUN: rm -f %tmp
+// RUN: echo "[implicit-signed-integer-truncation]" >> %tmp
+// RUN: echo "[implicit-integer-sign-change]" >> %tmp
+// RUN: echo "fun:implicitConversion" >> %tmp
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O0 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O1 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O2 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change -fno-sanitize-recover=implicit-signed-integer-truncation,implicit-integer-sign-change -fsanitize-blacklist=%tmp -O3 %s -o %t && not %run %t 2>&1 | not FileCheck %s
+
+#include <stdint.h>
+
+int8_t implicitConversion(uint32_t argc) {
+ return argc; // BOOM
+// CHECK: {{.*}}signed-integer-truncation-or-sign-change-blacklist.c:[[@LINE-1]]:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+}
+
+int main(int argc, char **argv) {
+ return implicitConversion(~0U);
+}
diff --git a/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-or-sign-change-summary.cpp b/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-or-sign-change-summary.cpp
new file mode 100644
index 000000000000..13eaef274157
--- /dev/null
+++ b/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-or-sign-change-summary.cpp
@@ -0,0 +1,13 @@
+// RUN: %clangxx -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOTYPE
+// RUN: %env_ubsan_opts=report_error_type=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TYPE
+// REQUIRES: !ubsan-standalone && !ubsan-standalone-static
+
+#include <stdint.h>
+
+int main() {
+ int8_t t0 = (~(uint32_t(0)));
+ // CHECK-NOTYPE: SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]:15
+ // CHECK-TYPE: SUMMARY: UndefinedBehaviorSanitizer: implicit-signed-integer-truncation-or-sign-change {{.*}}summary.cpp:[[@LINE-2]]:15
+ return 0;
+}
diff --git a/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-summary.cpp b/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-summary.cpp
new file mode 100644
index 000000000000..1da9e3b41e36
--- /dev/null
+++ b/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation-summary.cpp
@@ -0,0 +1,13 @@
+// RUN: %clangxx -fsanitize=implicit-signed-integer-truncation %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOTYPE
+// RUN: %env_ubsan_opts=report_error_type=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TYPE
+// REQUIRES: !ubsan-standalone && !ubsan-standalone-static
+
+#include <stdint.h>
+
+int main() {
+ uint8_t t0 = int32_t(-1);
+ // CHECK-NOTYPE: SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]:16
+ // CHECK-TYPE: SUMMARY: UndefinedBehaviorSanitizer: implicit-signed-integer-truncation {{.*}}summary.cpp:[[@LINE-2]]:16
+ return 0;
+}
diff --git a/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation.c b/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation.c
new file mode 100644
index 000000000000..1ff67eb0579b
--- /dev/null
+++ b/test/ubsan/TestCases/ImplicitConversion/signed-integer-truncation.c
@@ -0,0 +1,318 @@
+// RUN: %clang -x c -fsanitize=implicit-signed-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-signed-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-signed-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-signed-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-signed-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-signed-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-signed-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-signed-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-signed-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-signed-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-signed-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-signed-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-signed-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-signed-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-signed-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-signed-integer-truncation %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.
+
+uint32_t convert_unsigned_int_to_unsigned_int(uint32_t x) {
+#line 100
+ return x;
+}
+
+uint8_t convert_unsigned_char_to_unsigned_char(uint8_t x) {
+#line 200
+ return x;
+}
+
+int32_t convert_signed_int_to_signed_int(int32_t x) {
+#line 300
+ return x;
+}
+
+int8_t convert_signed_char_to_signed_char(int8_t x) {
+#line 400
+ return x;
+}
+
+uint8_t convert_unsigned_int_to_unsigned_char(uint32_t x) {
+#line 500
+ return x;
+}
+
+uint32_t convert_unsigned_char_to_unsigned_int(uint8_t x) {
+#line 600
+ return x;
+}
+
+int32_t convert_unsigned_char_to_signed_int(uint8_t x) {
+#line 700
+ return x;
+}
+
+int32_t convert_signed_char_to_signed_int(int8_t x) {
+#line 800
+ return x;
+}
+
+int32_t convert_unsigned_int_to_signed_int(uint32_t x) {
+#line 900
+ return x;
+}
+
+uint32_t convert_signed_int_to_unsigned_int(int32_t x) {
+#line 1000
+ return x;
+}
+
+uint8_t convert_signed_int_to_unsigned_char(int32_t x) {
+#line 1100
+ return x;
+}
+
+uint8_t convert_signed_char_to_unsigned_char(int8_t x) {
+#line 1200
+ return x;
+}
+
+int8_t convert_unsigned_char_to_signed_char(uint8_t x) {
+#line 1300
+ return x;
+}
+
+uint32_t convert_signed_char_to_unsigned_int(int8_t x) {
+#line 1400
+ return x;
+}
+
+int8_t convert_unsigned_int_to_signed_char(uint32_t x) {
+#line 1500
+ return x;
+}
+
+int8_t convert_signed_int_to_signed_char(int32_t 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);
+ 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: {{.*}}signed-integer-truncation.c:1100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -1 (32-bit, signed) to type '{{.*}}' (aka '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);
+ 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: {{.*}}signed-integer-truncation.c:1100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka '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: {{.*}}signed-integer-truncation.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to 0 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)INT32_MIN);
+// CHECK-V1: {{.*}}signed-integer-truncation.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483648 (32-bit, signed) to type '{{.*}}' (aka '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);
+ 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: {{.*}}signed-integer-truncation.c:1100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 2147483647 (32-bit, signed) to type '{{.*}}' (aka '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: {{.*}}signed-integer-truncation.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483647 (32-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)INT32_MAX);
+// CHECK-V2: {{.*}}signed-integer-truncation.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 2147483647 (32-bit, signed) to type '{{.*}}' (aka '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: {{.*}}signed-integer-truncation.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 255 (32-bit, unsigned) to type '{{.*}}' (aka 'signed char') changed the value to -1 (8-bit, signed)
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)UINT8_MAX);
+// CHECK-V3: {{.*}}signed-integer-truncation.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 255 (32-bit, signed) to type '{{.*}}' (aka '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: {{.*}}signed-integer-truncation.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 128 (32-bit, unsigned) to type '{{.*}}' (aka '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: {{.*}}signed-integer-truncation.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 128 (32-bit, signed) to type '{{.*}}' (aka '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)));
+ 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: {{.*}}signed-integer-truncation.c:1100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -129 (32-bit, signed) to type '{{.*}}' (aka '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: {{.*}}signed-integer-truncation.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967167 (32-bit, unsigned) to type '{{.*}}' (aka '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: {{.*}}signed-integer-truncation.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -129 (32-bit, signed) to type '{{.*}}' (aka '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));
+ 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: {{.*}}signed-integer-truncation.c:1100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483520 (32-bit, signed) to type '{{.*}}' (aka '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: {{.*}}signed-integer-truncation.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483776 (32-bit, unsigned) to type '{{.*}}' (aka '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: {{.*}}signed-integer-truncation.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value -2147483520 (32-bit, signed) to type '{{.*}}' (aka '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)));
+ 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: {{.*}}signed-integer-truncation.c:1100:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 2147483519 (32-bit, signed) to type '{{.*}}' (aka '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: {{.*}}signed-integer-truncation.c:1500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483519 (32-bit, unsigned) to type '{{.*}}' (aka '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: {{.*}}signed-integer-truncation.c:1600:10: runtime error: implicit conversion from type '{{.*}}' (aka 'int') of value 2147483519 (32-bit, signed) to type '{{.*}}' (aka '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/unsigned-integer-truncation-blacklist.c b/test/ubsan/TestCases/ImplicitConversion/unsigned-integer-truncation-blacklist.c
new file mode 100644
index 000000000000..938b7a8c102c
--- /dev/null
+++ b/test/ubsan/TestCases/ImplicitConversion/unsigned-integer-truncation-blacklist.c
@@ -0,0 +1,60 @@
+// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316
+// I'm not sure this is actually *that* issue, but this seems oddly similar to the other XFAIL'ed cases.
+// 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 "[integer]" >> %tmp
+// RUN: echo "fun:implicitUnsignedTruncation" >> %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
+
+// RUN: rm -f %tmp
+// RUN: echo "[implicit-conversion]" >> %tmp
+// RUN: echo "fun:implicitUnsignedTruncation" >> %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
+
+// RUN: rm -f %tmp
+// RUN: echo "[implicit-integer-truncation]" >> %tmp
+// RUN: echo "fun:implicitUnsignedTruncation" >> %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
+
+// RUN: rm -f %tmp
+// RUN: echo "[implicit-unsigned-integer-truncation]" >> %tmp
+// RUN: echo "fun:implicitUnsignedTruncation" >> %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
+
+// RUN: rm -f %tmp
+// RUN: echo "[implicit-signed-integer-truncation]" >> %tmp
+// RUN: echo "fun:implicitUnsignedTruncation" >> %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 | 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 | 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 | 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 | FileCheck %s
+
+#include <stdint.h>
+
+uint8_t implicitUnsignedTruncation(uint32_t argc) {
+ return argc; // BOOM
+// CHECK: {{.*}}unsigned-integer-truncation-blacklist.c:[[@LINE-1]]:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type '{{.*}}' (aka 'unsigned char') changed the value to 255 (8-bit, unsigned)
+}
+
+int main(int argc, char **argv) {
+ return implicitUnsignedTruncation(~0U);
+}
diff --git a/test/ubsan/TestCases/ImplicitConversion/unsigned-integer-truncation-summary.cpp b/test/ubsan/TestCases/ImplicitConversion/unsigned-integer-truncation-summary.cpp
new file mode 100644
index 000000000000..5117dc2a34f3
--- /dev/null
+++ b/test/ubsan/TestCases/ImplicitConversion/unsigned-integer-truncation-summary.cpp
@@ -0,0 +1,13 @@
+// RUN: %clangxx -fsanitize=implicit-unsigned-integer-truncation %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOTYPE
+// RUN: %env_ubsan_opts=report_error_type=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TYPE
+// REQUIRES: !ubsan-standalone && !ubsan-standalone-static
+
+#include <stdint.h>
+
+int main() {
+ uint8_t t0 = (~(uint32_t(0)));
+ // CHECK-NOTYPE: SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]:16
+ // CHECK-TYPE: SUMMARY: UndefinedBehaviorSanitizer: implicit-unsigned-integer-truncation {{.*}}summary.cpp:[[@LINE-2]]:16
+ return 0;
+}
diff --git a/test/ubsan/TestCases/ImplicitConversion/unsigned-integer-truncation.c b/test/ubsan/TestCases/ImplicitConversion/unsigned-integer-truncation.c
new file mode 100644
index 000000000000..49d223ccda39
--- /dev/null
+++ b/test/ubsan/TestCases/ImplicitConversion/unsigned-integer-truncation.c
@@ -0,0 +1,304 @@
+// RUN: %clang -x c -fsanitize=implicit-unsigned-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-unsigned-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-unsigned-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-unsigned-integer-truncation %s -DV3 -o %t && %run %t 2>&1 | not FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V3
+// RUN: %clang -x c -fsanitize=implicit-unsigned-integer-truncation %s -DV4 -o %t && %run %t 2>&1 | not FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V4
+// RUN: %clang -x c -fsanitize=implicit-unsigned-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-unsigned-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-unsigned-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-unsigned-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-unsigned-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-unsigned-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-unsigned-integer-truncation %s -DV3 -o %t && %run %t 2>&1 | not FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V3
+// RUN: %clang -x c++ -fsanitize=implicit-unsigned-integer-truncation %s -DV4 -o %t && %run %t 2>&1 | not FileCheck %s --implicit-check-not="implicit conversion" --check-prefixes=CHECK-V4
+// RUN: %clang -x c++ -fsanitize=implicit-unsigned-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-unsigned-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-unsigned-integer-truncation %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.
+
+uint32_t convert_unsigned_int_to_unsigned_int(uint32_t x) {
+#line 100
+ return x;
+}
+
+uint8_t convert_unsigned_char_to_unsigned_char(uint8_t x) {
+#line 200
+ return x;
+}
+
+int32_t convert_signed_int_to_signed_int(int32_t x) {
+#line 300
+ return x;
+}
+
+int8_t convert_signed_char_to_signed_char(int8_t x) {
+#line 400
+ return x;
+}
+
+uint8_t convert_unsigned_int_to_unsigned_char(uint32_t x) {
+#line 500
+ return x;
+}
+
+uint32_t convert_unsigned_char_to_unsigned_int(uint8_t x) {
+#line 600
+ return x;
+}
+
+int32_t convert_unsigned_char_to_signed_int(uint8_t x) {
+#line 700
+ return x;
+}
+
+int32_t convert_signed_char_to_signed_int(int8_t x) {
+#line 800
+ return x;
+}
+
+int32_t convert_unsigned_int_to_signed_int(uint32_t x) {
+#line 900
+ return x;
+}
+
+uint32_t convert_signed_int_to_unsigned_int(int32_t x) {
+#line 1000
+ return x;
+}
+
+uint8_t convert_signed_int_to_unsigned_char(int32_t x) {
+#line 1100
+ return x;
+}
+
+uint8_t convert_signed_char_to_unsigned_char(int8_t x) {
+#line 1200
+ return x;
+}
+
+int8_t convert_unsigned_char_to_signed_char(uint8_t x) {
+#line 1300
+ return x;
+}
+
+uint32_t convert_signed_char_to_unsigned_int(int8_t x) {
+#line 1400
+ return x;
+}
+
+int8_t convert_unsigned_int_to_signed_char(uint32_t x) {
+#line 1500
+ return x;
+}
+
+int8_t convert_signed_int_to_signed_char(int32_t 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: {{.*}}unsigned-integer-truncation.c:500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967295 (32-bit, unsigned) to type '{{.*}}' (aka '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);
+ 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: {{.*}}unsigned-integer-truncation.c:500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483648 (32-bit, unsigned) to type '{{.*}}' (aka '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);
+ 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);
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)INT32_MIN);
+#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: {{.*}}unsigned-integer-truncation.c:500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483647 (32-bit, unsigned) to type '{{.*}}' (aka '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);
+ 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);
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)INT32_MAX);
+#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);
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)UINT8_MAX);
+#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);
+ convert_signed_int_to_signed_char((int32_t)(uint32_t)(uint8_t)INT8_MIN);
+#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: {{.*}}unsigned-integer-truncation.c:500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 4294967167 (32-bit, unsigned) to type '{{.*}}' (aka '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)));
+ 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)));
+ convert_signed_int_to_signed_char((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
+#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: {{.*}}unsigned-integer-truncation.c:500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483776 (32-bit, unsigned) to type '{{.*}}' (aka '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)));
+ 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)));
+ convert_signed_int_to_signed_char((int32_t)(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+#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: {{.*}}unsigned-integer-truncation.c:500:10: runtime error: implicit conversion from type '{{.*}}' (aka 'unsigned int') of value 2147483519 (32-bit, unsigned) to type '{{.*}}' (aka '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))));
+ 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)));
+ convert_signed_int_to_signed_char((int32_t)~(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
+#else
+#error Some V* needs to be defined!
+#endif
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Integer/no-recover.cpp b/test/ubsan/TestCases/Integer/no-recover.cpp
index 515ebbd0702e..45aeb9e75241 100644
--- a/test/ubsan/TestCases/Integer/no-recover.cpp
+++ b/test/ubsan/TestCases/Integer/no-recover.cpp
@@ -1,7 +1,9 @@
// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=RECOVER
// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fno-sanitize-recover=all -fsanitize-recover=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=RECOVER
// RUN: %env_ubsan_opts=silence_unsigned_overflow=1 %run %t 2>&1 | FileCheck %s --check-prefix=SILENT-RECOVER --allow-empty
-// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fno-sanitize-recover=unsigned-integer-overflow %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=ABORT
+// RUN: %clangxx -fsanitize=unsigned-integer-overflow -fno-sanitize-recover=unsigned-integer-overflow %s -o %t
+// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=ABORT
+// RUN: %env_ubsan_opts=silence_unsigned_overflow=1 not %run %t 2>&1 | FileCheck %s --check-prefix=ABORT
#include <stdint.h>
diff --git a/test/ubsan/TestCases/Misc/enum.cpp b/test/ubsan/TestCases/Misc/enum.cpp
index 5dbecf161262..8e95f8b403a9 100644
--- a/test/ubsan/TestCases/Misc/enum.cpp
+++ b/test/ubsan/TestCases/Misc/enum.cpp
@@ -4,7 +4,7 @@
// FIXME: UBSan fails to add the correct instrumentation code for some reason on
// Windows.
-// XFAIL: win32
+// XFAIL: windows-msvc
enum E { a = 1 } e;
#undef E
diff --git a/test/ubsan/TestCases/Misc/log-path_test.cc b/test/ubsan/TestCases/Misc/log-path_test.cc
index 40bb35a06aaf..23ad7e27d21f 100644
--- a/test/ubsan/TestCases/Misc/log-path_test.cc
+++ b/test/ubsan/TestCases/Misc/log-path_test.cc
@@ -12,16 +12,18 @@
// 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.*
// FIXME: log_path is not supported on Windows yet.
-// XFAIL: win32
+// XFAIL: windows-msvc
#include <stdio.h>
#include <stdlib.h>
diff --git a/test/ubsan/TestCases/Misc/monitor.cpp b/test/ubsan/TestCases/Misc/monitor.cpp
index 6c5cacfed8be..c02702847afb 100644
--- a/test/ubsan/TestCases/Misc/monitor.cpp
+++ b/test/ubsan/TestCases/Misc/monitor.cpp
@@ -4,7 +4,7 @@
// __ubsan_on_report is not defined as weak. Redefining it here isn't supported
// on Windows.
//
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
// Linkage issue
// XFAIL: openbsd
diff --git a/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-align_value-on-lvalue.cpp b/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-align_value-on-lvalue.cpp
new file mode 100644
index 000000000000..97d4b4623c2d
--- /dev/null
+++ b/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-align_value-on-lvalue.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang -x c -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+// RUN: %clang -x c++ -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+#include <stdlib.h>
+
+typedef char *__attribute__((align_value(0x8000))) aligned_char;
+
+struct ac_struct {
+ aligned_char a;
+};
+
+char *load_from_ac_struct(struct ac_struct *x) {
+ return x->a;
+}
+
+int main(int argc, char* argv[]) {
+ char *ptr = (char *)malloc(2);
+
+ struct ac_struct x;
+ x.a = ptr + 1; // FIXME: it is weird that this does not also have an assumption.
+ load_from_ac_struct(&x);
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-9]]:13: runtime error: assumption of 32768 byte alignment for pointer of type 'aligned_char' (aka 'char *') failed
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-17]]:30: note: alignment assumption was specified here
+ // CHECK: 0x{{.*}}: note: address is {{.*}} aligned, misalignment offset is {{.*}} byte
+
+ free(ptr);
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-align_value-on-paramvar.cpp b/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-align_value-on-paramvar.cpp
new file mode 100644
index 000000000000..774ba90fbe92
--- /dev/null
+++ b/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-align_value-on-paramvar.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang -x c -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+// RUN: %clang -x c++ -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+#include <stdlib.h>
+
+char *passthrough(__attribute__((align_value(0x8000))) char *x) {
+ return x;
+}
+
+int main(int argc, char* argv[]) {
+ char *ptr = (char *)malloc(2);
+
+ passthrough(ptr + 1);
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-7]]:10: runtime error: assumption of 32768 byte alignment for pointer of type 'char *' failed
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-9]]:34: note: alignment assumption was specified here
+ // CHECK: 0x{{.*}}: note: address is {{.*}} aligned, misalignment offset is {{.*}} byte
+
+ // FIXME: shouldn't there be an assumption on the caller's side too?
+
+ free(ptr);
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-alloc_align-on-function-variable.cpp b/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-alloc_align-on-function-variable.cpp
new file mode 100644
index 000000000000..c71cb1e1bc7b
--- /dev/null
+++ b/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-alloc_align-on-function-variable.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang -x c -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+// RUN: %clang -x c++ -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+#include <stdlib.h>
+
+char *__attribute__((alloc_align(2)))
+passthrough(char *x, unsigned long alignment) {
+ return x;
+}
+
+unsigned long alignment;
+
+int main(int argc, char* argv[]) {
+ char *ptr = (char *)malloc(2);
+
+ alignment = 0x8000;
+
+ passthrough(ptr + 1, alignment);
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-1]]:3: runtime error: assumption of 32768 byte alignment for pointer of type 'char *' failed
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-14]]:22: note: alignment assumption was specified here
+ // CHECK: 0x{{.*}}: note: address is {{.*}} aligned, misalignment offset is {{.*}} byte
+
+ free(ptr);
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-alloc_align-on-function.cpp b/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-alloc_align-on-function.cpp
new file mode 100644
index 000000000000..db22e3a8fcff
--- /dev/null
+++ b/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-alloc_align-on-function.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang -x c -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+// RUN: %clang -x c++ -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+#include <stdlib.h>
+
+char *__attribute__((alloc_align(2)))
+passthrough(char *x, unsigned long alignment) {
+ return x;
+}
+
+int main(int argc, char* argv[]) {
+ char *ptr = (char *)malloc(2);
+
+ passthrough(ptr + 1, 0x8000);
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-1]]:3: runtime error: assumption of 32768 byte alignment for pointer of type 'char *' failed
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-10]]:22: note: alignment assumption was specified here
+ // CHECK: 0x{{.*}}: note: address is {{.*}} aligned, misalignment offset is {{.*}} byte
+
+ free(ptr);
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-assume_aligned-on-function-two-params.cpp b/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-assume_aligned-on-function-two-params.cpp
new file mode 100644
index 000000000000..33aa132f601b
--- /dev/null
+++ b/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-assume_aligned-on-function-two-params.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang -x c -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+// RUN: %clang -x c++ -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+#include <stdlib.h>
+
+char *__attribute__((assume_aligned(0x8000, 1))) passthrough(char *x) {
+ return x;
+}
+
+int main(int argc, char* argv[]) {
+ char *ptr = (char *)malloc(3);
+
+ passthrough(ptr + 2);
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-1]]:3: runtime error: assumption of 32768 byte alignment (with offset of 1 byte) for pointer of type 'char *' failed
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-9]]:22: note: alignment assumption was specified here
+ // CHECK: 0x{{.*}}: note: offset address is {{.*}} aligned, misalignment offset is {{.*}} byte
+
+ free(ptr);
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-assume_aligned-on-function.cpp b/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-assume_aligned-on-function.cpp
new file mode 100644
index 000000000000..202c0ce00639
--- /dev/null
+++ b/test/ubsan/TestCases/Pointer/alignment-assumption-attribute-assume_aligned-on-function.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang -x c -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+// RUN: %clang -x c++ -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+#include <stdlib.h>
+
+char *__attribute__((assume_aligned(0x8000))) passthrough(char *x) {
+ return x;
+}
+
+int main(int argc, char* argv[]) {
+ char *ptr = (char *)malloc(2);
+
+ passthrough(ptr + 1);
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-1]]:3: runtime error: assumption of 32768 byte alignment for pointer of type 'char *' failed
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-9]]:22: note: alignment assumption was specified here
+ // CHECK: 0x{{.*}}: note: address is {{.*}} aligned, misalignment offset is {{.*}} byte
+
+ free(ptr);
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Pointer/alignment-assumption-blacklist.cpp b/test/ubsan/TestCases/Pointer/alignment-assumption-blacklist.cpp
new file mode 100644
index 000000000000..c0c7b373f623
--- /dev/null
+++ b/test/ubsan/TestCases/Pointer/alignment-assumption-blacklist.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang -fsanitize=alignment -fno-sanitize-recover=alignment -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption "
+
+// RUN: rm -f %tmp
+// RUN: echo "[alignment]" >> %tmp
+// RUN: echo "fun:main" >> %tmp
+// RUN: %clang -fsanitize=alignment -fno-sanitize-recover=alignment -fsanitize-blacklist=%tmp -O0 %s -o %t && %run %t 2>&1
+
+#include <stdlib.h>
+
+int main(int argc, char* argv[]) {
+ char *ptr = (char *)malloc(2);
+
+ __builtin_assume_aligned(ptr + 1, 0x8000);
+ // CHECK: {{.*}}alignment-assumption-blacklist.cpp:[[@LINE-1]]:32: runtime error: assumption of 32768 byte alignment for pointer of type 'char *' failed
+ // CHECK: 0x{{.*}}: note: address is {{.*}} aligned, misalignment offset is {{.*}} byte
+
+ free(ptr);
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Pointer/alignment-assumption-builtin_assume_aligned-three-params-variable.cpp b/test/ubsan/TestCases/Pointer/alignment-assumption-builtin_assume_aligned-three-params-variable.cpp
new file mode 100644
index 000000000000..cc4e1f2184da
--- /dev/null
+++ b/test/ubsan/TestCases/Pointer/alignment-assumption-builtin_assume_aligned-three-params-variable.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang -x c -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+// RUN: %clang -x c++ -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+#include <stdlib.h>
+
+volatile long offset;
+
+int main(int argc, char* argv[]) {
+ char *ptr = (char *)malloc(3);
+
+ offset = 1;
+
+ __builtin_assume_aligned(ptr + 2, 0x8000, offset);
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-1]]:32: runtime error: assumption of 32768 byte alignment (with offset of 1 byte) for pointer of type 'char *' failed
+ // CHECK: 0x{{.*}}: note: offset address is {{.*}} aligned, misalignment offset is {{.*}} byte
+
+ free(ptr);
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Pointer/alignment-assumption-builtin_assume_aligned-three-params.cpp b/test/ubsan/TestCases/Pointer/alignment-assumption-builtin_assume_aligned-three-params.cpp
new file mode 100644
index 000000000000..724abd540e78
--- /dev/null
+++ b/test/ubsan/TestCases/Pointer/alignment-assumption-builtin_assume_aligned-three-params.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang -x c -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+// RUN: %clang -x c++ -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+#include <stdlib.h>
+
+int main(int argc, char* argv[]) {
+ char *ptr = (char *)malloc(3);
+
+ __builtin_assume_aligned(ptr + 2, 0x8000, 1);
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-1]]:32: runtime error: assumption of 32768 byte alignment (with offset of 1 byte) for pointer of type 'char *' failed
+ // CHECK: 0x{{.*}}: note: offset address is {{.*}} aligned, misalignment offset is {{.*}} byte
+
+ free(ptr);
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Pointer/alignment-assumption-builtin_assume_aligned-two-params.cpp b/test/ubsan/TestCases/Pointer/alignment-assumption-builtin_assume_aligned-two-params.cpp
new file mode 100644
index 000000000000..2737f3d08b44
--- /dev/null
+++ b/test/ubsan/TestCases/Pointer/alignment-assumption-builtin_assume_aligned-two-params.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang -x c -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+// RUN: %clang -x c++ -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+#include <stdlib.h>
+
+int main(int argc, char* argv[]) {
+ char *ptr = (char *)malloc(2);
+
+ __builtin_assume_aligned(ptr + 1, 0x8000);
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-1]]:32: runtime error: assumption of 32768 byte alignment for pointer of type 'char *' failed
+ // CHECK: 0x{{.*}}: note: address is {{.*}} aligned, misalignment offset is {{.*}} byte
+
+ free(ptr);
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Pointer/alignment-assumption-openmp.cpp b/test/ubsan/TestCases/Pointer/alignment-assumption-openmp.cpp
new file mode 100644
index 000000000000..482316ee254f
--- /dev/null
+++ b/test/ubsan/TestCases/Pointer/alignment-assumption-openmp.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang -x c -fsanitize=alignment -fopenmp-simd -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -fopenmp-simd -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -fopenmp-simd -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c -fsanitize=alignment -fopenmp-simd -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+// RUN: %clang -x c++ -fsanitize=alignment -fopenmp-simd -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -fopenmp-simd -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -fopenmp-simd -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+// RUN: %clang -x c++ -fsanitize=alignment -fopenmp-simd -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:"
+
+#include <stdlib.h>
+
+int main(int argc, char* argv[]) {
+ char *ptr = (char *)malloc(2);
+ char *data = ptr + 1;
+
+ data[0] = 0;
+
+#pragma omp for simd aligned(data : 0x8000)
+ for(int x = 0; x < 1; x++)
+ data[x] = data[x];
+ // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-3]]:30: runtime error: assumption of 32768 byte alignment for pointer of type 'char *' failed
+ // CHECK: 0x{{.*}}: note: address is {{.*}} aligned, misalignment offset is {{.*}} byte
+
+ free(ptr);
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Pointer/alignment-assumption-summary.cpp b/test/ubsan/TestCases/Pointer/alignment-assumption-summary.cpp
new file mode 100644
index 000000000000..cc7769a06fe5
--- /dev/null
+++ b/test/ubsan/TestCases/Pointer/alignment-assumption-summary.cpp
@@ -0,0 +1,17 @@
+// RUN: %clangxx -fsanitize=alignment %s -o %t
+// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NOTYPE
+// RUN: %env_ubsan_opts=report_error_type=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TYPE
+// REQUIRES: !ubsan-standalone && !ubsan-standalone-static
+
+#include <stdlib.h>
+
+int main(int argc, char* argv[]) {
+ char *ptr = (char *)malloc(2);
+
+ __builtin_assume_aligned(ptr + 1, 0x8000);
+ // CHECK-NOTYPE: SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]:32
+ // CHECK-TYPE: SUMMARY: UndefinedBehaviorSanitizer: alignment-assumption {{.*}}summary.cpp:[[@LINE-2]]:32
+ free(ptr);
+
+ return 0;
+}
diff --git a/test/ubsan/TestCases/Pointer/index-overflow.cpp b/test/ubsan/TestCases/Pointer/index-overflow.cpp
index eb7f95e85c2c..f9b1fea08c94 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 0002c713f866..5a1432625a54 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/TestCases/TypeCheck/Function/function.cpp b/test/ubsan/TestCases/TypeCheck/Function/function.cpp
index 7b9f0982639a..31baa2af8ca9 100644
--- a/test/ubsan/TestCases/TypeCheck/Function/function.cpp
+++ b/test/ubsan/TestCases/TypeCheck/Function/function.cpp
@@ -2,7 +2,7 @@
// RUN: %run %t 2>&1 | FileCheck %s
// Verify that we can disable symbolization if needed:
// RUN: %env_ubsan_opts=symbolize=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOSYM
-// XFAIL: win32,win64
+// XFAIL: windows-msvc
// Unsupported function flag
// UNSUPPORTED: openbsd
diff --git a/test/ubsan/TestCases/TypeCheck/PR33221.cpp b/test/ubsan/TestCases/TypeCheck/PR33221.cpp
index 65cbf5d000d9..6088338a3dee 100644
--- a/test/ubsan/TestCases/TypeCheck/PR33221.cpp
+++ b/test/ubsan/TestCases/TypeCheck/PR33221.cpp
@@ -2,7 +2,7 @@
// RUN: %run %t 2>&1 | FileCheck %s
// REQUIRES: cxxabi
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
#include <string.h>
diff --git a/test/ubsan/TestCases/TypeCheck/vptr-corrupted-vtable-itanium.cpp b/test/ubsan/TestCases/TypeCheck/vptr-corrupted-vtable-itanium.cpp
index f0659f439438..fd1e7ed779a6 100644
--- a/test/ubsan/TestCases/TypeCheck/vptr-corrupted-vtable-itanium.cpp
+++ b/test/ubsan/TestCases/TypeCheck/vptr-corrupted-vtable-itanium.cpp
@@ -1,7 +1,7 @@
// RUN: %clangxx -frtti -fsanitize=vptr -fno-sanitize-recover=vptr,null -g %s -O3 -o %t
// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-CORRUPTED-VTABLE --strict-whitespace
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
// REQUIRES: stable-runtime, cxxabi
#include <cstddef>
diff --git a/test/ubsan/TestCases/TypeCheck/vptr-non-unique-typeinfo.cpp b/test/ubsan/TestCases/TypeCheck/vptr-non-unique-typeinfo.cpp
index 7bc19bdae8fa..13b05238610e 100644
--- a/test/ubsan/TestCases/TypeCheck/vptr-non-unique-typeinfo.cpp
+++ b/test/ubsan/TestCases/TypeCheck/vptr-non-unique-typeinfo.cpp
@@ -3,7 +3,8 @@
// RUN: %run %t
//
// REQUIRES: cxxabi
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
+// XFAIL: i386-netbsd
struct X {
virtual ~X() {}
diff --git a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base-construction.cpp b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base-construction.cpp
index a86960d12ad7..75a5d7670fb9 100644
--- a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base-construction.cpp
+++ b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base-construction.cpp
@@ -2,7 +2,7 @@
// RUN: %run %t
// REQUIRES: cxxabi
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
int volatile n;
diff --git a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp
index aa0123c46e9c..925373bdee1f 100644
--- a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp
+++ b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp
@@ -2,7 +2,9 @@
// RUN: not %run %t 2>&1 | FileCheck %s
// REQUIRES: cxxabi
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
+// Nested crash reported
+// UNSUPPORTED: freebsd
struct S { virtual int f() { return 0; } };
struct T : virtual S {};
diff --git a/test/ubsan/TestCases/TypeCheck/vptr.cpp b/test/ubsan/TestCases/TypeCheck/vptr.cpp
index 9b53e8095777..67239e82d340 100644
--- a/test/ubsan/TestCases/TypeCheck/vptr.cpp
+++ b/test/ubsan/TestCases/TypeCheck/vptr.cpp
@@ -37,11 +37,13 @@
// RUN: %env_ubsan_opts=halt_on_error=1:suppressions='"%t.loc-supp"' not %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS
// REQUIRES: stable-runtime, cxxabi
-// UNSUPPORTED: win32
+// UNSUPPORTED: windows-msvc
// Suppressions file not pushed to the device.
// UNSUPPORTED: android
// Compilation error
// UNSUPPORTED: openbsd
+// Compilation error
+// UNSUPPORTED: freebsd
#include <new>
#include <typeinfo>
#include <assert.h>
diff --git a/test/ubsan/lit.common.cfg b/test/ubsan/lit.common.cfg
index e20832bd655f..9a7a8900e342 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 a4d7b50edafc..60c9c84e3c10 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/alignment-assumption.c b/test/ubsan_minimal/TestCases/alignment-assumption.c
new file mode 100644
index 000000000000..ed6fff359296
--- /dev/null
+++ b/test/ubsan_minimal/TestCases/alignment-assumption.c
@@ -0,0 +1,17 @@
+// RUN: %clang -fsanitize=alignment %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+
+#include <stdlib.h>
+
+int main(int argc, char* argv[]) {
+// CHECK-NOT: alignment-assumption
+
+char *ptr = (char *)malloc(2);
+
+__builtin_assume_aligned(ptr + 1, 0x8000);
+// CHECK: alignment-assumption
+// CHECK-NOT: alignment-assumption
+
+free(ptr);
+
+return 0;
+}
diff --git a/test/ubsan_minimal/TestCases/implicit-integer-sign-change.c b/test/ubsan_minimal/TestCases/implicit-integer-sign-change.c
new file mode 100644
index 000000000000..1e208921922e
--- /dev/null
+++ b/test/ubsan_minimal/TestCases/implicit-integer-sign-change.c
@@ -0,0 +1,17 @@
+// RUN: %clang -fsanitize=implicit-integer-sign-change %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+
+#include <stdint.h>
+
+int main() {
+// CHECK-NOT: implicit-conversion
+
+ // Explicitly casting hides it,
+ int32_t n0 = (int32_t)(~((uint32_t)0));
+
+ // Positive tests.
+ int32_t t0 = (~((uint32_t)0));
+// CHECK: implicit-conversion
+// CHECK-NOT: implicit-conversion
+
+ return 0;
+}
diff --git a/test/ubsan_minimal/TestCases/implicit-signed-integer-truncation-or-sign-change.c b/test/ubsan_minimal/TestCases/implicit-signed-integer-truncation-or-sign-change.c
new file mode 100644
index 000000000000..3a72a406a8b0
--- /dev/null
+++ b/test/ubsan_minimal/TestCases/implicit-signed-integer-truncation-or-sign-change.c
@@ -0,0 +1,17 @@
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation,implicit-integer-sign-change %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+
+#include <stdint.h>
+
+int main() {
+// CHECK-NOT: implicit-conversion
+
+ // Explicitly casting hides it,
+ int8_t n0 = (int8_t)((uint32_t)-1);
+
+ // Positive tests.
+ int8_t t0 = (uint32_t)-1;
+// CHECK: implicit-conversion
+// CHECK-NOT: implicit-conversion
+
+ return 0;
+}
diff --git a/test/ubsan_minimal/TestCases/implicit-signed-integer-truncation.c b/test/ubsan_minimal/TestCases/implicit-signed-integer-truncation.c
new file mode 100644
index 000000000000..9677407e0ce0
--- /dev/null
+++ b/test/ubsan_minimal/TestCases/implicit-signed-integer-truncation.c
@@ -0,0 +1,25 @@
+// RUN: %clang -fsanitize=implicit-signed-integer-truncation %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+
+#include <stdint.h>
+
+int main() {
+// 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.
+ uint8_t n2 = 128;
+ uint8_t n3 = 255;
+ // Bools do not count
+ _Bool b0 = (~((uint32_t)(0)));
+ _Bool b1 = 255;
+
+ // Explicit and-ing of bits will silence it.
+ uint8_t nc0 = ((int32_t)(-1)) & 255;
+
+ // Positive tests.
+ uint8_t t0 = (int32_t)(-1);
+// CHECK: implicit-conversion
+// CHECK-NOT: implicit-conversion
+
+ return 0;
+}
diff --git a/test/ubsan_minimal/TestCases/implicit-integer-truncation.c b/test/ubsan_minimal/TestCases/implicit-unsigned-integer-truncation.c
index 1db6e6976815..8b9e166af812 100644
--- a/test/ubsan_minimal/TestCases/implicit-integer-truncation.c
+++ b/test/ubsan_minimal/TestCases/implicit-unsigned-integer-truncation.c
@@ -1,9 +1,9 @@
-// RUN: %clang -fsanitize=implicit-integer-truncation %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
+// RUN: %clang -fsanitize=implicit-unsigned-integer-truncation %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK
#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 2f9a234f8064..a2cb11b4d0d9 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/basic-filtering.cc b/test/xray/TestCases/Posix/basic-filtering.cc
index db07ef510c5c..3d4f4dda2d72 100644
--- a/test/xray/TestCases/Posix/basic-filtering.cc
+++ b/test/xray/TestCases/Posix/basic-filtering.cc
@@ -57,5 +57,5 @@ always_shows() {
// TRACE-NOT: - { type: 0, func-id: {{.*}}, function: {{.*filtered.*}}, {{.*}} }
// TRACE-NOT: - { type: 0, func-id: {{.*}}, function: {{.*beyond_stack.*}}, {{.*}} }
-// TRACE-DAG: - { type: 0, func-id: [[FID:[0-9]+]], function: {{.*always_shows.*}}, cpu: {{.*}}, thread: {{.*}}, kind: function-enter, tsc: {{[0-9]+}} }
-// TRACE-DAG: - { type: 0, func-id: [[FID]], function: {{.*always_shows.*}}, cpu: {{.*}}, thread: {{.*}}, kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}} }
+// TRACE-DAG: - { type: 0, func-id: [[FID:[0-9]+]], function: {{.*always_shows.*}}, cpu: {{.*}}, thread: {{.*}}, kind: function-enter, tsc: {{[0-9]+}}, data: '' }
+// TRACE-DAG: - { type: 0, func-id: [[FID]], function: {{.*always_shows.*}}, cpu: {{.*}}, thread: {{.*}}, kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}}, data: '' }
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 000000000000..c2444b18ab6e
--- /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 b12d97c0005a..8b9cb2bbfd53 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") ==
@@ -81,32 +80,32 @@ int main(int argc, char *argv[]) {
}
// Check that we're able to see two threads, each entering and exiting fA().
-// TRACE-DAG: - { type: 0, func-id: [[FIDA:[0-9]+]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} }
-// TRACE: - { type: 0, func-id: [[FIDA]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}} }
-// TRACE-DAG: - { type: 0, func-id: [[FIDA]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], process: [[PROCESS]], kind: function-enter, tsc: {{[0-9]+}} }
-// TRACE: - { type: 0, func-id: [[FIDA]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}} }
+// TRACE-DAG: - { type: 0, func-id: [[FIDA:[0-9]+]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}}, data: '' }
+// TRACE: - { type: 0, func-id: [[FIDA]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}}, data: '' }
+// TRACE-DAG: - { type: 0, func-id: [[FIDA]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], process: [[PROCESS]], kind: function-enter, tsc: {{[0-9]+}}, data: '' }
+// TRACE: - { type: 0, func-id: [[FIDA]], function: {{.*fA.*}}, cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}}, data: '' }
//
// Do the same as above for fC()
-// TRACE-DAG: - { type: 0, func-id: [[FIDC:[0-9]+]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS]], kind: function-enter, tsc: {{[0-9]+}} }
-// TRACE: - { type: 0, func-id: [[FIDC]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}} }
-// TRACE-DAG: - { type: 0, func-id: [[FIDC]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], process: [[PROCESS]], kind: function-enter, tsc: {{[0-9]+}} }
-// TRACE: - { type: 0, func-id: [[FIDC]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}} }
+// TRACE-DAG: - { type: 0, func-id: [[FIDC:[0-9]+]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS]], kind: function-enter, tsc: {{[0-9]+}}, data: '' }
+// TRACE: - { type: 0, func-id: [[FIDC]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}}, data: '' }
+// TRACE-DAG: - { type: 0, func-id: [[FIDC]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], process: [[PROCESS]], kind: function-enter, tsc: {{[0-9]+}}, data: '' }
+// TRACE: - { type: 0, func-id: [[FIDC]], function: {{.*fC.*}}, cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}}, data: '' }
// Do the same as above for fB()
-// TRACE-DAG: - { type: 0, func-id: [[FIDB:[0-9]+]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS]], kind: function-enter, tsc: {{[0-9]+}} }
-// TRACE: - { type: 0, func-id: [[FIDB]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}} }
-// TRACE-DAG: - { type: 0, func-id: [[FIDB]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], process: [[PROCESS]], kind: function-enter, tsc: {{[0-9]+}} }
-// TRACE: - { type: 0, func-id: [[FIDB]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}} }
+// TRACE-DAG: - { type: 0, func-id: [[FIDB:[0-9]+]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS]], kind: function-enter, tsc: {{[0-9]+}}, data: '' }
+// TRACE: - { type: 0, func-id: [[FIDB]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}}, data: '' }
+// TRACE-DAG: - { type: 0, func-id: [[FIDB]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], process: [[PROCESS]], kind: function-enter, tsc: {{[0-9]+}}, data: '' }
+// TRACE: - { type: 0, func-id: [[FIDB]], function: {{.*fB.*}}, cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}}, data: '' }
-// TRACE-DAG: - { type: 0, func-id: [[FIDARG:[0-9]+]], function: 'fArg(int)', args: [ 1 ], cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-enter-arg, tsc: {{[0-9]+}} }
-// TRACE-DAG: - { type: 0, func-id: [[FIDARG]], function: 'fArg(int)', cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-exit, tsc: {{[0-9]+}} }
+// TRACE-DAG: - { type: 0, func-id: [[FIDARG:[0-9]+]], function: 'fArg(int)', args: [ 1 ], cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-enter-arg, tsc: {{[0-9]+}}, data: '' }
+// TRACE-DAG: - { type: 0, func-id: [[FIDARG]], function: 'fArg(int)', cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-exit, tsc: {{[0-9]+}}, data: '' }
// Assert that when unwriting is enabled with a high threshold time, all the function records are erased. A CPU switch could erroneously fail this test, but
// is unlikely given the test program.
// Even with a high threshold, arg1 logging is never unwritten.
// UNWRITE: header:
// UNWRITE: records:
-// UNWRITE-NEXT: - { type: 0, func-id: [[FIDARG:[0-9]+]], function: 'fArg(int)', args: [ 1 ], cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], process: [[PROCESS:[0-9]+]], kind: function-enter-arg, tsc: {{[0-9]+}} }
-// UNWRITE-NEXT: - { type: 0, func-id: [[FIDARG]], function: 'fArg(int)', cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-exit, tsc: {{[0-9]+}} }
+// UNWRITE-NEXT: - { type: 0, func-id: [[FIDARG:[0-9]+]], function: 'fArg(int)', args: [ 1 ], cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], process: [[PROCESS:[0-9]+]], kind: function-enter-arg, tsc: {{[0-9]+}}, data: '' }
+// UNWRITE-NEXT: - { type: 0, func-id: [[FIDARG]], function: 'fArg(int)', cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: function-exit, tsc: {{[0-9]+}}, data: '' }
// UNWRITE-NOT: function-enter
// UNWRITE-NOT: function-{{exit|tail-exit}}
diff --git a/test/xray/TestCases/Posix/fdr-reinit.cc b/test/xray/TestCases/Posix/fdr-reinit.cc
new file mode 100644
index 000000000000..dc9888d6e48a
--- /dev/null
+++ b/test/xray/TestCases/Posix/fdr-reinit.cc
@@ -0,0 +1,73 @@
+// RUN: %clangxx_xray -g -std=c++11 %s -o %t
+// RUN: rm xray-log.fdr-reinit* || true
+// RUN: XRAY_OPTIONS="verbosity=1" %run %t
+// RUN: rm xray-log.fdr-reinit* || true
+#include "xray/xray_log_interface.h"
+#include <atomic>
+#include <cassert>
+#include <cstddef>
+#include <thread>
+
+volatile uint64_t var = 0;
+
+std::atomic_flag keep_going = ATOMIC_FLAG_INIT;
+
+[[clang::xray_always_instrument]] void __attribute__((noinline)) func() {
+ ++var;
+}
+
+int main(int argc, char *argv[]) {
+ // Start a thread that will just keep calling the function, to spam calls to
+ // the function call handler.
+ keep_going.test_and_set(std::memory_order_acquire);
+ std::thread t([] {
+ while (keep_going.test_and_set(std::memory_order_acquire))
+ func();
+ });
+
+ static constexpr char kConfig[] =
+ "buffer_size=1024:buffer_max=10:no_file_flush=true";
+
+ // Then we initialize the FDR mode implementation.
+ assert(__xray_log_select_mode("xray-fdr") ==
+ XRayLogRegisterStatus::XRAY_REGISTRATION_OK);
+ auto init_status = __xray_log_init_mode("xray-fdr", kConfig);
+ assert(init_status == XRayLogInitStatus::XRAY_LOG_INITIALIZED);
+
+ // Now we patch the instrumentation points.
+ __xray_patch();
+
+ // Spin for a bit, calling func() enough times.
+ for (auto i = 0; i < 1 << 20; ++i)
+ func();
+
+ // Then immediately finalize the implementation.
+ auto finalize_status = __xray_log_finalize();
+ assert(finalize_status == XRayLogInitStatus::XRAY_LOG_FINALIZED);
+
+ // Once we're here, we should then flush.
+ auto flush_status = __xray_log_flushLog();
+ assert(flush_status == XRayLogFlushStatus::XRAY_LOG_FLUSHED);
+
+ // Without doing anything else, we should re-initialize.
+ init_status = __xray_log_init_mode("xray-fdr", kConfig);
+ assert(init_status == XRayLogInitStatus::XRAY_LOG_INITIALIZED);
+
+ // Then we spin for a bit again calling func() enough times.
+ for (auto i = 0; i < 1 << 20; ++i)
+ func();
+
+ // Then immediately finalize the implementation.
+ finalize_status = __xray_log_finalize();
+ assert(finalize_status == XRayLogInitStatus::XRAY_LOG_FINALIZED);
+
+ // Once we're here, we should then flush.
+ flush_status = __xray_log_flushLog();
+ assert(flush_status == XRayLogFlushStatus::XRAY_LOG_FLUSHED);
+
+ // Finally, we should signal the sibling thread to stop.
+ keep_going.clear(std::memory_order_release);
+
+ // Then join.
+ t.join();
+}
diff --git a/test/xray/TestCases/Posix/fdr-single-thread.cc b/test/xray/TestCases/Posix/fdr-single-thread.cc
index 480502b0a60f..accc5925c682 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();
@@ -34,5 +28,5 @@ int main(int argc, char *argv[]) {
}
// CHECK: records:
-// CHECK-NEXT: - { type: 0, func-id: [[FID1:[0-9]+]], function: {{.*fn.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} }
-// CHECK-NEXT: - { type: 0, func-id: [[FID1:[0-9]+]], function: {{.*fn.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS:[0-9]+]], kind: function-exit, tsc: {{[0-9]+}} }
+// CHECK-NEXT: - { type: 0, func-id: [[FID1:[0-9]+]], function: {{.*fn.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}}, data: '' }
+// CHECK-NEXT: - { type: 0, func-id: [[FID1:[0-9]+]], function: {{.*fn.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS:[0-9]+]], kind: function-exit, tsc: {{[0-9]+}}, data: '' }
diff --git a/test/xray/TestCases/Posix/fdr-thread-order.cc b/test/xray/TestCases/Posix/fdr-thread-order.cc
index 1d6b01759f14..eb405967a0d8 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);
@@ -61,7 +52,7 @@ int main(int argc, char *argv[]) {
}
// We want to make sure that the order of the function log doesn't matter.
-// TRACE-DAG: - { type: 0, func-id: [[FID1:[0-9]+]], function: {{.*f1.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} }
-// TRACE-DAG: - { type: 0, func-id: [[FID2:[0-9]+]], function: {{.*f2.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], process: [[PROCESS]], kind: function-enter, tsc: {{[0-9]+}} }
-// TRACE-DAG: - { type: 0, func-id: [[FID1]], function: {{.*f1.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS]], kind: {{function-exit|function-tail-exit}}, tsc: {{[0-9]+}} }
-// TRACE-DAG: - { type: 0, func-id: [[FID2]], function: {{.*f2.*}}, cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: {{function-exit|function-tail-exit}}, tsc: {{[0-9]+}} }
+// TRACE-DAG: - { type: 0, func-id: [[FID1:[0-9]+]], function: {{.*f1.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}}, data: '' }
+// TRACE-DAG: - { type: 0, func-id: [[FID2:[0-9]+]], function: {{.*f2.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], process: [[PROCESS]], kind: function-enter, tsc: {{[0-9]+}}, data: '' }
+// TRACE-DAG: - { type: 0, func-id: [[FID1]], function: {{.*f1.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS]], kind: {{function-exit|function-tail-exit}}, tsc: {{[0-9]+}}, data: '' }
+// TRACE-DAG: - { type: 0, func-id: [[FID2]], function: {{.*f2.*}}, cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS]], kind: {{function-exit|function-tail-exit}}, tsc: {{[0-9]+}}, data: '' }
diff --git a/test/xray/TestCases/Posix/fork_basic_logging.cc b/test/xray/TestCases/Posix/fork_basic_logging.cc
index 5aefdec08016..3873325ef746 100644
--- a/test/xray/TestCases/Posix/fork_basic_logging.cc
+++ b/test/xray/TestCases/Posix/fork_basic_logging.cc
@@ -11,6 +11,9 @@
// REQUIRES: x86_64-target-arch
// REQUIRES: built-in-llvm-tree
+// Not ported.
+// UNSUPPORTED: netbsd
+
#include "xray/xray_log_interface.h"
#include <stdio.h>
#include <unistd.h>
@@ -81,20 +84,20 @@ int main()
}
// Make sure we know which thread is the parent process
-// TRACE-DAG: - { type: 0, func-id: [[LSGT:[0-9]+]], function: {{.*log_syscall_gettid.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS1:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} }
+// TRACE-DAG: - { type: 0, func-id: [[LSGT:[0-9]+]], function: {{.*log_syscall_gettid.*}}, cpu: {{.*}}, thread: [[THREAD1:[0-9]+]], process: [[PROCESS1:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}}, data: '' }
-// TRACE-DAG: - { type: 0, func-id: [[PPOC:[0-9]+]], function: {{.*print_parent_or_child.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS1]], kind: function-enter, tsc: {{[0-9]+}} }
+// TRACE-DAG: - { type: 0, func-id: [[PPOC:[0-9]+]], function: {{.*print_parent_or_child.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS1]], kind: function-enter, tsc: {{[0-9]+}}, data: '' }
//
// The parent will print its pid
-// TRACE-DAG: - { type: 0, func-id: [[PPTARG:[0-9]+]], function: {{.*print_parent_tid.*}}, args: [ [[THREAD1]] ], cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS1]], kind: function-enter-arg, tsc: {{[0-9]+}} }
-// TRACE-DAG: - { type: 0, func-id: [[PPTARG]], function: {{.*print_parent_tid.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS1]], kind: function-exit, tsc: {{[0-9]+}} }
+// TRACE-DAG: - { type: 0, func-id: [[PPTARG:[0-9]+]], function: {{.*print_parent_tid.*}}, args: [ [[THREAD1]] ], cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS1]], kind: function-enter-arg, tsc: {{[0-9]+}}, data: '' }
+// TRACE-DAG: - { type: 0, func-id: [[PPTARG]], function: {{.*print_parent_tid.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS1]], kind: function-exit, tsc: {{[0-9]+}}, data: '' }
//
-// TRACE-DAG - { type: 0, func-id: [[PPOC]], function: {{.*print_parent_or_child.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS1]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}} }
+// TRACE-DAG - { type: 0, func-id: [[PPOC]], function: {{.*print_parent_or_child.*}}, cpu: {{.*}}, thread: [[THREAD1]], process: [[PROCESS1]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}}, data: '' }
-// TRACE-DAG: - { type: 0, func-id: [[PPOC]], function: {{.*print_parent_or_child.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], process: [[PROCESS2:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}} }
+// TRACE-DAG: - { type: 0, func-id: [[PPOC]], function: {{.*print_parent_or_child.*}}, cpu: {{.*}}, thread: [[THREAD2:[0-9]+]], process: [[PROCESS2:[0-9]+]], kind: function-enter, tsc: {{[0-9]+}}, data: '' }
//
// The child will print its pid
-// TRACE-DAG: - { type: 0, func-id: [[PCTARG:[0-9]+]], function: {{.*print_child_tid.*}}, args: [ [[THREAD2]] ], cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS2]], kind: function-enter-arg, tsc: {{[0-9]+}} }
-// TRACE-DAG: - { type: 0, func-id: [[PCTARG]], function: {{.*print_child_tid.*}}, cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS2]], kind: function-exit, tsc: {{[0-9]+}} }
+// TRACE-DAG: - { type: 0, func-id: [[PCTARG:[0-9]+]], function: {{.*print_child_tid.*}}, args: [ [[THREAD2]] ], cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS2]], kind: function-enter-arg, tsc: {{[0-9]+}}, data: '' }
+// TRACE-DAG: - { type: 0, func-id: [[PCTARG]], function: {{.*print_child_tid.*}}, cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS2]], kind: function-exit, tsc: {{[0-9]+}}, data: '' }
//
-// TRACE-DAG: - { type: 0, func-id: [[PPOC]], function: {{.*print_parent_or_child.*}}, cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS2]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}} }
+// TRACE-DAG: - { type: 0, func-id: [[PPOC]], function: {{.*print_parent_or_child.*}}, cpu: {{.*}}, thread: [[THREAD2]], process: [[PROCESS2]], kind: function-{{exit|tail-exit}}, tsc: {{[0-9]+}}, data: '' }
diff --git a/test/xray/TestCases/Posix/profiling-multi-threaded.cc b/test/xray/TestCases/Posix/profiling-multi-threaded.cc
index 45e5e70226da..8bd15b7d312d 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 be860deafd08..54fcc1cbd23c 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 fafac19b7594..9f6fadaffc6b 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.
diff --git a/utils/generate_netbsd_ioctls.awk b/utils/generate_netbsd_ioctls.awk
index 9a92ff82a686..82b199214377 100755
--- a/utils/generate_netbsd_ioctls.awk
+++ b/utils/generate_netbsd_ioctls.awk
@@ -44,133 +44,13 @@ BEGIN {
rootdir = ENVIRON["ROOTDIR"]
}
- # hardcode list of headers with ioctl(2) entries
- # List generated manually with the following script:
- # for w in `find /usr/include/ -type f -name '*.h' -exec echo {} \;`; \
- # do awk '/[^a-zA-Z0-9_]_IO[W]*[R]*[ ]*\(/ && $2 ~ /^[A-Z_]+$/ {got=1} END{if(got) {print ARGV[1]}}' $w; \
- # done|awk '{print " ARGV[ARGC++] = rootdir \"" substr($0, 14) "\""}'
-
- ARGV[ARGC++] = rootdir "altq/altq_afmap.h"
- ARGV[ARGC++] = rootdir "altq/altq.h"
- ARGV[ARGC++] = rootdir "altq/altq_blue.h"
- ARGV[ARGC++] = rootdir "altq/altq_cbq.h"
- ARGV[ARGC++] = rootdir "altq/altq_cdnr.h"
- ARGV[ARGC++] = rootdir "altq/altq_fifoq.h"
- ARGV[ARGC++] = rootdir "altq/altq_hfsc.h"
- ARGV[ARGC++] = rootdir "altq/altq_jobs.h"
- ARGV[ARGC++] = rootdir "altq/altq_priq.h"
- ARGV[ARGC++] = rootdir "altq/altq_red.h"
- ARGV[ARGC++] = rootdir "altq/altq_rio.h"
- ARGV[ARGC++] = rootdir "altq/altq_wfq.h"
- ARGV[ARGC++] = rootdir "crypto/cryptodev.h"
- ARGV[ARGC++] = rootdir "dev/apm/apmio.h"
- ARGV[ARGC++] = rootdir "dev/dm/netbsd-dm.h"
- ARGV[ARGC++] = rootdir "dev/dmover/dmover_io.h"
- ARGV[ARGC++] = rootdir "dev/dtv/dtvio_demux.h"
- ARGV[ARGC++] = rootdir "dev/dtv/dtvio_frontend.h"
- ARGV[ARGC++] = rootdir "dev/filemon/filemon.h"
- ARGV[ARGC++] = rootdir "dev/hdaudio/hdaudioio.h"
- ARGV[ARGC++] = rootdir "dev/hdmicec/hdmicecio.h"
- ARGV[ARGC++] = rootdir "dev/hpc/hpcfbio.h"
- ARGV[ARGC++] = rootdir "dev/i2o/iopio.h"
- ARGV[ARGC++] = rootdir "dev/ic/athioctl.h"
- ARGV[ARGC++] = rootdir "dev/ic/bt8xx.h"
- ARGV[ARGC++] = rootdir "dev/ic/hd44780var.h"
- ARGV[ARGC++] = rootdir "dev/ic/icp_ioctl.h"
- ARGV[ARGC++] = rootdir "dev/ic/isp_ioctl.h"
- ARGV[ARGC++] = rootdir "dev/ic/mlxio.h"
- ARGV[ARGC++] = rootdir "dev/ic/nvmeio.h"
- ARGV[ARGC++] = rootdir "dev/ir/irdaio.h"
- ARGV[ARGC++] = rootdir "dev/isa/satlinkio.h"
- ARGV[ARGC++] = rootdir "dev/isa/isvio.h"
- ARGV[ARGC++] = rootdir "dev/isa/wtreg.h"
- ARGV[ARGC++] = rootdir "dev/iscsi/iscsi_ioctl.h"
- ARGV[ARGC++] = rootdir "dev/ofw/openfirmio.h"
- ARGV[ARGC++] = rootdir "dev/pci/amrio.h"
- ARGV[ARGC++] = rootdir "dev/pci/mlyio.h"
- ARGV[ARGC++] = rootdir "dev/pci/pciio.h"
- ARGV[ARGC++] = rootdir "dev/pci/tweio.h"
- ARGV[ARGC++] = rootdir "dev/pcmcia/if_cnwioctl.h"
- ARGV[ARGC++] = rootdir "dev/pcmcia/if_rayreg.h"
- ARGV[ARGC++] = rootdir "dev/raidframe/raidframeio.h"
- ARGV[ARGC++] = rootdir "dev/sbus/mbppio.h"
- ARGV[ARGC++] = rootdir "dev/scsipi/ses.h"
- ARGV[ARGC++] = rootdir "dev/sun/disklabel.h"
- ARGV[ARGC++] = rootdir "dev/sun/fbio.h"
- ARGV[ARGC++] = rootdir "dev/sun/kbio.h"
- ARGV[ARGC++] = rootdir "dev/sun/vuid_event.h"
- ARGV[ARGC++] = rootdir "dev/tc/sticio.h"
- ARGV[ARGC++] = rootdir "dev/usb/ukyopon.h"
- ARGV[ARGC++] = rootdir "dev/usb/urio.h"
- ARGV[ARGC++] = rootdir "dev/usb/usb.h"
- ARGV[ARGC++] = rootdir "dev/usb/utoppy.h"
- ARGV[ARGC++] = rootdir "dev/vme/xio.h"
- ARGV[ARGC++] = rootdir "dev/wscons/wsdisplay_usl_io.h"
- ARGV[ARGC++] = rootdir "dev/wscons/wsconsio.h"
- ARGV[ARGC++] = rootdir "dev/biovar.h"
- ARGV[ARGC++] = rootdir "dev/md.h"
- ARGV[ARGC++] = rootdir "dev/ccdvar.h"
- ARGV[ARGC++] = rootdir "dev/cgdvar.h"
- ARGV[ARGC++] = rootdir "dev/fssvar.h"
- ARGV[ARGC++] = rootdir "dev/bluetooth/btdev.h"
- ARGV[ARGC++] = rootdir "dev/bluetooth/btsco.h"
- ARGV[ARGC++] = rootdir "dev/kttcpio.h"
- ARGV[ARGC++] = rootdir "dev/lockstat.h"
- ARGV[ARGC++] = rootdir "dev/vndvar.h"
- ARGV[ARGC++] = rootdir "dev/spkrio.h"
- ARGV[ARGC++] = rootdir "net/bpf.h"
- ARGV[ARGC++] = rootdir "net/if_atm.h"
- ARGV[ARGC++] = rootdir "net/if_gre.h"
- ARGV[ARGC++] = rootdir "net/if_ppp.h"
- ARGV[ARGC++] = rootdir "net/npf.h"
- ARGV[ARGC++] = rootdir "net/if_pppoe.h"
- ARGV[ARGC++] = rootdir "net/if_sppp.h"
- ARGV[ARGC++] = rootdir "net/if_srt.h"
- ARGV[ARGC++] = rootdir "net/if_tap.h"
- ARGV[ARGC++] = rootdir "net/if_tun.h"
- ARGV[ARGC++] = rootdir "net/pfvar.h"
- ARGV[ARGC++] = rootdir "net/slip.h"
- ARGV[ARGC++] = rootdir "netbt/hci.h"
- ARGV[ARGC++] = rootdir "netinet/ip_nat.h"
- ARGV[ARGC++] = rootdir "netinet/ip_proxy.h"
- ARGV[ARGC++] = rootdir "netinet6/in6_var.h"
- ARGV[ARGC++] = rootdir "netnatm/natm.h"
- ARGV[ARGC++] = rootdir "netsmb/smb_dev.h"
- ARGV[ARGC++] = rootdir "sys/agpio.h"
- ARGV[ARGC++] = rootdir "sys/audioio.h"
- ARGV[ARGC++] = rootdir "sys/ataio.h"
- ARGV[ARGC++] = rootdir "sys/cdio.h"
- ARGV[ARGC++] = rootdir "sys/chio.h"
- ARGV[ARGC++] = rootdir "sys/clockctl.h"
- ARGV[ARGC++] = rootdir "sys/cpuio.h"
- ARGV[ARGC++] = rootdir "sys/dkio.h"
- ARGV[ARGC++] = rootdir "sys/drvctlio.h"
- ARGV[ARGC++] = rootdir "sys/dvdio.h"
- ARGV[ARGC++] = rootdir "sys/envsys.h"
- ARGV[ARGC++] = rootdir "sys/event.h"
- ARGV[ARGC++] = rootdir "sys/fdio.h"
- ARGV[ARGC++] = rootdir "sys/filio.h"
- ARGV[ARGC++] = rootdir "sys/gpio.h"
- ARGV[ARGC++] = rootdir "sys/ioctl.h"
- ARGV[ARGC++] = rootdir "sys/ioctl_compat.h"
- ARGV[ARGC++] = rootdir "sys/joystick.h"
- ARGV[ARGC++] = rootdir "sys/ksyms.h"
- ARGV[ARGC++] = rootdir "sys/lua.h"
- ARGV[ARGC++] = rootdir "sys/midiio.h"
- ARGV[ARGC++] = rootdir "sys/mtio.h"
- ARGV[ARGC++] = rootdir "sys/power.h"
- ARGV[ARGC++] = rootdir "sys/radioio.h"
- ARGV[ARGC++] = rootdir "sys/rndio.h"
- ARGV[ARGC++] = rootdir "sys/scanio.h"
- ARGV[ARGC++] = rootdir "sys/scsiio.h"
- ARGV[ARGC++] = rootdir "sys/sockio.h"
- ARGV[ARGC++] = rootdir "sys/timepps.h"
- ARGV[ARGC++] = rootdir "sys/ttycom.h"
- ARGV[ARGC++] = rootdir "sys/verified_exec.h"
- ARGV[ARGC++] = rootdir "sys/videoio.h"
- ARGV[ARGC++] = rootdir "sys/wdog.h"
- ARGV[ARGC++] = rootdir "soundcard.h"
- ARGV[ARGC++] = rootdir "xen/xenio.h"
+ # detect and register files to detect ioctl() definitions
+ ARGC = 1
+ cmd = "find " rootdir " -type f -name '*.h'"
+ while (cmd | getline) {
+ ARGV[ARGC++] = $0
+ }
+ close(cmd)
ioctl_table_max = 0
}
@@ -314,6 +194,7 @@ FNR == 1 {
$0 ~ /PRIQ_IF_DETACH/ ||
$0 ~ /PRIQ_ENABLE/ ||
$0 ~ /WFQ_IF_ATTACH/ ||
+ $0 ~ /POWER_IOC_GET_TYPE_WITH_LOSSAGE/ ||
$0 ~ /HFSC_DEL_FILTER/) {
# There are entries with duplicate codes.. disable the less used ones
next
@@ -345,17 +226,12 @@ FNR == 1 {
# This !NONE check allows to skip some unparsable entries
if (ioctl_mode[ioctl_table_max] != "NONE") {
- # special cases first
- if ($0 ~ /POWER_IOC_GET_TYPE_WITH_LOSSAGE/) {
- ioctl_type[ioctl_table_max] = "sizeof(uptr)"
- } else {
- n = split($0, a, ",")
- if (n == 3) {
- gsub(/^[ ]+/, "", a[3])
- match(a[3], /[a-zA-Z0-9_* ]+/)
- type = get_type(substr(a[3], 0, RLENGTH))
- ioctl_type[ioctl_table_max] = type
- }
+ n = split($0, a, ",")
+ if (n == 3) {
+ gsub(/^[ ]+/, "", a[3])
+ match(a[3], /[a-zA-Z0-9_* ]+/)
+ type = get_type(substr(a[3], 0, RLENGTH))
+ ioctl_type[ioctl_table_max] = type
}
}
@@ -748,6 +624,8 @@ function get_type(string)
return "struct_RF_SingleComponent_sz"
} else if (string == "RF_ProgressInfo_t") {
return "struct_RF_ProgressInfo_sz"
+ } else if (string == "nvlist_ref_t") {
+ return "struct_nvlist_ref_sz"
} else {
print "Unrecognized entry: " string
print "Aborting"
diff --git a/utils/generate_netbsd_syscalls.awk b/utils/generate_netbsd_syscalls.awk
index 5e08900a17ee..ac08025460fb 100755
--- a/utils/generate_netbsd_syscalls.awk
+++ b/utils/generate_netbsd_syscalls.awk
@@ -1464,6 +1464,8 @@ function syscall_body(syscall, mode)
pcmd(" }")
pcmd("}")
}
+ } else if (syscall == "getsockopt2") {
+ pcmd("/* TODO */")
} else if (syscall == "fpathconf") {
pcmd("/* Nothing to do */")
} else if (syscall == "getrlimit") {
@@ -1982,10 +1984,6 @@ function syscall_body(syscall, mode)
pcmd("if (nsa_) {")
pcmd(" PRE_READ(nsa_, sizeof(__sanitizer_sigaction));")
pcmd("}")
- } else if (syscall == "pmc_get_info") {
- pcmd("/* TODO */")
- } else if (syscall == "pmc_control") {
- pcmd("/* TODO */")
} else if (syscall == "rasctl") {
pcmd("/* Nothing to do */")
} else if (syscall == "kqueue") {
@@ -2935,16 +2933,14 @@ function syscall_body(syscall, mode)
} else if (syscall == "sendmmsg") {
if (mode == "pre") {
pcmd("struct __sanitizer_mmsghdr *mmsg = (struct __sanitizer_mmsghdr *)mmsg_;")
- pcmd("unsigned int vlen = (vlen_ > 1024 ? 1024 : vlen_);")
pcmd("if (mmsg) {")
- pcmd(" PRE_READ(mmsg, sizeof(struct __sanitizer_mmsghdr) * vlen);")
+ pcmd(" PRE_READ(mmsg, sizeof(struct __sanitizer_mmsghdr) * (vlen_ > 1024 ? 1024 : vlen_));")
pcmd("}")
} else {
pcmd("struct __sanitizer_mmsghdr *mmsg = (struct __sanitizer_mmsghdr *)mmsg_;")
- pcmd("unsigned int vlen = (vlen_ > 1024 ? 1024 : vlen_);")
pcmd("if (res >= 0) {")
pcmd(" if (mmsg) {")
- pcmd(" POST_READ(mmsg, sizeof(struct __sanitizer_mmsghdr) * vlen);")
+ pcmd(" POST_READ(mmsg, sizeof(struct __sanitizer_mmsghdr) * (vlen_ > 1024 ? 1024 : vlen_));")
pcmd(" }")
pcmd("}")
}