summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Rupprecht <rupprecht@google.com>2018-12-19 02:24:12 +0000
committerJordan Rupprecht <rupprecht@google.com>2018-12-19 02:24:12 +0000
commit6fc0ad0a5de45f80140620e2dd606f65d547362a (patch)
tree70c48eb656ed8eaa179a465c30efe4433a7a7494
parentf8f9fdb8560e9d0abac7edaa5ca40e9bed881695 (diff)
parentcd24f2f94f4edc9a636d02ee841b5a729b2b2ec3 (diff)
downloadcompiler-rt-6fc0ad0a5de45f80140620e2dd606f65d547362a.tar.gz
Creating branches/google/stable and tags/google/stable/2018-12-18 from r349201
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/branches/google/stable@349597 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--CMakeLists.txt9
-rw-r--r--cmake/Modules/AddCompilerRT.cmake30
-rw-r--r--cmake/Modules/CompilerRTUtils.cmake5
-rw-r--r--cmake/Modules/HandleCompilerRT.cmake4
-rw-r--r--cmake/base-config-ix.cmake2
-rw-r--r--cmake/config-ix.cmake14
-rw-r--r--include/sanitizer/common_interface_defs.h6
-rw-r--r--include/sanitizer/netbsd_syscall_hooks.h46
-rw-r--r--lib/asan/CMakeLists.txt37
-rw-r--r--lib/asan/asan_allocator.h15
-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_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.cc6
-rw-r--r--lib/asan/asan_rtems.cc6
-rw-r--r--lib/asan/asan_rtl.cc10
-rw-r--r--lib/asan/asan_win.cc7
-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/arm/aeabi_cdcmp.S6
-rw-r--r--lib/builtins/arm/aeabi_cfcmp.S6
-rw-r--r--lib/builtins/cpu_model.c113
-rw-r--r--lib/builtins/divdf3.c46
-rw-r--r--lib/builtins/divsf3.c40
-rw-r--r--lib/builtins/gcc_personality_v0.c5
-rw-r--r--lib/builtins/int_lib.h2
-rw-r--r--lib/builtins/int_types.h23
-rw-r--r--lib/builtins/os_version_check.c41
-rw-r--r--lib/cfi/CMakeLists.txt2
-rw-r--r--lib/cfi/cfi.cc40
-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.txt2
-rw-r--r--lib/fuzzer/FuzzerCommand.h4
-rw-r--r--lib/fuzzer/FuzzerIO.cpp4
-rw-r--r--lib/fuzzer/FuzzerLoop.cpp3
-rw-r--r--lib/fuzzer/FuzzerTracePC.cpp46
-rw-r--r--lib/fuzzer/FuzzerUtilFuchsia.cpp21
-rw-r--r--lib/fuzzer/afl/afl_driver.cpp2
-rw-r--r--lib/fuzzer/tests/CMakeLists.txt10
-rw-r--r--lib/hwasan/CMakeLists.txt25
-rw-r--r--lib/hwasan/hwasan.cc42
-rw-r--r--lib/hwasan/hwasan.h7
-rw-r--r--lib/hwasan/hwasan_allocator.cc120
-rw-r--r--lib/hwasan/hwasan_allocator.h8
-rw-r--r--lib/hwasan/hwasan_flags.inc31
-rw-r--r--lib/hwasan/hwasan_interceptors.cc38
-rw-r--r--lib/hwasan/hwasan_interface_internal.h3
-rw-r--r--lib/hwasan/hwasan_linux.cc32
-rw-r--r--lib/hwasan/hwasan_report.cc183
-rw-r--r--lib/hwasan/hwasan_report.h7
-rw-r--r--lib/interception/interception.h2
-rw-r--r--lib/interception/interception_linux.h2
-rw-r--r--lib/lsan/lsan_allocator.h12
-rw-r--r--lib/lsan/lsan_common_mac.cc4
-rw-r--r--lib/lsan/lsan_interceptors.cc2
-rw-r--r--lib/msan/msan_allocator.cc6
-rw-r--r--lib/msan/msan_interceptors.cc72
-rw-r--r--lib/msan/tests/msan_test.cc4
-rw-r--r--lib/profile/GCDAProfiling.c49
-rw-r--r--lib/profile/InstrProfilingValue.c5
-rw-r--r--lib/profile/WindowsMMap.c8
-rw-r--r--lib/profile/WindowsMMap.h8
-rw-r--r--lib/sanitizer_common/CMakeLists.txt3
-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_internal.h3
-rw-r--r--lib/sanitizer_common/sanitizer_allocator_primary32.h4
-rw-r--r--lib/sanitizer_common/sanitizer_allocator_secondary.h16
-rw-r--r--lib/sanitizer_common/sanitizer_common.h2
-rw-r--r--lib/sanitizer_common/sanitizer_common_interceptors.inc1808
-rw-r--r--lib/sanitizer_common/sanitizer_common_libcdep.cc17
-rw-r--r--lib/sanitizer_common/sanitizer_coverage_fuchsia.cc3
-rw-r--r--lib/sanitizer_common/sanitizer_file.h3
-rw-r--r--lib/sanitizer_common/sanitizer_fuchsia.cc7
-rw-r--r--lib/sanitizer_common/sanitizer_interceptors_ioctl_netbsd.inc37
-rw-r--r--lib/sanitizer_common/sanitizer_internal_defs.h3
-rw-r--r--lib/sanitizer_common/sanitizer_linux.cc204
-rw-r--r--lib/sanitizer_common/sanitizer_linux.h3
-rw-r--r--lib/sanitizer_common/sanitizer_linux_libcdep.cc35
-rw-r--r--lib/sanitizer_common/sanitizer_local_address_space_view.h53
-rw-r--r--lib/sanitizer_common/sanitizer_mac.cc83
-rw-r--r--lib/sanitizer_common/sanitizer_mac.h1
-rw-r--r--lib/sanitizer_common/sanitizer_malloc_mac.inc37
-rw-r--r--lib/sanitizer_common/sanitizer_netbsd.cc330
-rw-r--r--lib/sanitizer_common/sanitizer_openbsd.cc13
-rw-r--r--lib/sanitizer_common/sanitizer_platform.h9
-rw-r--r--lib/sanitizer_common/sanitizer_platform_interceptors.h36
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_freebsd.cc513
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_freebsd.h648
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_netbsd.cc313
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_netbsd.h123
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_posix.cc73
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_posix.h132
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_solaris.h3
-rw-r--r--lib/sanitizer_common/sanitizer_posix.cc5
-rw-r--r--lib/sanitizer_common/sanitizer_posix.h3
-rw-r--r--lib/sanitizer_common/sanitizer_posix_libcdep.cc8
-rw-r--r--lib/sanitizer_common/sanitizer_procmaps_bsd.cc8
-rw-r--r--lib/sanitizer_common/sanitizer_procmaps_solaris.cc2
-rw-r--r--lib/sanitizer_common/sanitizer_rtems.cc7
-rw-r--r--lib/sanitizer_common/sanitizer_solaris.cc45
-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_thread_registry.cc11
-rw-r--r--lib/sanitizer_common/sanitizer_thread_registry.h1
-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.cc17
-rw-r--r--lib/sanitizer_common/symbolizer/scripts/global_symbols.txt3
-rw-r--r--lib/sanitizer_common/tests/CMakeLists.txt8
-rw-r--r--lib/sanitizer_common/tests/sanitizer_allocator_test.cc47
-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.h3
-rw-r--r--lib/scudo/scudo_malloc.cpp2
-rwxr-xr-xlib/tsan/check_analyze.sh6
-rwxr-xr-xlib/tsan/go/buildgo.sh1
-rw-r--r--lib/tsan/rtl/tsan_interceptors.cc50
-rw-r--r--lib/tsan/rtl/tsan_interceptors.h4
-rw-r--r--lib/tsan/rtl/tsan_interceptors_mac.cc2
-rw-r--r--lib/tsan/rtl/tsan_platform.h38
-rw-r--r--lib/tsan/rtl/tsan_rtl.cc4
-rw-r--r--lib/tsan/rtl/tsan_rtl.h8
-rw-r--r--lib/tsan/rtl/tsan_rtl_thread.cc6
-rw-r--r--lib/ubsan/ubsan_checks.inc14
-rw-r--r--lib/ubsan/ubsan_flags.inc4
-rw-r--r--lib/ubsan/ubsan_handlers.cc41
-rw-r--r--lib/ubsan/ubsan_handlers.h6
-rw-r--r--lib/ubsan_minimal/CMakeLists.txt8
-rw-r--r--lib/xray/CMakeLists.txt4
-rw-r--r--lib/xray/tests/CMakeLists.txt1
-rw-r--r--lib/xray/tests/unit/CMakeLists.txt8
-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.cc122
-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.h135
-rw-r--r--lib/xray/xray_basic_logging.cc68
-rw-r--r--lib/xray/xray_buffer_queue.cc236
-rw-r--r--lib/xray/xray_buffer_queue.h67
-rw-r--r--lib/xray/xray_fdr_controller.h373
-rw-r--r--lib/xray/xray_fdr_log_writer.h117
-rw-r--r--lib/xray/xray_fdr_logging.cc929
-rw-r--r--lib/xray/xray_function_call_trie.h368
-rw-r--r--lib/xray/xray_init.cc6
-rw-r--r--lib/xray/xray_interface.cc41
-rw-r--r--lib/xray/xray_profile_collector.cc241
-rw-r--r--lib/xray/xray_profile_collector.h26
-rw-r--r--lib/xray/xray_profiling.cc391
-rw-r--r--lib/xray/xray_profiling_flags.inc5
-rw-r--r--lib/xray/xray_segmented_array.h525
-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.cc19
-rw-r--r--test/.clang-format2
-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/Linux/local_alias.cc2
-rw-r--r--test/asan/TestCases/Linux/odr-violation.cc16
-rw-r--r--test/asan/TestCases/Linux/odr_indicators.cc26
-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/stack-use-after-return.cc4
-rw-r--r--test/asan/TestCases/Posix/tsd_dtor_leak.cc1
-rw-r--r--test/asan/TestCases/asan_and_llvm_coverage_test.cc3
-rw-r--r--test/asan/TestCases/heavy_uar_test.cc4
-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/suppressions-library.cc1
-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/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/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/cross-dso/lit.local.cfg2
-rw-r--r--test/esan/TestCases/large-stack-linux.c2
-rw-r--r--test/esan/TestCases/workingset-early-fault.c2
-rw-r--r--test/esan/TestCases/workingset-memset.cpp2
-rw-r--r--test/esan/TestCases/workingset-midreport.cpp2
-rw-r--r--test/esan/TestCases/workingset-samples.cpp2
-rw-r--r--test/esan/TestCases/workingset-signal-posix.cpp2
-rw-r--r--test/esan/TestCases/workingset-simple.cpp2
-rw-r--r--test/esan/lit.cfg3
-rw-r--r--test/fuzzer/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/ReadBinaryTest.cpp18
-rw-r--r--test/fuzzer/counters.test3
-rw-r--r--test/fuzzer/coverage.test2
-rw-r--r--test/fuzzer/dead-stripping.test23
-rw-r--r--test/fuzzer/dump_coverage.test2
-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-oom.test2
-rw-r--r--test/fuzzer/handle-unstable.test2
-rw-r--r--test/fuzzer/lit.cfg1
-rw-r--r--test/fuzzer/read-binary.test7
-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-div.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-switch.test2
-rw-r--r--test/fuzzer/windows-opt-ref.test9
-rw-r--r--test/hwasan/TestCases/abort-message-android.cc28
-rw-r--r--test/hwasan/TestCases/cfi.cc18
-rw-r--r--test/hwasan/TestCases/heap-buffer-overflow.c45
-rw-r--r--test/hwasan/TestCases/random-align-right.c35
-rw-r--r--test/hwasan/TestCases/stack-history-length.c10
-rw-r--r--test/hwasan/TestCases/stack-uar.c36
-rw-r--r--test/hwasan/TestCases/tail-magic.c28
-rw-r--r--test/hwasan/TestCases/thread-uaf.c2
-rw-r--r--test/hwasan/TestCases/use-after-free.c9
-rw-r--r--test/lit.common.cfg3
-rw-r--r--test/msan/Linux/reexec_unlimited_stack.cc23
-rw-r--r--test/msan/chained_origin_with_signals.cc3
-rw-r--r--test/msan/dtls_test.c3
-rw-r--r--test/msan/fork.cc3
-rw-r--r--test/msan/ioctl_custom.cc3
-rw-r--r--test/msan/signal_stress_test.cc3
-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.gcov4
-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.gcov2
-rw-r--r--test/profile/Inputs/instrprof-gcov-switch2.c.gcov2
-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.gcov2
-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-gcov-__gcov_flush-multiple.test10
-rw-r--r--test/profile/instrprof-gcov-one-line-function.test9
-rw-r--r--test/sanitizer_common/TestCases/FreeBSD/capsicum.cc62
-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/fts.cc40
-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/regex.cc101
-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/strtonum.cc52
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/sysctlgetmibinfo.cc36
-rw-r--r--test/sanitizer_common/TestCases/NetBSD/vis.cc245
-rw-r--r--test/sanitizer_common/TestCases/Posix/dedup_token_length_test.cc2
-rw-r--r--test/sanitizer_common/TestCases/Posix/devname_r.cc5
-rw-r--r--test/sanitizer_common/TestCases/Posix/dump_instruction_bytes.cc2
-rw-r--r--test/sanitizer_common/TestCases/Posix/fseek.cc53
-rw-r--r--test/sanitizer_common/TestCases/Posix/getmntinfo.cc35
-rw-r--r--test/sanitizer_common/TestCases/Posix/nl_langinfo.cc20
-rw-r--r--test/sanitizer_common/TestCases/Posix/readlinkat.c2
-rw-r--r--test/sanitizer_common/TestCases/Posix/setvbuf.cc81
-rw-r--r--test/sanitizer_common/TestCases/Posix/sysctl.cc64
-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/pthread_mutexattr_get.cc3
-rw-r--r--test/sanitizer_common/TestCases/sanitizer_coverage_symbolize.cc2
-rw-r--r--test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc2
-rw-r--r--test/sanitizer_common/TestCases/symbolize_pc.cc23
-rw-r--r--test/sanitizer_common/TestCases/symbolize_pc_inline.cc32
-rwxr-xr-xtest/sanitizer_common/ios_commands/iossim_run.py2
-rw-r--r--test/sanitizer_common/lit.common.cfg6
-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/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.cc2
-rw-r--r--test/tsan/large_malloc_meta.cc3
-rw-r--r--test/tsan/mmap_large.cc3
-rw-r--r--test/tsan/mutex_lock_destroyed.cc3
-rw-r--r--test/tsan/strerror_r.cc8
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/integer-arithmetic-value-change.c345
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/integer-conversion.c117
-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.c26
-rw-r--r--test/ubsan/TestCases/ImplicitConversion/integer-truncation.c84
-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/TypeCheck/vptr-non-unique-typeinfo.cpp1
-rw-r--r--test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp2
-rw-r--r--test/ubsan/TestCases/TypeCheck/vptr.cpp2
-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)2
-rw-r--r--test/xray/TestCases/Posix/basic-filtering.cc4
-rw-r--r--test/xray/TestCases/Posix/fdr-mode.cc32
-rw-r--r--test/xray/TestCases/Posix/fdr-reinit.cc73
-rw-r--r--test/xray/TestCases/Posix/fdr-single-thread.cc4
-rw-r--r--test/xray/TestCases/Posix/fdr-thread-order.cc8
-rw-r--r--test/xray/TestCases/Posix/fork_basic_logging.cc21
-rwxr-xr-xutils/generate_netbsd_ioctls.awk154
-rwxr-xr-xutils/generate_netbsd_syscalls.awk12
407 files changed, 13996 insertions, 2941 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fbab64394..4697dba62 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -375,6 +375,7 @@ append_list_if(COMPILER_RT_HAS_NODEFAULTLIBS_FLAG -nodefaultlibs SANITIZER_COMMO
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)
@@ -385,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)
diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake
index 4efbd3a4a..81b110203 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")
@@ -371,15 +371,6 @@ 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()
@@ -560,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
@@ -572,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 d2cb48677..c4500328e 100644
--- a/cmake/Modules/CompilerRTUtils.cmake
+++ b/cmake/Modules/CompilerRTUtils.cmake
@@ -337,7 +337,10 @@ endfunction()
function(get_compiler_rt_target arch variable)
string(FIND ${COMPILER_RT_DEFAULT_TARGET_TRIPLE} "-" dash_index)
string(SUBSTRING ${COMPILER_RT_DEFAULT_TARGET_TRIPLE} ${dash_index} -1 triple_suffix)
- if(ANDROID AND ${arch} STREQUAL "i386")
+ 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}${triple_suffix}")
diff --git a/cmake/Modules/HandleCompilerRT.cmake b/cmake/Modules/HandleCompilerRT.cmake
index 855d0ff9d..61b779278 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/base-config-ix.cmake b/cmake/base-config-ix.cmake
index b92e29a2e..2a44d830b 100644
--- a/cmake/base-config-ix.cmake
+++ b/cmake/base-config-ix.cmake
@@ -122,8 +122,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
diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake
index 6ab79dc76..7e9ad6355 100644
--- a/cmake/config-ix.cmake
+++ b/cmake/config-ix.cmake
@@ -53,6 +53,7 @@ if (COMPILER_RT_HAS_NODEFAULTLIBS_FLAG)
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)
@@ -64,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)
@@ -121,10 +121,12 @@ check_library_exists(pthread pthread_create "" COMPILER_RT_HAS_LIBPTHREAD)
# Look for terminfo library, used in unittests that depend on LLVMSupport.
if(LLVM_ENABLE_TERMINFO)
- foreach(library tinfo terminfo curses ncurses ncursesw)
+ foreach(library terminfo tinfo curses ncurses ncursesw)
+ string(TOUPPER ${library} library_suffix)
check_library_exists(
- ${library} setupterm "" COMPILER_RT_HAS_TERMINFO)
- if(COMPILER_RT_HAS_TERMINFO)
+ ${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()
@@ -625,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)
@@ -639,7 +641,7 @@ 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)
diff --git a/include/sanitizer/common_interface_defs.h b/include/sanitizer/common_interface_defs.h
index d11cb1add..bf015eb23 100644
--- a/include/sanitizer/common_interface_defs.h
+++ b/include/sanitizer/common_interface_defs.h
@@ -124,6 +124,12 @@ extern "C" {
// Symbolizes the supplied 'pc' using the format string 'fmt'.
// Outputs at most 'out_buf_size' bytes into 'out_buf'.
+ // If 'out_buf' is not empty then output is zero or more non empty C strings
+ // followed by single empty C string. Multiple strings can be returned if PC
+ // corresponds to inlined function. Inlined frames are printed in the order
+ // from "most-inlined" to the "least-inlined", so the last frame should be the
+ // not inlined function.
+ // Inlined frames can be removed with 'symbolize_inline_frames=0'.
// The format syntax is described in
// lib/sanitizer_common/sanitizer_stacktrace_printer.h.
void __sanitizer_symbolize_pc(void *pc, const char *fmt, char *out_buf,
diff --git a/include/sanitizer/netbsd_syscall_hooks.h b/include/sanitizer/netbsd_syscall_hooks.h
index 4c6c6a88a..b69f3d2be 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/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt
index 3ec9c500c..726da27d0 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)
@@ -92,7 +100,10 @@ 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)
-append_list_if(MINGW psapi ASAN_DYNAMIC_LIBS)
+
+if (TARGET cxx-headers OR HAVE_LIBCXX)
+ set(ASAN_DEPS cxx-headers)
+endif()
# Compile ASan sources into an object library.
@@ -102,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
@@ -110,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.
@@ -224,7 +240,8 @@ else()
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
@@ -269,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
@@ -294,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 93d6f29c5..51fba254a 100644
--- a/lib/asan/asan_allocator.h
+++ b/lib/asan/asan_allocator.h
@@ -162,22 +162,29 @@ typedef SizeClassAllocator64<AP64> PrimaryAllocator;
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;
diff --git a/lib/asan/asan_flags.inc b/lib/asan/asan_flags.inc
index 4af94c56f..a9c97d53b 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 0b5bff4f5..34399c923 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 898f7f40d..146234ac6 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_internal.h b/lib/asan/asan_internal.h
index 654878cd1..57869497c 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 625f32d40..a150b1955 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 76bdff999..0a534fe21 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 733ba2d86..27281f1bc 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 a094e051c..887936431 100644
--- a/lib/asan/asan_malloc_win.cc
+++ b/lib/asan/asan_malloc_win.cc
@@ -141,6 +141,11 @@ size_t _msize(void *ptr) {
}
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.
@@ -235,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_rtems.cc b/lib/asan/asan_rtems.cc
index a4af94005..b48cc6a75 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 4cff736f2..0ecbcd594 100644
--- a/lib/asan/asan_rtl.cc
+++ b/lib/asan/asan_rtl.cc
@@ -396,6 +396,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 +428,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();
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index 5661d911c..068f4a5d2 100644
--- a/lib/asan/asan_win.cc
+++ b/lib/asan/asan_win.cc
@@ -322,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 c6a313d24..df593ab92 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 68b6f093b..2dbb05283 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 1b7060591..9e640d1d8 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 69c8fe6f4..b5c8303cb 100644
--- a/lib/asan/tests/asan_interface_test.cc
+++ b/lib/asan/tests/asan_interface_test.cc
@@ -102,6 +102,7 @@ TEST(AddressSanitizerInterface, GetHeapSizeTest) {
}
}
+#if !defined(__NetBSD__)
static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357};
static const size_t kManyThreadsIterations = 250;
static const size_t kManyThreadsNumThreads =
@@ -135,6 +136,7 @@ TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) {
// so we can't check for equality here.
EXPECT_LT(after_test, before_test + (1UL<<20));
}
+#endif
static void DoDoubleFree() {
int *x = Ident(new int);
diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc
index 65acb2839..3e366842c 100644
--- a/lib/asan/tests/asan_noinst_test.cc
+++ b/lib/asan/tests/asan_noinst_test.cc
@@ -153,6 +153,7 @@ TEST(AddressSanitizer, QuarantineTest) {
EXPECT_LT(i, max_i);
}
+#if !defined(__NetBSD__)
void *ThreadedQuarantineTestWorker(void *unused) {
(void)unused;
u32 seed = my_rand();
@@ -187,6 +188,7 @@ TEST(AddressSanitizer, ThreadedQuarantineTest) {
EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20));
}
}
+#endif
void *ThreadedOneSizeMallocStress(void *unused) {
(void)unused;
diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc
index 25d6a2083..464e99f61 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/arm/aeabi_cdcmp.S b/lib/builtins/arm/aeabi_cdcmp.S
index 87dd03dce..adc2d55d9 100644
--- a/lib/builtins/arm/aeabi_cdcmp.S
+++ b/lib/builtins/arm/aeabi_cdcmp.S
@@ -55,7 +55,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmpeq)
mov ip, #APSR_C
msr APSR_nzcvq, ip
#else
- msr CPSR_f, #APSR_C
+ msr APSR_nzcvq, #APSR_C
#endif
JMP(lr)
#endif
@@ -115,11 +115,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmple)
movne ip, #(APSR_C)
1:
-#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
msr APSR_nzcvq, ip
-#else
- msr CPSR_f, ip
-#endif
pop {r0-r3}
POP_PC()
#endif
diff --git a/lib/builtins/arm/aeabi_cfcmp.S b/lib/builtins/arm/aeabi_cfcmp.S
index c5fee6b6a..4b1de9976 100644
--- a/lib/builtins/arm/aeabi_cfcmp.S
+++ b/lib/builtins/arm/aeabi_cfcmp.S
@@ -55,7 +55,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmpeq)
mov ip, #APSR_C
msr APSR_nzcvq, ip
#else
- msr CPSR_f, #APSR_C
+ msr APSR_nzcvq, #APSR_C
#endif
JMP(lr)
#endif
@@ -115,11 +115,7 @@ DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmple)
movne ip, #(APSR_C)
1:
-#if defined(__ARM_ARCH_7M__) || defined(__ARM_ARCH_7EM__)
msr APSR_nzcvq, ip
-#else
- msr CPSR_f, ip
-#endif
pop {r0-r3}
POP_PC()
#endif
diff --git a/lib/builtins/cpu_model.c b/lib/builtins/cpu_model.c
index 43b913390..fb2b899fc 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/divdf3.c b/lib/builtins/divdf3.c
index 04a4dc557..411c82ebb 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/divsf3.c b/lib/builtins/divsf3.c
index 65294d70f..a74917fd1 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/gcc_personality_v0.c b/lib/builtins/gcc_personality_v0.c
index 0bc765624..68581ef16 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 9d09e2dc9..fe8a3bded 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_types.h b/lib/builtins/int_types.h
index f53f343d3..9f8da56cb 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/os_version_check.c b/lib/builtins/os_version_check.c
index 772e33333..e0d40edc7 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/cfi/CMakeLists.txt b/lib/cfi/CMakeLists.txt
index 7ed72bca5..463a1fd59 100644
--- a/lib/cfi/CMakeLists.txt
+++ b/lib/cfi/CMakeLists.txt
@@ -1,6 +1,6 @@
add_compiler_rt_component(cfi)
-if(OS_NAME MATCHES "Linux")
+if(OS_NAME MATCHES "Linux" OR OS_NAME MATCHES "FreeBSD" OR OS_NAME MATCHES "NetBSD")
set(CFI_SOURCES cfi.cc)
include_directories(..)
diff --git a/lib/cfi/cfi.cc b/lib/cfi/cfi.cc
index a2f127f93..b0a943759 100644
--- a/lib/cfi/cfi.cc
+++ b/lib/cfi/cfi.cc
@@ -13,15 +13,33 @@
#include <assert.h>
#include <elf.h>
+
+#include "sanitizer_common/sanitizer_common.h"
+#if SANITIZER_FREEBSD
+#include <sys/link_elf.h>
+#endif
#include <link.h>
#include <string.h>
+#include <stdlib.h>
#include <sys/mman.h>
+#if SANITIZER_LINUX
typedef ElfW(Phdr) Elf_Phdr;
typedef ElfW(Ehdr) Elf_Ehdr;
+typedef ElfW(Addr) Elf_Addr;
+typedef ElfW(Sym) Elf_Sym;
+typedef ElfW(Dyn) Elf_Dyn;
+#elif SANITIZER_FREEBSD
+#if SANITIZER_WORDSIZE == 64
+#define ElfW64_Dyn Elf_Dyn
+#define ElfW64_Sym Elf_Sym
+#else
+#define ElfW32_Dyn Elf_Dyn
+#define ElfW32_Sym Elf_Sym
+#endif
+#endif
#include "interception/interception.h"
-#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "ubsan/ubsan_init.h"
#include "ubsan/ubsan_flags.h"
@@ -154,15 +172,25 @@ void ShadowBuilder::Add(uptr begin, uptr end, uptr cfi_check) {
*s = sv;
}
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD
void ShadowBuilder::Install() {
MprotectReadOnly(shadow_, GetShadowSize());
uptr main_shadow = GetShadow();
if (main_shadow) {
// Update.
+#if SANITIZER_LINUX
void *res = mremap((void *)shadow_, GetShadowSize(), GetShadowSize(),
MREMAP_MAYMOVE | MREMAP_FIXED, (void *)main_shadow);
CHECK(res != MAP_FAILED);
+#elif SANITIZER_NETBSD
+ void *res = mremap((void *)shadow_, GetShadowSize(), (void *)main_shadow,
+ GetShadowSize(), MAP_FIXED);
+ CHECK(res != MAP_FAILED);
+#else
+ void *res = MmapFixedOrDie(shadow_, GetShadowSize());
+ CHECK(res != MAP_FAILED);
+ ::memcpy(&shadow_, &main_shadow, GetShadowSize());
+#endif
} else {
// Initial setup.
CHECK_EQ(kCfiShadowLimitsStorageSize, GetPageSizeCached());
@@ -183,17 +211,17 @@ void ShadowBuilder::Install() {
// dlopen(RTLD_NOLOAD | RTLD_LAZY)
// dlsym("__cfi_check").
uptr find_cfi_check_in_dso(dl_phdr_info *info) {
- const ElfW(Dyn) *dynamic = nullptr;
+ const Elf_Dyn *dynamic = nullptr;
for (int i = 0; i < info->dlpi_phnum; ++i) {
if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
dynamic =
- (const ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
+ (const Elf_Dyn *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
break;
}
}
if (!dynamic) return 0;
uptr strtab = 0, symtab = 0, strsz = 0;
- for (const ElfW(Dyn) *p = dynamic; p->d_tag != PT_NULL; ++p) {
+ for (const Elf_Dyn *p = dynamic; p->d_tag != PT_NULL; ++p) {
if (p->d_tag == DT_SYMTAB)
symtab = p->d_un.d_ptr;
else if (p->d_tag == DT_STRTAB)
@@ -227,7 +255,7 @@ uptr find_cfi_check_in_dso(dl_phdr_info *info) {
return 0;
}
- for (const ElfW(Sym) *p = (const ElfW(Sym) *)symtab; (ElfW(Addr))p < strtab;
+ for (const Elf_Sym *p = (const Elf_Sym *)symtab; (Elf_Addr)p < strtab;
++p) {
// There is no reliable way to find the end of the symbol table. In
// lld-produces files, there are other sections between symtab and strtab.
diff --git a/lib/dfsan/dfsan.cc b/lib/dfsan/dfsan.cc
index d4dbebc43..585bdceac 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 4de5c0205..c880971e3 100644
--- a/lib/esan/CMakeLists.txt
+++ b/lib/esan/CMakeLists.txt
@@ -14,6 +14,7 @@ set(ESAN_SOURCES
esan_interceptors.cpp
esan_linux.cpp
esan_sideline_linux.cpp
+ esan_sideline_bsd.cpp
cache_frag.cpp
working_set.cpp
working_set_posix.cpp)
diff --git a/lib/esan/esan_interceptors.cpp b/lib/esan/esan_interceptors.cpp
index 0c596f1cf..833faa2cd 100644
--- a/lib/esan/esan_interceptors.cpp
+++ b/lib/esan/esan_interceptors.cpp
@@ -327,7 +327,7 @@ INTERCEPTOR(int, rmdir, char *path) {
// Signal-related interceptors
//===----------------------------------------------------------------------===//
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
typedef void (*signal_handler_t)(int);
INTERCEPTOR(signal_handler_t, signal, int signum, signal_handler_t handler) {
void *ctx;
@@ -344,7 +344,7 @@ INTERCEPTOR(signal_handler_t, signal, int signum, signal_handler_t handler) {
#define ESAN_MAYBE_INTERCEPT_SIGNAL
#endif
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
struct sigaction *oldact)
INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act,
@@ -363,7 +363,11 @@ int real_sigaction(int signum, const void *act, void *oldact) {
if (REAL(sigaction) == nullptr) {
// With an instrumented allocator, this is called during interceptor init
// and we need a raw syscall solution.
+#if SANITIZER_LINUX
return internal_sigaction_syscall(signum, act, oldact);
+#else
+ return internal_sigaction(signum, act, oldact);
+#endif
}
return REAL(sigaction)(signum, (const struct sigaction *)act,
(struct sigaction *)oldact);
@@ -376,7 +380,7 @@ int real_sigaction(int signum, const void *act, void *oldact) {
#define ESAN_MAYBE_INTERCEPT_SIGACTION
#endif
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set,
__sanitizer_sigset_t *oldset) {
void *ctx;
diff --git a/lib/esan/esan_shadow.h b/lib/esan/esan_shadow.h
index 72a919a19..b76a9ccbd 100644
--- a/lib/esan/esan_shadow.h
+++ b/lib/esan/esan_shadow.h
@@ -30,7 +30,7 @@ struct ApplicationRegion {
bool ShadowMergedWithPrev;
};
-#if SANITIZER_LINUX && defined(__x86_64__)
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && defined(__x86_64__)
// Linux x86_64
//
// Application memory falls into these 5 regions (ignoring the corner case
diff --git a/lib/esan/esan_sideline.h b/lib/esan/esan_sideline.h
index 04aff22f4..74551fbde 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 000000000..3134d3776
--- /dev/null
+++ b/lib/esan/esan_sideline_bsd.cpp
@@ -0,0 +1,35 @@
+//===-- esan_sideline_bsd.cpp -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of EfficiencySanitizer, a family of performance tuners.
+//
+// Support for a separate or "sideline" tool thread on FreeBSD.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_FREEBSD
+
+#include "esan_sideline.h"
+
+namespace __esan {
+
+static SidelineThread *TheThread;
+
+bool SidelineThread::launchThread(SidelineFunc takeSample, void *Arg,
+ u32 FreqMilliSec) {
+ return true;
+}
+
+bool SidelineThread::joinThread() {
+ return true;
+}
+
+} // namespace __esan
+
+#endif // SANITIZER_FREEBSD
diff --git a/lib/fuzzer/CMakeLists.txt b/lib/fuzzer/CMakeLists.txt
index aae3df8db..8ba3f8a5a 100644
--- a/lib/fuzzer/CMakeLists.txt
+++ b/lib/fuzzer/CMakeLists.txt
@@ -124,12 +124,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/FuzzerCommand.h b/lib/fuzzer/FuzzerCommand.h
index 255f571ec..9d258a228 100644
--- a/lib/fuzzer/FuzzerCommand.h
+++ b/lib/fuzzer/FuzzerCommand.h
@@ -81,7 +81,7 @@ public:
}
// Like hasArgument, but checks for "-[Flag]=...".
- bool hasFlag(const std::string &Flag) {
+ bool hasFlag(const std::string &Flag) const {
std::string Arg("-" + Flag + "=");
auto IsMatch = [&](const std::string &Other) {
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
@@ -92,7 +92,7 @@ public:
// Returns the value of the first instance of a given flag, or an empty string
// if the flag isn't present. Ignores any occurrences after
// "-ignore_remaining_args=1", if present.
- std::string getFlagValue(const std::string &Flag) {
+ std::string getFlagValue(const std::string &Flag) const {
std::string Arg("-" + Flag + "=");
auto IsMatch = [&](const std::string &Other) {
return Arg.compare(0, std::string::npos, Other, 0, Arg.length()) == 0;
diff --git a/lib/fuzzer/FuzzerIO.cpp b/lib/fuzzer/FuzzerIO.cpp
index dac5ec658..c4c31e824 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>());
}
diff --git a/lib/fuzzer/FuzzerLoop.cpp b/lib/fuzzer/FuzzerLoop.cpp
index 7b98f55ae..09c57c3f6 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
diff --git a/lib/fuzzer/FuzzerTracePC.cpp b/lib/fuzzer/FuzzerTracePC.cpp
index 75130840c..7ba75c7b2 100644
--- a/lib/fuzzer/FuzzerTracePC.cpp
+++ b/lib/fuzzer/FuzzerTracePC.cpp
@@ -194,11 +194,42 @@ void TracePC::HandleCallerCallee(uintptr_t Caller, uintptr_t Callee) {
ValueProfileMap.AddValueModPrime(Idx);
}
+/// \return the address of the previous instruction.
+/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.h`
+inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) {
+#if defined(__arm__)
+ // T32 (Thumb) branch instructions might be 16 or 32 bit long,
+ // so we return (pc-2) in that case in order to be safe.
+ // For A32 mode we return (pc-4) because all instructions are 32 bit long.
+ return (PC - 3) & (~1);
+#elif defined(__powerpc__) || defined(__powerpc64__) || defined(__aarch64__)
+ // PCs are always 4 byte aligned.
+ return PC - 4;
+#elif defined(__sparc__) || defined(__mips__)
+ return PC - 8;
+#else
+ return PC - 1;
+#endif
+}
+
+/// \return the address of the next instruction.
+/// Note: the logic is copied from `sanitizer_common/sanitizer_stacktrace.cc`
+inline ALWAYS_INLINE uintptr_t GetNextInstructionPc(uintptr_t PC) {
+#if defined(__mips__)
+ return PC + 8;
+#elif defined(__powerpc__) || defined(__sparc__) || defined(__arm__) || \
+ defined(__aarch64__)
+ return PC + 4;
+#else
+ return PC + 1;
+#endif
+}
+
void TracePC::UpdateObservedPCs() {
Vector<uintptr_t> CoveredFuncs;
auto ObservePC = [&](uintptr_t PC) {
if (ObservedPCs.insert(PC).second && DoPrintNewPCs) {
- PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", PC + 1);
+ PrintPC("\tNEW_PC: %p %F %L", "\tNEW_PC: %p", GetNextInstructionPc(PC));
Printf("\n");
}
};
@@ -233,22 +264,11 @@ void TracePC::UpdateObservedPCs() {
for (size_t i = 0, N = Min(CoveredFuncs.size(), NumPrintNewFuncs); i < N;
i++) {
Printf("\tNEW_FUNC[%zd/%zd]: ", i + 1, CoveredFuncs.size());
- PrintPC("%p %F %L", "%p", CoveredFuncs[i] + 1);
+ PrintPC("%p %F %L", "%p", GetNextInstructionPc(CoveredFuncs[i]));
Printf("\n");
}
}
-inline ALWAYS_INLINE uintptr_t GetPreviousInstructionPc(uintptr_t PC) {
- // TODO: this implementation is x86 only.
- // see sanitizer_common GetPreviousInstructionPc for full implementation.
- return PC - 1;
-}
-
-inline ALWAYS_INLINE uintptr_t GetNextInstructionPc(uintptr_t PC) {
- // TODO: this implementation is x86 only.
- // see sanitizer_common GetPreviousInstructionPc for full implementation.
- return PC + 1;
-}
static std::string GetModuleName(uintptr_t PC) {
char ModulePathRaw[4096] = ""; // What's PATH_MAX in portable C++?
diff --git a/lib/fuzzer/FuzzerUtilFuchsia.cpp b/lib/fuzzer/FuzzerUtilFuchsia.cpp
index b9c70e461..cd48fefef 100644
--- a/lib/fuzzer/FuzzerUtilFuchsia.cpp
+++ b/lib/fuzzer/FuzzerUtilFuchsia.cpp
@@ -375,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;
diff --git a/lib/fuzzer/afl/afl_driver.cpp b/lib/fuzzer/afl/afl_driver.cpp
index fa494c03b..5a10c0d27 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 0b561c170..6abb72def 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,14 +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)
-elseif(WIN32)
- list(APPEND LIBFUZZER_UNITTEST_LINK_FLAGS -lstdc++)
-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)
@@ -47,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/hwasan/CMakeLists.txt b/lib/hwasan/CMakeLists.txt
index 3c00eddfb..ca257dfe1 100644
--- a/lib/hwasan/CMakeLists.txt
+++ b/lib/hwasan/CMakeLists.txt
@@ -41,6 +41,14 @@ append_list_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding HWASAN_RTL_CFLA
set(HWASAN_DYNAMIC_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS})
+if(ANDROID)
+# Put most Sanitizer shared libraries in the global group. For more details, see
+# android-changes-for-ndk-developers.md#changes-to-library-search-order
+ if (COMPILER_RT_HAS_Z_GLOBAL)
+ list(APPEND HWASAN_DYNAMIC_LINK_FLAGS -Wl,-z,global)
+ endif()
+endif()
+
set(HWASAN_DYNAMIC_CFLAGS ${HWASAN_RTL_CFLAGS})
append_list_if(COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC
-ftls-model=initial-exec HWASAN_DYNAMIC_CFLAGS)
@@ -53,6 +61,10 @@ 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)
+if (TARGET cxx-headers OR HAVE_LIBCXX)
+ set(HWASAN_DEPS cxx-headers)
+endif()
+
# Static runtime library.
add_compiler_rt_component(hwasan)
@@ -61,26 +73,30 @@ add_compiler_rt_object_libraries(RTHwasan
SOURCES ${HWASAN_RTL_SOURCES}
ADDITIONAL_HEADERS ${HWASAN_RTL_HEADERS}
CFLAGS ${HWASAN_RTL_CFLAGS}
- DEFS ${HWASAN_DEFINITIONS})
+ 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}
- DEFS ${HWASAN_DEFINITIONS})
+ 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}
- DEFS ${HWASAN_DEFINITIONS})
+ 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}
- DEFS ${HWASAN_DEFINITIONS})
+ DEFS ${HWASAN_DEFINITIONS}
+ DEPS ${HWASAN_DEPS})
foreach(arch ${HWASAN_SUPPORTED_ARCH})
add_compiler_rt_runtime(clang_rt.hwasan
@@ -129,6 +145,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 02aee4d61..da95891d8 100644
--- a/lib/hwasan/hwasan.cc
+++ b/lib/hwasan/hwasan.cc
@@ -159,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,
@@ -220,6 +215,36 @@ void UpdateMemoryUsage() {
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);
+ // Must have at least one entry, which we can use for a linked list.
+ CHECK_GE(end - beg, 1U);
+ 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.
@@ -238,6 +263,10 @@ void __hwasan_shadow_init() {
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;
@@ -254,6 +283,8 @@ void __hwasan_init() {
__sanitizer_set_report_path(common_flags()->log_path);
+ AndroidTestTlsSlot();
+
DisableCoreDumperIfNecessary();
__hwasan_shadow_init();
@@ -263,6 +294,7 @@ void __hwasan_init() {
MadviseShadow();
+ SetPrintfAndReportCallback(AppendToErrorMessageBuffer);
// This may call libc -> needs initialized shadow.
AndroidLogInit();
diff --git a/lib/hwasan/hwasan.h b/lib/hwasan/hwasan.h
index a3da09e88..64b1f1a3d 100644
--- a/lib/hwasan/hwasan.h
+++ b/lib/hwasan/hwasan.h
@@ -93,6 +93,7 @@ void InstallTrapHandler();
void InstallAtExitHandler();
const char *GetStackOriginDescr(u32 id, uptr *pc);
+const char *GetStackFrameDescr(uptr pc);
void EnterSymbolizer();
void ExitSymbolizer();
@@ -103,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);
@@ -153,6 +152,10 @@ 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 b9c379ea4..8fd2349d9 100644
--- a/lib/hwasan/hwasan_allocator.cc
+++ b/lib/hwasan/hwasan_allocator.cc
@@ -23,11 +23,44 @@
namespace __hwasan {
+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;
+
+enum RightAlignMode {
+ kRightAlignNever,
+ kRightAlignSometimes,
+ kRightAlignAlways
+};
+
+// 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_->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 {
@@ -40,13 +73,13 @@ u32 HwasanChunkView::GetAllocStackId() const {
return metadata_->alloc_context_id;
}
-static Allocator allocator;
-static AllocatorCache fallback_allocator_cache;
-static SpinMutex fallback_mutex;
-static atomic_uint8_t hwasan_allocator_tagging_enabled;
+uptr HwasanChunkView::ActualSize() const {
+ return allocator.GetActuallyAllocatedSize(reinterpret_cast<void *>(block_));
+}
-static const tag_t kFallbackAllocTag = 0xBB;
-static const tag_t kFallbackFreeTag = 0xBC;
+bool HwasanChunkView::FromSmallHeap() const {
+ return allocator.FromPrimary(reinterpret_cast<void *>(block_));
+}
void GetAllocatorStats(AllocatorStatCounters s) {
allocator.GetStats(s);
@@ -57,6 +90,31 @@ void HwasanAllocatorInit() {
!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();
}
void AllocatorSwallowThreadLocalCache(AllocatorCache *cache) {
@@ -102,12 +160,16 @@ static void *HwasanAllocate(StackTrace *stack, uptr orig_size, uptr alignment,
reinterpret_cast<Metadata *>(allocator.GetMetaData(allocated));
meta->requested_size = static_cast<u32>(orig_size);
meta->alloc_context_id = StackDepotPut(*stack);
+ 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 &&
@@ -115,6 +177,16 @@ static void *HwasanAllocate(StackTrace *stack, uptr orig_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;
}
@@ -135,33 +207,49 @@ void HwasanDeallocate(StackTrace *stack, void *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(untagged_ptr));
+ 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);
+ }
+
meta->requested_size = 0;
meta->alloc_context_id = 0;
// This memory will not be reused by anyone else, so we are free to keep it
// poisoned.
Thread *t = GetCurrentThread();
if (flags()->max_free_fill_size > 0) {
- uptr fill_size = Min(orig_size, (uptr)flags()->max_free_fill_size);
- internal_memset(untagged_ptr, flags()->free_fill_byte, fill_size);
+ 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)untagged_ptr, TaggedSize(orig_size),
+ TagMemoryAligned(reinterpret_cast<uptr>(aligned_ptr), TaggedSize(orig_size),
t ? t->GenerateRandomTag() : kFallbackFreeTag);
if (t) {
- allocator.Deallocate(t->allocator_cache(), untagged_ptr);
+ 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, untagged_ptr);
+ allocator.Deallocate(cache, aligned_ptr);
}
}
@@ -205,8 +293,14 @@ static uptr AllocationSize(const void *tagged_ptr) {
const void *untagged_ptr = UntagPtr(tagged_ptr);
if (!untagged_ptr) return 0;
const void *beg = allocator.GetBlockBegin(untagged_ptr);
- if (beg != untagged_ptr) return 0;
Metadata *b = (Metadata *)allocator.GetMetaData(untagged_ptr);
+ 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;
}
diff --git a/lib/hwasan/hwasan_allocator.h b/lib/hwasan/hwasan_allocator.h
index 3589212d8..b3f2d6c84 100644
--- a/lib/hwasan/hwasan_allocator.h
+++ b/lib/hwasan/hwasan_allocator.h
@@ -29,7 +29,8 @@
namespace __hwasan {
struct Metadata {
- u32 requested_size; // sizes are < 4G.
+ u32 requested_size : 31; // sizes are < 2G.
+ u32 right_aligned : 1;
u32 alloc_context_id;
};
@@ -54,7 +55,8 @@ struct AP32 {
static const uptr kMetadataSize = sizeof(Metadata);
typedef __sanitizer::CompactSizeClassMap SizeClassMap;
static const uptr kRegionSizeLog = __hwasan::kRegionSizeLog;
- typedef __hwasan::ByteMap ByteMap;
+ using AddressSpaceView = LocalAddressSpaceView;
+ using ByteMap = __hwasan::ByteMap;
typedef HwasanMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
@@ -76,7 +78,9 @@ class HwasanChunkView {
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;
+ bool FromSmallHeap() const;
private:
uptr block_;
Metadata *const metadata_;
diff --git a/lib/hwasan/hwasan_flags.inc b/lib/hwasan/hwasan_flags.inc
index f1b416d15..b450ab950 100644
--- a/lib/hwasan/hwasan_flags.inc
+++ b/lib/hwasan/hwasan_flags.inc
@@ -37,6 +37,37 @@ 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 "
diff --git a/lib/hwasan/hwasan_interceptors.cc b/lib/hwasan/hwasan_interceptors.cc
index 9a0770f56..d20bdd95a 100644
--- a/lib/hwasan/hwasan_interceptors.cc
+++ b/lib/hwasan/hwasan_interceptors.cc
@@ -92,42 +92,6 @@ 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) )
-
int __sanitizer_posix_memalign(void **memptr, uptr alignment, uptr size) {
GET_MALLOC_STACK_TRACE;
CHECK_NE(memptr, 0);
@@ -186,7 +150,7 @@ struct __sanitizer_struct_mallinfo __sanitizer_mallinfo() {
}
int __sanitizer_mallopt(int cmd, int value) {
- return -1;
+ return 0;
}
void __sanitizer_malloc_stats(void) {
diff --git a/lib/hwasan/hwasan_interface_internal.h b/lib/hwasan/hwasan_interface_internal.h
index 448997e5e..0e49a3fc2 100644
--- a/lib/hwasan/hwasan_interface_internal.h
+++ b/lib/hwasan/hwasan_interface_internal.h
@@ -37,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
diff --git a/lib/hwasan/hwasan_linux.cc b/lib/hwasan/hwasan_linux.cc
index f8e83ff03..af9378c31 100644
--- a/lib/hwasan/hwasan_linux.cc
+++ b/lib/hwasan/hwasan_linux.cc
@@ -24,6 +24,7 @@
#include "hwasan_thread.h"
#include "hwasan_thread_list.h"
+#include <dlfcn.h>
#include <elf.h>
#include <link.h>
#include <pthread.h>
@@ -283,6 +284,22 @@ uptr *GetCurrentThreadLongPtr() {
}
#endif
+#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();
+ }
+}
+#else
+void AndroidTestTlsSlot() {}
+#endif
+
Thread *GetCurrentThread() {
auto *R = (StackAllocationsRingBuffer*)GetCurrentThreadLongPtr();
return hwasanThreadList().GetThreadByBufferAddress((uptr)(R->Next()));
@@ -355,14 +372,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;
@@ -375,8 +391,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_report.cc b/lib/hwasan/hwasan_report.cc
index cf5f468c4..b5d310a34 100644
--- a/lib/hwasan/hwasan_report.cc
+++ b/lib/hwasan/hwasan_report.cc
@@ -23,12 +23,56 @@
#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);
@@ -95,6 +139,21 @@ void PrintAddressDescription(
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,
@@ -155,13 +214,13 @@ void PrintAddressDescription(
Printf("previously allocated here:\n", t);
Printf("%s", d.Default());
GetStackTraceFromId(har.alloc_context_id).Print();
- t->Announce();
// Print a developer note: the index of this heap object
// in the thread's deallocation ring buffer.
Printf("hwasan_dev_note_heap_rb_distance: %zd %zd\n", D,
flags()->heap_history_size);
+ t->Announce();
num_descriptions_printed++;
}
@@ -179,6 +238,7 @@ void PrintAddressDescription(
? 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)
@@ -186,63 +246,45 @@ void PrintAddressDescription(
uptr sp = (record >> 48) << 4;
uptr pc_mask = (1ULL << 48) - 1;
uptr pc = record & pc_mask;
- uptr fixed_pc = StackTrace::GetNextInstructionPc(pc);
- StackTrace stack(&fixed_pc, 1);
- Printf("record: %p pc: %p sp: %p", record, pc, sp);
- stack.Print();
+ 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 ReportInvalidAccess(StackTrace *stack, u32 origin) {
- ScopedErrorReportLock l;
-
- Decorator d;
- Printf("%s", d.Warning());
- Report("WARNING: HWAddressSanitizer: invalid access\n");
- Printf("%s", d.Default());
- stack->Print();
- ReportErrorSummary("invalid-access", stack);
-}
-
void ReportStats() {}
-void ReportInvalidAccessInsideAddressRange(const char *what, const void *start,
- uptr size, uptr offset) {
- ScopedErrorReportLock l;
- SavedStackAllocations current_stack_allocations(
- GetCurrentThread()->stack_allocations());
-
- Decorator d;
- Printf("%s", d.Warning());
- Printf("%sTag mismatch in %s%s%s at offset %zu inside [%p, %zu)%s\n",
- d.Warning(), d.Name(), what, d.Warning(), offset, start, size,
- d.Default());
- PrintAddressDescription((uptr)start + offset, 1,
- current_stack_allocations.get());
- // if (__sanitizer::Verbosity())
- // DescribeMemoryRange(start, size);
-}
-
static void PrintTagsAroundAddr(tag_t *tag_ptr) {
Printf(
"Memory tags around the buggy address (one tag corresponds to %zd "
"bytes):\n", kShadowAlignment);
const uptr row_len = 16; // better be power of two.
- const uptr num_rows = 11;
+ 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());
+ 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++) {
@@ -251,13 +293,13 @@ static void PrintTagsAroundAddr(tag_t *tag_ptr) {
s.append("%s", row + i == tag_ptr ? "]" : " ");
}
s.append("%s\n", row == center_row_beg ? "<=" : " ");
- Printf("%s", s.data());
- s.clear();
}
+ Printf("%s", s.data());
}
void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
- ScopedErrorReportLock l;
+ 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));
@@ -279,12 +321,71 @@ void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
PrintTagsAroundAddr(tag_ptr);
ReportErrorSummary(bug_type, stack);
- Die();
+}
+
+void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
+ uptr tail_size, const u8 *expected) {
+ ScopedReport R(flags()->halt_on_error);
+ Decorator d;
+ 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 tagged_addr, uptr access_size,
- bool is_store) {
- ScopedErrorReportLock l;
+ bool is_store, bool fatal) {
+ ScopedReport R(fatal);
SavedStackAllocations current_stack_allocations(
GetCurrentThread()->stack_allocations());
diff --git a/lib/hwasan/hwasan_report.h b/lib/hwasan/hwasan_report.h
index 7a9eec834..10fb20cc5 100644
--- a/lib/hwasan/hwasan_report.h
+++ b/lib/hwasan/hwasan_report.h
@@ -21,13 +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/interception/interception.h b/lib/interception/interception.h
index 07142f17b..87b2365fd 100644
--- a/lib/interception/interception.h
+++ b/lib/interception/interception.h
@@ -170,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 942c25609..765a186e5 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/lsan/lsan_allocator.h b/lib/lsan/lsan_allocator.h
index 7c70bb6d9..23ebc11f6 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;
diff --git a/lib/lsan/lsan_common_mac.cc b/lib/lsan/lsan_common_mac.cc
index 8dd247ca5..a355cea96 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;
diff --git a/lib/lsan/lsan_interceptors.cc b/lib/lsan/lsan_interceptors.cc
index fde52e496..a9bd2ba42 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_allocator.cc b/lib/msan/msan_allocator.cc
index 36f0497a9..8b9fa6506 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;
};
@@ -107,7 +108,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 f338d422e..497f943a8 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>
@@ -265,7 +267,7 @@ INTERCEPTOR(void, mallinfo, __sanitizer_struct_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/tests/msan_test.cc b/lib/msan/tests/msan_test.cc
index 29260f16e..19f46abdc 100644
--- a/lib/msan/tests/msan_test.cc
+++ b/lib/msan/tests/msan_test.cc
@@ -1934,12 +1934,14 @@ TEST(MemorySanitizer, remquof) {
EXPECT_NOT_POISONED(quo);
}
+#if !defined(__NetBSD__)
TEST(MemorySanitizer, remquol) {
int quo;
long double res = remquof(29.0, 3.0, &quo);
ASSERT_NE(0.0, res);
EXPECT_NOT_POISONED(quo);
}
+#endif
TEST(MemorySanitizer, lgamma) {
double res = lgamma(1.1);
@@ -1953,11 +1955,13 @@ TEST(MemorySanitizer, lgammaf) {
EXPECT_NOT_POISONED(signgam);
}
+#if !defined(__NetBSD__)
TEST(MemorySanitizer, lgammal) {
long double res = lgammal(1.1);
ASSERT_NE(0.0, res);
EXPECT_NOT_POISONED(signgam);
}
+#endif
TEST(MemorySanitizer, lgamma_r) {
int sgn;
diff --git a/lib/profile/GCDAProfiling.c b/lib/profile/GCDAProfiling.c
index cbca36551..0665a680c 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/InstrProfilingValue.c b/lib/profile/InstrProfilingValue.c
index 674a48609..c7b01a570 100644
--- a/lib/profile/InstrProfilingValue.c
+++ b/lib/profile/InstrProfilingValue.c
@@ -105,8 +105,7 @@ static int allocateValueProfileCounters(__llvm_profile_data *Data) {
return 1;
}
-static ValueProfNode *allocateOneNode(__llvm_profile_data *Data, uint32_t Index,
- uint64_t Value) {
+static ValueProfNode *allocateOneNode(void) {
ValueProfNode *Node;
if (!hasStaticCounters)
@@ -205,7 +204,7 @@ instrumentTargetValueImpl(uint64_t TargetValue, void *Data,
return;
}
- CurVNode = allocateOneNode(PData, CounterIndex, TargetValue);
+ CurVNode = allocateOneNode();
if (!CurVNode)
return;
CurVNode->Value = TargetValue;
diff --git a/lib/profile/WindowsMMap.c b/lib/profile/WindowsMMap.c
index dc87a888a..41cc67f41 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 ac2c911c8..51a130b31 100644
--- a/lib/profile/WindowsMMap.h
+++ b/lib/profile/WindowsMMap.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/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index 10b857ef8..a8bd56933 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
@@ -143,6 +145,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
diff --git a/lib/sanitizer_common/sanitizer_allocator.h b/lib/sanitizer_common/sanitizer_allocator.h
index 9655a2264..88017160a 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 7df3e4097..ef26941fe 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_internal.h b/lib/sanitizer_common/sanitizer_allocator_internal.h
index c0c03d3f4..30fc7042b 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 67970e95b..e5d637665 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;
@@ -108,6 +109,9 @@ class SizeClassAllocator32 {
typedef SizeClassAllocator32LocalCache<ThisT> AllocatorCache;
void Init(s32 release_to_os_interval_ms) {
+ static_assert(
+ is_same<typename ByteMap::AddressSpaceView, AddressSpaceView>::value,
+ "AddressSpaceView type mismatch");
possible_regions.Init();
internal_memset(size_class_info_array, 0, sizeof(size_class_info_array));
}
diff --git a/lib/sanitizer_common/sanitizer_allocator_secondary.h b/lib/sanitizer_common/sanitizer_allocator_secondary.h
index ab680b5e2..455fcf3b4 100644
--- a/lib/sanitizer_common/sanitizer_allocator_secondary.h
+++ b/lib/sanitizer_common/sanitizer_allocator_secondary.h
@@ -68,7 +68,8 @@ 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 AddressSpaceView = LocalAddressSpaceView>
class LargeMmapAllocator {
public:
void InitLinkerInitialized() {
@@ -202,9 +203,10 @@ class LargeMmapAllocator {
void EnsureSortedChunks() {
if (chunks_sorted_) return;
- Sort(reinterpret_cast<uptr *>(chunks_), n_chunks_);
+ Header **chunks = AddressSpaceView::Load(chunks_, n_chunks_);
+ Sort(reinterpret_cast<uptr *>(chunks), n_chunks_);
for (uptr i = 0; i < n_chunks_; i++)
- chunks_[i]->chunk_idx = i;
+ AddressSpaceView::Load(chunks[i])->chunk_idx = i;
chunks_sorted_ = true;
}
@@ -272,12 +274,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.
+ Header **chunks = AddressSpaceView::Load(chunks_, n_chunks_);
for (uptr i = 0; i < n_chunks_; i++) {
- auto t = chunks_[i];
+ 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);
}
}
@@ -316,4 +319,3 @@ class LargeMmapAllocator {
} stats;
StaticSpinMutex mutex_;
};
-
diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h
index cf060587e..a0615fa87 100644
--- a/lib/sanitizer_common/sanitizer_common.h
+++ b/lib/sanitizer_common/sanitizer_common.h
@@ -224,6 +224,7 @@ u32 GetUid();
void ReExec();
void CheckASLR();
char **GetArgv();
+char **GetEnviron();
void PrintCmdline();
bool StackSizeIsUnlimited();
uptr GetStackSizeLimitInBytes();
@@ -897,6 +898,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 530469997..36d5f9c9f 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
@@ -109,6 +117,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 +1819,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 +2046,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 +2106,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 +2173,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"
@@ -4254,11 +4280,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 \
@@ -4819,6 +4850,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);
@@ -4829,12 +4868,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
@@ -4853,6 +4890,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);
@@ -4860,12 +4905,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
@@ -7235,9 +7278,1693 @@ 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);
+}
+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_REGEX \
+ COMMON_INTERCEPT_FUNCTION(regcomp); \
+ COMMON_INTERCEPT_FUNCTION(regexec); \
+ COMMON_INTERCEPT_FUNCTION(regerror); \
+ COMMON_INTERCEPT_FUNCTION(regfree); \
+ COMMON_INTERCEPT_FUNCTION(regnsub); \
+ COMMON_INTERCEPT_FUNCTION(regasub);
+#else
+#define INIT_REGEX
+#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;
+ 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;
+ 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
+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_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[SHA##LEN##_digest_length], \
+ void *context) { \
+ void *ctx; \
+ 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
+
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;
@@ -7299,6 +9026,8 @@ static void InitializeCommonInterceptors() {
INIT_GETPWENT;
INIT_FGETPWENT;
INIT_GETPWENT_R;
+ INIT_FGETPWENT_R;
+ INIT_FGETGRENT_R;
INIT_SETPWENT;
INIT_CLOCK_GETTIME;
INIT_GETITIMER;
@@ -7408,7 +9137,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;
@@ -7484,6 +9215,31 @@ static void InitializeCommonInterceptors() {
INIT_TTYENT;
INIT_PROTOENT;
INIT_NETENT;
+ INIT_GETMNTINFO;
+ INIT_MI_VECTOR_HASH;
+ INIT_SETVBUF;
+ INIT_GETVFSSTAT;
+ INIT_REGEX;
+ 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___PRINTF_CHK;
}
diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc
index 796a05485..ab42b2167 100644
--- a/lib/sanitizer_common/sanitizer_common_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc
@@ -100,14 +100,17 @@ 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() {
diff --git a/lib/sanitizer_common/sanitizer_coverage_fuchsia.cc b/lib/sanitizer_common/sanitizer_coverage_fuchsia.cc
index f27b95fa9..9ff8798a5 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);
}
}
diff --git a/lib/sanitizer_common/sanitizer_file.h b/lib/sanitizer_common/sanitizer_file.h
index 9a12ab734..52e6ef8fb 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_fuchsia.cc b/lib/sanitizer_common/sanitizer_fuchsia.cc
index 9c54e1ed3..069866641 100644
--- a/lib/sanitizer_common/sanitizer_fuchsia.cc
+++ b/lib/sanitizer_common/sanitizer_fuchsia.cc
@@ -86,6 +86,7 @@ void GetThreadStackTopAndBottom(bool, uptr *stack_top, uptr *stack_bottom) {
*stack_top = *stack_bottom + size;
}
+void InitializePlatformEarly() {}
void MaybeReexec() {}
void CheckASLR() {}
void PlatformPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {}
@@ -119,8 +120,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);
+ 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);
}
@@ -451,6 +453,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 9a2a9f120..dcfce3c1e 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 = 1201;
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,9 @@ static void ioctl_table_fill() {
_(SPKRTUNE, NONE, 0);
_(SPKRGETVOL, WRITE, sizeof(unsigned int));
_(SPKRSETVOL, READ, sizeof(unsigned int));
+ /* 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 +671,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 +712,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 +840,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 +1002,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 +1097,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 +1134,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 +1192,9 @@ static void ioctl_table_fill() {
_(SIOCSLINKSTR, READ, struct_ifdrv_sz);
_(SIOCGETHERCAP, READWRITE, struct_eccapreq_sz);
_(SIOCGIFINDEX, READWRITE, struct_ifreq_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 34022430d..14258d617 100644
--- a/lib/sanitizer_common/sanitizer_internal_defs.h
+++ b/lib/sanitizer_common/sanitizer_internal_defs.h
@@ -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
diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc
index a2aa77869..6cfb61506 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
@@ -174,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
@@ -195,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
@@ -211,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
}
@@ -220,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;
}
@@ -317,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);
@@ -342,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
@@ -369,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;
@@ -408,10 +394,10 @@ uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
return internal_syscall(SYSCALL(readlinkat), AT_FDCWD, (uptr)path, (uptr)buf,
bufsize);
#elif SANITIZER_OPENBSD
- return internal_syscall_ptr(SYSCALL(readlinkat), AT_FDCWD, (uptr)path,
- (uptr)buf, bufsize);
+ return internal_syscall(SYSCALL(readlinkat), AT_FDCWD, (uptr)path, (uptr)buf,
+ bufsize);
#else
- return internal_syscall_ptr(SYSCALL(readlink), path, buf, bufsize);
+ return internal_syscall(SYSCALL(readlink), path, buf, bufsize);
#endif
}
@@ -419,7 +405,7 @@ uptr internal_unlink(const char *path) {
#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS || SANITIZER_OPENBSD
return internal_syscall(SYSCALL(unlinkat), AT_FDCWD, (uptr)path, 0);
#else
- return internal_syscall_ptr(SYSCALL(unlink), (uptr)path);
+ return internal_syscall(SYSCALL(unlink), (uptr)path);
#endif
}
@@ -428,7 +414,7 @@ uptr internal_rename(const char *oldpath, const char *newpath) {
return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD,
(uptr)newpath);
#else
- return internal_syscall_ptr(SYSCALL(rename), (uptr)oldpath, (uptr)newpath);
+ return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath);
#endif
}
@@ -437,7 +423,7 @@ uptr internal_sched_yield() {
}
void internal__exit(int exitcode) {
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
internal_syscall(SYSCALL(exit), exitcode);
#else
internal_syscall(SYSCALL(exit_group), exitcode);
@@ -447,19 +433,19 @@ 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) {
@@ -474,6 +460,7 @@ bool FileExists(const char *filename) {
return S_ISREG(st.st_mode);
}
+#if !SANITIZER_NETBSD
tid_t GetTid() {
#if SANITIZER_FREEBSD
long Tid;
@@ -481,8 +468,6 @@ tid_t GetTid() {
return Tid;
#elif SANITIZER_OPENBSD
return internal_syscall(SYSCALL(getthrid));
-#elif SANITIZER_NETBSD
- return _lwp_self();
#elif SANITIZER_SOLARIS
return thr_self();
#else
@@ -498,37 +483,35 @@ int TgKill(pid_t pid, tid_t tid, int sig) {
#elif SANITIZER_OPENBSD
(void)pid;
return internal_syscall(SYSCALL(thrkill), tid, sig, nullptr);
-#elif SANITIZER_NETBSD
- (void)pid;
- return _lwp_kill(tid, sig);
#elif SANITIZER_SOLARIS
(void)pid;
return thr_kill(tid, sig);
#endif
}
+#endif
-#if !SANITIZER_SOLARIS
+#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);
@@ -606,8 +589,8 @@ static void GetArgsAndEnv(char ***argv, char ***envp) {
// kern.ps_strings sysctl, which returns a pointer to a structure containing
// this information. See also <sys/exec.h>.
ps_strings *pss;
- size_t sz = sizeof(pss);
- if (sysctlbyname("kern.ps_strings", &pss, &sz, NULL, 0) == -1) {
+ uptr sz = sizeof(pss);
+ if (internal_sysctlbyname("kern.ps_strings", &pss, &sz, NULL, 0) == -1) {
Printf("sysctl kern.ps_strings failed\n");
Die();
}
@@ -640,33 +623,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];
- 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);
-#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 {
@@ -721,7 +684,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 {
@@ -748,27 +713,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 */);
}
@@ -786,16 +739,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
@@ -805,7 +754,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() {
@@ -816,16 +765,24 @@ int internal_fork() {
#endif
}
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
uptr *oldlenp, const void *newp, uptr newlen) {
#if SANITIZER_OPENBSD
return sysctl(name, namelen, oldp, (size_t *)oldlenp, (void *)newp,
(size_t)newlen);
#else
- return sysctl(name, namelen, oldp, (size_t *)oldlenp, newp, (size_t)newlen);
+ 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
@@ -897,8 +854,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;
@@ -936,9 +893,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];
@@ -1025,6 +993,7 @@ ThreadLister::~ThreadLister() {
if (!internal_iserror(descriptor_))
internal_close(descriptor_);
}
+#endif
#if SANITIZER_WORDSIZE == 32
// Take care of unusable kernel area in top gigabyte.
@@ -1108,6 +1077,14 @@ uptr GetPageSize() {
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
@@ -1958,14 +1935,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];
@@ -1999,6 +1976,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.
}
@@ -2022,6 +2003,17 @@ 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
diff --git a/lib/sanitizer_common/sanitizer_linux.h b/lib/sanitizer_common/sanitizer_linux.h
index 975d6541d..6f5387e16 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.
diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
index 7859557c8..84da23e3f 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"
@@ -806,6 +807,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 000000000..9fbf593f4
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_local_address_space_view.h
@@ -0,0 +1,53 @@
+//===-- 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 `AddressSpaveView` 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. It is guaranteed that
+ //
+ // * That the function will always return the same value
+ // for a given set of arguments.
+ // * That the memory returned is writable and that writes will persist.
+ //
+ // The lifetime of loaded memory is implementation defined.
+ template <typename T>
+ static T *Load(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 4861558e9..95c47babe 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);
}
@@ -215,8 +226,14 @@ int internal_fork() {
int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
uptr *oldlenp, const void *newp, uptr newlen) {
- return sysctl((int *)name, namelen, oldp, (size_t *)oldlenp, (void *)newp,
- (size_t)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) {
@@ -508,27 +525,35 @@ MacosVersion GetMacosVersionInternal() {
CHECK_NE(internal_sysctl(mib, 2, 0, &len, 0, 0), -1);
CHECK_LT(len, maxlen);
CHECK_NE(internal_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;
- case '5': return MACOS_VERSION_EL_CAPITAN;
- case '6': return MACOS_VERSION_SIERRA;
- case '7': return MACOS_VERSION_HIGH_SIERRA;
- case '8': return MACOS_VERSION_MOJAVE;
- default:
- if (IsDigit(version[1]))
- return MACOS_VERSION_UNKNOWN_NEWER;
- else
- return MACOS_VERSION_UNKNOWN;
- }
- }
- default: return MACOS_VERSION_UNKNOWN;
+
+ // 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;
}
}
@@ -671,6 +696,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;
diff --git a/lib/sanitizer_common/sanitizer_mac.h b/lib/sanitizer_common/sanitizer_mac.h
index 58423a774..52825f8cf 100644
--- a/lib/sanitizer_common/sanitizer_mac.h
+++ b/lib/sanitizer_common/sanitizer_mac.h
@@ -43,6 +43,7 @@ enum MacosVersion {
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 e69d6f94b..44c914cea 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 000000000..83beb008f
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_netbsd.cc
@@ -0,0 +1,330 @@
+//===-- sanitizer_netbsd.cc -----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between Sanitizer run-time libraries and implements
+// NetBSD-specific functions from sanitizer_libc.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_NETBSD
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_getauxval.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_linux.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <sys/exec.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <link.h>
+#include <lwp.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+extern "C" void *__mmap(void *, size_t, int, int, int, int,
+ off_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int __sysctl(const int *, unsigned int, void *, size_t *,
+ const void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys_close(int) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys_open(const char *, int, ...) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" ssize_t _sys_read(int, void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" ssize_t _sys_write(int, const void *,
+ size_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int __ftruncate(int, int, off_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" ssize_t _sys_readlink(const char *, char *,
+ size_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys_sched_yield() SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys___nanosleep50(const void *,
+ void *) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys_execve(const char *, char *const[],
+ char *const[]) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" off_t __lseek(int, int, off_t, int) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int __fork() SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys___sigprocmask14(int, const void *,
+ void *) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys___wait450(int wpid, int *, int,
+ void *) SANITIZER_WEAK_ATTRIBUTE;
+
+namespace __sanitizer {
+
+static void *GetRealLibcAddress(const char *symbol) {
+ void *real = dlsym(RTLD_NEXT, symbol);
+ if (!real)
+ real = dlsym(RTLD_DEFAULT, symbol);
+ if (!real) {
+ Printf("GetRealLibcAddress failed for symbol=%s", symbol);
+ Die();
+ }
+ return real;
+}
+
+#define _REAL(func, ...) real##_##func(__VA_ARGS__)
+#define DEFINE__REAL(ret_type, func, ...) \
+ static ret_type (*real_##func)(__VA_ARGS__) = NULL; \
+ if (!real_##func) { \
+ real_##func = (ret_type(*)(__VA_ARGS__))GetRealLibcAddress(#func); \
+ } \
+ CHECK(real_##func);
+
+// --------------- sanitizer_libc.h
+uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
+ OFF_T offset) {
+ CHECK(&__mmap);
+ return (uptr)__mmap(addr, length, prot, flags, fd, 0, offset);
+}
+
+uptr internal_munmap(void *addr, uptr length) {
+ DEFINE__REAL(int, munmap, void *a, uptr b);
+ return _REAL(munmap, addr, length);
+}
+
+int internal_mprotect(void *addr, uptr length, int prot) {
+ DEFINE__REAL(int, mprotect, void *a, uptr b, int c);
+ return _REAL(mprotect, addr, length, prot);
+}
+
+uptr internal_close(fd_t fd) {
+ CHECK(&_sys_close);
+ return _sys_close(fd);
+}
+
+uptr internal_open(const char *filename, int flags) {
+ CHECK(&_sys_open);
+ return _sys_open(filename, flags);
+}
+
+uptr internal_open(const char *filename, int flags, u32 mode) {
+ CHECK(&_sys_open);
+ return _sys_open(filename, flags, mode);
+}
+
+uptr internal_read(fd_t fd, void *buf, uptr count) {
+ sptr res;
+ CHECK(&_sys_read);
+ HANDLE_EINTR(res, (sptr)_sys_read(fd, buf, (size_t)count));
+ return res;
+}
+
+uptr internal_write(fd_t fd, const void *buf, uptr count) {
+ sptr res;
+ CHECK(&_sys_write);
+ HANDLE_EINTR(res, (sptr)_sys_write(fd, buf, count));
+ return res;
+}
+
+uptr internal_ftruncate(fd_t fd, uptr size) {
+ sptr res;
+ CHECK(&__ftruncate);
+ HANDLE_EINTR(res, __ftruncate(fd, 0, (s64)size));
+ return res;
+}
+
+uptr internal_stat(const char *path, void *buf) {
+ DEFINE__REAL(int, __stat50, const char *a, void *b);
+ return _REAL(__stat50, path, buf);
+}
+
+uptr internal_lstat(const char *path, void *buf) {
+ DEFINE__REAL(int, __lstat50, const char *a, void *b);
+ return _REAL(__lstat50, path, buf);
+}
+
+uptr internal_fstat(fd_t fd, void *buf) {
+ DEFINE__REAL(int, __fstat50, int a, void *b);
+ return _REAL(__fstat50, fd, buf);
+}
+
+uptr internal_filesize(fd_t fd) {
+ struct stat st;
+ if (internal_fstat(fd, &st))
+ return -1;
+ return (uptr)st.st_size;
+}
+
+uptr internal_dup2(int oldfd, int newfd) {
+ DEFINE__REAL(int, dup2, int a, int b);
+ return _REAL(dup2, oldfd, newfd);
+}
+
+uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
+ CHECK(&_sys_readlink);
+ return (uptr)_sys_readlink(path, buf, bufsize);
+}
+
+uptr internal_unlink(const char *path) {
+ DEFINE__REAL(int, unlink, const char *a);
+ return _REAL(unlink, path);
+}
+
+uptr internal_rename(const char *oldpath, const char *newpath) {
+ DEFINE__REAL(int, rename, const char *a, const char *b);
+ return _REAL(rename, oldpath, newpath);
+}
+
+uptr internal_sched_yield() {
+ CHECK(&_sys_sched_yield);
+ return _sys_sched_yield();
+}
+
+void internal__exit(int exitcode) {
+ DEFINE__REAL(void, _exit, int a);
+ _REAL(_exit, exitcode);
+ Die(); // Unreachable.
+}
+
+unsigned int internal_sleep(unsigned int seconds) {
+ struct timespec ts;
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ CHECK(&_sys___nanosleep50);
+ int res = _sys___nanosleep50(&ts, &ts);
+ if (res)
+ return ts.tv_sec;
+ return 0;
+}
+
+uptr internal_execve(const char *filename, char *const argv[],
+ char *const envp[]) {
+ CHECK(&_sys_execve);
+ return _sys_execve(filename, argv, envp);
+}
+
+tid_t GetTid() {
+ DEFINE__REAL(int, _lwp_self);
+ return _REAL(_lwp_self);
+}
+
+int TgKill(pid_t pid, tid_t tid, int sig) {
+ DEFINE__REAL(int, _lwp_kill, int a, int b);
+ (void)pid;
+ return _REAL(_lwp_kill, tid, sig);
+}
+
+u64 NanoTime() {
+ timeval tv;
+ DEFINE__REAL(int, __gettimeofday50, void *a, void *b);
+ internal_memset(&tv, 0, sizeof(tv));
+ _REAL(__gettimeofday50, &tv, 0);
+ return (u64)tv.tv_sec * 1000 * 1000 * 1000 + tv.tv_usec * 1000;
+}
+
+uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {
+ DEFINE__REAL(int, __clock_gettime50, __sanitizer_clockid_t a, void *b);
+ return _REAL(__clock_gettime50, clk_id, tp);
+}
+
+uptr internal_ptrace(int request, int pid, void *addr, void *data) {
+ Printf("internal_ptrace not implemented for NetBSD");
+ Die();
+ return 0;
+}
+
+uptr internal_waitpid(int pid, int *status, int options) {
+ CHECK(&_sys___wait450);
+ return _sys___wait450(pid, status, options, 0 /* rusage */);
+}
+
+uptr internal_getpid() {
+ DEFINE__REAL(int, getpid);
+ return _REAL(getpid);
+}
+
+uptr internal_getppid() {
+ DEFINE__REAL(int, getppid);
+ return _REAL(getppid);
+}
+
+uptr internal_getdents(fd_t fd, void *dirp, unsigned int count) {
+ DEFINE__REAL(int, __getdents30, int a, void *b, size_t c);
+ return _REAL(__getdents30, fd, dirp, count);
+}
+
+uptr internal_lseek(fd_t fd, OFF_T offset, int whence) {
+ CHECK(&__lseek);
+ return __lseek(fd, 0, offset, whence);
+}
+
+uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {
+ Printf("internal_prctl not implemented for NetBSD");
+ Die();
+ return 0;
+}
+
+uptr internal_sigaltstack(const void *ss, void *oss) {
+ DEFINE__REAL(int, __sigaltstack14, const void *a, void *b);
+ return _REAL(__sigaltstack14, ss, oss);
+}
+
+int internal_fork() {
+ CHECK(&__fork);
+ return __fork();
+}
+
+int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
+ uptr *oldlenp, const void *newp, uptr newlen) {
+ CHECK(&__sysctl);
+ return __sysctl(name, namelen, oldp, (size_t *)oldlenp, newp, (size_t)newlen);
+}
+
+int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
+ const void *newp, uptr newlen) {
+ DEFINE__REAL(int, sysctlbyname, const char *a, void *b, size_t *c,
+ const void *d, size_t e);
+ return _REAL(sysctlbyname, sname, oldp, (size_t *)oldlenp, newp,
+ (size_t)newlen);
+}
+
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+ __sanitizer_sigset_t *oldset) {
+ CHECK(&_sys___sigprocmask14);
+ return _sys___sigprocmask14(how, set, oldset);
+}
+
+void internal_sigfillset(__sanitizer_sigset_t *set) {
+ DEFINE__REAL(int, __sigfillset14, const void *a);
+ (void)_REAL(__sigfillset14, set);
+}
+
+void internal_sigemptyset(__sanitizer_sigset_t *set) {
+ DEFINE__REAL(int, __sigemptyset14, const void *a);
+ (void)_REAL(__sigemptyset14, set);
+}
+
+uptr intrnal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ Printf("internal_clone not implemented for NetBSD");
+ Die();
+ return 0;
+}
+
+} // namespace __sanitizer
+
+#endif
diff --git a/lib/sanitizer_common/sanitizer_openbsd.cc b/lib/sanitizer_common/sanitizer_openbsd.cc
index dc955109a..f0d071e51 100644
--- a/lib/sanitizer_common/sanitizer_openbsd.cc
+++ b/lib/sanitizer_common/sanitizer_openbsd.cc
@@ -51,6 +51,13 @@ 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;
@@ -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 106a147e5..fd7228c3b 100644
--- a/lib/sanitizer_common/sanitizer_platform.h
+++ b/lib/sanitizer_common/sanitizer_platform.h
@@ -342,4 +342,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 f95539a73..aedcc7c5f 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
@@ -510,5 +517,32 @@
#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
+#define SANITIZER_INTERCEPT_FTS SI_NETBSD
+#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
+#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
#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 000000000..cebb216ec
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_platform_limits_freebsd.cc
@@ -0,0 +1,513 @@
+//===-- 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 <grp.h>
+#include <limits.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <poll.h>
+#include <pthread.h>
+#include <pwd.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 <term.h>
+#include <utmpx.h>
+#include <wchar.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);
+
+ 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;
+} // 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 || HAVE_TIRPC_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 000000000..90329acb3
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_platform_limits_freebsd.h
@@ -0,0 +1,648 @@
+//===-- 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;
+
+ 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;
+} // 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 3c6bb2090..6633a36b4 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,6 +118,7 @@
#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>
@@ -55,39 +127,31 @@
#include <dev/iscsi/iscsi_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 +161,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 +181,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 +212,13 @@
#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>
+// clang-format on
// Include these after system headers to avoid name clashes and ambiguities.
#include "sanitizer_internal_defs.h"
@@ -238,6 +253,10 @@ 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_utimbuf_sz = sizeof(struct utimbuf);
unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
unsigned struct_timex_sz = sizeof(struct timex);
@@ -251,6 +270,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 +360,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 +379,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 +624,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 +683,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 +827,7 @@ 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);
const unsigned IOCTL_NOT_PRESENT = 0;
@@ -1061,6 +1092,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 +1422,8 @@ unsigned IOCTL_SPKRTONE = SPKRTONE;
unsigned IOCTL_SPKRTUNE = SPKRTUNE;
unsigned IOCTL_SPKRGETVOL = SPKRGETVOL;
unsigned IOCTL_SPKRSETVOL = SPKRSETVOL;
+unsigned IOCTL_AUTOFSREQUEST = AUTOFSREQUEST;
+unsigned IOCTL_AUTOFSDONE = AUTOFSDONE;
unsigned IOCTL_BIOCGBLEN = BIOCGBLEN;
unsigned IOCTL_BIOCSBLEN = BIOCSBLEN;
unsigned IOCTL_BIOCSETF = BIOCSETF;
@@ -1408,19 +1442,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 +1602,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 +1748,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 +1835,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 +1869,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 +1927,9 @@ unsigned IOCTL_SIOCGLINKSTR = SIOCGLINKSTR;
unsigned IOCTL_SIOCSLINKSTR = SIOCSLINKSTR;
unsigned IOCTL_SIOCGETHERCAP = SIOCGETHERCAP;
unsigned IOCTL_SIOCGIFINDEX = SIOCGIFINDEX;
+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 +2096,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;
@@ -2224,4 +2295,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 1718e3b1e..39581361d 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,27 @@ 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;
+
+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;
@@ -460,6 +481,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 +519,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 +821,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 +958,7 @@ 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;
// A special value to mark ioctls that are not present on the target platform,
@@ -1201,6 +1227,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 +1549,8 @@ extern unsigned IOCTL_SPKRTONE;
extern unsigned IOCTL_SPKRTUNE;
extern unsigned IOCTL_SPKRGETVOL;
extern unsigned IOCTL_SPKRSETVOL;
+extern unsigned IOCTL_AUTOFSREQUEST;
+extern unsigned IOCTL_AUTOFSDONE;
extern unsigned IOCTL_BIOCGBLEN;
extern unsigned IOCTL_BIOCSBLEN;
extern unsigned IOCTL_BIOCSETF;
@@ -1540,19 +1569,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 +1729,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 +1875,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 +1962,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 +1996,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 +2054,9 @@ extern unsigned IOCTL_SIOCGLINKSTR;
extern unsigned IOCTL_SIOCSLINKSTR;
extern unsigned IOCTL_SIOCGETHERCAP;
extern unsigned IOCTL_SIOCGIFINDEX;
+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 +2223,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 cd1b73d58..a383ebf9f 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,7 @@
#include <termios.h>
#include <time.h>
#include <wchar.h>
-#if !SANITIZER_MAC && !SANITIZER_FREEBSD
+#if !SANITIZER_MAC
#include <utmp.h>
#endif
@@ -78,43 +78,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__)
@@ -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);
@@ -244,12 +212,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 +237,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 +254,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 +290,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 +415,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 +432,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 +459,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 +469,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 +517,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 +707,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 +799,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 +892,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;
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
index 3f67916ad..f51644e3c 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;
@@ -123,7 +113,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,11 +170,9 @@ 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_struct_mallinfo {
@@ -306,35 +294,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 +351,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 +360,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 +432,7 @@ namespace __sanitizer {
};
#endif
-#if SANITIZER_MAC || SANITIZER_FREEBSD
+#if SANITIZER_MAC
struct __sanitizer_msghdr {
void *msg_name;
unsigned msg_namelen;
@@ -514,17 +478,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 +503,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 +563,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 +652,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 +698,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 +714,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 +740,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 +760,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 +774,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 +827,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 +964,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 +976,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 +998,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 +1060,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 +1222,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 +1322,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 +1409,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 +1447,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 c0aa4cc1b..08bd24cd2 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 f7dfc86f5..116270f8d 100644
--- a/lib/sanitizer_common/sanitizer_posix.cc
+++ b/lib/sanitizer_common/sanitizer_posix.cc
@@ -193,11 +193,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);
diff --git a/lib/sanitizer_common/sanitizer_posix.h b/lib/sanitizer_common/sanitizer_posix.h
index c81eda61c..2ebfae8ba 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"
@@ -62,6 +63,8 @@ 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.
diff --git a/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_posix_libcdep.cc
index 266e9bdba..3006e60d8 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_bsd.cc b/lib/sanitizer_common/sanitizer_procmaps_bsd.cc
index e41dc987d..4cebd9858 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_bsd.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_bsd.cc
@@ -67,8 +67,8 @@ void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
#endif
};
- size_t Size = 0;
- int Err = sysctl(Mib, ARRAY_SIZE(Mib), NULL, &Size, NULL, 0);
+ uptr Size = 0;
+ int Err = internal_sysctl(Mib, ARRAY_SIZE(Mib), NULL, &Size, NULL, 0);
CHECK_EQ(Err, 0);
CHECK_GT(Size, 0);
@@ -76,7 +76,7 @@ void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
size_t MmapedSize = Size * 4 / 3;
void *VmMap = MmapOrDie(MmapedSize, "ReadProcMaps()");
Size = MmapedSize;
- Err = sysctl(Mib, ARRAY_SIZE(Mib), VmMap, &Size, NULL, 0);
+ Err = internal_sysctl(Mib, ARRAY_SIZE(Mib), VmMap, &Size, NULL, 0);
CHECK_EQ(Err, 0);
proc_maps->data = (char *)VmMap;
#else
@@ -88,7 +88,7 @@ void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
if (Size > 0x10000)
Size = 0x10000;
Size = (Size / sizeof(struct kinfo_vmentry)) * sizeof(struct kinfo_vmentry);
- Err = sysctl(Mib, ARRAY_SIZE(Mib), Mem, &Size, NULL, 0);
+ Err = internal_sysctl(Mib, ARRAY_SIZE(Mib), Mem, &Size, NULL, 0);
CHECK_EQ(Err, 0);
MmapedSize = Size;
proc_maps->data = Mem;
diff --git a/lib/sanitizer_common/sanitizer_procmaps_solaris.cc b/lib/sanitizer_common/sanitizer_procmaps_solaris.cc
index bfe83170f..9e5e37e6b 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_solaris.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_solaris.cc
@@ -13,6 +13,8 @@
#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>
diff --git a/lib/sanitizer_common/sanitizer_rtems.cc b/lib/sanitizer_common/sanitizer_rtems.cc
index 4be367911..678906a18 100644
--- a/lib/sanitizer_common/sanitizer_rtems.cc
+++ b/lib/sanitizer_common/sanitizer_rtems.cc
@@ -95,6 +95,7 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
*tls_addr = *tls_size = 0;
}
+void InitializePlatformEarly() {}
void MaybeReexec() {}
void CheckASLR() {}
void DisableCoreDumperIfNecessary() {}
@@ -226,11 +227,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 +236,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 a5db22994..9d0c3d93d 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,14 +77,14 @@ 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) {
@@ -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_stacktrace.h b/lib/sanitizer_common/sanitizer_stacktrace.h
index 562d2e9f7..450a40a90 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 747a4a701..c87b18e1b 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
@@ -114,11 +114,25 @@ void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
return;
}
InternalScopedString frame_desc(GetPageSizeCached());
- RenderFrame(&frame_desc, fmt, 0, frame->info,
- common_flags()->symbolize_vs_style,
- common_flags()->strip_path_prefix);
- internal_strncpy(out_buf, frame_desc.data(), out_buf_size);
- out_buf[out_buf_size - 1] = 0;
+ uptr frame_num = 0;
+ // Reserve one byte for the final 0.
+ char *out_end = out_buf + out_buf_size - 1;
+ for (SymbolizedStack *cur = frame; cur && out_buf < out_end;
+ cur = cur->next) {
+ frame_desc.clear();
+ RenderFrame(&frame_desc, fmt, frame_num++, cur->info,
+ common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
+ if (!frame_desc.length())
+ continue;
+ // Reserve one byte for the terminating 0.
+ uptr n = out_end - out_buf - 1;
+ internal_strncpy(out_buf, frame_desc.data(), n);
+ out_buf += __sanitizer::Min<uptr>(n, frame_desc.length());
+ *out_buf++ = 0;
+ }
+ CHECK(out_buf <= out_end);
+ *out_buf = 0;
}
SANITIZER_INTERFACE_ATTRIBUTE
diff --git a/lib/sanitizer_common/sanitizer_stacktrace_printer.cc b/lib/sanitizer_common/sanitizer_stacktrace_printer.cc
index ac0731d46..f2b337433 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 9f9920ece..f41a3cefb 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 3c1c864c0..b241b9dbc 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 e4ed1b4de..ddfb26397 100644
--- a/lib/sanitizer_common/sanitizer_syscall_generic.inc
+++ b/lib/sanitizer_common/sanitizer_syscall_generic.inc
@@ -11,46 +11,21 @@
//
//===----------------------------------------------------------------------===//
-#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_NETBSD || \
- SANITIZER_OPENBSD || SANITIZER_SOLARIS
+// NetBSD uses libc calls directly
+#if !SANITIZER_NETBSD
+
+#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_OPENBSD || SANITIZER_SOLARIS
# define SYSCALL(name) SYS_ ## name
#else
# define SYSCALL(name) __NR_ ## name
#endif
-#if SANITIZER_NETBSD
-// We use 3 kinds of internal_syscall's for different types of retval in order
-// to address differences in calling conventions (e.g. registers to place the
-// return value in).
-// - internal_syscall for 32-bit length (int, pid_t)
-// - internal_syscall64 for 64-bit length (off_t)
-// - internal_syscall_ptr for pointer and (s)size_t
-# define internal_syscall syscall
-# define internal_syscall64 __syscall
-// Handle syscall renames manually
-# define SYS_stat SYS___stat50
-# define SYS_lstat SYS___lstat50
-# define SYS_fstat SYS___fstat50
-# define SYS_gettimeofday SYS___gettimeofday50
-# define SYS_wait4 SYS___wait450
-# define SYS_getdents SYS___getdents30
-# define SYS_sigaltstack SYS___sigaltstack14
-# define SYS_sigprocmask SYS___sigprocmask14
-# define SYS_nanosleep SYS___nanosleep50
-# define SYS_clock_gettime SYS___clock_gettime50
-# if SANITIZER_WORDSIZE == 64
-# define internal_syscall_ptr __syscall
-# else
-# define internal_syscall_ptr syscall
-# endif
-#elif defined(__x86_64__) && (SANITIZER_FREEBSD || SANITIZER_MAC)
+#if defined(__x86_64__) && (SANITIZER_FREEBSD || SANITIZER_MAC)
# define internal_syscall __syscall
-# define internal_syscall64 __syscall
-# define internal_syscall_ptr __syscall
# else
# define internal_syscall syscall
-# define internal_syscall64 syscall
-# define internal_syscall_ptr syscall
+#endif
+
#endif
bool internal_iserror(uptr retval, int *rverrno) {
diff --git a/lib/sanitizer_common/sanitizer_syscall_linux_aarch64.inc b/lib/sanitizer_common/sanitizer_syscall_linux_aarch64.inc
index 1f05ed9b6..7ab1d7641 100644
--- a/lib/sanitizer_common/sanitizer_syscall_linux_aarch64.inc
+++ b/lib/sanitizer_common/sanitizer_syscall_linux_aarch64.inc
@@ -127,9 +127,6 @@ static uptr __internal_syscall(u64 nr, u64 arg1, long arg2, long arg3,
#define internal_syscall(...) __SYSCALL_DISP(__internal_syscall, __VA_ARGS__)
-#define internal_syscall_ptr internal_syscall
-#define internal_syscall64 internal_syscall
-
// Helper function used to avoid cobbler errno.
bool internal_iserror(uptr retval, int *rverrno) {
if (retval >= (uptr)-4095) {
diff --git a/lib/sanitizer_common/sanitizer_syscall_linux_arm.inc b/lib/sanitizer_common/sanitizer_syscall_linux_arm.inc
index a3fdb9e60..b4fd0962a 100644
--- a/lib/sanitizer_common/sanitizer_syscall_linux_arm.inc
+++ b/lib/sanitizer_common/sanitizer_syscall_linux_arm.inc
@@ -127,9 +127,6 @@ static uptr __internal_syscall(u32 nr, u32 arg1, long arg2, long arg3,
#define internal_syscall(...) __SYSCALL_DISP(__internal_syscall, __VA_ARGS__)
-#define internal_syscall_ptr internal_syscall
-#define internal_syscall64 internal_syscall
-
// Helper function used to avoid cobbler errno.
bool internal_iserror(uptr retval, int *rverrno) {
if (retval >= (uptr)-4095) {
diff --git a/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc b/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc
index 327aaa80a..9853a6a67 100644
--- a/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc
+++ b/lib/sanitizer_common/sanitizer_syscall_linux_x86_64.inc
@@ -20,9 +20,6 @@ static uptr internal_syscall(u64 nr) {
return retval;
}
-#define internal_syscall_ptr internal_syscall
-#define internal_syscall64 internal_syscall
-
template <typename T1>
static uptr internal_syscall(u64 nr, T1 arg1) {
u64 retval;
diff --git a/lib/sanitizer_common/sanitizer_syscalls_netbsd.inc b/lib/sanitizer_common/sanitizer_syscalls_netbsd.inc
index 4f7661008..75aea2760 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_thread_registry.cc b/lib/sanitizer_common/sanitizer_thread_registry.cc
index d9fd6549b..eb35cb6c5 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 b203be2f4..30dc603fe 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.h b/lib/sanitizer_common/sanitizer_type_traits.h
new file mode 100644
index 000000000..572eaa5a0
--- /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 9e12c417c..c7a5ec86f 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 38e567d9a..d3b7df690 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.
}
@@ -1017,6 +1021,11 @@ char **GetArgv() {
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/symbolizer/scripts/global_symbols.txt b/lib/sanitizer_common/symbolizer/scripts/global_symbols.txt
index f77648d78..beee0acf4 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 4642c59ec..21ffe2528 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
@@ -36,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
@@ -58,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 05fef252b..f12b70e7b 100644
--- a/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_allocator_test.cc
@@ -118,17 +118,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() {
@@ -259,18 +264,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>();
@@ -426,13 +437,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;
};
@@ -440,7 +453,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);
@@ -615,6 +628,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));
@@ -1321,8 +1350,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_type_traits_test.cc b/lib/sanitizer_common/tests/sanitizer_type_traits_test.cc
new file mode 100644
index 000000000..0dce02fac
--- /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 0646c3dd4..79f69e934 100644
--- a/lib/scudo/CMakeLists.txt
+++ b/lib/scudo/CMakeLists.txt
@@ -17,6 +17,14 @@ set(SCUDO_DYNAMIC_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS})
# Use gc-sections by default to avoid unused code being pulled in.
list(APPEND SCUDO_DYNAMIC_LINK_FLAGS -Wl,--gc-sections)
+if(ANDROID)
+# Put most Sanitizer shared libraries in the global group. For more details, see
+# android-changes-for-ndk-developers.md#changes-to-library-search-order
+ if (COMPILER_RT_HAS_Z_GLOBAL)
+ list(APPEND SCUDO_DYNAMIC_LINK_FLAGS -Wl,-z,global)
+ endif()
+endif()
+
# The minimal Scudo runtime does not inlude the UBSan runtime.
set(SCUDO_MINIMAL_OBJECT_LIBS
RTSanitizerCommonNoTermination
diff --git a/lib/scudo/scudo_allocator.h b/lib/scudo/scudo_allocator.h
index 0002b4a44..869e74a5e 100644
--- a/lib/scudo/scudo_allocator.h
+++ b/lib/scudo/scudo_allocator.h
@@ -96,7 +96,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 91a77b365..eef776809 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/check_analyze.sh b/lib/tsan/check_analyze.sh
index b2beb8578..65c34d466 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,13 +34,13 @@ check() {
fi
}
-for f in write1 write2 write4 write8 read2 read4 read8; do
+for f in write1 write2 write4 write8 read2 read4; do
check $f rsp 1
check $f push 1
check $f pop 6
done
-for f in read1; do
+for f in read1 read8; do
check $f rsp 1
check $f push 2
check $f pop 12
diff --git a/lib/tsan/go/buildgo.sh b/lib/tsan/go/buildgo.sh
index 6f6183b8b..eec4cf15e 100755
--- a/lib/tsan/go/buildgo.sh
+++ b/lib/tsan/go/buildgo.sh
@@ -94,6 +94,7 @@ 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
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
index 5e64d11f3..cc6dab8f1 100644
--- a/lib/tsan/rtl/tsan_interceptors.cc
+++ b/lib/tsan/rtl/tsan_interceptors.cc
@@ -53,6 +53,8 @@ using namespace __tsan; // NOLINT
#define stdout ((char*)&__sF + (__sF_size * 1))
#define stderr ((char*)&__sF + (__sF_size * 2))
+#define nanosleep __nanosleep50
+#define vfork __vfork14
#endif
#if SANITIZER_ANDROID
@@ -226,6 +228,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) {
@@ -864,6 +876,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) {
@@ -1042,6 +1056,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.
@@ -2560,6 +2603,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 {
@@ -2636,6 +2681,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);
@@ -2769,6 +2818,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 959a39465..763b46b88 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 b58e6b707..5e8b58fc2 100644
--- a/lib/tsan/rtl/tsan_interceptors_mac.cc
+++ b/lib/tsan/rtl/tsan_interceptors_mac.cc
@@ -326,7 +326,7 @@ TSAN_INTERCEPTOR(int, objc_sync_enter, void *obj) {
}
TSAN_INTERCEPTOR(int, objc_sync_exit, void *obj) {
- SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj);
+ SCOPED_TSAN_INTERCEPTOR(objc_sync_exit, obj);
if (obj) Release(thr, pc, SyncAddressForObjCObject(obj));
return REAL(objc_sync_exit)(obj);
}
diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h
index 70ae6170a..8303c2418 100644
--- a/lib/tsan/rtl/tsan_platform.h
+++ b/lib/tsan/rtl/tsan_platform.h
@@ -473,7 +473,7 @@ struct Mapping47 {
6200 0000 0000 - 8000 0000 0000: -
*/
-struct Mapping48 {
+struct Mapping {
static const uptr kMetaShadowBeg = 0x300000000000ull;
static const uptr kMetaShadowEnd = 0x400000000000ull;
static const uptr kTraceMemBeg = 0x600000000000ull;
@@ -549,12 +549,10 @@ uptr MappingImpl(void) {
template<int Type>
uptr MappingArchImpl(void) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
-#if !SANITIZER_GO
case 39: return MappingImpl<Mapping39, Type>();
case 42: return MappingImpl<Mapping42, Type>();
-#endif
case 48: return MappingImpl<Mapping48, Type>();
}
DCHECK(0);
@@ -708,12 +706,10 @@ bool IsAppMemImpl(uptr mem) {
ALWAYS_INLINE
bool IsAppMem(uptr mem) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
-#if !SANITIZER_GO
case 39: return IsAppMemImpl<Mapping39>(mem);
case 42: return IsAppMemImpl<Mapping42>(mem);
-#endif
case 48: return IsAppMemImpl<Mapping48>(mem);
}
DCHECK(0);
@@ -741,12 +737,10 @@ bool IsShadowMemImpl(uptr mem) {
ALWAYS_INLINE
bool IsShadowMem(uptr mem) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
-#if !SANITIZER_GO
case 39: return IsShadowMemImpl<Mapping39>(mem);
case 42: return IsShadowMemImpl<Mapping42>(mem);
-#endif
case 48: return IsShadowMemImpl<Mapping48>(mem);
}
DCHECK(0);
@@ -774,12 +768,10 @@ bool IsMetaMemImpl(uptr mem) {
ALWAYS_INLINE
bool IsMetaMem(uptr mem) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
-#if !SANITIZER_GO
case 39: return IsMetaMemImpl<Mapping39>(mem);
case 42: return IsMetaMemImpl<Mapping42>(mem);
-#endif
case 48: return IsMetaMemImpl<Mapping48>(mem);
}
DCHECK(0);
@@ -817,12 +809,10 @@ uptr MemToShadowImpl(uptr x) {
ALWAYS_INLINE
uptr MemToShadow(uptr x) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
-#if !SANITIZER_GO
case 39: return MemToShadowImpl<Mapping39>(x);
case 42: return MemToShadowImpl<Mapping42>(x);
-#endif
case 48: return MemToShadowImpl<Mapping48>(x);
}
DCHECK(0);
@@ -862,12 +852,10 @@ u32 *MemToMetaImpl(uptr x) {
ALWAYS_INLINE
u32 *MemToMeta(uptr x) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
-#if !SANITIZER_GO
case 39: return MemToMetaImpl<Mapping39>(x);
case 42: return MemToMetaImpl<Mapping42>(x);
-#endif
case 48: return MemToMetaImpl<Mapping48>(x);
}
DCHECK(0);
@@ -920,12 +908,10 @@ uptr ShadowToMemImpl(uptr s) {
ALWAYS_INLINE
uptr ShadowToMem(uptr s) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
-#if !SANITIZER_GO
case 39: return ShadowToMemImpl<Mapping39>(s);
case 42: return ShadowToMemImpl<Mapping42>(s);
-#endif
case 48: return ShadowToMemImpl<Mapping48>(s);
}
DCHECK(0);
@@ -961,12 +947,10 @@ uptr GetThreadTraceImpl(int tid) {
ALWAYS_INLINE
uptr GetThreadTrace(int tid) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
-#if !SANITIZER_GO
case 39: return GetThreadTraceImpl<Mapping39>(tid);
case 42: return GetThreadTraceImpl<Mapping42>(tid);
-#endif
case 48: return GetThreadTraceImpl<Mapping48>(tid);
}
DCHECK(0);
@@ -997,12 +981,10 @@ uptr GetThreadTraceHeaderImpl(int tid) {
ALWAYS_INLINE
uptr GetThreadTraceHeader(int tid) {
-#if defined(__aarch64__) && !defined(__APPLE__)
+#if defined(__aarch64__) && !defined(__APPLE__) && !SANITIZER_GO
switch (vmaSize) {
-#if !SANITIZER_GO
case 39: return GetThreadTraceHeaderImpl<Mapping39>(tid);
case 42: return GetThreadTraceHeaderImpl<Mapping42>(tid);
-#endif
case 48: return GetThreadTraceHeaderImpl<Mapping48>(tid);
}
DCHECK(0);
diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc
index 584122292..f038e9682 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 523b69aaa..60e6f82f7 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;
};
@@ -772,6 +773,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_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc
index e4d65b9a9..766a0f5a5 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/ubsan/ubsan_checks.inc b/lib/ubsan/ubsan_checks.inc
index 5a7bdec2d..e976ea4f6 100644
--- a/lib/ubsan/ubsan_checks.inc
+++ b/lib/ubsan/ubsan_checks.inc
@@ -30,8 +30,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 1638a054e..e75a4c44e 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 e72a32cf3..53430a607 100644
--- a/lib/ubsan/ubsan_handlers.cc
+++ b/lib/ubsan/ubsan_handlers.cc
@@ -119,7 +119,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 +459,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 +502,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 07644b7ea..04405770e 100644
--- a/lib/ubsan/ubsan_handlers.h
+++ b/lib/ubsan/ubsan_handlers.h
@@ -125,7 +125,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_minimal/CMakeLists.txt b/lib/ubsan_minimal/CMakeLists.txt
index b70246845..e0910e80f 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/xray/CMakeLists.txt b/lib/xray/CMakeLists.txt
index 4bb25d454..0a86c52e6 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
diff --git a/lib/xray/tests/CMakeLists.txt b/lib/xray/tests/CMakeLists.txt
index 2f167e3ae..16fb129d3 100644
--- a/lib/xray/tests/CMakeLists.txt
+++ b/lib/xray/tests/CMakeLists.txt
@@ -93,6 +93,7 @@ macro(add_xray_unittest testname)
# 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 LLVMXRay LLVMTestingSupport
CFLAGS ${XRAY_UNITTEST_CFLAGS}
diff --git a/lib/xray/tests/unit/CMakeLists.txt b/lib/xray/tests/unit/CMakeLists.txt
index d0ead947d..42ea43750 100644
--- a/lib/xray/tests/unit/CMakeLists.txt
+++ b/lib/xray/tests/unit/CMakeLists.txt
@@ -1,8 +1,10 @@
add_xray_unittest(XRayTest SOURCES
- buffer_queue_test.cc
allocator_test.cc
- segmented_array_test.cc
+ buffer_queue_test.cc
+ fdr_controller_test.cc
+ fdr_log_writer_test.cc
function_call_trie_test.cc
profile_collector_test.cc
- fdr_log_writer_test.cc
+ segmented_array_test.cc
+ test_helpers.cc
xray_unit_test_main.cc)
diff --git a/lib/xray/tests/unit/allocator_test.cc b/lib/xray/tests/unit/allocator_test.cc
index be404160e..117074162 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 c0d4ccb26..a30343e18 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 000000000..8967c4919
--- /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
index 3a2138cd8..f2e7a5cba 100644
--- a/lib/xray/tests/unit/fdr_log_writer_test.cc
+++ b/lib/xray/tests/unit/fdr_log_writer_test.cc
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include <time.h>
+#include "test_helpers.h"
#include "xray/xray_records.h"
#include "xray_fdr_log_writer.h"
#include "llvm/Support/DataExtractor.h"
@@ -26,8 +27,13 @@ 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::SizeIs;
+using ::testing::IsEmpty;
+using ::testing::IsNull;
// Exercise the common code path where we initialize a buffer and are able to
// write some records successfully.
@@ -58,34 +64,98 @@ TEST(FdrLogWriterTest, WriteSomeRecords) {
// We then need to go through each element of the Buffers, and re-create a
// flat buffer that we would see if they were laid out in a file. This also
// means we need to write out the header manually.
- // TODO: Isolate the file header writing.
- std::string Serialized;
- std::aligned_storage<sizeof(XRayFileHeader), alignof(XRayFileHeader)>::type
- HeaderStorage;
- auto *Header = reinterpret_cast<XRayFileHeader *>(&HeaderStorage);
- new (Header) XRayFileHeader();
- Header->Version = 3;
- Header->Type = FileTypes::FDR_LOG;
- Header->CycleFrequency = 3e9;
- Header->ConstantTSC = 1;
- Header->NonstopTSC = 1;
- Serialized.append(reinterpret_cast<const char *>(&HeaderStorage),
- sizeof(XRayFileHeader));
- size_t BufferCount = 0;
- Buffers.apply([&](const BufferQueue::Buffer &B) {
- ++BufferCount;
- auto Size = atomic_load_relaxed(&B.Extents);
- auto Extents =
- createMetadataRecord<MetadataRecord::RecordKinds::BufferExtents>(Size);
- Serialized.append(reinterpret_cast<const char *>(&Extents),
- sizeof(Extents));
- Serialized.append(reinterpret_cast<const char *>(B.Data), Size);
- });
- ASSERT_EQ(BufferCount, 1u);
+ 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(SizeIs(2)));
+ EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty()));
}
} // namespace
diff --git a/lib/xray/tests/unit/function_call_trie_test.cc b/lib/xray/tests/unit/function_call_trie_test.cc
index 049ecfb07..01be69122 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 67049af2c..df786d46b 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 035674ccf..46aeb88f7 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 000000000..284492d10
--- /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 000000000..ff0311e9b
--- /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 f77bccbd9..907c54542 100644
--- a/lib/xray/xray_allocator.h
+++ b/lib/xray/xray_allocator.h
@@ -19,7 +19,13 @@
#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 <cstddef>
@@ -33,9 +39,31 @@ namespace __xray {
// 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;
+ int ErrNo = 0;
if (UNLIKELY(internal_iserror(B, &ErrNo))) {
if (Verbosity())
Report(
@@ -43,6 +71,7 @@ template <class T> T *allocate() XRAY_NEVER_INSTRUMENT {
RoundedSize, B);
return nullptr;
}
+#endif
return reinterpret_cast<T *>(B);
}
@@ -50,14 +79,40 @@ 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
}
-template <class T = uint8_t> T *allocateBuffer(size_t S) XRAY_NEVER_INSTRUMENT {
+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;
+ int ErrNo = 0;
if (UNLIKELY(internal_iserror(B, &ErrNo))) {
if (Verbosity())
Report(
@@ -65,6 +120,7 @@ template <class T = uint8_t> T *allocateBuffer(size_t S) XRAY_NEVER_INSTRUMENT {
RoundedSize, B);
return nullptr;
}
+#endif
return reinterpret_cast<T *>(B);
}
@@ -72,7 +128,12 @@ 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>
@@ -110,10 +171,11 @@ template <size_t N> struct Allocator {
};
private:
- const size_t MaxMemory{0};
- uint8_t *BackingStore = nullptr;
- uint8_t *AlignedNextBlock = nullptr;
+ size_t MaxMemory{0};
+ unsigned char *BackingStore = nullptr;
+ unsigned char *AlignedNextBlock = nullptr;
size_t AllocatedBlocks = 0;
+ bool Owned;
SpinMutex Mutex{};
void *Alloc() XRAY_NEVER_INSTRUMENT {
@@ -141,33 +203,82 @@ private:
return nullptr;
}
- AlignedNextBlock = reinterpret_cast<uint8_t *>(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<uint8_t *>(
- reinterpret_cast<uint8_t *>(AlignedNextBlock) + N);
+ AlignedNextBlock =
+ reinterpret_cast<unsigned char *>(AlignedNextBlock) + Block::Size;
++AllocatedBlocks;
return Result;
}
public:
explicit Allocator(size_t M) XRAY_NEVER_INSTRUMENT
- : MaxMemory(nearest_boundary(M, kCacheLineSize)) {}
+ : 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() XRAY_NEVER_INSTRUMENT { return {Alloc()}; }
~Allocator() NOEXCEPT XRAY_NEVER_INSTRUMENT {
- if (BackingStore != nullptr) {
+ if (Owned && BackingStore != nullptr) {
deallocateBuffer(BackingStore, MaxMemory);
}
}
diff --git a/lib/xray/xray_basic_logging.cc b/lib/xray/xray_basic_logging.cc
index ee28d598f..b65c0e43e 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>
@@ -60,7 +62,7 @@ struct alignas(64) ThreadLocalData {
void *ShadowStack = nullptr;
size_t StackSize = 0;
size_t StackEntries = 0;
- int Fd = -1;
+ __xray::LogWriter *LogWriter = nullptr;
};
struct BasicLoggingOptions {
@@ -83,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, +[] {
@@ -108,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 {
@@ -129,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)));
@@ -157,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
@@ -242,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;
}
@@ -257,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;
}
@@ -288,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;
}
@@ -347,25 +349,25 @@ 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(UNUSED size_t BufferSize,
diff --git a/lib/xray/xray_buffer_queue.cc b/lib/xray/xray_buffer_queue.cc
index 5a88ecd33..7d0e5a1f3 100644
--- a/lib/xray/xray_buffer_queue.cc
+++ b/lib/xray/xray_buffer_queue.cc
@@ -13,100 +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>
using namespace __xray;
-using namespace __sanitizer;
-BufferQueue::BufferQueue(size_t B, size_t N,
- bool &Success) XRAY_NEVER_INSTRUMENT
- : BufferSize(B),
- BufferCount(N),
- Mutex(),
- Finalizing{0},
- BackingStore(allocateBuffer(B *N)),
- Buffers(initArray<BufferQueue::BufferRep>(N)),
- Next(Buffers),
- First(Buffers),
- LiveBuffers(0) {
- if (BackingStore == nullptr) {
- Success = false;
+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);
+}
+
+void deallocControlBlock(BufferQueue::ControlBlock *C, size_t Size,
+ size_t Count) {
+ deallocateBuffer(reinterpret_cast<unsigned char *>(C),
+ (sizeof(BufferQueue::ControlBlock) - 1) + (Size * Count));
+}
+
+void decRefCount(BufferQueue::ControlBlock *C, size_t Size, size_t Count) {
+ if (C == nullptr)
return;
- }
- if (Buffers == nullptr) {
- deallocateBuffer(BackingStore, BufferSize * BufferCount);
- 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];
+ };
+};
+
+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;
- for (size_t i = 0; i < N; ++i) {
+ BackingStore = allocControlBlock(BufferSize, BufferCount);
+ if (BackingStore == nullptr)
+ return BufferQueue::ErrorCode::NotEnoughMemory;
+
+ auto CleanupBackingStore = at_scope_exit([&, this] {
+ if (Success)
+ return;
+ 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 = reinterpret_cast<char *>(BackingStore) + (BufferSize * i);
- Buf.Size = B;
- atomic_store(&Buf.Extents, 0, memory_order_release);
+ 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;
- auto Extents = atomic_load(&B.Extents, memory_order_acquire);
- atomic_store(&Buf.Extents, Extents, memory_order_release);
- Buf.Data = B.Data;
- Buf.Size = B.Size;
- T.Used = true;
- ++LiveBuffers;
-
- 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) {
// Check whether the buffer being referred to is within the bounds of the
// backing store's range.
- if (Buf.Data < BackingStore ||
- Buf.Data >
- reinterpret_cast<char *>(BackingStore) + (BufferCount * BufferSize))
- return ErrorCode::UnrecognizedBuffer;
+ 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;
+ }
- 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".
- auto Extents = atomic_load(&Buf.Extents, memory_order_acquire);
- atomic_store(&First->Buff.Extents, Extents, memory_order_release);
- First->Buff.Data = Buf.Data;
- First->Buff.Size = Buf.Size;
- First->Used = true;
- Buf.Data = nullptr;
- Buf.Size = 0;
- atomic_store(&Buf.Extents, 0, memory_order_release);
- --LiveBuffers;
- if (++First == (Buffers + BufferCount))
- First = Buffers;
-
+ 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;
}
@@ -116,9 +222,17 @@ BufferQueue::ErrorCode BufferQueue::finalize() {
return ErrorCode::Ok;
}
-BufferQueue::~BufferQueue() {
+void BufferQueue::cleanupBuffers() {
for (auto B = Buffers, E = Buffers + BufferCount; B != E; ++B)
B->~BufferRep();
deallocateBuffer(Buffers, BufferCount);
- deallocateBuffer(BackingStore, BufferSize * 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 c1fa9fab7..ef2b433f9 100644
--- a/lib/xray/xray_buffer_queue.h
+++ b/lib/xray/xray_buffer_queue.h
@@ -25,16 +25,44 @@
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:
+ /// 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{0};
+ atomic_uint64_t *Extents = nullptr;
+ uint64_t Generation{0};
void *Data = nullptr;
size_t Size = 0;
+
+ private:
+ friend class BufferQueue;
+ ControlBlock *BackingStore = nullptr;
+ ControlBlock *ExtentsBackingStore = nullptr;
+ size_t Count = 0;
};
struct BufferRep {
@@ -113,9 +141,11 @@ private:
SpinMutex Mutex;
atomic_uint8_t Finalizing;
- // A pointer to a contiguous block of memory to serve as the backing store for
- // all the individual buffers handed out.
- uint8_t *BackingStore;
+ // 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;
@@ -130,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,
@@ -137,6 +174,7 @@ public:
QueueFinalizing,
UnrecognizedBuffer,
AlreadyFinalized,
+ AlreadyInitialized,
};
static const char *getErrorString(ErrorCode E) {
@@ -151,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";
}
@@ -181,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; }
diff --git a/lib/xray/xray_fdr_controller.h b/lib/xray/xray_fdr_controller.h
new file mode 100644
index 000000000..d44d0309b
--- /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_writer.h b/lib/xray/xray_fdr_log_writer.h
index 28af356e7..dbd1a98fe 100644
--- a/lib/xray/xray_fdr_log_writer.h
+++ b/lib/xray/xray_fdr_log_writer.h
@@ -46,8 +46,27 @@ template <size_t Index> struct SerializerImpl {
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);
@@ -63,7 +82,11 @@ class FDRLogWriter {
template <class T> void writeRecord(const T &R) {
internal_memcpy(NextRecord, reinterpret_cast<const char *>(&R), sizeof(T));
NextRecord += sizeof(T);
- atomic_fetch_add(&Buffer.Extents, sizeof(T), memory_order_acq_rel);
+ // 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:
@@ -89,7 +112,11 @@ public:
constexpr auto Size = sizeof(MetadataRecord) * N;
internal_memcpy(NextRecord, reinterpret_cast<const char *>(Recs), Size);
NextRecord += Size;
- atomic_fetch_add(&Buffer.Extents, Size, memory_order_acq_rel);
+ // 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;
}
@@ -110,8 +137,94 @@ public:
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
diff --git a/lib/xray/xray_fdr_logging.cc b/lib/xray/xray_fdr_logging.cc
index 2d6af443d..3893b7a2a 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>
@@ -33,6 +32,7 @@
#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"
@@ -46,42 +46,34 @@ 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;
+ BufferQueue *BQ = nullptr;
- // The number of adjacent, consecutive pairs of FunctionEntry, Tail Exit
- // records preceding RecordPtr.
- uint8_t NumTailCalls = 0;
+ using LogWriterStorage =
+ typename std::aligned_storage<sizeof(FDRLogWriter),
+ alignof(FDRLogWriter)>::type;
- // 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.
- BufferQueue *BQ = nullptr;
+ 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;
@@ -89,6 +81,12 @@ static pthread_key_t Key;
static std::aligned_storage<sizeof(BufferQueue)>::type BufferQueueStorage;
static BufferQueue *BQ = nullptr;
+// Global thresholds for function durations.
+static atomic_uint64_t ThresholdTicks{0};
+
+// Global for ticks per second.
+static atomic_uint64_t TicksPerSec{0};
+
static atomic_sint32_t LogFlushStatus = {
XRayLogFlushStatus::XRAY_LOG_NOT_FLUSHING};
@@ -140,540 +138,36 @@ static ThreadLocalData &getThreadLocalData() {
return *reinterpret_cast<ThreadLocalData *>(&TLDStorage);
}
-static void writeNewBufferPreamble(tid_t Tid, timespec TS,
- pid_t Pid) XRAY_NEVER_INSTRUMENT {
- static_assert(sizeof(time_t) <= 8, "time_t needs to be at most 8 bytes");
- auto &TLD = getThreadLocalData();
- MetadataRecord Metadata[] = {
- // Write out a MetadataRecord to signify that this is the start of a new
- // buffer, associated with a particular thread, with a new CPU. For the
- // data, we have 15 bytes to squeeze as much information as we can. At
- // this point we only write down the following bytes:
- // - Thread ID (tid_t, cast to 4 bytes type due to Darwin being 8 bytes)
- createMetadataRecord<MetadataRecord::RecordKinds::NewBuffer>(
- static_cast<int32_t>(Tid)),
-
- // Also write the WalltimeMarker record. We only really need microsecond
- // precision here, and enforce across platforms that we need 64-bit
- // seconds and 32-bit microseconds encoded in the Metadata record.
- createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>(
- static_cast<int64_t>(TS.tv_sec),
- static_cast<int32_t>(TS.tv_nsec / 1000)),
-
- // Also write the Pid record.
- createMetadataRecord<MetadataRecord::RecordKinds::Pid>(
- static_cast<int32_t>(Pid)),
- };
-
- TLD.NumConsecutiveFnEnters = 0;
- TLD.NumTailCalls = 0;
- if (TLD.BQ == nullptr || TLD.BQ->finalizing())
- return;
- FDRLogWriter Writer(TLD.Buffer);
- TLD.RecordPtr += Writer.writeMetadataRecords(Metadata);
-}
-
-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);
- atomic_store(&B.Extents, 0, memory_order_release);
- 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, Add, memory_order_acq_rel);
-}
-
-static void decrementExtents(size_t Subtract) {
- auto &TLD = getThreadLocalData();
- atomic_fetch_sub(&TLD.Buffer.Extents, Subtract, memory_order_acq_rel);
-}
-
-static void writeNewCPUIdMetadata(uint16_t CPU,
- uint64_t TSC) XRAY_NEVER_INSTRUMENT {
- auto &TLD = getThreadLocalData();
- FDRLogWriter W(TLD.Buffer, TLD.RecordPtr);
-
- // The data for the New CPU will contain the following bytes:
- // - CPU ID (uint16_t, 2 bytes)
- // - Full TSC (uint64_t, 8 bytes)
- // Total = 10 bytes.
- W.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(CPU, TSC);
- TLD.RecordPtr = W.getNextRecord();
- TLD.NumConsecutiveFnEnters = 0;
- TLD.NumTailCalls = 0;
-}
-
-static void writeTSCWrapMetadata(uint64_t TSC) XRAY_NEVER_INSTRUMENT {
- auto &TLD = getThreadLocalData();
- FDRLogWriter W(TLD.Buffer, TLD.RecordPtr);
-
- // The data for the TSCWrap record contains the following bytes:
- // - Full TSC (uint64_t, 8 bytes)
- // Total = 8 bytes.
- W.writeMetadata<MetadataRecord::RecordKinds::TSCWrap>(TSC);
- TLD.RecordPtr = W.getNextRecord();
- TLD.NumConsecutiveFnEnters = 0;
- TLD.NumTailCalls = 0;
-}
-
-// 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();
- FDRLogWriter W(TLD.Buffer, TLD.RecordPtr);
- W.writeMetadata<MetadataRecord::RecordKinds::CallArgument>(A);
- TLD.RecordPtr = W.getNextRecord();
-}
-
-static void writeFunctionRecord(int32_t FuncId, uint32_t TSCDelta,
- XRayEntryType EntryType) XRAY_NEVER_INSTRUMENT {
- constexpr int32_t MaxFuncId = (1 << 29) - 1;
- if (UNLIKELY(FuncId > MaxFuncId)) {
- if (Verbosity())
- Report("Warning: Function ID '%d' > max function id: '%d'", FuncId,
- MaxFuncId);
- return;
- }
-
- auto &TLD = getThreadLocalData();
- FDRLogWriter W(TLD.Buffer, TLD.RecordPtr);
-
- // Only take 28 bits of the function id.
- //
- // We need to be careful about the sign bit and the bitwise operations being
- // performed here. In effect, we want to truncate the value of the function id
- // to the first 28 bits. To do this properly, this means we need to mask the
- // function id with (2 ^ 28) - 1 == 0x0fffffff.
- //
- auto TruncatedId = FuncId & MaxFuncId;
- auto Kind = FDRLogWriter::FunctionRecordKind::Enter;
-
- switch (EntryType) {
- case XRayEntryType::ENTRY:
- ++TLD.NumConsecutiveFnEnters;
- break;
- case XRayEntryType::LOG_ARGS_ENTRY:
- // We should not rewind functions with logged args.
- TLD.NumConsecutiveFnEnters = 0;
- TLD.NumTailCalls = 0;
- Kind = FDRLogWriter::FunctionRecordKind::EnterArg;
- break;
- case XRayEntryType::EXIT:
- // If we've decided to log the function exit, we will never erase the log
- // before it.
- TLD.NumConsecutiveFnEnters = 0;
- TLD.NumTailCalls = 0;
- Kind = FDRLogWriter::FunctionRecordKind::Exit;
- 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;
- }
- Kind = FDRLogWriter::FunctionRecordKind::TailExit;
- 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;
- }
- }
-
- W.writeFunction(Kind, TruncatedId, TSCDelta);
- TLD.RecordPtr = W.getNextRecord();
-}
-
-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) XRAY_NEVER_INSTRUMENT {
- 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 (TLD.Buffer.Data == nullptr)
- return true;
-
- if (EC != BufferQueue::ErrorCode::Ok) {
- Report("Failed to release buffer at %p; error=%s\n", TLD.Buffer.Data,
- BufferQueue::getErrorString(EC));
- 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 && 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. This means we have: 1 MetadataRecord + 1 Function
- // Record.
- // 2. When we learn that we've moved CPUs, we need to write a
- // MetadataRecord to indicate a "cpu change", and thus write out the
- // current TSC for that CPU before writing out the actual
- // FunctionRecord. This means we have: 1 MetadataRecord + 1 Function
- // Record.
- // 3. Given the previous two cases, in addition we can add at most one
- // function argument record. This means we have: 2 MetadataRecord + 1
- // Function Record.
- //
- // So the math we need to do is to determine whether writing 40 bytes past the
- // current pointer exceeds the buffer's maximum size. If we don't have enough
- // 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;
- }
-
- auto RecordTSCDelta = writeCurrentCPUTSC(TLD, TSC, CPU);
- TLD.LastTSC = TSC;
- TLD.CurrentCPU = CPU;
- switch (Entry) {
- case XRayEntryType::ENTRY:
- // Update the thread local state for the next invocation.
- TLD.LastFunctionEntryTSC = TSC;
- break;
- case XRayEntryType::LOG_ARGS_ENTRY:
- // Update the thread local state for the next invocation, but also prevent
- // rewinding when we have arguments logged.
- TLD.LastFunctionEntryTSC = TSC;
- TLD.NumConsecutiveFnEnters = 0;
- TLD.NumTailCalls = 0;
- break;
- case XRayEntryType::TAIL:
- case XRayEntryType::EXIT:
- // Break out and write the exit record if we can't erase any functions.
- 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);
}
@@ -711,9 +205,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);
@@ -746,7 +242,14 @@ 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, memory_order_acquire);
+ //
+ // 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)
@@ -811,13 +314,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();
- BQ = nullptr;
- }
+ auto &TLD = getThreadLocalData();
+ if (TLD.Controller != nullptr)
+ TLD.Controller->flush();
});
if (fdrFlags()->no_file_flush) {
@@ -838,8 +337,8 @@ XRayLogFlushStatus fdrLoggingFlush() XRAY_NEVER_INSTRUMENT {
// (fixed-sized) and let the tools reading the buffers deal with the data
// afterwards.
//
- int 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;
@@ -847,15 +346,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.RecordPtr != nullptr && TLD.BQ != nullptr)
- releaseThreadLocalBuffer(*TLD.BQ);
+ 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
@@ -864,18 +363,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, 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);
}
});
@@ -922,7 +421,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);
@@ -940,16 +440,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,
@@ -960,47 +559,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 + ReducedEventSize)) {
- TLD.BQ = nullptr;
+ auto &TLD = getThreadLocalData();
+ if (!setupTLD(TLD))
return;
- }
- // We need to reset the counts for the number of functions we're able to
- // rewind.
- TLD.NumConsecutiveFnEnters = 0;
- TLD.NumTailCalls = 0;
-
- // Write the custom event metadata record, which consists of the following
- // information:
- // - 8 bytes (64-bits) for the full TSC when the event started.
- // - 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);
- TLD.RecordPtr += ReducedEventSize;
- incrementExtents(MetadataRecSize + ReducedEventSize);
- endBufferIfFull();
+ int32_t ReducedEventSize = static_cast<int32_t>(EventSize);
+ TLD.Controller->customEvent(TSC, CPU, Event, ReducedEventSize);
}
void fdrLoggingHandleTypedEvent(
@@ -1012,52 +589,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 + ReducedEventSize)) {
- 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);
- TLD.RecordPtr += ReducedEventSize;
- incrementExtents(MetadataRecSize + EventSize);
- endBufferIfFull();
+ int32_t ReducedEventSize = static_cast<int32_t>(EventSize);
+ TLD.Controller->typedEvent(TSC, CPU, EventType, Event, ReducedEventSize);
}
-XRayLogInitStatus fdrLoggingInit(UNUSED size_t BufferSize,
- UNUSED 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;
@@ -1104,50 +657,48 @@ XRayLogInitStatus fdrLoggingInit(UNUSED size_t BufferSize,
// environment-variable defined options.
FDRParser.ParseString(static_cast<const char *>(Options));
*fdrFlags() = FDRFlags;
- BufferSize = FDRFlags.buffer_size;
- BufferMax = FDRFlags.buffer_max;
-
- bool Success = false;
-
- if (BQ != nullptr) {
- BQ->~BufferQueue();
- BQ = nullptr;
- }
+ auto BufferSize = FDRFlags.buffer_size;
+ auto BufferMax = FDRFlags.buffer_max;
if (BQ == nullptr) {
+ 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();
- 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;
- 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));
- });
- });
+ 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) *
diff --git a/lib/xray/xray_function_call_trie.h b/lib/xray/xray_function_call_trie.h
index f4c2fc335..d01ad20e3 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 "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,19 +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) XRAY_NEVER_INSTRUMENT : Parent(P),
- Callees(A),
- CallCount(CC),
- CumulativeLocalTime(CLT),
- FId(F) {}
-
// TODO: Include the compact histogram.
};
@@ -133,11 +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) XRAY_NEVER_INSTRUMENT : EntryTSC{T},
- NodePtr{N} {}
+ uint16_t EntryCPU;
};
using NodeArray = Array<Node>;
@@ -152,20 +138,100 @@ 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) XRAY_NEVER_INSTRUMENT
- : 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;
@@ -173,79 +239,83 @@ public:
}
Allocators &operator=(Allocators &&O) XRAY_NEVER_INSTRUMENT {
- {
- 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() XRAY_NEVER_INSTRUMENT {
- // Note that we cannot use delete on these pointers, as they need to be
- // returned to the sanitizer_common library's internal memory tracking
- // system.
- if (NodeAllocator != nullptr) {
+ // When moving into an existing instance, we ensure that we clean up the
+ // current allocators.
+ if (NodeAllocator)
NodeAllocator->~NodeAllocatorType();
- deallocate(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();
- deallocate(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();
- deallocate(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();
- deallocate(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() XRAY_NEVER_INSTRUMENT {
return InitAllocatorsCustom(profilingFlags()->per_thread_allocator_max);
}
static Allocators InitAllocatorsCustom(uptr Max) XRAY_NEVER_INSTRUMENT {
- Allocators A;
- auto NodeAllocator = allocate<Allocators::NodeAllocatorType>();
- new (NodeAllocator) Allocators::NodeAllocatorType(Max);
- A.NodeAllocator = NodeAllocator;
-
- auto RootAllocator = allocate<Allocators::RootAllocatorType>();
- new (RootAllocator) Allocators::RootAllocatorType(Max);
- A.RootAllocator = RootAllocator;
-
- auto ShadowStackAllocator =
- allocate<Allocators::ShadowStackAllocatorType>();
- new (ShadowStackAllocator) Allocators::ShadowStackAllocatorType(Max);
- A.ShadowStackAllocator = ShadowStackAllocator;
-
- auto NodeIdPairAllocator = allocate<NodeIdPairAllocatorType>();
- new (NodeIdPairAllocator) NodeIdPairAllocatorType(Max);
- A.NodeIdPairAllocator = NodeIdPairAllocator;
+ Allocators A(Max);
+ return A;
+ }
+
+ static Allocators
+ InitAllocatorsFromBuffers(Allocators::Buffers &Bufs) XRAY_NEVER_INSTRUMENT {
+ Allocators A(Bufs);
return A;
}
@@ -253,66 +323,135 @@ private:
NodeArray Nodes;
RootArray Roots;
ShadowStackArray ShadowStack;
- NodeIdPairAllocatorType *NodeIdPairAllocator = nullptr;
+ NodeIdPairAllocatorType *NodeIdPairAllocator;
+ uint32_t OverflowedFunctions;
public:
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;
+ }
- void enterFunction(const int32_t FId, uint64_t TSC) XRAY_NEVER_INSTRUMENT {
+ ~FunctionCallTrie() XRAY_NEVER_INSTRUMENT {}
+
+ 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) XRAY_NEVER_INSTRUMENT {
+ 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;
@@ -357,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);
@@ -376,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;
}
}
}
@@ -410,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;
@@ -420,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);
@@ -436,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 8886a600d..d1c750f0c 100644
--- a/lib/xray/xray_init.cc
+++ b/lib/xray/xray_init.cc
@@ -106,8 +106,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 01bf6ddc6..6f7b6615b 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 a2a8f1ffe..dc3a82069 100644
--- a/lib/xray/xray_profile_collector.cc
+++ b/lib/xray/xray_profile_collector.cc
@@ -57,51 +57,91 @@ struct BlockHeader {
u64 ThreadId;
};
-using ThreadTriesArray = Array<ThreadTrie>;
+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 ThreadTriesArrayAllocator = typename ThreadTriesArray::AllocatorType;
using ProfileBufferArrayAllocator = typename ProfileBufferArray::AllocatorType;
// These need to be global aligned storage to avoid dynamic initialization. We
// need these to be aligned to allow us to placement new objects into the
// storage, and have pointers to those objects be appropriately aligned.
-static typename std::aligned_storage<sizeof(FunctionCallTrie::Allocators)>::type
- AllocatorStorage;
-static typename std::aligned_storage<sizeof(ThreadTriesArray)>::type
- ThreadTriesStorage;
static typename std::aligned_storage<sizeof(ProfileBufferArray)>::type
ProfileBuffersStorage;
-static typename std::aligned_storage<sizeof(ThreadTriesArrayAllocator)>::type
- ThreadTriesArrayAllocatorStorage;
static typename std::aligned_storage<sizeof(ProfileBufferArrayAllocator)>::type
ProfileBufferArrayAllocatorStorage;
-static ThreadTriesArray *ThreadTries = nullptr;
-static ThreadTriesArrayAllocator *ThreadTriesAllocator = nullptr;
-static ProfileBufferArray *ProfileBuffers = nullptr;
static ProfileBufferArrayAllocator *ProfileBuffersAllocator = nullptr;
-static FunctionCallTrie::Allocators *GlobalAllocators = nullptr;
+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) XRAY_NEVER_INSTRUMENT {
- static pthread_once_t Once = PTHREAD_ONCE_INIT;
- pthread_once(&Once, +[] { reset(); });
+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;
+ }
- ThreadTrie *Item = nullptr;
{
SpinMutexLock Lock(&GlobalMutex);
- if (GlobalAllocators == nullptr || ThreadTries == nullptr)
- return;
-
- Item = ThreadTries->Append({});
- Item->TId = TId;
- auto Trie = reinterpret_cast<FunctionCallTrie *>(&Item->TrieStorage);
- new (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();
+ }
}
-
- auto Trie = reinterpret_cast<FunctionCallTrie *>(&Item->TrieStorage);
- T.deepCopyInto(*Trie);
}
// A PathArray represents the function id's representing a stack trace. In this
@@ -115,13 +155,7 @@ 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;
- const FunctionCallTrie::Node *Node = nullptr;
-
- // Constructor for in-place construction.
- ProfileRecord(PathAllocator &A,
- const FunctionCallTrie::Node *N) XRAY_NEVER_INSTRUMENT
- : Path(A),
- Node(N) {}
+ const FunctionCallTrie::Node *Node;
};
namespace {
@@ -137,12 +171,14 @@ populateRecords(ProfileRecordArray &PRs, ProfileRecord::PathAllocator &PA,
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);
@@ -195,40 +231,54 @@ static void serializeRecords(ProfileBuffer *Buffer, const BlockHeader &Header,
} // namespace
void serialize() XRAY_NEVER_INSTRUMENT {
- SpinMutexLock Lock(&GlobalMutex);
-
- if (GlobalAllocators == nullptr || ThreadTries == nullptr ||
- ProfileBuffers == nullptr)
+ if (!atomic_load(&CollectorInitialized, memory_order_acquire))
return;
+ SpinMutexLock Lock(&GlobalMutex);
+
// Clear out the global ProfileBuffers, if it's not empty.
for (auto &B : *ProfileBuffers)
- deallocateBuffer(reinterpret_cast<uint8_t *>(B.Data), B.Size);
+ deallocateBuffer(reinterpret_cast<unsigned char *>(B.Data), B.Size);
ProfileBuffers->trim(ProfileBuffers->size());
- if (ThreadTries->empty())
+ DCHECK_NE(TDArray, nullptr);
+ if (TDArray->empty())
return;
// Then repopulate the global ProfileBuffers.
u32 I = 0;
- for (const auto &ThreadTrie : *ThreadTries) {
+ 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 =
- *reinterpret_cast<const FunctionCallTrie *>(&(ThreadTrie.TrieStorage));
- 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.
@@ -245,15 +295,16 @@ void serialize() XRAY_NEVER_INSTRUMENT {
CumulativeSizes += 20 + (4 * Record.Path.size());
BlockHeader Header{16 + CumulativeSizes, I++, ThreadTrie.TId};
- auto Buffer = ProfileBuffers->Append({});
- Buffer->Size = sizeof(Header) + CumulativeSizes;
- Buffer->Data = allocateBuffer(Buffer->Size);
- DCHECK_NE(Buffer->Data, nullptr);
- serializeRecords(Buffer, Header, ProfileRecords);
+ 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() XRAY_NEVER_INSTRUMENT {
+ atomic_store(&CollectorInitialized, 0, memory_order_release);
SpinMutexLock Lock(&GlobalMutex);
if (ProfileBuffers != nullptr) {
@@ -261,46 +312,68 @@ void reset() XRAY_NEVER_INSTRUMENT {
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 (auto &T : *ThreadTries) {
- auto Trie = reinterpret_cast<FunctionCallTrie *>(&T.TrieStorage);
- Trie->~FunctionCallTrie();
+ 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->trim(ThreadTries->size());
+ // 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;
}
- // Reset the global allocators.
- if (GlobalAllocators != nullptr)
- GlobalAllocators->~Allocators();
+ if (TDAllocator != nullptr) {
+ TDAllocator->~Allocator();
+ TDAllocator = nullptr;
+ }
- GlobalAllocators =
- reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorStorage);
- new (GlobalAllocators) FunctionCallTrie::Allocators();
- *GlobalAllocators = FunctionCallTrie::InitAllocators();
+ if (Buffer.Data != nullptr) {
+ BQ->releaseBuffer(Buffer);
+ }
- if (ThreadTriesAllocator != nullptr)
- ThreadTriesAllocator->~ThreadTriesArrayAllocator();
+ 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();
- ThreadTriesAllocator = reinterpret_cast<ThreadTriesArrayAllocator *>(
- &ThreadTriesArrayAllocatorStorage);
- new (ThreadTriesAllocator)
- ThreadTriesArrayAllocator(profilingFlags()->global_allocator_max);
- ThreadTries = reinterpret_cast<ThreadTriesArray *>(&ThreadTriesStorage);
- new (ThreadTries) ThreadTriesArray(*ThreadTriesAllocator);
+ if (BQ->init(profilingFlags()->global_allocator_max, 1) !=
+ BufferQueue::ErrorCode::Ok)
+ return;
+ }
- if (ProfileBuffersAllocator != nullptr)
- ProfileBuffersAllocator->~ProfileBufferArrayAllocator();
+ if (BQ->getBuffer(Buffer) != BufferQueue::ErrorCode::Ok)
+ return;
+ new (&ProfileBufferArrayAllocatorStorage)
+ ProfileBufferArrayAllocator(profilingFlags()->global_allocator_max);
ProfileBuffersAllocator = reinterpret_cast<ProfileBufferArrayAllocator *>(
&ProfileBufferArrayAllocatorStorage);
- new (ProfileBuffersAllocator)
- ProfileBufferArrayAllocator(profilingFlags()->global_allocator_max);
+
+ new (&ProfileBuffersStorage) ProfileBufferArray(*ProfileBuffersAllocator);
ProfileBuffers =
reinterpret_cast<ProfileBufferArray *>(&ProfileBuffersStorage);
- new (ProfileBuffers) ProfileBufferArray(*ProfileBuffersAllocator);
+
+ 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) XRAY_NEVER_INSTRUMENT {
@@ -312,8 +385,10 @@ XRayBuffer nextBuffer(XRayBuffer B) XRAY_NEVER_INSTRUMENT {
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.
diff --git a/lib/xray/xray_profile_collector.h b/lib/xray/xray_profile_collector.h
index 335043db9..86c4ce853 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 6615de1a3..4323170cd 100644
--- a/lib/xray/xray_profiling.cc
+++ b/lib/xray/xray_profiling.cc
@@ -19,6 +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"
@@ -31,67 +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;
- FunctionCallTrie *FCT;
+struct ProfilingData {
+ atomic_uintptr_t Allocators;
+ atomic_uintptr_t FCT;
};
static pthread_key_t ProfilingKey;
-thread_local std::aligned_storage<sizeof(FunctionCallTrie::Allocators)>::type
+// 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)>::type
+thread_local std::aligned_storage<sizeof(FunctionCallTrie),
+ alignof(FunctionCallTrie)>::type
FunctionCallTrieStorage;
-thread_local std::aligned_storage<sizeof(ProfilingData)>::type ThreadStorage{};
-
-static ProfilingData &getThreadLocalData() XRAY_NEVER_INSTRUMENT {
- thread_local auto ThreadOnce = [] {
- new (&ThreadStorage) ProfilingData{};
- auto *Allocators =
- reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage);
- new (Allocators) FunctionCallTrie::Allocators();
- *Allocators = FunctionCallTrie::InitAllocators();
- auto *FCT = reinterpret_cast<FunctionCallTrie *>(&FunctionCallTrieStorage);
- new (FCT) FunctionCallTrie(*Allocators);
- auto &TLD = *reinterpret_cast<ProfilingData *>(&ThreadStorage);
- TLD.Allocators = Allocators;
- TLD.FCT = FCT;
- pthread_setspecific(ProfilingKey, &ThreadStorage);
+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);
-
- if (UNLIKELY(TLD.Allocators == nullptr || TLD.FCT == nullptr)) {
- auto *Allocators =
- reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorsStorage);
- new (Allocators) FunctionCallTrie::Allocators();
- *Allocators = FunctionCallTrie::InitAllocators();
- auto *FCT = reinterpret_cast<FunctionCallTrie *>(&FunctionCallTrieStorage);
- new (FCT) FunctionCallTrie(*Allocators);
- TLD.Allocators = Allocators;
- TLD.FCT = FCT;
+ RecursionGuard TLDInit(TLDInitGuard);
+ if (!TLDInit)
+ return nullptr;
+
+ 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 *reinterpret_cast<ProfilingData *>(&ThreadStorage);
+ 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();
- 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
@@ -104,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) {
@@ -115,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
@@ -133,51 +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) XRAY_NEVER_INSTRUMENT {
- if (TLD.Allocators == nullptr || TLD.FCT == nullptr)
- return;
-
- if (!TLD.FCT->getRoots().empty())
- profileCollectorService::post(*TLD.FCT, GetTid());
-
- cleanupTLD();
-}
-
-} // namespace
-
void profilingHandleArg0(int32_t FuncId,
XRayEntryType Entry) XRAY_NEVER_INSTRUMENT {
unsigned char CPU;
@@ -187,22 +278,29 @@ void profilingHandleArg0(int32_t FuncId,
return;
auto Status = atomic_load(&ProfilerLogStatus, memory_order_acquire);
+ 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)) {
- auto &TLD = getThreadLocalData();
postCurrentThreadFCT(TLD);
return;
}
- auto &TLD = getThreadLocalData();
+ 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.
@@ -225,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.
@@ -242,12 +350,16 @@ XRayLogInitStatus profilingFinalize() XRAY_NEVER_INSTRUMENT {
}
XRayLogInitStatus
-profilingLoggingInit(UNUSED size_t BufferSize, UNUSED size_t BufferMax,
- void *Options, size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
+profilingLoggingInit(size_t, size_t, void *Options,
+ size_t OptionsSize) XRAY_NEVER_INSTRUMENT {
+ 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(UNUSED size_t BufferSize, UNUSED size_t BufferMax,
// 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 e9230ae64..ccd70860b 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 c723c7de0..bc7e9379f 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,89 +57,35 @@ 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() 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) {
- 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;
- }
+ static Segment SentinelSegment;
- Segment *InitHeadAndTail() XRAY_NEVER_INSTRUMENT {
- 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;
- }
-
- 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);
- 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) XRAY_NEVER_INSTRUMENT
+ Iterator(Segment *IS, uint64_t Off, uint64_t S) XRAY_NEVER_INSTRUMENT
: S(IS),
Offset(Off),
Size(S) {}
@@ -213,7 +154,7 @@ private:
// 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);
}
@@ -221,17 +162,183 @@ private:
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) XRAY_NEVER_INSTRUMENT : 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;
+ }
+
+ 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();
}
bool empty() const XRAY_NEVER_INSTRUMENT { return Size == 0; }
@@ -241,52 +348,71 @@ public:
return *Alloc;
}
- size_t size() const XRAY_NEVER_INSTRUMENT { return Size; }
+ uint64_t size() const XRAY_NEVER_INSTRUMENT { return Size; }
- T *Append(const T &E) XRAY_NEVER_INSTRUMENT {
- 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) XRAY_NEVER_INSTRUMENT {
- 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 XRAY_NEVER_INSTRUMENT {
+ 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;
@@ -295,7 +421,7 @@ 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);
@@ -330,39 +456,172 @@ 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) XRAY_NEVER_INSTRUMENT {
- if (Elements == 0)
- return;
-
- 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.
@@ -384,8 +643,8 @@ public:
// 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_tsc.h b/lib/xray/xray_tsc.h
index 4507564e7..180d6df18 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 68f4e8c10..59ba6c308 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 eafa16e1a..60438973f 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 508f749c2..e63ee1b3b 100644
--- a/lib/xray/xray_x86_64.cc
+++ b/lib/xray/xray_x86_64.cc
@@ -1,5 +1,8 @@
#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"
@@ -10,6 +13,8 @@
#include <machine/cpu.h>
#endif
#include <sys/sysctl.h>
+#elif SANITIZER_FUCHSIA
+#include <zircon/syscalls.h>
#endif
#include <atomic>
@@ -87,14 +92,14 @@ uint64_t getTSCFrequency() XRAY_NEVER_INSTRUMENT {
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 (sysctlbyname("machdep.tsc.frequency", &TSCFrequency, &tscfreqsz,
- NULL, 0) != -1 ) {
+ 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 {
@@ -103,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;
@@ -320,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 {
@@ -342,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 000000000..4799b66f3
--- /dev/null
+++ b/test/.clang-format
@@ -0,0 +1,2 @@
+BasedOnStyle: LLVM
+ColumnLimit: 0
diff --git a/test/asan/CMakeLists.txt b/test/asan/CMakeLists.txt
index e6d1df5e0..6c22ef3b1 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 000000000..8a0fbf943
--- /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 56dd89b16..e1e454be0 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/Linux/local_alias.cc b/test/asan/TestCases/Linux/local_alias.cc
index 266d3fe6b..635e30b87 100644
--- a/test/asan/TestCases/Linux/local_alias.cc
+++ b/test/asan/TestCases/Linux/local_alias.cc
@@ -11,7 +11,7 @@
// RUN: %clangxx -DBUILD_UNINSTRUMENTED_DSO=1 -fPIC -shared %s -o %t-UNINSTRUMENTED-SO.so
// 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: %run %t-EXE
#if defined (BUILD_INSTRUMENTED_DSO)
long h = 15;
diff --git a/test/asan/TestCases/Linux/odr-violation.cc b/test/asan/TestCases/Linux/odr-violation.cc
index 70437a832..05ee1e5c1 100644
--- a/test/asan/TestCases/Linux/odr-violation.cc
+++ b/test/asan/TestCases/Linux/odr-violation.cc
@@ -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: use indicator symbol to detect ODR violation.
+// Use private aliases for global variables without indicator symbol.
// 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: %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 -mllvm -asan-use-odr-indicator %s -o %t-ODR-SO.so -DSZ=100
+// RUN: %clangxx_asan -mllvm -asan-use-private-alias -mllvm -asan-use-odr-indicator %s %t-ODR-SO.so -Wl,-R. -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 %t-ODR-SO.so -DSZ=100
+// RUN: %clangxx_asan -fsanitize-address-use-odr-indicator %s %t-ODR-SO.so -Wl,-R. -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
diff --git a/test/asan/TestCases/Linux/odr_indicators.cc b/test/asan/TestCases/Linux/odr_indicators.cc
new file mode 100644
index 000000000..36176b552
--- /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/preinstalled_signal.cc b/test/asan/TestCases/Linux/preinstalled_signal.cc
index ac4ea93a5..2b50944c6 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 6d76a309b..152be2ab3 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 12a88402e..9dbd72eab 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 0aafa3e79..160c1c940 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 a7d2bfb9b..b31b035f9 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/stack-use-after-return.cc b/test/asan/TestCases/Posix/stack-use-after-return.cc
index 237c880f8..02588d708 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/tsd_dtor_leak.cc b/test/asan/TestCases/Posix/tsd_dtor_leak.cc
index 9e71ff61c..860f3459e 100644
--- a/test/asan/TestCases/Posix/tsd_dtor_leak.cc
+++ b/test/asan/TestCases/Posix/tsd_dtor_leak.cc
@@ -2,6 +2,7 @@
// 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
#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 8a770786e..005bf7de1 100644
--- a/test/asan/TestCases/asan_and_llvm_coverage_test.cc
+++ b/test/asan/TestCases/asan_and_llvm_coverage_test.cc
@@ -3,6 +3,9 @@
// We don't really support running tests using profile runtime on Windows.
// UNSUPPORTED: windows-msvc
+
+// profile is disabled by default
+// UNSUPPORTED: netbsd
#include <stdio.h>
int foo() { return 1; }
int XXX = foo();
diff --git a/test/asan/TestCases/heavy_uar_test.cc b/test/asan/TestCases/heavy_uar_test.cc
index a83115022..29a1d1965 100644
--- a/test/asan/TestCases/heavy_uar_test.cc
+++ b/test/asan/TestCases/heavy_uar_test.cc
@@ -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/intercept-rethrow-exception.cc b/test/asan/TestCases/intercept-rethrow-exception.cc
index e81dc5398..019092a9e 100644
--- a/test/asan/TestCases/intercept-rethrow-exception.cc
+++ b/test/asan/TestCases/intercept-rethrow-exception.cc
@@ -4,6 +4,10 @@
// RUN: %clangxx_asan -fexceptions -O0 %s -o %t
// RUN: %run %t
+// The current implementation of this functionality requires special
+// combination of libraries that are not used by default on NetBSD
+// XFAIL: netbsd
+
#include <assert.h>
#include <exception>
#include <sanitizer/asan_interface.h>
diff --git a/test/asan/TestCases/interception_failure_test.cc b/test/asan/TestCases/interception_failure_test.cc
index d85500b50..4f6698e3a 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/suppressions-library.cc b/test/asan/TestCases/suppressions-library.cc
index 39ede0840..181ed7582 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/lit.cfg b/test/asan/lit.cfg
index f8994d069..3deb4ccc5 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 a9199ebf1..eb4391fca 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 0c0117dfe..3c2ded7b7 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 3ffcf4b2d..23e52d345 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 06186614f..2dcd4494f 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 1f0865352..69d0715b7 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 157838b6b..082583cb3 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 a2215f3c1..e74f379c8 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/ctzti2_test.c b/test/builtins/Unit/ctzti2_test.c
index bef79b6f8..2ddcf0d0a 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 9a6bf178b..77776e28a 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 3b312c430..a7f0ca02b 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 0abe187ae..2b9db0f51 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 ec4e8ddb0..25cf092c4 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 249d8f886..ca0bb76f1 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 fd2d108f2..2306d283b 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 256ba0cf4..4e6fd39dc 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 40ad2b04f..dcfa75c4b 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 518ef44fb..86f4c02da 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 9da8c5dc6..51178f28e 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 9d7282cde..9162471a3 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 77a6f7dbf..501a8dc6c 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 1bf19ba14..f91d0e1bd 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 c7c11ba48..f460a059e 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 0f7ad4634..a9a8441d0 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 4b63948b5..d5fa73b0c 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 4241bdc9c..75d70f703 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 91356c8bf..51020edbf 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 53fbf5bba..9faa0b5af 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 e7c78cf16..9eb56f648 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 8227f2426..d92bae68b 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 36e96ad60..f964ed699 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 bb7379cae..89eb0b04f 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 980f44869..9c2765ec6 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 bcd26d0af..11d578be5 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 ef8b2c383..91c169fb5 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/subvti3_test.c b/test/builtins/Unit/subvti3_test.c
index 66806ba0b..3add1ebc4 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 17a857fb5..39a6f5b95 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 b07ce8c24..38da855ae 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 81cedadff..2db440d5b 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 5bce0258a..adfc8eba3 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/cross-dso/lit.local.cfg b/test/cfi/cross-dso/lit.local.cfg
index afdac4246..245d434fa 100644
--- a/test/cfi/cross-dso/lit.local.cfg
+++ b/test/cfi/cross-dso/lit.local.cfg
@@ -5,7 +5,7 @@ def getRoot(config):
root = getRoot(config)
-if root.host_os not in ['Linux']:
+if root.host_os not in ['Linux', 'FreeBSD', 'NetBSD']:
config.unsupported = True
# Android O (API level 26) has support for cross-dso cfi in libdl.so.
diff --git a/test/esan/TestCases/large-stack-linux.c b/test/esan/TestCases/large-stack-linux.c
index 1af32f8ba..17d88674b 100644
--- a/test/esan/TestCases/large-stack-linux.c
+++ b/test/esan/TestCases/large-stack-linux.c
@@ -1,5 +1,7 @@
// RUN: %clang_esan_wset -O0 %s -o %t 2>&1
// RUN: %env_esan_opts="verbosity=1 record_snapshots=0" %run %t %t 2>&1 | FileCheck %s
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <assert.h>
#include <stdio.h>
diff --git a/test/esan/TestCases/workingset-early-fault.c b/test/esan/TestCases/workingset-early-fault.c
index 1c420c368..971285b3f 100644
--- a/test/esan/TestCases/workingset-early-fault.c
+++ b/test/esan/TestCases/workingset-early-fault.c
@@ -3,6 +3,8 @@
//
// RUN: %clang_esan_wset %s -o %t
// RUN: %run %t 2>&1 | FileCheck %s
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <stdio.h>
#include <stdlib.h>
diff --git a/test/esan/TestCases/workingset-memset.cpp b/test/esan/TestCases/workingset-memset.cpp
index 9c972ec7a..56ed2f5b7 100644
--- a/test/esan/TestCases/workingset-memset.cpp
+++ b/test/esan/TestCases/workingset-memset.cpp
@@ -1,5 +1,7 @@
// RUN: %clang_esan_wset -O0 %s -o %t 2>&1
// RUN: %run %t 2>&1 | FileCheck %s
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <stdlib.h>
#include <string.h>
diff --git a/test/esan/TestCases/workingset-midreport.cpp b/test/esan/TestCases/workingset-midreport.cpp
index 38c376554..acd1eed17 100644
--- a/test/esan/TestCases/workingset-midreport.cpp
+++ b/test/esan/TestCases/workingset-midreport.cpp
@@ -6,6 +6,8 @@
// FIXME: Re-enable once PR33590 is fixed.
// UNSUPPORTED: x86_64
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <sanitizer/esan_interface.h>
#include <sched.h>
diff --git a/test/esan/TestCases/workingset-samples.cpp b/test/esan/TestCases/workingset-samples.cpp
index d97b62ba4..1f8e97dad 100644
--- a/test/esan/TestCases/workingset-samples.cpp
+++ b/test/esan/TestCases/workingset-samples.cpp
@@ -3,6 +3,8 @@
// FIXME: Re-enable once PR33590 is fixed.
// UNSUPPORTED: x86_64
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <sanitizer/esan_interface.h>
#include <sched.h>
diff --git a/test/esan/TestCases/workingset-signal-posix.cpp b/test/esan/TestCases/workingset-signal-posix.cpp
index ba776fc02..6f9787bd7 100644
--- a/test/esan/TestCases/workingset-signal-posix.cpp
+++ b/test/esan/TestCases/workingset-signal-posix.cpp
@@ -1,5 +1,7 @@
// RUN: %clang_esan_wset -O0 %s -o %t 2>&1
// RUN: %run %t 2>&1 | FileCheck %s
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <assert.h>
#include <setjmp.h>
diff --git a/test/esan/TestCases/workingset-simple.cpp b/test/esan/TestCases/workingset-simple.cpp
index f1ac2ecfe..dc17bcfd5 100644
--- a/test/esan/TestCases/workingset-simple.cpp
+++ b/test/esan/TestCases/workingset-simple.cpp
@@ -3,6 +3,8 @@
// FIXME: Re-enable once PR33590 is fixed.
// UNSUPPORTED: x86_64
+// Stucks at init and no clone feature equivalent.
+// UNSUPPORTED: freebsd
#include <stdlib.h>
#include <string.h>
diff --git a/test/esan/lit.cfg b/test/esan/lit.cfg
index 8b8457d66..1bb34ee08 100644
--- a/test/esan/lit.cfg
+++ b/test/esan/lit.cfg
@@ -39,6 +39,5 @@ config.substitutions.append(('%env_esan_opts=',
# Default test suffixes.
config.suffixes = ['.c', '.cpp']
-# EfficiencySanitizer tests are currently supported on Linux x86-64 only.
-if config.host_os not in ['Linux'] or config.target_arch not in ['x86_64', 'mips64'] :
+if config.host_os not in ['Linux', 'FreeBSD'] or config.target_arch not in ['x86_64', 'mips64'] :
config.unsupported = True
diff --git a/test/fuzzer/ImplicitIntegerSignChangeTest.cpp b/test/fuzzer/ImplicitIntegerSignChangeTest.cpp
new file mode 100644
index 000000000..0fd7df0e2
--- /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 000000000..6e65f5442
--- /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 cb935da0c..9a17802e2 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 000000000..c0bf40ab0
--- /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/ReadBinaryTest.cpp b/test/fuzzer/ReadBinaryTest.cpp
new file mode 100644
index 000000000..de7a40036
--- /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/counters.test b/test/fuzzer/counters.test
index f75d3a037..8f461c6e1 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 12a91c8ca..ff7a436e3 100644
--- a/test/fuzzer/coverage.test
+++ b/test/fuzzer/coverage.test
@@ -1,5 +1,5 @@
# FIXME: Disabled on Windows because -fPIC cannot be used to compile for Windows.
-UNSUPPORTED: aarch64, 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 000000000..85445ea9f
--- /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/dump_coverage.test b/test/fuzzer/dump_coverage.test
index 21e4348f1..803a4fbb8 100644
--- a/test/fuzzer/dump_coverage.test
+++ b/test/fuzzer/dump_coverage.test
@@ -8,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/fuzzer-implicit-integer-sign-change.test b/test/fuzzer/fuzzer-implicit-integer-sign-change.test
new file mode 100644
index 000000000..7524f6cc4
--- /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 c968ea7d6..000000000
--- 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: ImplicitIntegerTruncationTest.cpp:22:17: runtime error: implicit conversion from type 'int' of value 256 (32-bit, signed) to type 'unsigned char' changed the value to 0 (8-bit, unsigned)
-CHECK: Test unit written to ./crash-
diff --git a/test/fuzzer/fuzzer-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 000000000..532b36a03
--- /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 000000000..d41625d3a
--- /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 000000000..e62a01e9e
--- /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-oom.test b/test/fuzzer/fuzzer-oom.test
index 362ec314b..9bc451c50 100644
--- a/test/fuzzer/fuzzer-oom.test
+++ b/test/fuzzer/fuzzer-oom.test
@@ -1,4 +1,4 @@
-UNSUPPORTED: aarch64
+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
diff --git a/test/fuzzer/handle-unstable.test b/test/fuzzer/handle-unstable.test
index ea8d9e8d8..d20224689 100644
--- a/test/fuzzer/handle-unstable.test
+++ b/test/fuzzer/handle-unstable.test
@@ -1,6 +1,6 @@
# Tests -handle_unstable
# FIXME: Disabled on Windows until symbolization works properly.
-UNSUPPORTED: aarch64, windows
+UNSUPPORTED: windows
RUN: %cpp_compiler %S/PrintUnstableStatsTest.cpp -o %t-HandleUnstableTest
diff --git a/test/fuzzer/lit.cfg b/test/fuzzer/lit.cfg
index dff48cac2..608991c07 100644
--- a/test/fuzzer/lit.cfg
+++ b/test/fuzzer/lit.cfg
@@ -27,6 +27,7 @@ config.test_format = lit.formats.ShTest(execute_external)
# 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:
diff --git a/test/fuzzer/read-binary.test b/test/fuzzer/read-binary.test
new file mode 100644
index 000000000..c80858e81
--- /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/value-profile-cmp.test b/test/fuzzer/value-profile-cmp.test
index 8d964b10b..8f6ffe99c 100644
--- a/test/fuzzer/value-profile-cmp.test
+++ b/test/fuzzer/value-profile-cmp.test
@@ -1,5 +1,5 @@
# FIXME: Disabled on Windows because of hangs.
-UNSUPPORTED: windows
+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 4bf119fcb..5935ed6d1 100644
--- a/test/fuzzer/value-profile-cmp2.test
+++ b/test/fuzzer/value-profile-cmp2.test
@@ -1,3 +1,4 @@
+UNSUPPORTED: ios
CHECK: BINGO
RUN: %cpp_compiler -fno-sanitize=address %S/SimpleHashTest.cpp -o %t-SimpleHashTest
RUN: not %run %t-SimpleHashTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 -max_len=64 2>&1 | FileCheck %s
diff --git a/test/fuzzer/value-profile-cmp3.test b/test/fuzzer/value-profile-cmp3.test
index 58ba18b90..fe715925f 100644
--- a/test/fuzzer/value-profile-cmp3.test
+++ b/test/fuzzer/value-profile-cmp3.test
@@ -1,3 +1,4 @@
+UNSUPPORTED: ios
CHECK: BINGO
RUN: %cpp_compiler %S/AbsNegAndConstantTest.cpp -o %t-AbsNegAndConstantTest
RUN: not %run %t-AbsNegAndConstantTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s
diff --git a/test/fuzzer/value-profile-div.test b/test/fuzzer/value-profile-div.test
index 59cc7c2f9..38f211241 100644
--- a/test/fuzzer/value-profile-div.test
+++ b/test/fuzzer/value-profile-div.test
@@ -1,4 +1,4 @@
-XFAIL: ios
+UNSUPPORTED: ios
UNSUPPORTED: aarch64
CHECK: AddressSanitizer: {{FPE|int-divide-by-zero}}
RUN: %cpp_compiler %S/DivTest.cpp -fsanitize-coverage=trace-div -o %t-DivTest
diff --git a/test/fuzzer/value-profile-mem.test b/test/fuzzer/value-profile-mem.test
index 57c844e92..7d68b8811 100644
--- a/test/fuzzer/value-profile-mem.test
+++ b/test/fuzzer/value-profile-mem.test
@@ -1,3 +1,4 @@
+UNSUPPORTED: ios
UNSUPPORTED: freebsd
CHECK: BINGO
RUN: %cpp_compiler %S/SingleMemcmpTest.cpp -o %t-SingleMemcmpTest
diff --git a/test/fuzzer/value-profile-set.test b/test/fuzzer/value-profile-set.test
index e55f1e4a8..7515e3651 100644
--- a/test/fuzzer/value-profile-set.test
+++ b/test/fuzzer/value-profile-set.test
@@ -1,3 +1,4 @@
+UNSUPPORTED: ios
CHECK: BINGO
RUN: %cpp_compiler %S/FourIndependentBranchesTest.cpp -o %t-FourIndependentBranchesTest
RUN: not %run %t-FourIndependentBranchesTest -seed=1 -use_cmp=0 -use_value_profile=1 -runs=100000000 2>&1 | FileCheck %s
diff --git a/test/fuzzer/value-profile-strcmp.test b/test/fuzzer/value-profile-strcmp.test
index 647121f22..9b7a244d7 100644
--- a/test/fuzzer/value-profile-strcmp.test
+++ b/test/fuzzer/value-profile-strcmp.test
@@ -1,3 +1,4 @@
+UNSUPPORTED: ios
UNSUPPORTED: freebsd
CHECK: BINGO
RUN: %cpp_compiler %S/SingleStrcmpTest.cpp -o %t-SingleStrcmpTest
diff --git a/test/fuzzer/value-profile-switch.test b/test/fuzzer/value-profile-switch.test
index cc3d4944c..a71682d79 100644
--- a/test/fuzzer/value-profile-switch.test
+++ b/test/fuzzer/value-profile-switch.test
@@ -1,4 +1,4 @@
-XFAIL: ios
+UNSUPPORTED: ios
CHECK: BINGO
RUN: %cpp_compiler %S/SwitchTest.cpp -o %t-SwitchTest
RUN: %cpp_compiler %S/Switch2Test.cpp -o %t-Switch2Test
diff --git a/test/fuzzer/windows-opt-ref.test b/test/fuzzer/windows-opt-ref.test
new file mode 100644
index 000000000..1f3386d13
--- /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/TestCases/abort-message-android.cc b/test/hwasan/TestCases/abort-message-android.cc
new file mode 100644
index 000000000..f89b929d4
--- /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 000000000..457e29659
--- /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/heap-buffer-overflow.c b/test/hwasan/TestCases/heap-buffer-overflow.c
index 40b6e6e9d..bff39d293 100644
--- a/test/hwasan/TestCases/heap-buffer-overflow.c
+++ b/test/hwasan/TestCases/heap-buffer-overflow.c
@@ -1,26 +1,61 @@
// RUN: %clang_hwasan %s -o %t
-// RUN: not %run %t 40 2>&1 | FileCheck %s --check-prefix=CHECK40
-// RUN: not %run %t 80 2>&1 | FileCheck %s --check-prefix=CHECK80
+// RUN: not %run %t 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);
- x[offset] = 42;
-// CHECK40: is located 10 bytes to the right of 30-byte region
-// CHECK80: is located 50 bytes to the right of 30-byte region
+ 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: allocated heap chunk; size: 32 offset: 2
// 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/random-align-right.c b/test/hwasan/TestCases/random-align-right.c
new file mode 100644
index 000000000..8c524ef47
--- /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/stack-history-length.c b/test/hwasan/TestCases/stack-history-length.c
index f4c0b036f..c8583c67c 100644
--- a/test/hwasan/TestCases/stack-history-length.c
+++ b/test/hwasan/TestCases/stack-history-length.c
@@ -1,7 +1,6 @@
-// RUN: %clang_hwasan -O1 -DX=2046 %s -o %t.2046
-// RUN: %clang_hwasan -O1 -DX=2047 %s -o %t.2047
-// RUN: %env_hwasan_opts=stack_history_size=2048 not %run %t.2046 2>&1 | FileCheck %s --check-prefix=YES
-// RUN: %env_hwasan_opts=stack_history_size=2048 not %run %t.2047 2>&1 | FileCheck %s --check-prefix=NO
+// 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
@@ -16,7 +15,8 @@ __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 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();
diff --git a/test/hwasan/TestCases/stack-uar.c b/test/hwasan/TestCases/stack-uar.c
index 2c59b1785..0b1faf8b5 100644
--- a/test/hwasan/TestCases/stack-uar.c
+++ b/test/hwasan/TestCases/stack-uar.c
@@ -1,23 +1,41 @@
-// RUN: %clang_hwasan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+// 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
-#include <stdlib.h>
-#include <sanitizer/hwasan_interface.h>
+void USE(void *x) { // pretend_to_do_something(void *x)
+ __asm__ __volatile__("" : : "r" (x) : "memory");
+}
__attribute__((noinline))
-char *f() {
- char z[0x1000];
- char *volatile p = z;
+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() {
- return *f();
+ char *p = buggy();
+ Unrelated1();
+ Unrelated2();
+ Unrelated3();
+ return *p;
// CHECK: READ of size 1 at
- // CHECK: #0 {{.*}} in main{{.*}}stack-uar.c:16
-
+ // CHECK: #0 {{.*}} in main{{.*}}stack-uar.c:[[@LINE-2]]
// CHECK: is located in stack of thread
+ // CHECK: Previosly 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/tail-magic.c b/test/hwasan/TestCases/tail-magic.c
new file mode 100644
index 000000000..95c5ada08
--- /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
index 33cea1018..f091167e3 100644
--- a/test/hwasan/TestCases/thread-uaf.c
+++ b/test/hwasan/TestCases/thread-uaf.c
@@ -36,6 +36,8 @@ void *Use(void *arg) {
// 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;
}
diff --git a/test/hwasan/TestCases/use-after-free.c b/test/hwasan/TestCases/use-after-free.c
index 3dae97b0c..fcdd0771c 100644
--- a/test/hwasan/TestCases/use-after-free.c
+++ b/test/hwasan/TestCases/use-after-free.c
@@ -22,14 +22,17 @@ int main() {
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: 16 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:[[@LINE-11]]
+ // 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:[[@LINE-16]]
+ // 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
diff --git a/test/lit.common.cfg b/test/lit.common.cfg
index 0c2355f1d..cdcba5fc9 100644
--- a/test/lit.common.cfg
+++ b/test/lit.common.cfg
@@ -59,6 +59,7 @@ if config.asan_shadow_scale != '':
# transitive shared library dependency (via asan runtime).
if config.android:
config.target_cflags += " -pie -fuse-ld=gold -Wl,--enable-new-dtags"
+ 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',
@@ -317,7 +318,7 @@ if config.host_os == 'Darwin' and is_darwin_lto_supported():
config.lto_supported = True
config.lto_launch = ["env", "DYLD_LIBRARY_PATH=" + config.llvm_shlib_dir]
config.lto_flags = []
-elif config.host_os == 'Linux' and is_linux_lto_supported():
+elif config.host_os in ['Linux', 'FreeBSD', 'NetBSD'] and is_linux_lto_supported():
config.lto_supported = True
config.lto_launch = []
if config.use_lld:
diff --git a/test/msan/Linux/reexec_unlimited_stack.cc b/test/msan/Linux/reexec_unlimited_stack.cc
new file mode 100644
index 000000000..61492ec34
--- /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/chained_origin_with_signals.cc b/test/msan/chained_origin_with_signals.cc
index 43dbdcca7..9c071569a 100644
--- a/test/msan/chained_origin_with_signals.cc
+++ b/test/msan/chained_origin_with_signals.cc
@@ -10,6 +10,9 @@
// RUN: not %run %t >%t.out 2>&1
// RUN: FileCheck %s < %t.out
+// Reported deadly signal due to stack-overflow
+// XFAIL: netbsd
+
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
diff --git a/test/msan/dtls_test.c b/test/msan/dtls_test.c
index b9021e0da..ee91be2ff 100644
--- a/test/msan/dtls_test.c
+++ b/test/msan/dtls_test.c
@@ -8,6 +8,9 @@
XFAIL: FreeBSD
UNSUPPORTED: powerpc
+
+ // Reports use-of-uninitialized-value, not analyzed
+ XFAIL: netbsd
*/
#ifndef BUILD_SO
diff --git a/test/msan/fork.cc b/test/msan/fork.cc
index e4dc54908..87d71f87e 100644
--- a/test/msan/fork.cc
+++ b/test/msan/fork.cc
@@ -14,6 +14,9 @@
// UNSUPPORTED: powerpc64-target-arch
// UNSUPPORTED: powerpc64le-target-arch
+// Sometimes hangs
+// UNSUPPORTED: netbsd
+
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
diff --git a/test/msan/ioctl_custom.cc b/test/msan/ioctl_custom.cc
index eaab63384..794f34535 100644
--- a/test/msan/ioctl_custom.cc
+++ b/test/msan/ioctl_custom.cc
@@ -4,6 +4,9 @@
// RUN: %clangxx_msan -DPOSITIVE -O0 -g %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clangxx_msan -DPOSITIVE -O3 -g %s -o %t && not %run %t 2>&1 | FileCheck %s
+// Reports different report (not analyzed)
+// XFAIL: netbsd
+
#include <assert.h>
#include <stdlib.h>
#include <net/if.h>
diff --git a/test/msan/signal_stress_test.cc b/test/msan/signal_stress_test.cc
index 5bc6f5921..dfbc580fa 100644
--- a/test/msan/signal_stress_test.cc
+++ b/test/msan/signal_stress_test.cc
@@ -2,6 +2,9 @@
//
// Test that va_arg shadow from a signal handler does not leak outside.
+// Reported deadly signal due to stack-overflow
+// XFAIL: netbsd
+
#include <signal.h>
#include <stdarg.h>
#include <sanitizer/msan_interface.h>
diff --git a/test/profile/Inputs/instrprof-dlopen-dlclose-main.c.gcov b/test/profile/Inputs/instrprof-dlopen-dlclose-main.c.gcov
index acb2076fd..2d538f63e 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 97eef4c3b..f1dd17571 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 000000000..dadd89599
--- /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 000000000..f2141229b
--- /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 2a7b72488..f8e382974 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: 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 000000000..5213a5b80
--- /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 000000000..7542f6ff7
--- /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 000000000..f2e411c2a
--- /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 000000000..37cd1e59a
--- /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 9591f62ed..c667c7b9e 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 92532af30..4bc1c1cd5 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 000000000..ee1582351
--- /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 000000000..a91b20fdc
--- /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 f19431e17..6e9c5228b 100644
--- a/test/profile/Inputs/instrprof-gcov-switch1.c.gcov
+++ b/test/profile/Inputs/instrprof-gcov-switch1.c.gcov
@@ -3,7 +3,7 @@
// 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: 1: 3: int i = 22;
// CHECK-NEXT: -: 4:
diff --git a/test/profile/Inputs/instrprof-gcov-switch2.c.gcov b/test/profile/Inputs/instrprof-gcov-switch2.c.gcov
index 0b85e0f50..47d7f1d7c 100644
--- a/test/profile/Inputs/instrprof-gcov-switch2.c.gcov
+++ b/test/profile/Inputs/instrprof-gcov-switch2.c.gcov
@@ -3,7 +3,7 @@
// 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: 1: 3: int i = 22;
// CHECK-NEXT: -: 4:
diff --git a/test/profile/Inputs/instrprof-shared-lib.c.gcov b/test/profile/Inputs/instrprof-shared-lib.c.gcov
index fbc43d5f7..620a85257 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 779c885d8..39b32b8c0 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 76503d914..0fc7ccbab 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 b2dfe2acd..6027c64af 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 f70e34e52..fba3f3fe2 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 b9ecff698..86beda22a 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 7c9e0afa1..2e55741cc 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 1636ca635..05cd4e31d 100644
--- a/test/profile/Inputs/instrprof-shared-main.c.gcov
+++ b/test/profile/Inputs/instrprof-shared-main.c.gcov
@@ -6,7 +6,7 @@
// 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: 1001: 6: for (i = 0; i < 1000; i++)
// CHECK-NEXT: 1001000: 7: for (j = 0; j < 1000; j++)
diff --git a/test/profile/Posix/instrprof-gcov-execlp.test b/test/profile/Posix/instrprof-gcov-execlp.test
new file mode 100644
index 000000000..1d136ce97
--- /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 000000000..8e5cbc609
--- /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 436d7e663..5a406fda4 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 6667cabdb..1ef36ae8b 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-gcov-__gcov_flush-multiple.test b/test/profile/instrprof-gcov-__gcov_flush-multiple.test
new file mode 100644
index 000000000..fdc93c93e
--- /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-one-line-function.test b/test/profile/instrprof-gcov-one-line-function.test
new file mode 100644
index 000000000..d67c21a86
--- /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/sanitizer_common/TestCases/FreeBSD/capsicum.cc b/test/sanitizer_common/TestCases/FreeBSD/capsicum.cc
new file mode 100644
index 000000000..d17f27b38
--- /dev/null
+++ b/test/sanitizer_common/TestCases/FreeBSD/capsicum.cc
@@ -0,0 +1,62 @@
+// 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);
+ 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/lit.local.cfg b/test/sanitizer_common/TestCases/FreeBSD/lit.local.cfg
new file mode 100644
index 000000000..6f2f4280f
--- /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 e17de1853..bd58f4bd9 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 9c5263f8e..2a73c5088 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 43f7a7830..bbde13b56 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 000000000..9ac3c5dc5
--- /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 d6c3ecbff..9802617c2 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 000000000..acdfb17f2
--- /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 000000000..065623b7d
--- /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 000000000..8a71d5fcd
--- /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/fts.cc b/test/sanitizer_common/TestCases/NetBSD/fts.cc
new file mode 100644
index 000000000..1461005ef
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/fts.cc
@@ -0,0 +1,40 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#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/NetBSD/getgroupmembership.cc b/test/sanitizer_common/TestCases/NetBSD/getgroupmembership.cc
index ee27ad6cf..025ca9052 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 000000000..ea72e41ed
--- /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 000000000..f76a35aac
--- /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: 'a409cff8627afa00f3b563cf5f09af05'
+ // CHECK: test5: 'a409cff8627afa00f3b563cf5f09af05'
+ // CHECK: test6: '{{.*}}'
+ // CHECK: test7: '{{.*}}'
+
+ 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 000000000..831832691
--- /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: '85b3d78ce68be51f710272728fe606af'
+ // CHECK: test5: '85b3d78ce68be51f710272728fe606af'
+ // CHECK: test6: '{{.*}}'
+ // CHECK: test7: '{{.*}}'
+
+ 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 000000000..e27997906
--- /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: 'd6798ca88175b5feece4dda691a5b9b5'
+ // CHECK: test5: 'd6798ca88175b5feece4dda691a5b9b5'
+ // 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 000000000..1f6c14848
--- /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/regex.cc b/test/sanitizer_common/TestCases/NetBSD/regex.cc
new file mode 100644
index 000000000..8a4fb3885
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/regex.cc
@@ -0,0 +1,101 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <assert.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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();
+}
+
+void test_nsub(const regex_t *preg, const char *string) {
+ regmatch_t rm[10];
+ int rv = regexec(preg, string, __arraycount(rm), rm, 0);
+ if (!rv) {
+ char buf[1024];
+ ssize_t ss = regnsub(buf, __arraycount(buf), "\\1xyz", rm, string);
+ assert(ss != -1);
+
+ printf("'%s' -> '%s'\n", string, buf);
+ } else if (rv == REG_NOMATCH)
+ printf("%s: not-matched\n", string);
+ else
+ abort();
+}
+
+void test_asub(const regex_t *preg, const char *string) {
+ regmatch_t rm[10];
+ int rv = regexec(preg, string, __arraycount(rm), rm, 0);
+ if (!rv) {
+ char *buf;
+ ssize_t ss = regasub(&buf, "\\1xyz", rm, string);
+ assert(ss != -1);
+
+ printf("'%s' -> '%s'\n", string, buf);
+ free(buf);
+ } 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");
+
+ test_nsub(&regex, "ABC DEF");
+ test_asub(&regex, "GHI JKL");
+
+ 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: 'ABC DEF' -> 'Bxyz'
+ // CHECK: 'GHI JKL' -> 'Hxyz'
+ // CHECK: error:{{.*}}
+
+ 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 000000000..5b9ff0cb2
--- /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 000000000..ee5060a66
--- /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 000000000..4c9066db8
--- /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: 'bb058583870ed830d9b74b4c24af7fa2ab5684f4d88158a8094a68bcf908dc48'
+ // CHECK-256: test5: 'bb058583870ed830d9b74b4c24af7fa2ab5684f4d88158a8094a68bcf908dc48'
+ // CHECK-256: test6: 'cc1c596e07913a44fe35a4d4fd76b4bd6313604fa4264e2e6fbae1db78c24b22'
+ // CHECK-256: test7: 'cc1c596e07913a44fe35a4d4fd76b4bd6313604fa4264e2e6fbae1db78c24b22'
+ // 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: '330d1528c9828d46e200fbfe05cac41717bed2e5f87ba10d47c9d6098e94f5e902ad90d4dd9be0f4347bc026e1206abd'
+ // CHECK-384: test5: '330d1528c9828d46e200fbfe05cac41717bed2e5f87ba10d47c9d6098e94f5e902ad90d4dd9be0f4347bc026e1206abd'
+ // CHECK-384: test6: '60686e8385598c69a2309483b91c04a1e0deeef1201730607a1818d097e726a9cbde8f8b8de7ab76c1d347def17f5ab5'
+ // CHECK-384: test7: '60686e8385598c69a2309483b91c04a1e0deeef1201730607a1818d097e726a9cbde8f8b8de7ab76c1d347def17f5ab5'
+ // 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: '56b925cd62d73643e0f5ab821e062be9157035652cfb6396cb08d84f88981b1a9dae79f3d2707ad4f821d86b5608ae93f71788724328bb6e4ed1704a985d07a7'
+ // CHECK-512: test5: '56b925cd62d73643e0f5ab821e062be9157035652cfb6396cb08d84f88981b1a9dae79f3d2707ad4f821d86b5608ae93f71788724328bb6e4ed1704a985d07a7'
+ // CHECK-512: test6: '273b577200f95cfcb1627d94c0e6f3dd1682f63b4dfcc315355a7b90586e4cfd8f65c7cb315bd2b29c7ec0942510ffaceb1f02c7041f45528a6357fd38f2fe39'
+ // CHECK-512: test7: '273b577200f95cfcb1627d94c0e6f3dd1682f63b4dfcc315355a7b90586e4cfd8f65c7cb315bd2b29c7ec0942510ffaceb1f02c7041f45528a6357fd38f2fe39'
+ // 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 000000000..40dfca37b
--- /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 000000000..4d0d8b3ae
--- /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/strtonum.cc b/test/sanitizer_common/TestCases/NetBSD/strtonum.cc
new file mode 100644
index 000000000..2f47a3030
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/strtonum.cc
@@ -0,0 +1,52 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#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/NetBSD/sysctlgetmibinfo.cc b/test/sanitizer_common/TestCases/NetBSD/sysctlgetmibinfo.cc
new file mode 100644
index 000000000..d81c1567f
--- /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/NetBSD/vis.cc b/test/sanitizer_common/TestCases/NetBSD/vis.cc
new file mode 100644
index 000000000..89ea259e5
--- /dev/null
+++ b/test/sanitizer_common/TestCases/NetBSD/vis.cc
@@ -0,0 +1,245 @@
+// RUN: %clangxx -O0 -g %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#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/Posix/dedup_token_length_test.cc b/test/sanitizer_common/TestCases/Posix/dedup_token_length_test.cc
index d9a1bc660..94c50be16 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_r.cc b/test/sanitizer_common/TestCases/Posix/devname_r.cc
index 826b7c92e..3a05dae72 100644
--- a/test/sanitizer_common/TestCases/Posix/devname_r.cc
+++ b/test/sanitizer_common/TestCases/Posix/devname_r.cc
@@ -17,8 +17,13 @@ int main(void) {
type = S_ISCHR(st.st_mode) ? S_IFCHR : S_IFBLK;
+#if defined(__NetBSD__)
+ if (devname_r(st.st_rdev, type, name, sizeof(name)))
+ exit(1);
+#else
if (!devname_r(st.st_rdev, type, name, sizeof(name)))
exit(1);
+#endif
printf("%s\n", name);
diff --git a/test/sanitizer_common/TestCases/Posix/dump_instruction_bytes.cc b/test/sanitizer_common/TestCases/Posix/dump_instruction_bytes.cc
index 87e797a00..f42802a34 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/fseek.cc b/test/sanitizer_common/TestCases/Posix/fseek.cc
new file mode 100644
index 000000000..26f3b849f
--- /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/getmntinfo.cc b/test/sanitizer_common/TestCases/Posix/getmntinfo.cc
new file mode 100644
index 000000000..26c065d4d
--- /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/nl_langinfo.cc b/test/sanitizer_common/TestCases/Posix/nl_langinfo.cc
new file mode 100644
index 000000000..b8123542f
--- /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/readlinkat.c b/test/sanitizer_common/TestCases/Posix/readlinkat.c
index 0afb5efe6..227c6da54 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/setvbuf.cc b/test/sanitizer_common/TestCases/Posix/setvbuf.cc
new file mode 100644
index 000000000..bc29ba45d
--- /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/sysctl.cc b/test/sanitizer_common/TestCases/Posix/sysctl.cc
new file mode 100644
index 000000000..2cb8764f3
--- /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/corelimit.cc b/test/sanitizer_common/TestCases/corelimit.cc
index eb02afc01..2378a4cfd 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 0591d356f..b313df87c 100644
--- a/test/sanitizer_common/TestCases/get_module_and_offset_for_pc.cc
+++ b/test/sanitizer_common/TestCases/get_module_and_offset_for_pc.cc
@@ -1,6 +1,6 @@
// RUN: mkdir -p %t-dir
// RUN: %clangxx -DSHARED %s -shared -o %t-dir/get_module_and_offset_for_pc.so -fPIC
-// RUN: %clangxx -DSO_DIR=\"%t-dir\" -O0 %s -ldl -o %t
+// RUN: %clangxx -DSO_DIR=\"%t-dir\" -O0 %s -o %t
// RUN: %run %t 2>&1 | FileCheck %s
// UNSUPPORTED: i386-darwin
diff --git a/test/sanitizer_common/TestCases/pthread_mutexattr_get.cc b/test/sanitizer_common/TestCases/pthread_mutexattr_get.cc
index 26060f395..742ae72c4 100644
--- a/test/sanitizer_common/TestCases/pthread_mutexattr_get.cc
+++ b/test/sanitizer_common/TestCases/pthread_mutexattr_get.cc
@@ -1,5 +1,8 @@
// RUN: %clangxx -O0 %s -o %t && %run %t
+// pthread_mutexattr_setpshared and pthread_mutexattr_getpshared unavailable
+// UNSUPPORTED: netbsd
+
#include <assert.h>
#include <pthread.h>
diff --git a/test/sanitizer_common/TestCases/sanitizer_coverage_symbolize.cc b/test/sanitizer_common/TestCases/sanitizer_coverage_symbolize.cc
index 28e237802..daa994c81 100644
--- a/test/sanitizer_common/TestCases/sanitizer_coverage_symbolize.cc
+++ b/test/sanitizer_common/TestCases/sanitizer_coverage_symbolize.cc
@@ -7,7 +7,7 @@
// RUN: rm -rf $DIR
// RUN: mkdir -p $DIR
// RUN: cd $DIR
-// RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -ldl -o %t
+// RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -o %t
// RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s
// RUN: rm -rf $DIR
diff --git a/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc b/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc
index 1adbf653b..5e01a803b 100644
--- a/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc
+++ b/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc
@@ -9,7 +9,7 @@
// RUN: rm -rf $DIR
// RUN: mkdir -p $DIR
// RUN: cd $DIR
-// RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -ldl -o %t
+// RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -o %t
// RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s
// RUN: %sancovcc -covered-functions -strip_path_prefix=TestCases/ *.sancov %t 2>&1 | \
// RUN: FileCheck --check-prefix=CHECK-SANCOV %s
diff --git a/test/sanitizer_common/TestCases/symbolize_pc.cc b/test/sanitizer_common/TestCases/symbolize_pc.cc
index 68a6733f4..16a81f1bb 100644
--- a/test/sanitizer_common/TestCases/symbolize_pc.cc
+++ b/test/sanitizer_common/TestCases/symbolize_pc.cc
@@ -7,6 +7,17 @@
int GLOBAL_VAR_ABC;
+void SymbolizeSmallBuffer() {
+ char data[] = "abcdef";
+ __sanitizer_symbolize_pc(__builtin_return_address(0), "%p %F %L", data, 0);
+ printf("UNCHANGED '%s'\n", data);
+ __sanitizer_symbolize_pc(__builtin_return_address(0), "%p %F %L", data, 1);
+ printf("EMPTY '%s'\n", data);
+ __sanitizer_symbolize_pc(__builtin_return_address(0), "%p %F %L", data,
+ sizeof(data));
+ printf("PARTIAL '%s'\n", data);
+}
+
void SymbolizeCaller() {
char data[100];
__sanitizer_symbolize_pc(__builtin_return_address(0), "%p %F %L", data,
@@ -31,10 +42,16 @@ void SymbolizeData() {
printf("GLOBAL: %s\n", data);
}
-// CHECK: FIRST_FORMAT 0x{{.*}} in main symbolize_pc.cc:[[@LINE+3]]
-// CHECK: SECOND_FORMAT FUNC:main LINE:[[@LINE+2]] FILE:symbolize_pc.cc
int main() {
+ // CHECK: UNCHANGED 'abcdef'
+ // CHECK: EMPTY ''
+ // CHECK: PARTIAL '0x{{.*}}'
+ SymbolizeSmallBuffer();
+
+ // CHECK: FIRST_FORMAT 0x{{.*}} in main symbolize_pc.cc:[[@LINE+2]]
+ // CHECK: SECOND_FORMAT FUNC:main LINE:[[@LINE+1]] FILE:symbolize_pc.cc
SymbolizeCaller();
+
+ // CHECK: GLOBAL: GLOBAL_VAR_ABC
SymbolizeData();
}
-// CHECK: GLOBAL: GLOBAL_VAR_ABC
diff --git a/test/sanitizer_common/TestCases/symbolize_pc_inline.cc b/test/sanitizer_common/TestCases/symbolize_pc_inline.cc
new file mode 100644
index 000000000..9589a3af0
--- /dev/null
+++ b/test/sanitizer_common/TestCases/symbolize_pc_inline.cc
@@ -0,0 +1,32 @@
+// RUN: %clangxx -O3 %s -o %t
+// RUN: %env_tool_opts=strip_path_prefix=/TestCases/ %run %t 2>&1 | FileCheck %s
+// RUN: %env_tool_opts=strip_path_prefix=/TestCases/:symbolize_inline_frames=0 \
+// RUN: %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK-NOINLINE
+
+// XFAIL: darwin
+
+#include <sanitizer/common_interface_defs.h>
+#include <stdio.h>
+#include <string.h>
+
+char buffer[10000];
+
+__attribute__((noinline)) static void Symbolize() {
+ __sanitizer_symbolize_pc(__builtin_return_address(0), "%p %F %L", buffer,
+ sizeof(buffer));
+ for (char *p = buffer; strlen(p); p += strlen(p) + 1)
+ printf("%s\n", p);
+}
+
+// CHECK-NOINLINE: {{0x[0-9a-f]+}} in main symbolize_pc_inline.cc:[[@LINE+2]]
+// CHECK: [[ADDR:0x[0-9a-f]+]] in C2 symbolize_pc_inline.cc:[[@LINE+1]]
+static inline void C2() { Symbolize(); }
+
+// CHECK: [[ADDR]] in C3 symbolize_pc_inline.cc:[[@LINE+1]]
+static inline void C3() { C2(); }
+
+// CHECK: [[ADDR]] in C4 symbolize_pc_inline.cc:[[@LINE+1]]
+static inline void C4() { C3(); }
+
+// CHECK: [[ADDR]] in main symbolize_pc_inline.cc:[[@LINE+1]]
+int main() { C4(); }
diff --git a/test/sanitizer_common/ios_commands/iossim_run.py b/test/sanitizer_common/ios_commands/iossim_run.py
index 0d9ca935c..e1f633e87 100755
--- a/test/sanitizer_common/ios_commands/iossim_run.py
+++ b/test/sanitizer_common/ios_commands/iossim_run.py
@@ -8,7 +8,7 @@ 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", "UBSAN_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]
diff --git a/test/sanitizer_common/lit.common.cfg b/test/sanitizer_common/lit.common.cfg
index 1b347bf2b..75cc7b77e 100644
--- a/test/sanitizer_common/lit.common.cfg
+++ b/test/sanitizer_common/lit.common.cfg
@@ -46,7 +46,13 @@ if default_tool_options_str:
config.environment[tool_options] = default_tool_options_str
default_tool_options_str += ':'
+if config.host_os in ['Linux']:
+ extra_link_flags = ["-ldl"]
+else:
+ extra_link_flags = []
+
clang_cflags = config.debug_info_flags + tool_cflags + [config.target_cflags]
+clang_cflags += extra_link_flags
clang_cxxflags = config.cxx_mode_flags + clang_cflags
def build_invocation(compile_flags):
diff --git a/test/tsan/Linux/thread_timedjoin.c b/test/tsan/Linux/thread_timedjoin.c
new file mode 100644
index 000000000..1d3f1098e
--- /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 000000000..675e15955
--- /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 000000000..cdbe60900
--- /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/dtls.c b/test/tsan/dtls.c
index 51697565f..b00ca7691 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 d103839b8..027ba7bac 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 d6c3f870e..43780daf7 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 796ea9323..4686e8eb3 100644
--- a/test/tsan/ignored-interceptors-mmap.cc
+++ b/test/tsan/ignored-interceptors-mmap.cc
@@ -1,7 +1,7 @@
// 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
+// XFAIL: freebsd,netbsd
#include <errno.h>
#include <sys/mman.h>
diff --git a/test/tsan/large_malloc_meta.cc b/test/tsan/large_malloc_meta.cc
index e83004824..0d0eec34b 100644
--- a/test/tsan/large_malloc_meta.cc
+++ b/test/tsan/large_malloc_meta.cc
@@ -1,4 +1,7 @@
// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// UNSUPPORTED: ios
+
#include "test.h"
#include <sys/mman.h>
diff --git a/test/tsan/mmap_large.cc b/test/tsan/mmap_large.cc
index 764e954f2..c8d258e80 100644
--- a/test/tsan/mmap_large.cc
+++ b/test/tsan/mmap_large.cc
@@ -1,4 +1,7 @@
// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+// UNSUPPORTED: ios
+
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
diff --git a/test/tsan/mutex_lock_destroyed.cc b/test/tsan/mutex_lock_destroyed.cc
index 52d6be621..581d8cce2 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 cfe8a18c1..438f54914 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/ubsan/TestCases/ImplicitConversion/integer-arithmetic-value-change.c b/test/ubsan/TestCases/ImplicitConversion/integer-arithmetic-value-change.c
new file mode 100644
index 000000000..ef06fbf83
--- /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
index d49d04385..5c1530a63 100644
--- a/test/ubsan/TestCases/ImplicitConversion/integer-conversion.c
+++ b/test/ubsan/TestCases/ImplicitConversion/integer-conversion.c
@@ -27,87 +27,87 @@
// However, not all of them should result in the check.
// So here, we *only* check which should and which should not result in checks.
-unsigned int convert_unsigned_int_to_unsigned_int(unsigned int x) {
+uint32_t convert_unsigned_int_to_unsigned_int(uint32_t x) {
#line 100
return x;
}
-unsigned char convert_unsigned_char_to_unsigned_char(unsigned char x) {
+uint8_t convert_unsigned_char_to_unsigned_char(uint8_t x) {
#line 200
return x;
}
-signed int convert_signed_int_to_signed_int(signed int x) {
+int32_t convert_signed_int_to_signed_int(int32_t x) {
#line 300
return x;
}
-signed char convert_signed_char_to_signed_char(signed char x) {
+int8_t convert_signed_char_to_signed_char(int8_t x) {
#line 400
return x;
}
-unsigned char convert_unsigned_int_to_unsigned_char(unsigned int x) {
+uint8_t convert_unsigned_int_to_unsigned_char(uint32_t x) {
#line 500
return x;
}
-unsigned int convert_unsigned_char_to_unsigned_int(unsigned char x) {
+uint32_t convert_unsigned_char_to_unsigned_int(uint8_t x) {
#line 600
return x;
}
-signed int convert_unsigned_char_to_signed_int(unsigned char x) {
+int32_t convert_unsigned_char_to_signed_int(uint8_t x) {
#line 700
return x;
}
-signed int convert_signed_char_to_signed_int(signed char x) {
+int32_t convert_signed_char_to_signed_int(int8_t x) {
#line 800
return x;
}
-signed int convert_unsigned_int_to_signed_int(unsigned int x) {
+int32_t convert_unsigned_int_to_signed_int(uint32_t x) {
#line 900
return x;
}
-unsigned int convert_signed_int_to_unsigned_int(signed int x) {
+uint32_t convert_signed_int_to_unsigned_int(int32_t x) {
#line 1000
return x;
}
-unsigned char convert_signed_int_to_unsigned_char(signed int x) {
+uint8_t convert_signed_int_to_unsigned_char(int32_t x) {
#line 1100
return x;
}
-unsigned char convert_signed_char_to_unsigned_char(signed char x) {
+uint8_t convert_signed_char_to_unsigned_char(int8_t x) {
#line 1200
return x;
}
-signed char convert_unsigned_char_to_signed_char(unsigned char x) {
+int8_t convert_unsigned_char_to_signed_char(uint8_t x) {
#line 1300
return x;
}
-unsigned int convert_signed_char_to_unsigned_int(signed char x) {
+uint32_t convert_signed_char_to_unsigned_int(int8_t x) {
#line 1400
return x;
}
-signed char convert_unsigned_int_to_signed_char(unsigned int x) {
+int8_t convert_unsigned_int_to_signed_char(uint32_t x) {
#line 1500
return x;
}
-signed char convert_signed_int_to_signed_char(signed int x) {
+int8_t convert_signed_int_to_signed_char(int32_t x) {
#line 1600
return x;
}
-#line 1111 // !!!
+#line 10111 // !!!
int main() {
// No bits set.
@@ -153,18 +153,24 @@ int main() {
convert_signed_int_to_signed_int((int32_t)(uint32_t)UINT32_MAX);
convert_signed_char_to_signed_char((int8_t)UINT8_MAX);
convert_unsigned_int_to_unsigned_char((uint32_t)UINT32_MAX);
-// CHECK-V0: {{.*}}integer-conversion.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 4294967295 (32-bit, unsigned) to type 'unsigned char' changed the value to 255 (8-bit, unsigned
+// 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 'int' of value -1 (32-bit, signed) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
+// 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.
@@ -173,21 +179,26 @@ int main() {
convert_signed_int_to_signed_int((int32_t)(uint32_t)INT32_MIN);
convert_signed_char_to_signed_char((int8_t)INT8_MIN);
convert_unsigned_int_to_unsigned_char((uint32_t)INT32_MIN);
-// CHECK-V1: {{.*}}integer-conversion.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483648 (32-bit, unsigned) to type 'unsigned char' changed the value to 0 (8-bit, unsigned)
+// 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 'int' of value -2147483648 (32-bit, signed) to type 'unsigned char' changed the value to 0 (8-bit, unsigned)
+// 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 'unsigned int' of value 2147483648 (32-bit, unsigned) to type 'signed char' changed the value to 0 (8-bit, signed)
+// 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 'int' of value -2147483648 (32-bit, signed) to type 'signed char' changed the value to 0 (8-bit, signed)
+// 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);
@@ -195,21 +206,21 @@ int main() {
convert_signed_int_to_signed_int((int32_t)(uint32_t)INT32_MAX);
convert_signed_char_to_signed_char((int8_t)INT8_MAX);
convert_unsigned_int_to_unsigned_char((uint32_t)INT32_MAX);
-// CHECK-V2: {{.*}}integer-conversion.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483647 (32-bit, unsigned) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
+// 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 'int' of value 2147483647 (32-bit, signed) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
+// 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 'unsigned int' of value 2147483647 (32-bit, unsigned) to type 'signed char' changed the value to -1 (8-bit, signed)
+// 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 'int' of value 2147483647 (32-bit, signed) to type 'signed char' changed the value to -1 (8-bit, signed)
+// 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);
@@ -224,12 +235,15 @@ int main() {
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 'unsigned int' of value 255 (32-bit, unsigned) to type 'signed char' changed the value to -1 (8-bit, signed)
+// 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 'int' of value 255 (32-bit, signed) to type 'signed char' changed the value to -1 (8-bit, signed)
+// 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);
@@ -244,12 +258,15 @@ int main() {
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 'unsigned int' of value 128 (32-bit, unsigned) to type 'signed char' changed the value to -128 (8-bit, signed)
+// 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 'int' of value 128 (32-bit, signed) to type 'signed char' changed the value to -128 (8-bit, signed)
+// 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)));
@@ -257,21 +274,26 @@ int main() {
convert_signed_int_to_signed_int((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
convert_signed_char_to_signed_char((int8_t)(uint8_t)INT8_MIN);
convert_unsigned_int_to_unsigned_char((~((uint32_t)(uint8_t)INT8_MIN)));
-// CHECK-V5: {{.*}}integer-conversion.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 4294967167 (32-bit, unsigned) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+// 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 'int' of value -129 (32-bit, signed) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+// 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 'unsigned int' of value 4294967167 (32-bit, unsigned) to type 'signed char' changed the value to 127 (8-bit, signed)
+// 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 'int' of value -129 (32-bit, signed) to type 'signed char' changed the value to 127 (8-bit, signed)
+// 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);
@@ -279,21 +301,26 @@ int main() {
convert_signed_int_to_signed_int((int32_t)INT32_MIN);
convert_signed_char_to_signed_char((int8_t)INT8_MIN);
convert_unsigned_int_to_unsigned_char(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN));
-// CHECK-V6: {{.*}}integer-conversion.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483776 (32-bit, unsigned) to type 'unsigned char' changed the value to 128 (8-bit, unsigned)
+// 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);
- convert_signed_int_to_unsigned_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 'int' of value -2147483520 (32-bit, signed) to type 'unsigned char' changed the value to 128 (8-bit, unsigned)
+// 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 'unsigned int' of value 2147483776 (32-bit, unsigned) to type 'signed char' changed the value to -128 (8-bit, signed)
+// 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 'int' of value -2147483520 (32-bit, signed) to type 'signed char' changed the value to -128 (8-bit, signed)
+// 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);
@@ -301,21 +328,21 @@ int main() {
convert_signed_int_to_signed_int((int32_t)INT32_MAX);
convert_signed_char_to_signed_char((int8_t)INT8_MAX);
convert_unsigned_int_to_unsigned_char(~(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
-// CHECK-V7: {{.*}}integer-conversion.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483519 (32-bit, unsigned) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+// 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((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 'int' of value 2147483519 (32-bit, signed) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+// 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 'unsigned int' of value 2147483519 (32-bit, unsigned) to type 'signed char' changed the value to 127 (8-bit, signed)
+// 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 'int' of value 2147483519 (32-bit, signed) to type 'signed char' changed the value to 127 (8-bit, signed)
+// 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
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 000000000..a3a745e5f
--- /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 a92e01fb4..7cdc4c5a1 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 000000000..192c69ade
--- /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 a3650e0a9..000000000
--- a/test/ubsan/TestCases/ImplicitConversion/integer-truncation-blacklist.c
+++ /dev/null
@@ -1,26 +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: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
-// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
-// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
-// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s --implicit-check-not="implicit conversion"
-
-// RUN: rm -f %tmp
-// RUN: echo "[implicit-integer-truncation]" >> %tmp
-// RUN: echo "fun:implicitTruncation" >> %tmp
-// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O0 %s -o %t && not %run %t 2>&1 | not FileCheck %s
-// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O1 %s -o %t && not %run %t 2>&1 | not FileCheck %s
-// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O2 %s -o %t && not %run %t 2>&1 | not FileCheck %s
-// RUN: %clang -fsanitize=implicit-integer-truncation -fno-sanitize-recover=implicit-integer-truncation -fsanitize-blacklist=%tmp -O3 %s -o %t && not %run %t 2>&1 | not FileCheck %s
-
-unsigned char implicitTruncation(unsigned int argc) {
- return argc; // BOOM
-// CHECK: {{.*}}integer-truncation-blacklist.c:[[@LINE-1]]:10: runtime error: implicit conversion from type 'unsigned int' of value 4294967295 (32-bit, unsigned) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
-}
-
-int main(int argc, char **argv) {
- return implicitTruncation(~0U);
-}
diff --git a/test/ubsan/TestCases/ImplicitConversion/integer-truncation.c b/test/ubsan/TestCases/ImplicitConversion/integer-truncation.c
index 34a0f00de..157564871 100644
--- a/test/ubsan/TestCases/ImplicitConversion/integer-truncation.c
+++ b/test/ubsan/TestCases/ImplicitConversion/integer-truncation.c
@@ -27,82 +27,82 @@
// However, not all of them should result in the check.
// So here, we *only* check which should and which should not result in checks.
-unsigned int convert_unsigned_int_to_unsigned_int(unsigned int x) {
+uint32_t convert_unsigned_int_to_unsigned_int(uint32_t x) {
#line 100
return x;
}
-unsigned char convert_unsigned_char_to_unsigned_char(unsigned char x) {
+uint8_t convert_unsigned_char_to_unsigned_char(uint8_t x) {
#line 200
return x;
}
-signed int convert_signed_int_to_signed_int(signed int x) {
+int32_t convert_signed_int_to_signed_int(int32_t x) {
#line 300
return x;
}
-signed char convert_signed_char_to_signed_char(signed char x) {
+int8_t convert_signed_char_to_signed_char(int8_t x) {
#line 400
return x;
}
-unsigned char convert_unsigned_int_to_unsigned_char(unsigned int x) {
+uint8_t convert_unsigned_int_to_unsigned_char(uint32_t x) {
#line 500
return x;
}
-unsigned int convert_unsigned_char_to_unsigned_int(unsigned char x) {
+uint32_t convert_unsigned_char_to_unsigned_int(uint8_t x) {
#line 600
return x;
}
-signed int convert_unsigned_char_to_signed_int(unsigned char x) {
+int32_t convert_unsigned_char_to_signed_int(uint8_t x) {
#line 700
return x;
}
-signed int convert_signed_char_to_signed_int(signed char x) {
+int32_t convert_signed_char_to_signed_int(int8_t x) {
#line 800
return x;
}
-signed int convert_unsigned_int_to_signed_int(unsigned int x) {
+int32_t convert_unsigned_int_to_signed_int(uint32_t x) {
#line 900
return x;
}
-unsigned int convert_signed_int_to_unsigned_int(signed int x) {
+uint32_t convert_signed_int_to_unsigned_int(int32_t x) {
#line 1000
return x;
}
-unsigned char convert_signed_int_to_unsigned_char(signed int x) {
+uint8_t convert_signed_int_to_unsigned_char(int32_t x) {
#line 1100
return x;
}
-unsigned char convert_signed_char_to_unsigned_char(signed char x) {
+uint8_t convert_signed_char_to_unsigned_char(int8_t x) {
#line 1200
return x;
}
-signed char convert_unsigned_char_to_signed_char(unsigned char x) {
+int8_t convert_unsigned_char_to_signed_char(uint8_t x) {
#line 1300
return x;
}
-unsigned int convert_signed_char_to_unsigned_int(signed char x) {
+uint32_t convert_signed_char_to_unsigned_int(int8_t x) {
#line 1400
return x;
}
-signed char convert_unsigned_int_to_signed_char(unsigned int x) {
+int8_t convert_unsigned_int_to_signed_char(uint32_t x) {
#line 1500
return x;
}
-signed char convert_signed_int_to_signed_char(signed int x) {
+int8_t convert_signed_int_to_signed_char(int32_t x) {
#line 1600
return x;
}
@@ -153,14 +153,14 @@ int main() {
convert_signed_int_to_signed_int((int32_t)(uint32_t)UINT32_MAX);
convert_signed_char_to_signed_char((int8_t)UINT8_MAX);
convert_unsigned_int_to_unsigned_char((uint32_t)UINT32_MAX);
-// CHECK-V0: {{.*}}integer-truncation.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 4294967295 (32-bit, unsigned) to type 'unsigned char' changed the value to 255 (8-bit, unsigned
+// 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 'int' of value -1 (32-bit, signed) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
+// 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);
@@ -173,21 +173,21 @@ int main() {
convert_signed_int_to_signed_int((int32_t)(uint32_t)INT32_MIN);
convert_signed_char_to_signed_char((int8_t)INT8_MIN);
convert_unsigned_int_to_unsigned_char((uint32_t)INT32_MIN);
-// CHECK-V1: {{.*}}integer-truncation.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483648 (32-bit, unsigned) to type 'unsigned char' changed the value to 0 (8-bit, unsigned)
+// 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 'int' of value -2147483648 (32-bit, signed) to type 'unsigned char' changed the value to 0 (8-bit, unsigned)
+// 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 'unsigned int' of value 2147483648 (32-bit, unsigned) to type 'signed char' changed the value to 0 (8-bit, signed)
+// 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 'int' of value -2147483648 (32-bit, signed) to type 'signed char' changed the value to 0 (8-bit, signed)
+// 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);
@@ -195,21 +195,21 @@ int main() {
convert_signed_int_to_signed_int((int32_t)(uint32_t)INT32_MAX);
convert_signed_char_to_signed_char((int8_t)INT8_MAX);
convert_unsigned_int_to_unsigned_char((uint32_t)INT32_MAX);
-// CHECK-V2: {{.*}}integer-truncation.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483647 (32-bit, unsigned) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
+// 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 'int' of value 2147483647 (32-bit, signed) to type 'unsigned char' changed the value to 255 (8-bit, unsigned)
+// 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 'unsigned int' of value 2147483647 (32-bit, unsigned) to type 'signed char' changed the value to -1 (8-bit, signed)
+// 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 'int' of value 2147483647 (32-bit, signed) to type 'signed char' changed the value to -1 (8-bit, signed)
+// 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);
@@ -227,9 +227,9 @@ int main() {
convert_unsigned_char_to_signed_char((uint8_t)UINT8_MAX);
convert_signed_char_to_unsigned_int((int8_t)UINT8_MAX);
convert_unsigned_int_to_signed_char((uint32_t)UINT8_MAX);
-// CHECK-V3: {{.*}}integer-truncation.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 255 (32-bit, unsigned) to type 'signed char' changed the value to -1 (8-bit, signed)
+// 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 'int' of value 255 (32-bit, signed) to type 'signed char' changed the value to -1 (8-bit, signed)
+// 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);
@@ -247,9 +247,9 @@ int main() {
convert_unsigned_char_to_signed_char((uint8_t)(uint8_t)INT8_MIN);
convert_signed_char_to_unsigned_int((int8_t)(uint8_t)INT8_MIN);
convert_unsigned_int_to_signed_char((uint32_t)(uint8_t)INT8_MIN);
-// CHECK-V4: {{.*}}integer-truncation.c:1500:10: runtime error: implicit conversion from type 'unsigned int' of value 128 (32-bit, unsigned) to type 'signed char' changed the value to -128 (8-bit, signed)
+// 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 'int' of value 128 (32-bit, signed) to type 'signed char' changed the value to -128 (8-bit, signed)
+// 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)));
@@ -257,21 +257,21 @@ int main() {
convert_signed_int_to_signed_int((int32_t)(~((uint32_t)(uint8_t)INT8_MIN)));
convert_signed_char_to_signed_char((int8_t)(uint8_t)INT8_MIN);
convert_unsigned_int_to_unsigned_char((~((uint32_t)(uint8_t)INT8_MIN)));
-// CHECK-V5: {{.*}}integer-truncation.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 4294967167 (32-bit, unsigned) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+// 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 'int' of value -129 (32-bit, signed) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+// 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 'unsigned int' of value 4294967167 (32-bit, unsigned) to type 'signed char' changed the value to 127 (8-bit, signed)
+// 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 'int' of value -129 (32-bit, signed) to type 'signed char' changed the value to 127 (8-bit, signed)
+// 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);
@@ -279,21 +279,21 @@ int main() {
convert_signed_int_to_signed_int((int32_t)INT32_MIN);
convert_signed_char_to_signed_char((int8_t)INT8_MIN);
convert_unsigned_int_to_unsigned_char(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN));
-// CHECK-V6: {{.*}}integer-truncation.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483776 (32-bit, unsigned) to type 'unsigned char' changed the value to 128 (8-bit, unsigned)
+// 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 'int' of value -2147483520 (32-bit, signed) to type 'unsigned char' changed the value to 128 (8-bit, unsigned)
+// 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 'unsigned int' of value 2147483776 (32-bit, unsigned) to type 'signed char' changed the value to -128 (8-bit, signed)
+// 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 'int' of value -2147483520 (32-bit, signed) to type 'signed char' changed the value to -128 (8-bit, signed)
+// 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);
@@ -301,21 +301,21 @@ int main() {
convert_signed_int_to_signed_int((int32_t)INT32_MAX);
convert_signed_char_to_signed_char((int8_t)INT8_MAX);
convert_unsigned_int_to_unsigned_char(~(((uint32_t)INT32_MIN) | ((uint32_t)(uint8_t)INT8_MIN)));
-// CHECK-V7: {{.*}}integer-truncation.c:500:10: runtime error: implicit conversion from type 'unsigned int' of value 2147483519 (32-bit, unsigned) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+// 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 'int' of value 2147483519 (32-bit, signed) to type 'unsigned char' changed the value to 127 (8-bit, unsigned)
+// 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 'unsigned int' of value 2147483519 (32-bit, unsigned) to type 'signed char' changed the value to 127 (8-bit, signed)
+// 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 'int' of value 2147483519 (32-bit, signed) to type 'signed char' changed the value to 127 (8-bit, signed)
+// 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
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 000000000..229f83260
--- /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 000000000..0e79df0b5
--- /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 000000000..13eaef274
--- /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 000000000..1da9e3b41
--- /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 000000000..1ff67eb05
--- /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 000000000..938b7a8c1
--- /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 000000000..5117dc2a3
--- /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 000000000..49d223ccd
--- /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 515ebbd07..45aeb9e75 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/TypeCheck/vptr-non-unique-typeinfo.cpp b/test/ubsan/TestCases/TypeCheck/vptr-non-unique-typeinfo.cpp
index f24d074a5..13b052386 100644
--- a/test/ubsan/TestCases/TypeCheck/vptr-non-unique-typeinfo.cpp
+++ b/test/ubsan/TestCases/TypeCheck/vptr-non-unique-typeinfo.cpp
@@ -4,6 +4,7 @@
//
// REQUIRES: cxxabi
// UNSUPPORTED: windows-msvc
+// XFAIL: i386-netbsd
struct X {
virtual ~X() {}
diff --git a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp
index 09e9785b9..925373bde 100644
--- a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp
+++ b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp
@@ -3,6 +3,8 @@
// REQUIRES: cxxabi
// 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 b4ee22747..67239e82d 100644
--- a/test/ubsan/TestCases/TypeCheck/vptr.cpp
+++ b/test/ubsan/TestCases/TypeCheck/vptr.cpp
@@ -42,6 +42,8 @@
// UNSUPPORTED: android
// Compilation error
// UNSUPPORTED: openbsd
+// Compilation error
+// UNSUPPORTED: freebsd
#include <new>
#include <typeinfo>
#include <assert.h>
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 000000000..1e2089219
--- /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 000000000..3a72a406a
--- /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 000000000..9677407e0
--- /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 28711d2ee..8b9e166af 100644
--- a/test/ubsan_minimal/TestCases/implicit-integer-truncation.c
+++ b/test/ubsan_minimal/TestCases/implicit-unsigned-integer-truncation.c
@@ -1,4 +1,4 @@
-// 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>
diff --git a/test/xray/TestCases/Posix/basic-filtering.cc b/test/xray/TestCases/Posix/basic-filtering.cc
index db07ef510..3d4f4dda2 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/fdr-mode.cc b/test/xray/TestCases/Posix/fdr-mode.cc
index 17d115a22..8b9cb2bbf 100644
--- a/test/xray/TestCases/Posix/fdr-mode.cc
+++ b/test/xray/TestCases/Posix/fdr-mode.cc
@@ -80,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 000000000..dc9888d6e
--- /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 fb65ad895..accc5925c 100644
--- a/test/xray/TestCases/Posix/fdr-single-thread.cc
+++ b/test/xray/TestCases/Posix/fdr-single-thread.cc
@@ -28,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 a5d7e6f36..eb405967a 100644
--- a/test/xray/TestCases/Posix/fdr-thread-order.cc
+++ b/test/xray/TestCases/Posix/fdr-thread-order.cc
@@ -52,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 5aefdec08..3873325ef 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/utils/generate_netbsd_ioctls.awk b/utils/generate_netbsd_ioctls.awk
index 9a92ff82a..82b199214 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 5e08900a1..ac0802546 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("}")
}