aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/CMakeLists.txt16
-rw-r--r--lib/asan/CMakeLists.txt105
-rw-r--r--lib/asan/asan_allocator.cc9
-rw-r--r--lib/asan/asan_allocator.h2
-rw-r--r--lib/asan/asan_fake_stack.cc23
-rw-r--r--lib/asan/asan_flags.cc22
-rw-r--r--lib/asan/asan_globals.cc76
-rw-r--r--lib/asan/asan_interceptors.cc156
-rw-r--r--lib/asan/asan_interceptors.h12
-rw-r--r--lib/asan/asan_interface_internal.h21
-rw-r--r--lib/asan/asan_internal.h18
-rw-r--r--lib/asan/asan_linux.cc77
-rw-r--r--lib/asan/asan_mac.cc66
-rw-r--r--lib/asan/asan_mapping.h15
-rw-r--r--lib/asan/asan_poisoning.cc6
-rw-r--r--lib/asan/asan_poisoning.h2
-rw-r--r--lib/asan/asan_posix.cc8
-rw-r--r--lib/asan/asan_report.cc65
-rw-r--r--lib/asan/asan_report.h13
-rw-r--r--lib/asan/asan_rtl.cc161
-rw-r--r--lib/asan/asan_stats.cc9
-rw-r--r--lib/asan/asan_stats.h8
-rw-r--r--lib/asan/asan_suppressions.cc33
-rw-r--r--lib/asan/asan_suppressions.h1
-rw-r--r--lib/asan/asan_thread.h13
-rw-r--r--lib/asan/asan_win.cc148
-rw-r--r--lib/asan/asan_win_dll_thunk.cc9
-rw-r--r--lib/asan/asan_win_dynamic_runtime_thunk.cc94
-rwxr-xr-xlib/asan/scripts/asan_symbolize.py3
-rw-r--r--lib/asan/tests/CMakeLists.txt22
-rw-r--r--lib/asan/tests/asan_asm_test.cc4
-rw-r--r--lib/asan/tests/asan_str_test.cc33
-rw-r--r--lib/asan/tests/asan_test.cc32
-rw-r--r--lib/builtins/CMakeLists.txt17
-rw-r--r--lib/builtins/atomic_flag_clear.c19
-rw-r--r--lib/builtins/atomic_flag_clear_explicit.c20
-rw-r--r--lib/builtins/atomic_flag_test_and_set.c19
-rw-r--r--lib/builtins/atomic_flag_test_and_set_explicit.c20
-rw-r--r--lib/builtins/atomic_signal_fence.c19
-rw-r--r--lib/builtins/atomic_thread_fence.c19
-rw-r--r--lib/builtins/clear_cache.c11
-rw-r--r--lib/builtins/enable_execute_stack.c14
-rw-r--r--lib/builtins/extendhfsf2.c23
-rw-r--r--lib/builtins/fixdfdi.c57
-rw-r--r--lib/builtins/fixdfsi.c56
-rw-r--r--lib/builtins/fixdfti.c33
-rw-r--r--lib/builtins/fixsfdi.c54
-rw-r--r--lib/builtins/fixsfsi.c53
-rw-r--r--lib/builtins/fixsfti.c33
-rw-r--r--lib/builtins/fixtfdi.c23
-rw-r--r--lib/builtins/fixtfsi.c23
-rw-r--r--lib/builtins/fixtfti.c23
-rw-r--r--lib/builtins/fixunsdfdi.c55
-rw-r--r--lib/builtins/fixunsdfsi.c35
-rw-r--r--lib/builtins/fixunsdfti.c36
-rw-r--r--lib/builtins/fixunssfdi.c55
-rw-r--r--lib/builtins/fixunssfsi.c32
-rw-r--r--lib/builtins/fixunssfti.c37
-rw-r--r--lib/builtins/fixunstfdi.c22
-rw-r--r--lib/builtins/fixunstfsi.c22
-rw-r--r--lib/builtins/fixunstfti.c22
-rw-r--r--lib/builtins/fixunsxfdi.c2
-rw-r--r--lib/builtins/fixunsxfsi.c3
-rw-r--r--lib/builtins/fixunsxfti.c5
-rw-r--r--lib/builtins/fixxfdi.c6
-rw-r--r--lib/builtins/fixxfti.c8
-rw-r--r--lib/builtins/fp_extend.h19
-rw-r--r--lib/builtins/fp_extend_impl.inc4
-rw-r--r--lib/builtins/fp_fixint_impl.inc41
-rw-r--r--lib/builtins/fp_fixuint_impl.inc39
-rw-r--r--lib/builtins/fp_trunc.h14
-rw-r--r--lib/builtins/fp_trunc_impl.inc2
-rw-r--r--lib/builtins/i386/chkstk.S34
-rw-r--r--lib/builtins/int_endianness.h28
-rw-r--r--lib/builtins/int_lib.h6
-rw-r--r--lib/builtins/truncdfhf2.c16
-rw-r--r--lib/builtins/truncsfhf2.c22
-rw-r--r--lib/builtins/x86_64/chkstk.S39
-rw-r--r--lib/dfsan/dfsan.cc14
-rw-r--r--lib/dfsan/dfsan_custom.cc410
-rw-r--r--lib/dfsan/done_abilist.txt15
-rwxr-xr-xlib/dfsan/scripts/check_custom_wrappers.sh6
-rw-r--r--lib/interception/CMakeLists.txt20
-rw-r--r--lib/interception/interception.h1
-rw-r--r--lib/interception/interception_win.cc13
-rw-r--r--lib/lsan/CMakeLists.txt19
-rw-r--r--lib/lsan/lsan_common.cc110
-rw-r--r--lib/lsan/lsan_common.h3
-rw-r--r--lib/lsan/lsan_common_linux.cc31
-rw-r--r--lib/lsan/lsan_interceptors.cc2
-rw-r--r--lib/lsan/lsan_thread.h4
-rw-r--r--lib/msan/CMakeLists.txt20
-rw-r--r--lib/msan/msan.cc44
-rw-r--r--lib/msan/msan.h27
-rw-r--r--lib/msan/msan.syms.extra1
-rw-r--r--lib/msan/msan_allocator.cc9
-rw-r--r--lib/msan/msan_interceptors.cc30
-rw-r--r--lib/msan/msan_interface_internal.h7
-rw-r--r--lib/msan/msan_linux.cc43
-rw-r--r--lib/msan/msan_new_delete.cc7
-rw-r--r--lib/msan/msan_origin.h2
-rw-r--r--lib/msan/msan_poisoning.cc2
-rw-r--r--lib/msan/msan_report.cc4
-rw-r--r--lib/msan/msan_thread.cc2
-rw-r--r--lib/msan/tests/CMakeLists.txt3
-rw-r--r--lib/msan/tests/msan_test.cc46
-rw-r--r--lib/profile/CMakeLists.txt5
-rw-r--r--lib/profile/GCDAProfiling.c35
-rw-r--r--lib/profile/InstrProfiling.h17
-rw-r--r--lib/profile/InstrProfilingFile.c95
-rw-r--r--lib/profile/InstrProfilingUtil.c35
-rw-r--r--lib/profile/InstrProfilingUtil.h16
-rw-r--r--lib/safestack/CMakeLists.txt28
-rw-r--r--lib/safestack/safestack.cc246
-rw-r--r--lib/sanitizer_common/CMakeLists.txt49
-rw-r--r--lib/sanitizer_common/sanitizer_allocator.h2
-rw-r--r--lib/sanitizer_common/sanitizer_atomic.h2
-rw-r--r--lib/sanitizer_common/sanitizer_atomic_msvc.h36
-rw-r--r--lib/sanitizer_common/sanitizer_common.cc100
-rw-r--r--lib/sanitizer_common/sanitizer_common.h144
-rw-r--r--lib/sanitizer_common/sanitizer_common_interceptors.inc286
-rwxr-xr-xlib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc20
-rw-r--r--lib/sanitizer_common/sanitizer_common_libcdep.cc35
-rw-r--r--lib/sanitizer_common/sanitizer_common_syscalls.inc8
-rw-r--r--lib/sanitizer_common/sanitizer_coverage_libcdep.cc498
-rw-r--r--lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc17
-rw-r--r--lib/sanitizer_common/sanitizer_deadlock_detector1.cc21
-rw-r--r--lib/sanitizer_common/sanitizer_flags.cc2
-rw-r--r--lib/sanitizer_common/sanitizer_flags.inc33
-rw-r--r--lib/sanitizer_common/sanitizer_internal_defs.h11
-rw-r--r--lib/sanitizer_common/sanitizer_libc.h55
-rw-r--r--lib/sanitizer_common/sanitizer_libignore.cc1
-rw-r--r--lib/sanitizer_common/sanitizer_linux.cc240
-rw-r--r--lib/sanitizer_common/sanitizer_linux.h6
-rw-r--r--lib/sanitizer_common/sanitizer_linux_libcdep.cc56
-rw-r--r--lib/sanitizer_common/sanitizer_mac.cc105
-rw-r--r--lib/sanitizer_common/sanitizer_mac.h3
-rw-r--r--lib/sanitizer_common/sanitizer_platform.h25
-rw-r--r--lib/sanitizer_common/sanitizer_platform_interceptors.h28
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_posix.cc41
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_posix.h45
-rw-r--r--lib/sanitizer_common/sanitizer_posix.cc104
-rw-r--r--lib/sanitizer_common/sanitizer_posix.h81
-rw-r--r--lib/sanitizer_common/sanitizer_posix_libcdep.cc79
-rw-r--r--lib/sanitizer_common/sanitizer_printf.cc30
-rw-r--r--lib/sanitizer_common/sanitizer_procmaps_common.cc4
-rw-r--r--lib/sanitizer_common/sanitizer_procmaps_mac.cc6
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace.cc2
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc7
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace_printer.cc27
-rw-r--r--lib/sanitizer_common/sanitizer_stacktrace_printer.h6
-rw-r--r--lib/sanitizer_common/sanitizer_stoptheworld.h3
-rw-r--r--lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc93
-rw-r--r--lib/sanitizer_common/sanitizer_suppressions.cc36
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer.cc37
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer.h86
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_internal.h109
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc74
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h11
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc167
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_mac.cc148
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_mac.h48
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc755
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc229
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_win.cc233
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_win.h31
-rw-r--r--lib/sanitizer_common/sanitizer_tls_get_addr.cc8
-rw-r--r--lib/sanitizer_common/sanitizer_tls_get_addr.h3
-rw-r--r--lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cc (renamed from lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc)10
-rw-r--r--lib/sanitizer_common/sanitizer_win.cc341
-rwxr-xr-xlib/sanitizer_common/scripts/check_lint.sh4
-rwxr-xr-xlib/sanitizer_common/scripts/cpplint.py2
-rwxr-xr-xlib/sanitizer_common/scripts/gen_dynamic_list.py39
-rwxr-xr-xlib/sanitizer_common/scripts/litlint.py2
-rwxr-xr-xlib/sanitizer_common/scripts/sancov.py138
-rw-r--r--lib/sanitizer_common/tests/CMakeLists.txt15
-rw-r--r--lib/sanitizer_common/tests/sanitizer_libc_test.cc26
-rw-r--r--lib/sanitizer_common/tests/sanitizer_posix_test.cc4
-rw-r--r--lib/sanitizer_common/tests/sanitizer_procmaps_test.cc4
-rw-r--r--lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc58
-rw-r--r--lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc24
-rw-r--r--lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc10
-rw-r--r--lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc58
-rw-r--r--lib/tsan/CMakeLists.txt79
-rw-r--r--lib/tsan/Makefile.old7
-rw-r--r--lib/tsan/dd/CMakeLists.txt4
-rw-r--r--lib/tsan/dd/dd_rtl.h2
-rwxr-xr-xlib/tsan/go/buildgo.sh2
-rw-r--r--lib/tsan/rtl/Makefile.old3
-rw-r--r--lib/tsan/rtl/tsan.syms.extra1
-rw-r--r--lib/tsan/rtl/tsan_defs.h5
-rw-r--r--lib/tsan/rtl/tsan_fd.cc36
-rw-r--r--lib/tsan/rtl/tsan_fd.h4
-rw-r--r--lib/tsan/rtl/tsan_flags.cc33
-rw-r--r--lib/tsan/rtl/tsan_interceptors.cc308
-rw-r--r--lib/tsan/rtl/tsan_interceptors.h37
-rw-r--r--lib/tsan/rtl/tsan_interface.cc10
-rw-r--r--lib/tsan/rtl/tsan_interface.h12
-rw-r--r--lib/tsan/rtl/tsan_interface_inl.h32
-rw-r--r--lib/tsan/rtl/tsan_mman.cc17
-rw-r--r--lib/tsan/rtl/tsan_new_delete.cc88
-rw-r--r--lib/tsan/rtl/tsan_platform_linux.cc16
-rw-r--r--lib/tsan/rtl/tsan_platform_mac.cc2
-rw-r--r--lib/tsan/rtl/tsan_report.cc7
-rw-r--r--lib/tsan/rtl/tsan_rtl.cc32
-rw-r--r--lib/tsan/rtl/tsan_rtl.h21
-rw-r--r--lib/tsan/rtl/tsan_rtl_mutex.cc8
-rw-r--r--lib/tsan/rtl/tsan_rtl_thread.cc1
-rw-r--r--lib/tsan/rtl/tsan_suppressions.cc36
-rw-r--r--lib/tsan/rtl/tsan_suppressions.h1
-rw-r--r--lib/tsan/rtl/tsan_symbolize.cc14
-rw-r--r--lib/tsan/rtl/tsan_symbolize.h4
-rw-r--r--lib/tsan/rtl/tsan_sync.cc70
-rw-r--r--lib/tsan/rtl/tsan_sync.h3
-rw-r--r--lib/tsan/rtl/tsan_update_shadow_word_inl.h3
-rw-r--r--lib/tsan/tests/CMakeLists.txt2
-rw-r--r--lib/ubsan/CMakeLists.txt102
-rw-r--r--lib/ubsan/Makefile.mk4
-rw-r--r--lib/ubsan/ubsan_diag.cc49
-rw-r--r--lib/ubsan/ubsan_diag.h12
-rw-r--r--lib/ubsan/ubsan_flags.cc48
-rw-r--r--lib/ubsan/ubsan_flags.h9
-rw-r--r--lib/ubsan/ubsan_handlers.cc4
-rw-r--r--lib/ubsan/ubsan_handlers_cxx.cc51
-rw-r--r--lib/ubsan/ubsan_handlers_cxx.h13
-rw-r--r--lib/ubsan/ubsan_init.cc83
-rw-r--r--lib/ubsan/ubsan_init.h13
-rw-r--r--lib/ubsan/ubsan_init_standalone.cc35
-rw-r--r--lib/ubsan/ubsan_platform.h27
-rw-r--r--lib/ubsan/ubsan_type_hash.cc234
-rw-r--r--lib/ubsan/ubsan_type_hash.h5
-rw-r--r--lib/ubsan/ubsan_type_hash_itanium.cc251
-rw-r--r--lib/ubsan/ubsan_type_hash_win.cc81
-rw-r--r--lib/ubsan/ubsan_value.cc13
-rw-r--r--lib/ubsan/ubsan_value.h7
235 files changed, 7168 insertions, 3588 deletions
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 6929e682fac6..009c59f4d68e 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -4,9 +4,14 @@
include(AddCompilerRT)
include(SanitizerUtils)
-if(COMPILER_RT_HAS_SANITIZER_COMMON)
+if(COMPILER_RT_HAS_INTERCEPTION)
add_subdirectory(interception)
+endif()
+
+if(COMPILER_RT_HAS_SANITIZER_COMMON)
add_subdirectory(sanitizer_common)
+ add_subdirectory(lsan)
+ add_subdirectory(ubsan)
endif()
if(COMPILER_RT_HAS_ASAN)
@@ -19,10 +24,6 @@ if(COMPILER_RT_HAS_DFSAN)
add_subdirectory(dfsan)
endif()
-if(COMPILER_RT_HAS_LSAN OR COMPILER_RT_HAS_LSAN_COMMON)
- add_subdirectory(lsan)
-endif()
-
if(COMPILER_RT_HAS_MSAN)
add_subdirectory(msan)
endif()
@@ -36,7 +37,6 @@ if(COMPILER_RT_HAS_TSAN)
add_subdirectory(tsan/dd)
endif()
-if(COMPILER_RT_HAS_UBSAN)
- add_subdirectory(ubsan)
+if(COMPILER_RT_HAS_SAFESTACK)
+ add_subdirectory(safestack)
endif()
-
diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt
index d4c5c17d36a2..28611a8a4659 100644
--- a/lib/asan/CMakeLists.txt
+++ b/lib/asan/CMakeLists.txt
@@ -37,9 +37,22 @@ append_no_rtti_flag(ASAN_CFLAGS)
set(ASAN_COMMON_DEFINITIONS
ASAN_HAS_EXCEPTIONS=1)
+set(ASAN_DYNAMIC_LINK_FLAGS)
+
if(ANDROID)
list(APPEND ASAN_COMMON_DEFINITIONS
ASAN_LOW_MEMORY=1)
+# On Android, -z global does not do what it is documented to do.
+# On Android, -z global moves the library ahead in the lookup order,
+# placing it right after the LD_PRELOADs. This is used to compensate for the fact
+# that Android linker does not look at the dependencies of the main executable
+# that aren't dependencies of the current DSO when resolving symbols from said DSO.
+# As a net result, this allows running ASan executables without LD_PRELOAD-ing the
+# ASan runtime library.
+# The above is applicable to L MR1 or newer.
+ if (COMPILER_RT_HAS_Z_GLOBAL)
+ list(APPEND ASAN_DYNAMIC_LINK_FLAGS -Wl,-z,global)
+ endif()
endif()
set(ASAN_DYNAMIC_DEFINITIONS
@@ -53,6 +66,7 @@ append_list_if(MSVC /DEBUG ASAN_DYNAMIC_CFLAGS)
append_list_if(COMPILER_RT_HAS_LIBC c ASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBDL dl ASAN_DYNAMIC_LIBS)
+append_list_if(COMPILER_RT_HAS_LIBRT rt ASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBM m ASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread ASAN_DYNAMIC_LIBS)
append_list_if(COMPILER_RT_HAS_LIBSTDCXX stdc++ ASAN_DYNAMIC_LIBS)
@@ -61,41 +75,50 @@ append_list_if(ANDROID log ASAN_DYNAMIC_LIBS)
# Compile ASan sources into an object library.
if(APPLE)
- foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS})
- add_compiler_rt_darwin_object_library(RTAsan ${os}
- ARCH ${ASAN_SUPPORTED_ARCH}
- SOURCES ${ASAN_SOURCES} ${ASAN_CXX_SOURCES}
- CFLAGS ${ASAN_DYNAMIC_CFLAGS}
- DEFS ${ASAN_DYNAMIC_DEFINITIONS})
- endforeach()
+ add_compiler_rt_object_libraries(RTAsan
+ OS ${SANITIZER_COMMON_SUPPORTED_OS}
+ ARCHS ${ASAN_SUPPORTED_ARCH}
+ SOURCES ${ASAN_SOURCES} ${ASAN_CXX_SOURCES}
+ CFLAGS ${ASAN_DYNAMIC_CFLAGS}
+ DEFS ${ASAN_DYNAMIC_DEFINITIONS})
else()
- foreach(arch ${ASAN_SUPPORTED_ARCH})
- add_compiler_rt_object_library(RTAsan ${arch}
- SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_CFLAGS}
- DEFS ${ASAN_COMMON_DEFINITIONS})
- add_compiler_rt_object_library(RTAsan_cxx ${arch}
- SOURCES ${ASAN_CXX_SOURCES} CFLAGS ${ASAN_CFLAGS}
- DEFS ${ASAN_COMMON_DEFINITIONS})
- add_compiler_rt_object_library(RTAsan_preinit ${arch}
- SOURCES ${ASAN_PREINIT_SOURCES} CFLAGS ${ASAN_CFLAGS}
- DEFS ${ASAN_COMMON_DEFINITIONS})
- add_compiler_rt_object_library(RTAsan_dynamic ${arch}
- SOURCES ${ASAN_SOURCES} ${ASAN_CXX_SOURCES}
- CFLAGS ${ASAN_DYNAMIC_CFLAGS}
- DEFS ${ASAN_DYNAMIC_DEFINITIONS})
- endforeach()
+ add_compiler_rt_object_libraries(RTAsan
+ ARCHS ${ASAN_SUPPORTED_ARCH}
+ SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_CFLAGS}
+ DEFS ${ASAN_COMMON_DEFINITIONS})
+ add_compiler_rt_object_libraries(RTAsan_cxx
+ ARCHS ${ASAN_SUPPORTED_ARCH}
+ SOURCES ${ASAN_CXX_SOURCES} CFLAGS ${ASAN_CFLAGS}
+ DEFS ${ASAN_COMMON_DEFINITIONS})
+ add_compiler_rt_object_libraries(RTAsan_preinit
+ ARCHS ${ASAN_SUPPORTED_ARCH}
+ SOURCES ${ASAN_PREINIT_SOURCES} CFLAGS ${ASAN_CFLAGS}
+ DEFS ${ASAN_COMMON_DEFINITIONS})
+ add_compiler_rt_object_libraries(RTAsan_dynamic
+ ARCHS ${ASAN_SUPPORTED_ARCH}
+ SOURCES ${ASAN_SOURCES} ${ASAN_CXX_SOURCES}
+ CFLAGS ${ASAN_DYNAMIC_CFLAGS}
+ DEFS ${ASAN_DYNAMIC_DEFINITIONS})
+
+ 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})
endif()
# Build ASan runtimes shipped with Clang.
add_custom_target(asan)
if(APPLE)
- foreach (os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS})
+ foreach (os ${SANITIZER_COMMON_SUPPORTED_OS})
add_compiler_rt_darwin_dynamic_runtime(clang_rt.asan_${os}_dynamic ${os}
- ARCH ${ASAN_SUPPORTED_ARCH}
+ ARCHS ${ASAN_SUPPORTED_ARCH}
SOURCES $<TARGET_OBJECTS:RTAsan.${os}>
$<TARGET_OBJECTS:RTInterception.${os}>
$<TARGET_OBJECTS:RTSanitizerCommon.${os}>
$<TARGET_OBJECTS:RTLSanCommon.${os}>
+ $<TARGET_OBJECTS:RTUbsan.${os}>
CFLAGS ${ASAN_DYNAMIC_CFLAGS}
DEFS ${ASAN_DYNAMIC_DEFINITIONS})
add_dependencies(asan clang_rt.asan_${os}_dynamic)
@@ -106,12 +129,9 @@ else()
set(ASAN_COMMON_RUNTIME_OBJECTS
$<TARGET_OBJECTS:RTInterception.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
- $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>)
- if(NOT WIN32)
- # We can't build Leak Sanitizer on Windows yet.
- list(APPEND ASAN_COMMON_RUNTIME_OBJECTS
- $<TARGET_OBJECTS:RTLSanCommon.${arch}>)
- endif()
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
+ $<TARGET_OBJECTS:RTLSanCommon.${arch}>
+ $<TARGET_OBJECTS:RTUbsan.${arch}>)
add_compiler_rt_runtime(clang_rt.asan-${arch} ${arch} STATIC
SOURCES $<TARGET_OBJECTS:RTAsan_preinit.${arch}>
@@ -123,6 +143,7 @@ else()
add_compiler_rt_runtime(clang_rt.asan_cxx-${arch} ${arch} STATIC
SOURCES $<TARGET_OBJECTS:RTAsan_cxx.${arch}>
+ $<TARGET_OBJECTS:RTUbsan_cxx.${arch}>
CFLAGS ${ASAN_CFLAGS}
DEFS ${ASAN_COMMON_DEFINITIONS})
add_dependencies(asan clang_rt.asan_cxx-${arch})
@@ -133,6 +154,20 @@ else()
DEFS ${ASAN_COMMON_DEFINITIONS})
add_dependencies(asan clang_rt.asan-preinit-${arch})
+ if (UNIX AND NOT ${arch} MATCHES "i386|i686")
+ add_sanitizer_rt_version_list(clang_rt.asan-dynamic-${arch}
+ LIBS clang_rt.asan-${arch} clang_rt.asan_cxx-${arch}
+ EXTRA asan.syms.extra)
+ set(VERSION_SCRIPT_FLAG
+ -Wl,--version-script,${CMAKE_CURRENT_BINARY_DIR}/clang_rt.asan-dynamic-${arch}.vers)
+ set_source_files_properties(
+ ${CMAKE_CURRENT_BINARY_DIR}/dummy.cc
+ PROPERTIES
+ OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/clang_rt.asan-dynamic-${arch}.vers)
+ else()
+ set(VERSION_SCRIPT_FLAG)
+ endif()
+
if (WIN32)
set(SHARED_ASAN_NAME clang_rt.asan_dynamic-${arch}${COMPILER_RT_OS_SUFFIX})
else()
@@ -141,13 +176,21 @@ else()
add_compiler_rt_runtime(clang_rt.asan-dynamic-${arch} ${arch} SHARED
OUTPUT_NAME ${SHARED_ASAN_NAME}
SOURCES $<TARGET_OBJECTS:RTAsan_dynamic.${arch}>
+ # The only purpose of RTAsan_dynamic_version_script_dummy is to carry
+ # a dependency of the shared runtime on the version script. With CMake
+ # 3.1 or later it can be replaced with a straightforward
+ # add_dependencies(clang_rt.asan-dynamic-${arch} clang_rt.asan-dynamic-${arch}-version-list)
+ $<TARGET_OBJECTS:RTAsan_dynamic_version_script_dummy.${arch}>
+ $<TARGET_OBJECTS:RTUbsan_cxx.${arch}>
${ASAN_COMMON_RUNTIME_OBJECTS}
CFLAGS ${ASAN_DYNAMIC_CFLAGS}
+ LINKFLAGS ${ASAN_DYNAMIC_LINK_FLAGS}
+ ${VERSION_SCRIPT_FLAG}
DEFS ${ASAN_DYNAMIC_DEFINITIONS})
target_link_libraries(clang_rt.asan-dynamic-${arch} ${ASAN_DYNAMIC_LIBS})
add_dependencies(asan clang_rt.asan-dynamic-${arch})
- if (UNIX AND NOT ${arch} STREQUAL "i386" AND NOT ${arch} STREQUAL "i686")
+ if (UNIX AND NOT ${arch} MATCHES "i386|i686")
add_sanitizer_rt_symbols(clang_rt.asan_cxx-${arch})
add_dependencies(asan clang_rt.asan_cxx-${arch}-symbols)
add_sanitizer_rt_symbols(clang_rt.asan-${arch} asan.syms.extra)
diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc
index fd63ac68c09e..2df9a510bd9a 100644
--- a/lib/asan/asan_allocator.cc
+++ b/lib/asan/asan_allocator.cc
@@ -223,7 +223,7 @@ void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) {
struct Allocator {
static const uptr kMaxAllowedMallocSize =
- FIRST_32_SECOND_64(3UL << 30, 64UL << 30);
+ FIRST_32_SECOND_64(3UL << 30, 1UL << 40);
static const uptr kMaxThreadLocalQuarantine =
FIRST_32_SECOND_64(1 << 18, 1 << 20);
@@ -354,7 +354,7 @@ struct Allocator {
}
CHECK(IsAligned(needed_size, min_alignment));
if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
- Report("WARNING: AddressSanitizer failed to allocate %p bytes\n",
+ Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n",
(void*)size);
return allocator.ReturnNullOrDie();
}
@@ -437,11 +437,10 @@ struct Allocator {
thread_stats.mallocs++;
thread_stats.malloced += size;
thread_stats.malloced_redzones += needed_size - size;
- uptr class_id =
- Min(kNumberOfSizeClasses, SizeClassMap::ClassID(needed_size));
- thread_stats.malloced_by_size[class_id]++;
if (needed_size > SizeClassMap::kMaxSize)
thread_stats.malloc_large++;
+ else
+ thread_stats.malloced_by_size[SizeClassMap::ClassID(needed_size)]++;
void *res = reinterpret_cast<void *>(user_beg);
if (can_fill && fl.max_malloc_fill_size) {
diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h
index 3208d1f950cd..5ccd00c97bab 100644
--- a/lib/asan/asan_allocator.h
+++ b/lib/asan/asan_allocator.h
@@ -29,7 +29,6 @@ enum AllocType {
FROM_NEW_BR = 3 // Memory block came from operator new [ ]
};
-static const uptr kNumberOfSizeClasses = 255;
struct AsanChunk;
struct AllocatorOptions {
@@ -137,6 +136,7 @@ typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, 16,
AsanMapUnmapCallback> PrimaryAllocator;
#endif // SANITIZER_CAN_USE_ALLOCATOR64
+static const uptr kNumberOfSizeClasses = SizeClassMap::kNumClasses;
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
typedef LargeMmapAllocator<AsanMapUnmapCallback> SecondaryAllocator;
typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
diff --git a/lib/asan/asan_fake_stack.cc b/lib/asan/asan_fake_stack.cc
index bf4f1eb4c781..d20641155b88 100644
--- a/lib/asan/asan_fake_stack.cc
+++ b/lib/asan/asan_fake_stack.cc
@@ -22,6 +22,9 @@ static const u64 kMagic2 = (kMagic1 << 8) | kMagic1;
static const u64 kMagic4 = (kMagic2 << 16) | kMagic2;
static const u64 kMagic8 = (kMagic4 << 32) | kMagic4;
+static const u64 kAllocaRedzoneSize = 32UL;
+static const u64 kAllocaRedzoneMask = 31UL;
+
// For small size classes inline PoisonShadow for better performance.
ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) {
CHECK_EQ(SHADOW_SCALE, 3); // This code expects SHADOW_SCALE=3.
@@ -253,4 +256,24 @@ void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
if (end) *end = reinterpret_cast<void*>(frame_end);
return reinterpret_cast<void*>(frame->real_stack);
}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_alloca_poison(uptr addr, uptr size) {
+ uptr LeftRedzoneAddr = addr - kAllocaRedzoneSize;
+ uptr PartialRzAddr = addr + size;
+ uptr RightRzAddr = (PartialRzAddr + kAllocaRedzoneMask) & ~kAllocaRedzoneMask;
+ uptr PartialRzAligned = PartialRzAddr & ~(SHADOW_GRANULARITY - 1);
+ FastPoisonShadow(LeftRedzoneAddr, kAllocaRedzoneSize, kAsanAllocaLeftMagic);
+ FastPoisonShadowPartialRightRedzone(
+ PartialRzAligned, PartialRzAddr % SHADOW_GRANULARITY,
+ RightRzAddr - PartialRzAligned, kAsanAllocaRightMagic);
+ FastPoisonShadow(RightRzAddr, kAllocaRedzoneSize, kAsanAllocaRightMagic);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_allocas_unpoison(uptr top, uptr bottom) {
+ if ((!top) || (top > bottom)) return;
+ REAL(memset)(reinterpret_cast<void*>(MemToShadow(top)), 0,
+ (bottom - top) / SHADOW_GRANULARITY);
+}
} // extern "C"
diff --git a/lib/asan/asan_flags.cc b/lib/asan/asan_flags.cc
index efb7767d5d91..e8ea549b62e3 100644
--- a/lib/asan/asan_flags.cc
+++ b/lib/asan/asan_flags.cc
@@ -20,6 +20,8 @@
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "ubsan/ubsan_flags.h"
+#include "ubsan/ubsan_platform.h"
namespace __asan {
@@ -72,8 +74,8 @@ void InitializeFlags() {
RegisterAsanFlags(&asan_parser, f);
RegisterCommonFlags(&asan_parser);
- // Set the default values and prepare for parsing LSan flags (which can also
- // overwrite common flags).
+ // Set the default values and prepare for parsing LSan and UBSan flags
+ // (which can also overwrite common flags).
#if CAN_SANITIZE_LEAKS
__lsan::Flags *lf = __lsan::flags();
lf->SetDefaults();
@@ -83,6 +85,15 @@ void InitializeFlags() {
RegisterCommonFlags(&lsan_parser);
#endif
+#if CAN_SANITIZE_UB
+ __ubsan::Flags *uf = __ubsan::flags();
+ uf->SetDefaults();
+
+ FlagParser ubsan_parser;
+ __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
+ RegisterCommonFlags(&ubsan_parser);
+#endif
+
// Override from ASan compile definition.
const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition();
asan_parser.ParseString(asan_compile_def);
@@ -90,12 +101,19 @@ void InitializeFlags() {
// Override from user-specified string.
const char *asan_default_options = MaybeCallAsanDefaultOptions();
asan_parser.ParseString(asan_default_options);
+#if CAN_SANITIZE_UB
+ const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
+ ubsan_parser.ParseString(ubsan_default_options);
+#endif
// Override from command line.
asan_parser.ParseString(GetEnv("ASAN_OPTIONS"));
#if CAN_SANITIZE_LEAKS
lsan_parser.ParseString(GetEnv("LSAN_OPTIONS"));
#endif
+#if CAN_SANITIZE_UB
+ ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
+#endif
// Let activation flags override current settings. On Android they come
// from a system property. On other platforms this is no-op.
diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc
index 06140bbb360a..c34b1d3cedf2 100644
--- a/lib/asan/asan_globals.cc
+++ b/lib/asan/asan_globals.cc
@@ -18,6 +18,7 @@
#include "asan_report.h"
#include "asan_stack.h"
#include "asan_stats.h"
+#include "asan_suppressions.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_mutex.h"
@@ -73,7 +74,7 @@ ALWAYS_INLINE void PoisonRedZones(const Global &g) {
const uptr kMinimalDistanceFromAnotherGlobal = 64;
-bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
+static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
if (addr >= g.beg + g.size_with_redzone) return false;
return true;
@@ -90,36 +91,40 @@ static void ReportGlobal(const Global &g, const char *prefix) {
}
}
-static bool DescribeOrGetInfoIfGlobal(uptr addr, uptr size, bool print,
- Global *output_global) {
- if (!flags()->report_globals) return false;
+static u32 FindRegistrationSite(const Global *g) {
+ mu_for_globals.CheckLocked();
+ CHECK(global_registration_site_vector);
+ for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) {
+ GlobalRegistrationSite &grs = (*global_registration_site_vector)[i];
+ if (g >= grs.g_first && g <= grs.g_last)
+ return grs.stack_id;
+ }
+ return 0;
+}
+
+int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites,
+ int max_globals) {
+ if (!flags()->report_globals) return 0;
BlockingMutexLock lock(&mu_for_globals);
- bool res = false;
+ int res = 0;
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
const Global &g = *l->g;
- if (print) {
- if (flags()->report_globals >= 2)
- ReportGlobal(g, "Search");
- res |= DescribeAddressRelativeToGlobal(addr, size, g);
- } else {
- if (IsAddressNearGlobal(addr, g)) {
- CHECK(output_global);
- *output_global = g;
- return true;
- }
+ if (flags()->report_globals >= 2)
+ ReportGlobal(g, "Search");
+ if (IsAddressNearGlobal(addr, g)) {
+ globals[res] = g;
+ if (reg_sites)
+ reg_sites[res] = FindRegistrationSite(&g);
+ res++;
+ if (res == max_globals) break;
}
}
return res;
}
-bool DescribeAddressIfGlobal(uptr addr, uptr size) {
- return DescribeOrGetInfoIfGlobal(addr, size, /* print */ true,
- /* output_global */ nullptr);
-}
-
bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) {
Global g = {};
- if (DescribeOrGetInfoIfGlobal(addr, /* size */ 1, /* print */ false, &g)) {
+ if (GetGlobalsForAddress(addr, &g, nullptr, 1)) {
internal_strncpy(descr->name, g.name, descr->name_size);
descr->region_address = g.beg;
descr->region_size = g.size;
@@ -129,16 +134,6 @@ bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) {
return false;
}
-u32 FindRegistrationSite(const Global *g) {
- CHECK(global_registration_site_vector);
- for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) {
- GlobalRegistrationSite &grs = (*global_registration_site_vector)[i];
- if (g >= grs.g_first && g <= grs.g_last)
- return grs.stack_id;
- }
- return 0;
-}
-
// Register a global variable.
// This function may be called more than once for every global
// so we store the globals in a map.
@@ -158,7 +153,8 @@ static void RegisterGlobal(const Global *g) {
// the entire redzone of the second global may be within the first global.
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
if (g->beg == l->g->beg &&
- (flags()->detect_odr_violation >= 2 || g->size != l->g->size))
+ (flags()->detect_odr_violation >= 2 || g->size != l->g->size) &&
+ !IsODRViolationSuppressed(g->name))
ReportODRViolation(g, FindRegistrationSite(g),
l->g, FindRegistrationSite(l->g));
}
@@ -210,20 +206,6 @@ void StopInitOrderChecking() {
}
}
-#if SANITIZER_WINDOWS // Should only be called on Windows.
-SANITIZER_INTERFACE_ATTRIBUTE
-void UnregisterGlobalsInRange(void *beg, void *end) {
- if (!flags()->report_globals)
- return;
- BlockingMutexLock lock(&mu_for_globals);
- for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
- void *address = (void *)l->g->beg;
- if (beg <= address && address < end)
- UnregisterGlobal(l->g);
- }
-}
-#endif
-
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
@@ -232,7 +214,7 @@ using namespace __asan; // NOLINT
// Register an array of globals.
void __asan_register_globals(__asan_global *globals, uptr n) {
if (!flags()->report_globals) return;
- GET_STACK_TRACE_FATAL_HERE;
+ GET_STACK_TRACE_MALLOC;
u32 stack_id = StackDepotPut(stack);
BlockingMutexLock lock(&mu_for_globals);
if (!global_registration_site_vector)
diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc
index 3dc7ec67a3e5..d8b48d391ab8 100644
--- a/lib/asan/asan_interceptors.cc
+++ b/lib/asan/asan_interceptors.cc
@@ -23,6 +23,10 @@
#include "asan_suppressions.h"
#include "sanitizer_common/sanitizer_libc.h"
+#if SANITIZER_POSIX
+#include "sanitizer_common/sanitizer_posix.h"
+#endif
+
namespace __asan {
// Return true if we can quickly decide that the region is unpoisoned.
@@ -65,7 +69,7 @@ struct AsanInterceptorContext {
} \
if (!suppressed) { \
GET_CURRENT_PC_BP_SP; \
- __asan_report_error(pc, bp, sp, __bad, isWrite, __size); \
+ __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0); \
} \
} \
} while (0)
@@ -75,6 +79,13 @@ struct AsanInterceptorContext {
#define ASAN_WRITE_RANGE(ctx, offset, size) \
ACCESS_MEMORY_RANGE(ctx, offset, size, true)
+#define ASAN_READ_STRING_OF_LEN(ctx, s, len, n) \
+ ASAN_READ_RANGE((ctx), (s), \
+ common_flags()->strict_string_checks ? (len) + 1 : (n))
+
+#define ASAN_READ_STRING(ctx, s, n) \
+ ASAN_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n))
+
// Behavior of functions like "memcpy" or "strcpy" is undefined
// if memory intervals overlap. We report error in this case.
// Macro is used to avoid creation of new frames.
@@ -120,17 +131,6 @@ using namespace __asan; // NOLINT
DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr)
DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
-#if !SANITIZER_MAC
-#define ASAN_INTERCEPT_FUNC(name) \
- do { \
- if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \
- VReport(1, "AddressSanitizer: failed to intercept '" #name "'\n"); \
- } while (0)
-#else
-// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.
-#define ASAN_INTERCEPT_FUNC(name)
-#endif // SANITIZER_MAC
-
#define ASAN_INTERCEPTOR_ENTER(ctx, func) \
AsanInterceptorContext _ctx = {#func}; \
ctx = (void *)&_ctx; \
@@ -171,11 +171,24 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
do { \
} while (false)
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
+// Strict init-order checking is dlopen-hostile:
+// https://code.google.com/p/address-sanitizer/issues/detail?id=178
+#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \
+ if (flags()->strict_init_order) { \
+ StopInitOrderChecking(); \
+ }
#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \
CoverageUpdateMapping()
#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CoverageUpdateMapping()
#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited)
+#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
+ if (AsanThread *t = GetCurrentThread()) { \
+ *begin = t->tls_begin(); \
+ *end = t->tls_end(); \
+ } else { \
+ *begin = *end = 0; \
+ }
#include "sanitizer_common/sanitizer_common_interceptors.inc"
// Syscall interceptors don't have contexts, we don't support suppressions
@@ -200,12 +213,6 @@ struct ThreadStartParam {
};
static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
-#if SANITIZER_WINDOWS
- // FIXME: this is a bandaid fix for PR22025.
- AsanThread *t = (AsanThread*)arg;
- SetCurrentThread(t);
- return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr);
-#else
ThreadStartParam *param = reinterpret_cast<ThreadStartParam *>(arg);
AsanThread *t = nullptr;
while ((t = reinterpret_cast<AsanThread *>(
@@ -213,7 +220,6 @@ static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
internal_sched_yield();
SetCurrentThread(t);
return t->ThreadStart(GetTid(), &param->is_registered);
-#endif
}
#if ASAN_INTERCEPT_PTHREAD_CREATE
@@ -302,7 +308,7 @@ static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) {
ssize += stack - bottom;
ssize = RoundUpTo(ssize, PageSize);
static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb
- if (ssize && ssize <= kMaxSaneContextStackSize) {
+ if (AddrIsInMem(bottom) && ssize && ssize <= kMaxSaneContextStackSize) {
PoisonShadow(bottom, ssize, 0);
}
}
@@ -357,30 +363,6 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
}
#endif
-#if SANITIZER_WINDOWS
-INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
- CHECK(REAL(RaiseException));
- __asan_handle_no_return();
- REAL(RaiseException)(a, b, c, d);
-}
-
-INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
- CHECK(REAL(_except_handler3));
- __asan_handle_no_return();
- return REAL(_except_handler3)(a, b, c, d);
-}
-
-#if ASAN_DYNAMIC
-// This handler is named differently in -MT and -MD CRTs.
-#define _except_handler4 _except_handler4_common
-#endif
-INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
- CHECK(REAL(_except_handler4));
- __asan_handle_no_return();
- return REAL(_except_handler4)(a, b, c, d);
-}
-#endif
-
static inline int CharCmp(unsigned char c1, unsigned char c2) {
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
}
@@ -511,8 +493,9 @@ INTERCEPTOR(char*, strchr, const char *str, int c) {
ENSURE_ASAN_INITED();
char *result = REAL(strchr)(str, c);
if (flags()->replace_str) {
- uptr bytes_read = (result ? result - str : REAL(strlen)(str)) + 1;
- ASAN_READ_RANGE(ctx, str, bytes_read);
+ uptr len = REAL(strlen)(str);
+ uptr bytes_read = (result ? result - str : len) + 1;
+ ASAN_READ_STRING_OF_LEN(ctx, str, len, bytes_read);
}
return result;
}
@@ -541,7 +524,7 @@ INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT
uptr from_length = REAL(strlen)(from);
ASAN_READ_RANGE(ctx, from, from_length + 1);
uptr to_length = REAL(strlen)(to);
- ASAN_READ_RANGE(ctx, to, to_length);
+ ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length);
ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1);
// If the copying actually happens, the |from| string should not overlap
// with the resulting string starting at |to|, which has a length of
@@ -563,7 +546,7 @@ INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) {
uptr copy_length = Min(size, from_length + 1);
ASAN_READ_RANGE(ctx, from, copy_length);
uptr to_length = REAL(strlen)(to);
- ASAN_READ_RANGE(ctx, to, to_length);
+ ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length);
ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1);
if (from_length > 0) {
CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1,
@@ -665,23 +648,6 @@ INTERCEPTOR(uptr, strnlen, const char *s, uptr maxlen) {
}
#endif // ASAN_INTERCEPT_STRNLEN
-static inline bool IsValidStrtolBase(int base) {
- return (base == 0) || (2 <= base && base <= 36);
-}
-
-static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) {
- CHECK(endptr);
- if (nptr == *endptr) {
- // No digits were found at strtol call, we need to find out the last
- // symbol accessed by strtoll on our own.
- // We get this symbol by skipping leading blanks and optional +/- sign.
- while (IsSpace(*nptr)) nptr++;
- if (*nptr == '+' || *nptr == '-') nptr++;
- *endptr = const_cast<char *>(nptr);
- }
- CHECK(*endptr >= nptr);
-}
-
INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
char **endptr, int base) {
void *ctx;
@@ -692,13 +658,7 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
}
char *real_endptr;
long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT
- if (endptr != 0) {
- *endptr = real_endptr;
- }
- if (IsValidStrtolBase(base)) {
- FixRealStrtolEndptr(nptr, &real_endptr);
- ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
- }
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return result;
}
@@ -719,7 +679,7 @@ INTERCEPTOR(int, atoi, const char *nptr) {
// different from int). So, we just imitate this behavior.
int result = REAL(strtol)(nptr, &real_endptr, 10);
FixRealStrtolEndptr(nptr, &real_endptr);
- ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
+ ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
return result;
}
@@ -736,7 +696,7 @@ INTERCEPTOR(long, atol, const char *nptr) { // NOLINT
char *real_endptr;
long result = REAL(strtol)(nptr, &real_endptr, 10); // NOLINT
FixRealStrtolEndptr(nptr, &real_endptr);
- ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
+ ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
return result;
}
@@ -751,16 +711,7 @@ INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT
}
char *real_endptr;
long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT
- if (endptr != 0) {
- *endptr = real_endptr;
- }
- // If base has unsupported value, strtoll can exit with EINVAL
- // without reading any characters. So do additional checks only
- // if base is valid.
- if (IsValidStrtolBase(base)) {
- FixRealStrtolEndptr(nptr, &real_endptr);
- ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
- }
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return result;
}
@@ -774,7 +725,7 @@ INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT
char *real_endptr;
long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT
FixRealStrtolEndptr(nptr, &real_endptr);
- ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1);
+ ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
return result;
}
#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL
@@ -807,36 +758,6 @@ INTERCEPTOR(int, fork, void) {
}
#endif // ASAN_INTERCEPT_FORK
-#if SANITIZER_WINDOWS
-INTERCEPTOR_WINAPI(DWORD, CreateThread,
- void* security, uptr stack_size,
- DWORD (__stdcall *start_routine)(void*), void* arg,
- DWORD thr_flags, void* tid) {
- // Strict init-order checking is thread-hostile.
- if (flags()->strict_init_order)
- StopInitOrderChecking();
- GET_STACK_TRACE_THREAD;
- // FIXME: The CreateThread interceptor is not the same as a pthread_create
- // one. This is a bandaid fix for PR22025.
- bool detached = false; // FIXME: how can we determine it on Windows?
- u32 current_tid = GetCurrentTidOrInvalid();
- AsanThread *t =
- AsanThread::Create(start_routine, arg, current_tid, &stack, detached);
- return REAL(CreateThread)(security, stack_size,
- asan_thread_start, t, thr_flags, tid);
-}
-
-namespace __asan {
-void InitializeWindowsInterceptors() {
- ASAN_INTERCEPT_FUNC(CreateThread);
- ASAN_INTERCEPT_FUNC(RaiseException);
- ASAN_INTERCEPT_FUNC(_except_handler3);
- ASAN_INTERCEPT_FUNC(_except_handler4);
-}
-
-} // namespace __asan
-#endif
-
// ---------------------- InitializeAsanInterceptors ---------------- {{{1
namespace __asan {
void InitializeAsanInterceptors() {
@@ -919,10 +840,7 @@ void InitializeAsanInterceptors() {
ASAN_INTERCEPT_FUNC(fork);
#endif
- // Some Windows-specific interceptors.
-#if SANITIZER_WINDOWS
- InitializeWindowsInterceptors();
-#endif
+ InitializePlatformInterceptors();
VReport(1, "AddressSanitizer: libc interceptors initialized\n");
}
diff --git a/lib/asan/asan_interceptors.h b/lib/asan/asan_interceptors.h
index ee3b82aa7ef9..488ada78ab8b 100644
--- a/lib/asan/asan_interceptors.h
+++ b/lib/asan/asan_interceptors.h
@@ -92,9 +92,21 @@ struct sigaction;
DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
struct sigaction *oldact)
+#if !SANITIZER_MAC
+#define ASAN_INTERCEPT_FUNC(name) \
+ do { \
+ if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \
+ VReport(1, "AddressSanitizer: failed to intercept '" #name "'\n"); \
+ } while (0)
+#else
+// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.
+#define ASAN_INTERCEPT_FUNC(name)
+#endif // SANITIZER_MAC
+
namespace __asan {
void InitializeAsanInterceptors();
+void InitializePlatformInterceptors();
#define ENSURE_ASAN_INITED() do { \
CHECK(!asan_init_is_running); \
diff --git a/lib/asan/asan_interface_internal.h b/lib/asan/asan_interface_internal.h
index ea7540f6bb56..ad8ebcd91ad9 100644
--- a/lib/asan/asan_interface_internal.h
+++ b/lib/asan/asan_interface_internal.h
@@ -128,7 +128,7 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_report_error(uptr pc, uptr bp, uptr sp,
- uptr addr, int is_write, uptr access_size);
+ uptr addr, int is_write, uptr access_size, u32 exp);
SANITIZER_INTERFACE_ATTRIBUTE
int __asan_set_error_exit_code(int exit_code);
@@ -165,6 +165,21 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN(uptr p, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN(uptr p, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load1(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load2(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load4(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load8(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load16(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store1(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store2(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store4(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store8(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store16(uptr p, u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_loadN(uptr p, uptr size,
+ u32 exp);
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_storeN(uptr p, uptr size,
+ u32 exp);
+
SANITIZER_INTERFACE_ATTRIBUTE
void* __asan_memcpy(void *dst, const void *src, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE
@@ -180,6 +195,10 @@ extern "C" {
void __asan_poison_intra_object_redzone(uptr p, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_unpoison_intra_object_redzone(uptr p, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_alloca_poison(uptr addr, uptr size);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __asan_allocas_unpoison(uptr top, uptr bottom);
} // extern "C"
#endif // ASAN_INTERFACE_INTERNAL_H
diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h
index ffd3ff82d71e..107e16ee31b9 100644
--- a/lib/asan/asan_internal.h
+++ b/lib/asan/asan_internal.h
@@ -62,21 +62,6 @@ namespace __asan {
class AsanThread;
using __sanitizer::StackTrace;
-struct SignalContext {
- void *context;
- uptr addr;
- uptr pc;
- uptr sp;
- uptr bp;
-
- SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp) :
- context(context), addr(addr), pc(pc), sp(sp), bp(bp) {
- }
-
- // Creates signal context in a platform-specific manner.
- static SignalContext Create(void *siginfo, void *context);
-};
-
void AsanInitFromRtl();
// asan_rtl.cc
@@ -90,7 +75,6 @@ void *AsanDoesNotSupportStaticLinkage();
void AsanCheckDynamicRTPrereqs();
void AsanCheckIncompatibleRT();
-void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
void AsanOnSIGSEGV(int, void *siginfo, void *context);
void DisableReexec();
@@ -109,7 +93,7 @@ void AppendToErrorMessageBuffer(const char *buffer);
void *AsanDlSymNext(const char *sym);
-void ReserveShadowMemoryRange(uptr beg, uptr end);
+void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name);
// Platform-specific options.
#if SANITIZER_MAC
diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc
index 8e8bafd47af6..9580fc7c06d4 100644
--- a/lib/asan/asan_linux.cc
+++ b/lib/asan/asan_linux.cc
@@ -68,6 +68,8 @@ asan_rt_version_t __asan_rt_version;
namespace __asan {
+void InitializePlatformInterceptors() {}
+
void DisableReexec() {
// No need to re-exec on Linux.
}
@@ -111,6 +113,9 @@ static void ReportIncompatibleRT() {
}
void AsanCheckDynamicRTPrereqs() {
+ if (!ASAN_DYNAMIC)
+ return;
+
// Ensure that dynamic RT is the first DSO in the list
const char *first_dso_name = 0;
dl_iterate_phdr(FindFirstDSOCallback, &first_dso_name);
@@ -152,78 +157,6 @@ void AsanCheckIncompatibleRT() {
}
#endif // SANITIZER_ANDROID
-void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
-#if defined(__arm__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.arm_pc;
- *bp = ucontext->uc_mcontext.arm_fp;
- *sp = ucontext->uc_mcontext.arm_sp;
-#elif defined(__aarch64__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.pc;
- *bp = ucontext->uc_mcontext.regs[29];
- *sp = ucontext->uc_mcontext.sp;
-#elif defined(__hppa__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.sc_iaoq[0];
- /* GCC uses %r3 whenever a frame pointer is needed. */
- *bp = ucontext->uc_mcontext.sc_gr[3];
- *sp = ucontext->uc_mcontext.sc_gr[30];
-#elif defined(__x86_64__)
-# if SANITIZER_FREEBSD
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.mc_rip;
- *bp = ucontext->uc_mcontext.mc_rbp;
- *sp = ucontext->uc_mcontext.mc_rsp;
-# else
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.gregs[REG_RIP];
- *bp = ucontext->uc_mcontext.gregs[REG_RBP];
- *sp = ucontext->uc_mcontext.gregs[REG_RSP];
-# endif
-#elif defined(__i386__)
-# if SANITIZER_FREEBSD
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.mc_eip;
- *bp = ucontext->uc_mcontext.mc_ebp;
- *sp = ucontext->uc_mcontext.mc_esp;
-# else
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.gregs[REG_EIP];
- *bp = ucontext->uc_mcontext.gregs[REG_EBP];
- *sp = ucontext->uc_mcontext.gregs[REG_ESP];
-# endif
-#elif defined(__powerpc__) || defined(__powerpc64__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.regs->nip;
- *sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
- // The powerpc{,64}-linux ABIs do not specify r31 as the frame
- // pointer, but GCC always uses r31 when we need a frame pointer.
- *bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
-#elif defined(__sparc__)
- ucontext_t *ucontext = (ucontext_t*)context;
- uptr *stk_ptr;
-# if defined (__arch64__)
- *pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
- *sp = ucontext->uc_mcontext.mc_gregs[MC_O6];
- stk_ptr = (uptr *) (*sp + 2047);
- *bp = stk_ptr[15];
-# else
- *pc = ucontext->uc_mcontext.gregs[REG_PC];
- *sp = ucontext->uc_mcontext.gregs[REG_O6];
- stk_ptr = (uptr *) *sp;
- *bp = stk_ptr[15];
-# endif
-#elif defined(__mips__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.gregs[31];
- *bp = ucontext->uc_mcontext.gregs[30];
- *sp = ucontext->uc_mcontext.gregs[29];
-#else
-# error "Unsupported arch"
-#endif
-}
-
void AsanPlatformThreadInit() {
// Nothing here for now.
}
diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc
index b35368617dca..3e028378df28 100644
--- a/lib/asan/asan_mac.cc
+++ b/lib/asan/asan_mac.cc
@@ -24,7 +24,14 @@
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_mac.h"
-#include <crt_externs.h> // for _NSGetArgv
+#if !SANITIZER_IOS
+#include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron
+#else
+extern "C" {
+ extern char ***_NSGetArgv(void);
+}
+#endif
+
#include <dlfcn.h> // for dladdr()
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
@@ -40,19 +47,7 @@
namespace __asan {
-void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
- ucontext_t *ucontext = (ucontext_t*)context;
-# if SANITIZER_WORDSIZE == 64
- *pc = ucontext->uc_mcontext->__ss.__rip;
- *bp = ucontext->uc_mcontext->__ss.__rbp;
- *sp = ucontext->uc_mcontext->__ss.__rsp;
-# else
- *pc = ucontext->uc_mcontext->__ss.__eip;
- *bp = ucontext->uc_mcontext->__ss.__ebp;
- *sp = ucontext->uc_mcontext->__ss.__esp;
-# endif // SANITIZER_WORDSIZE
-}
-
+void InitializePlatformInterceptors() {}
bool PlatformHasDifferentMemcpyAndMemmove() {
// On OS X 10.7 memcpy() and memmove() are both resolved
@@ -74,30 +69,27 @@ LowLevelAllocator allocator_for_env;
// otherwise the corresponding "NAME=value" string is replaced with
// |name_value|.
void LeakyResetEnv(const char *name, const char *name_value) {
- char ***env_ptr = _NSGetEnviron();
- CHECK(env_ptr);
- char **environ = *env_ptr;
- CHECK(environ);
+ char **env = GetEnviron();
uptr name_len = internal_strlen(name);
- while (*environ != 0) {
- uptr len = internal_strlen(*environ);
+ while (*env != 0) {
+ uptr len = internal_strlen(*env);
if (len > name_len) {
- const char *p = *environ;
+ const char *p = *env;
if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
// Match.
if (name_value) {
// Replace the old value with the new one.
- *environ = const_cast<char*>(name_value);
+ *env = const_cast<char*>(name_value);
} else {
// Shift the subsequent pointers back.
- char **del = environ;
+ char **del = env;
do {
del[0] = del[1];
} while (*del++);
}
}
}
- environ++;
+ env++;
}
}
@@ -107,6 +99,23 @@ void DisableReexec() {
reexec_disabled = true;
}
+bool DyldNeedsEnvVariable() {
+// If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if
+// DYLD_INSERT_LIBRARIES is not set.
+
+#if SANITIZER_IOSSIM
+ // GetMacosVersion will not work for the simulator, whose kernel version
+ // is tied to the host. Use a weak linking hack for the simulator.
+ // This API was introduced in the same version of the OS as the dyld
+ // optimization.
+
+ // Check for presence of a symbol that is available on OS X 10.11+, iOS 9.0+.
+ return (dlsym(RTLD_NEXT, "mach_memory_info") == nullptr);
+#else
+ return (GetMacosVersion() <= MACOS_VERSION_YOSEMITE);
+#endif
+}
+
void MaybeReexec() {
if (reexec_disabled) return;
@@ -122,8 +131,10 @@ void MaybeReexec() {
uptr fname_len = internal_strlen(info.dli_fname);
const char *dylib_name = StripModuleName(info.dli_fname);
uptr dylib_name_len = internal_strlen(dylib_name);
- if (!dyld_insert_libraries ||
- !REAL(strstr)(dyld_insert_libraries, dylib_name)) {
+
+ bool lib_is_in_env =
+ dyld_insert_libraries && REAL(strstr)(dyld_insert_libraries, dylib_name);
+ if (DyldNeedsEnvVariable() && !lib_is_in_env) {
// DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
// library.
char program_name[1024];
@@ -160,6 +171,9 @@ void MaybeReexec() {
CHECK("execv failed" && 0);
}
+ if (!lib_is_in_env)
+ return;
+
// DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove
// the dylib from the environment variable, because interceptors are installed
// and we don't want our children to inherit the variable.
diff --git a/lib/asan/asan_mapping.h b/lib/asan/asan_mapping.h
index 5cb011d683d3..f9e1a527de18 100644
--- a/lib/asan/asan_mapping.h
+++ b/lib/asan/asan_mapping.h
@@ -98,9 +98,12 @@
static const u64 kDefaultShadowScale = 3;
static const u64 kDefaultShadowOffset32 = 1ULL << 29; // 0x20000000
-static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000
static const u64 kDefaultShadowOffset64 = 1ULL << 44;
static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G.
+static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000
+static const u64 kIosShadowOffset64 = 0x130000000;
+static const u64 kIosSimShadowOffset32 = 1ULL << 30;
+static const u64 kIosSimShadowOffset64 = kDefaultShadowOffset64;
static const u64 kAArch64_ShadowOffset64 = 1ULL << 36;
static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000;
static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37;
@@ -118,10 +121,12 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
# define SHADOW_OFFSET kMIPS32_ShadowOffset32
# elif SANITIZER_FREEBSD
# define SHADOW_OFFSET kFreeBSD_ShadowOffset32
-# elif SANITIZER_IOS
-# define SHADOW_OFFSET kIosShadowOffset32
# elif SANITIZER_WINDOWS
# define SHADOW_OFFSET kWindowsShadowOffset32
+# elif SANITIZER_IOSSIM
+# define SHADOW_OFFSET kIosSimShadowOffset32
+# elif SANITIZER_IOS
+# define SHADOW_OFFSET kIosShadowOffset32
# else
# define SHADOW_OFFSET kDefaultShadowOffset32
# endif
@@ -136,6 +141,10 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
# define SHADOW_OFFSET kDefaultShadowOffset64
# elif defined(__mips64)
# define SHADOW_OFFSET kMIPS64_ShadowOffset64
+# elif SANITIZER_IOSSIM
+# define SHADOW_OFFSET kIosSimShadowOffset64
+# elif SANITIZER_IOS
+# define SHADOW_OFFSET kIosShadowOffset64
# else
# define SHADOW_OFFSET kDefaultShort64bitShadowOffset
# endif
diff --git a/lib/asan/asan_poisoning.cc b/lib/asan/asan_poisoning.cc
index e2b1f4dc4d5e..569d359aa425 100644
--- a/lib/asan/asan_poisoning.cc
+++ b/lib/asan/asan_poisoning.cc
@@ -112,7 +112,7 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) {
if (!flags()->allow_user_poisoning || size == 0) return;
uptr beg_addr = (uptr)addr;
uptr end_addr = beg_addr + size;
- VPrintf(1, "Trying to poison memory region [%p, %p)\n", (void *)beg_addr,
+ VPrintf(3, "Trying to poison memory region [%p, %p)\n", (void *)beg_addr,
(void *)end_addr);
ShadowSegmentEndpoint beg(beg_addr);
ShadowSegmentEndpoint end(end_addr);
@@ -152,7 +152,7 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
if (!flags()->allow_user_poisoning || size == 0) return;
uptr beg_addr = (uptr)addr;
uptr end_addr = beg_addr + size;
- VPrintf(1, "Trying to unpoison memory region [%p, %p)\n", (void *)beg_addr,
+ VPrintf(3, "Trying to unpoison memory region [%p, %p)\n", (void *)beg_addr,
(void *)end_addr);
ShadowSegmentEndpoint beg(beg_addr);
ShadowSegmentEndpoint end(end_addr);
@@ -218,7 +218,7 @@ uptr __asan_region_is_poisoned(uptr beg, uptr size) {
__asan::AddressIsPoisoned(__p + __size - 1))) { \
GET_CURRENT_PC_BP_SP; \
uptr __bad = __asan_region_is_poisoned(__p, __size); \
- __asan_report_error(pc, bp, sp, __bad, isWrite, __size);\
+ __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0);\
} \
} while (false); \
diff --git a/lib/asan/asan_poisoning.h b/lib/asan/asan_poisoning.h
index 3fc94649fb39..6344225f0f64 100644
--- a/lib/asan/asan_poisoning.h
+++ b/lib/asan/asan_poisoning.h
@@ -64,7 +64,7 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
if (page_end != shadow_end) {
REAL(memset)((void *)page_end, 0, shadow_end - page_end);
}
- ReserveShadowMemoryRange(page_beg, page_end - 1);
+ ReserveShadowMemoryRange(page_beg, page_end - 1, nullptr);
}
}
}
diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc
index ad31458df28d..2e857f6f624c 100644
--- a/lib/asan/asan_posix.cc
+++ b/lib/asan/asan_posix.cc
@@ -21,6 +21,7 @@
#include "asan_report.h"
#include "asan_stack.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_posix.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include <pthread.h>
@@ -32,13 +33,6 @@
namespace __asan {
-SignalContext SignalContext::Create(void *siginfo, void *context) {
- uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr;
- uptr pc, sp, bp;
- GetPcSpBp(context, &pc, &sp, &bp);
- return SignalContext(context, addr, pc, sp, bp);
-}
-
void AsanOnSIGSEGV(int, void *siginfo, void *context) {
ScopedDeadlySignal signal_scope(GetCurrentThread());
int code = (int)((siginfo_t*)siginfo)->si_code;
diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc
index 8706d5decc0b..c1681e644464 100644
--- a/lib/asan/asan_report.cc
+++ b/lib/asan/asan_report.cc
@@ -281,9 +281,8 @@ static void PrintGlobalLocation(InternalScopedString *str,
str->append(":%d", g.location->column_no);
}
-bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
- const __asan_global &g) {
- if (!IsAddressNearGlobal(addr, g)) return false;
+static void DescribeAddressRelativeToGlobal(uptr addr, uptr size,
+ const __asan_global &g) {
InternalScopedString str(4096);
Decorator d;
str.append("%s", d.Location());
@@ -306,6 +305,26 @@ bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
str.append("%s", d.EndLocation());
PrintGlobalNameIfASCII(&str, g);
Printf("%s", str.data());
+}
+
+static bool DescribeAddressIfGlobal(uptr addr, uptr size,
+ const char *bug_type) {
+ // Assume address is close to at most four globals.
+ const int kMaxGlobalsInReport = 4;
+ __asan_global globals[kMaxGlobalsInReport];
+ u32 reg_sites[kMaxGlobalsInReport];
+ int globals_num =
+ GetGlobalsForAddress(addr, globals, reg_sites, ARRAY_SIZE(globals));
+ if (globals_num == 0)
+ return false;
+ for (int i = 0; i < globals_num; i++) {
+ DescribeAddressRelativeToGlobal(addr, size, globals[i]);
+ if (0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&
+ reg_sites[i]) {
+ Printf(" registered at:\n");
+ StackDepotGet(reg_sites[i]).Print();
+ }
+ }
return true;
}
@@ -551,12 +570,12 @@ void DescribeHeapAddress(uptr addr, uptr access_size) {
DescribeThread(alloc_thread);
}
-void DescribeAddress(uptr addr, uptr access_size) {
+static void DescribeAddress(uptr addr, uptr access_size, const char *bug_type) {
// Check if this is shadow or shadow gap.
if (DescribeAddressIfShadow(addr))
return;
CHECK(AddrIsInMem(addr));
- if (DescribeAddressIfGlobal(addr, access_size))
+ if (DescribeAddressIfGlobal(addr, access_size, bug_type))
return;
if (DescribeAddressIfStack(addr, access_size))
return;
@@ -578,6 +597,11 @@ void DescribeThread(AsanThreadContext *context) {
InternalScopedString str(1024);
str.append("Thread T%d%s", context->tid,
ThreadNameWithParenthesis(context->tid, tname, sizeof(tname)));
+ if (context->parent_tid == kInvalidTid) {
+ str.append(" created by unknown thread\n");
+ Printf("%s", str.data());
+ return;
+ }
str.append(
" created by T%d%s here:\n", context->parent_tid,
ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname)));
@@ -805,8 +829,8 @@ void ReportStringFunctionMemoryRangesOverlap(const char *function,
bug_type, offset1, offset1 + length1, offset2, offset2 + length2);
Printf("%s", d.EndWarning());
stack->Print();
- DescribeAddress((uptr)offset1, length1);
- DescribeAddress((uptr)offset2, length2);
+ DescribeAddress((uptr)offset1, length1, bug_type);
+ DescribeAddress((uptr)offset2, length2, bug_type);
ReportErrorSummary(bug_type, stack);
}
@@ -819,7 +843,7 @@ void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", bug_type, size);
Printf("%s", d.EndWarning());
stack->Print();
- DescribeAddress(offset, size);
+ DescribeAddress(offset, size, bug_type);
ReportErrorSummary(bug_type, stack);
}
@@ -834,6 +858,9 @@ void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
" old_mid : %p\n"
" new_mid : %p\n",
beg, end, old_mid, new_mid);
+ uptr granularity = SHADOW_GRANULARITY;
+ if (!IsAligned(beg, granularity))
+ Report("ERROR: beg is not aligned by %d\n", granularity);
stack->Print();
ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack);
}
@@ -871,15 +898,16 @@ void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
static NOINLINE void
ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, uptr a1, uptr a2) {
ScopedInErrorReport in_report;
+ const char *bug_type = "invalid-pointer-pair";
Decorator d;
Printf("%s", d.Warning());
Report("ERROR: AddressSanitizer: invalid-pointer-pair: %p %p\n", a1, a2);
Printf("%s", d.EndWarning());
GET_STACK_TRACE_FATAL(pc, bp);
stack.Print();
- DescribeAddress(a1, 1);
- DescribeAddress(a2, 1);
- ReportErrorSummary("invalid-pointer-pair", &stack);
+ DescribeAddress(a1, 1, bug_type);
+ DescribeAddress(a2, 1, bug_type);
+ ReportErrorSummary(bug_type, &stack);
}
static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
@@ -936,9 +964,18 @@ void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
using namespace __asan; // NOLINT
void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
- uptr access_size) {
+ uptr access_size, u32 exp) {
ENABLE_FRAME_POINTER;
+ // Optimization experiments.
+ // The experiments can be used to evaluate potential optimizations that remove
+ // instrumentation (assess false negatives). Instead of completely removing
+ // some instrumentation, compiler can emit special calls into runtime
+ // (e.g. __asan_report_exp_load1 instead of __asan_report_load1) and pass
+ // mask of experiments (exp).
+ // The reaction to a non-zero value of exp is to be defined.
+ (void)exp;
+
// Determine the error type.
const char *bug_descr = "unknown-crash";
if (AddrIsInMem(addr)) {
@@ -1017,7 +1054,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
GET_STACK_TRACE_FATAL(pc, bp);
stack.Print();
- DescribeAddress(addr, access_size);
+ DescribeAddress(addr, access_size, bug_descr);
ReportErrorSummary(bug_descr, &stack);
PrintShadowMemoryForAddress(addr);
}
@@ -1035,7 +1072,7 @@ void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
void __asan_describe_address(uptr addr) {
// Thread registry must be locked while we're describing an address.
asanThreadRegistry().Lock();
- DescribeAddress(addr, 1);
+ DescribeAddress(addr, 1, "");
asanThreadRegistry().Unlock();
}
diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h
index 029c914b8a04..e2786b0f260c 100644
--- a/lib/asan/asan_report.h
+++ b/lib/asan/asan_report.h
@@ -33,22 +33,19 @@ struct AddressDescription {
const char *region_kind;
};
+// Returns the number of globals close to the provided address and copies
+// them to "globals" array.
+int GetGlobalsForAddress(uptr addr, __asan_global *globals, u32 *reg_sites,
+ int max_globals);
+bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr);
// The following functions prints address description depending
// on the memory type (shadow/heap/stack/global).
void DescribeHeapAddress(uptr addr, uptr access_size);
-bool DescribeAddressIfGlobal(uptr addr, uptr access_size);
-bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
- const __asan_global &g);
-bool IsAddressNearGlobal(uptr addr, const __asan_global &g);
-bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr);
bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr = nullptr,
bool print = true);
bool ParseFrameDescription(const char *frame_descr,
InternalMmapVector<StackVarDescr> *vars);
bool DescribeAddressIfStack(uptr addr, uptr access_size);
-// Determines memory type on its own.
-void DescribeAddress(uptr addr, uptr access_size);
-
void DescribeThread(AsanThreadContext *context);
// Different kinds of error reports.
diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc
index 9126e71a6437..a8d92b915a9a 100644
--- a/lib/asan/asan_rtl.cc
+++ b/lib/asan/asan_rtl.cc
@@ -28,6 +28,8 @@
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include "lsan/lsan_common.h"
+#include "ubsan/ubsan_init.h"
+#include "ubsan/ubsan_platform.h"
int __asan_option_detect_stack_use_after_return; // Global interface symbol.
uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan.
@@ -87,12 +89,12 @@ void ShowStatsAndAbort() {
// ---------------------- mmap -------------------- {{{1
// Reserve memory range [beg, end].
// We need to use inclusive range because end+1 may not be representable.
-void ReserveShadowMemoryRange(uptr beg, uptr end) {
+void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
CHECK_EQ((beg % GetPageSizeCached()), 0);
CHECK_EQ(((end + 1) % GetPageSizeCached()), 0);
uptr size = end - beg + 1;
DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb.
- void *res = MmapFixedNoReserve(beg, size);
+ void *res = MmapFixedNoReserve(beg, size, name);
if (res != (void*)beg) {
Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
"Perhaps you're using ulimit -v\n", size);
@@ -112,11 +114,15 @@ static void OnLowLevelAllocate(uptr ptr, uptr size) {
// -------------------------- Run-time entry ------------------- {{{1
// exported functions
#define ASAN_REPORT_ERROR(type, is_write, size) \
-extern "C" NOINLINE INTERFACE_ATTRIBUTE \
-void __asan_report_ ## type ## size(uptr addr); \
-void __asan_report_ ## type ## size(uptr addr) { \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_ ## type ## size(uptr addr) { \
+ GET_CALLER_PC_BP_SP; \
+ __asan_report_error(pc, bp, sp, addr, is_write, size, 0); \
+} \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_exp_ ## type ## size(uptr addr, u32 exp) { \
GET_CALLER_PC_BP_SP; \
- __asan_report_error(pc, bp, sp, addr, is_write, size); \
+ __asan_report_error(pc, bp, sp, addr, is_write, size, exp); \
}
ASAN_REPORT_ERROR(load, false, 1)
@@ -132,18 +138,20 @@ ASAN_REPORT_ERROR(store, true, 16)
#define ASAN_REPORT_ERROR_N(type, is_write) \
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
-void __asan_report_ ## type ## _n(uptr addr, uptr size); \
void __asan_report_ ## type ## _n(uptr addr, uptr size) { \
GET_CALLER_PC_BP_SP; \
- __asan_report_error(pc, bp, sp, addr, is_write, size); \
+ __asan_report_error(pc, bp, sp, addr, is_write, size, 0); \
+} \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_exp_ ## type ## _n(uptr addr, uptr size, u32 exp) { \
+ GET_CALLER_PC_BP_SP; \
+ __asan_report_error(pc, bp, sp, addr, is_write, size, exp); \
}
ASAN_REPORT_ERROR_N(load, false)
ASAN_REPORT_ERROR_N(store, true)
-#define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \
- extern "C" NOINLINE INTERFACE_ATTRIBUTE void __asan_##type##size(uptr addr); \
- void __asan_##type##size(uptr addr) { \
+#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg) \
uptr sp = MEM_TO_SHADOW(addr); \
uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast<u8 *>(sp) \
: *reinterpret_cast<u16 *>(sp); \
@@ -155,10 +163,19 @@ ASAN_REPORT_ERROR_N(store, true)
*__asan_test_only_reported_buggy_pointer = addr; \
} else { \
GET_CALLER_PC_BP_SP; \
- __asan_report_error(pc, bp, sp, addr, is_write, size); \
+ __asan_report_error(pc, bp, sp, addr, is_write, size, exp_arg); \
} \
} \
- } \
+ }
+
+#define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \
+ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+ void __asan_##type##size(uptr addr) { \
+ ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0) \
+ } \
+ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+ void __asan_exp_##type##size(uptr addr, u32 exp) { \
+ ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp) \
}
ASAN_MEMORY_ACCESS_CALLBACK(load, false, 1)
@@ -173,18 +190,38 @@ ASAN_MEMORY_ACCESS_CALLBACK(store, true, 8)
ASAN_MEMORY_ACCESS_CALLBACK(store, true, 16)
extern "C"
-NOINLINE INTERFACE_ATTRIBUTE void __asan_loadN(uptr addr, uptr size) {
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_loadN(uptr addr, uptr size) {
if (__asan_region_is_poisoned(addr, size)) {
GET_CALLER_PC_BP_SP;
- __asan_report_error(pc, bp, sp, addr, false, size);
+ __asan_report_error(pc, bp, sp, addr, false, size, 0);
}
}
extern "C"
-NOINLINE INTERFACE_ATTRIBUTE void __asan_storeN(uptr addr, uptr size) {
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_exp_loadN(uptr addr, uptr size, u32 exp) {
if (__asan_region_is_poisoned(addr, size)) {
GET_CALLER_PC_BP_SP;
- __asan_report_error(pc, bp, sp, addr, true, size);
+ __asan_report_error(pc, bp, sp, addr, false, size, exp);
+ }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_storeN(uptr addr, uptr size) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ __asan_report_error(pc, bp, sp, addr, true, size, 0);
+ }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_exp_storeN(uptr addr, uptr size, u32 exp) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ __asan_report_error(pc, bp, sp, addr, true, size, exp);
}
}
@@ -203,26 +240,40 @@ static NOINLINE void force_interface_symbols() {
case 3: __asan_report_load4(0); break;
case 4: __asan_report_load8(0); break;
case 5: __asan_report_load16(0); break;
- case 6: __asan_report_store1(0); break;
- case 7: __asan_report_store2(0); break;
- case 8: __asan_report_store4(0); break;
- case 9: __asan_report_store8(0); break;
- case 10: __asan_report_store16(0); break;
- case 12: __asan_register_globals(0, 0); break;
- case 13: __asan_unregister_globals(0, 0); break;
- case 14: __asan_set_death_callback(0); break;
- case 15: __asan_set_error_report_callback(0); break;
- case 16: __asan_handle_no_return(); break;
- case 17: __asan_address_is_poisoned(0); break;
- case 25: __asan_poison_memory_region(0, 0); break;
- case 26: __asan_unpoison_memory_region(0, 0); break;
- case 27: __asan_set_error_exit_code(0); break;
- case 30: __asan_before_dynamic_init(0); break;
- case 31: __asan_after_dynamic_init(); break;
- case 32: __asan_poison_stack_memory(0, 0); break;
- case 33: __asan_unpoison_stack_memory(0, 0); break;
- case 34: __asan_region_is_poisoned(0, 0); break;
- case 35: __asan_describe_address(0); break;
+ case 6: __asan_report_load_n(0, 0); break;
+ case 7: __asan_report_store1(0); break;
+ case 8: __asan_report_store2(0); break;
+ case 9: __asan_report_store4(0); break;
+ case 10: __asan_report_store8(0); break;
+ case 11: __asan_report_store16(0); break;
+ case 12: __asan_report_store_n(0, 0); break;
+ case 13: __asan_report_exp_load1(0, 0); break;
+ case 14: __asan_report_exp_load2(0, 0); break;
+ case 15: __asan_report_exp_load4(0, 0); break;
+ case 16: __asan_report_exp_load8(0, 0); break;
+ case 17: __asan_report_exp_load16(0, 0); break;
+ case 18: __asan_report_exp_load_n(0, 0, 0); break;
+ case 19: __asan_report_exp_store1(0, 0); break;
+ case 20: __asan_report_exp_store2(0, 0); break;
+ case 21: __asan_report_exp_store4(0, 0); break;
+ case 22: __asan_report_exp_store8(0, 0); break;
+ case 23: __asan_report_exp_store16(0, 0); break;
+ case 24: __asan_report_exp_store_n(0, 0, 0); break;
+ case 25: __asan_register_globals(0, 0); break;
+ case 26: __asan_unregister_globals(0, 0); break;
+ case 27: __asan_set_death_callback(0); break;
+ case 28: __asan_set_error_report_callback(0); break;
+ case 29: __asan_handle_no_return(); break;
+ case 30: __asan_address_is_poisoned(0); break;
+ case 31: __asan_poison_memory_region(0, 0); break;
+ case 32: __asan_unpoison_memory_region(0, 0); break;
+ case 33: __asan_set_error_exit_code(0); break;
+ case 34: __asan_before_dynamic_init(0); break;
+ case 35: __asan_after_dynamic_init(); break;
+ case 36: __asan_poison_stack_memory(0, 0); break;
+ case 37: __asan_unpoison_stack_memory(0, 0); break;
+ case 38: __asan_region_is_poisoned(0, 0); break;
+ case 39: __asan_describe_address(0); break;
}
}
@@ -246,9 +297,9 @@ static void InitializeHighMemEnd() {
CHECK_EQ((kHighMemBeg % GetPageSizeCached()), 0);
}
-static void ProtectGap(uptr a, uptr size) {
- void *res = Mprotect(a, size);
- if (a == (uptr)res)
+static void ProtectGap(uptr addr, uptr size) {
+ void *res = MmapNoAccess(addr, size, "shadow gap");
+ if (addr == (uptr)res)
return;
Report("ERROR: Failed to protect the shadow gap. "
"ASan cannot proceed correctly. ABORTING.\n");
@@ -296,9 +347,9 @@ static void PrintAddressSpaceLayout() {
Printf("malloc_context_size=%zu\n",
(uptr)common_flags()->malloc_context_size);
- Printf("SHADOW_SCALE: %zx\n", (uptr)SHADOW_SCALE);
- Printf("SHADOW_GRANULARITY: %zx\n", (uptr)SHADOW_GRANULARITY);
- Printf("SHADOW_OFFSET: %zx\n", (uptr)SHADOW_OFFSET);
+ Printf("SHADOW_SCALE: %d\n", (int)SHADOW_SCALE);
+ Printf("SHADOW_GRANULARITY: %d\n", (int)SHADOW_GRANULARITY);
+ Printf("SHADOW_OFFSET: 0x%zx\n", (uptr)SHADOW_OFFSET);
CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7);
if (kMidMemBeg)
CHECK(kMidShadowBeg > kLowShadowEnd &&
@@ -316,6 +367,11 @@ static void AsanInitInternal() {
// initialization steps look at flags().
InitializeFlags();
+ CacheBinaryName();
+
+ AsanCheckIncompatibleRT();
+ AsanCheckDynamicRTPrereqs();
+
SetCanPoisonMemory(flags()->poison_heap);
SetMallocContextSize(common_flags()->malloc_context_size);
@@ -371,9 +427,9 @@ static void AsanInitInternal() {
if (full_shadow_is_available) {
// mmap the low shadow plus at least one page at the left.
if (kLowShadowBeg)
- ReserveShadowMemoryRange(shadow_start, kLowShadowEnd);
+ ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
// mmap the high shadow.
- ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd);
+ ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
// protect the gap.
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1);
@@ -382,11 +438,11 @@ static void AsanInitInternal() {
MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) {
CHECK(kLowShadowBeg != kLowShadowEnd);
// mmap the low shadow plus at least one page at the left.
- ReserveShadowMemoryRange(shadow_start, kLowShadowEnd);
+ ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
// mmap the mid shadow.
- ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd);
+ ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow");
// mmap the high shadow.
- ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd);
+ ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
// protect the gaps.
ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);
@@ -446,6 +502,10 @@ static void AsanInitInternal() {
}
#endif // CAN_SANITIZE_LEAKS
+#if CAN_SANITIZE_UB
+ __ubsan::InitAsPlugin();
+#endif
+
InitializeSuppressions();
VReport(1, "AddressSanitizer Init done\n");
@@ -459,13 +519,11 @@ void AsanInitFromRtl() {
#if ASAN_DYNAMIC
// Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable
-// (and thus normal initializer from .preinit_array haven't run).
+// (and thus normal initializers from .preinit_array or modules haven't run).
class AsanInitializer {
public: // NOLINT
AsanInitializer() {
- AsanCheckIncompatibleRT();
- AsanCheckDynamicRTPrereqs();
AsanInitFromRtl();
}
};
@@ -517,7 +575,6 @@ void NOINLINE __asan_set_death_callback(void (*callback)(void)) {
// Initialize as requested from instrumented application code.
// We use this call as a trigger to wake up ASan from deactivated state.
void __asan_init() {
- AsanCheckIncompatibleRT();
AsanActivate();
AsanInitInternal();
}
diff --git a/lib/asan/asan_stats.cc b/lib/asan/asan_stats.cc
index a78b7b1e926d..b8c68c32dfbf 100644
--- a/lib/asan/asan_stats.cc
+++ b/lib/asan/asan_stats.cc
@@ -51,12 +51,8 @@ void AsanStats::Print() {
(mmaped-munmaped)>>20, mmaped>>20, munmaped>>20,
mmaps, munmaps);
- PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size);
PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size);
- PrintMallocStatsArray(" frees by size class: ", freed_by_size);
- PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size);
- Printf("Stats: malloc large: %zu small slow: %zu\n",
- malloc_large, malloc_small_slow);
+ Printf("Stats: malloc large: %zu\n", malloc_large);
}
void AsanStats::MergeFrom(const AsanStats *stats) {
@@ -161,8 +157,7 @@ uptr __sanitizer_get_free_bytes() {
GetAccumulatedStats(&stats);
uptr total_free = stats.mmaped
- stats.munmaped
- + stats.really_freed
- + stats.really_freed_redzones;
+ + stats.really_freed;
uptr total_used = stats.malloced
+ stats.malloced_redzones;
// Return sane value if total_free < total_used due to racy
diff --git a/lib/asan/asan_stats.h b/lib/asan/asan_stats.h
index c66848dc5713..4605135e166f 100644
--- a/lib/asan/asan_stats.h
+++ b/lib/asan/asan_stats.h
@@ -32,20 +32,14 @@ struct AsanStats {
uptr freed;
uptr real_frees;
uptr really_freed;
- uptr really_freed_redzones;
uptr reallocs;
uptr realloced;
uptr mmaps;
uptr mmaped;
uptr munmaps;
uptr munmaped;
- uptr mmaped_by_size[kNumberOfSizeClasses];
- uptr malloced_by_size[kNumberOfSizeClasses];
- uptr freed_by_size[kNumberOfSizeClasses];
- uptr really_freed_by_size[kNumberOfSizeClasses];
-
uptr malloc_large;
- uptr malloc_small_slow;
+ uptr malloced_by_size[kNumberOfSizeClasses];
// Ctor for global AsanStats (accumulated stats for dead threads).
explicit AsanStats(LinkerInitialized) { }
diff --git a/lib/asan/asan_suppressions.cc b/lib/asan/asan_suppressions.cc
index 62198aec64e7..41887b5c88b4 100644
--- a/lib/asan/asan_suppressions.cc
+++ b/lib/asan/asan_suppressions.cc
@@ -26,14 +26,28 @@ static SuppressionContext *suppression_ctx = nullptr;
static const char kInterceptorName[] = "interceptor_name";
static const char kInterceptorViaFunction[] = "interceptor_via_fun";
static const char kInterceptorViaLibrary[] = "interceptor_via_lib";
+static const char kODRViolation[] = "odr_violation";
static const char *kSuppressionTypes[] = {
- kInterceptorName, kInterceptorViaFunction, kInterceptorViaLibrary};
+ kInterceptorName, kInterceptorViaFunction, kInterceptorViaLibrary,
+ kODRViolation};
+
+extern "C" {
+#if SANITIZER_SUPPORTS_WEAK_HOOKS
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+const char *__asan_default_suppressions();
+#else
+// No week hooks, provide empty implementation.
+const char *__asan_default_suppressions() { return ""; }
+#endif // SANITIZER_SUPPORTS_WEAK_HOOKS
+} // extern "C"
void InitializeSuppressions() {
CHECK_EQ(nullptr, suppression_ctx);
suppression_ctx = new (suppression_placeholder) // NOLINT
SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
suppression_ctx->ParseFromFile(flags()->suppressions);
+ if (&__asan_default_suppressions)
+ suppression_ctx->Parse(__asan_default_suppressions());
}
bool IsInterceptorSuppressed(const char *interceptor_name) {
@@ -49,6 +63,13 @@ bool HaveStackTraceBasedSuppressions() {
suppression_ctx->HasSuppressionType(kInterceptorViaLibrary);
}
+bool IsODRViolationSuppressed(const char *global_var_name) {
+ CHECK(suppression_ctx);
+ Suppression *s;
+ // Match "odr_violation" suppressions.
+ return suppression_ctx->Match(global_var_name, kODRViolation, &s);
+}
+
bool IsStackTraceSuppressed(const StackTrace *stack) {
if (!HaveStackTraceBasedSuppressions())
return false;
@@ -60,14 +81,10 @@ bool IsStackTraceSuppressed(const StackTrace *stack) {
uptr addr = stack->trace[i];
if (suppression_ctx->HasSuppressionType(kInterceptorViaLibrary)) {
- const char *module_name;
- uptr module_offset;
// Match "interceptor_via_lib" suppressions.
- if (symbolizer->GetModuleNameAndOffsetForPC(addr, &module_name,
- &module_offset) &&
- suppression_ctx->Match(module_name, kInterceptorViaLibrary, &s)) {
- return true;
- }
+ if (const char *module_name = symbolizer->GetModuleNameForPc(addr))
+ if (suppression_ctx->Match(module_name, kInterceptorViaLibrary, &s))
+ return true;
}
if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) {
diff --git a/lib/asan/asan_suppressions.h b/lib/asan/asan_suppressions.h
index cd7ba2ef0ae7..5246b4b30334 100644
--- a/lib/asan/asan_suppressions.h
+++ b/lib/asan/asan_suppressions.h
@@ -23,6 +23,7 @@ void InitializeSuppressions();
bool IsInterceptorSuppressed(const char *interceptor_name);
bool HaveStackTraceBasedSuppressions();
bool IsStackTraceSuppressed(const StackTrace *stack);
+bool IsODRViolationSuppressed(const char *global_var_name);
} // namespace __asan
diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h
index 9da136c87ef1..50acfc42d6a2 100644
--- a/lib/asan/asan_thread.h
+++ b/lib/asan/asan_thread.h
@@ -34,19 +34,16 @@ class AsanThread;
class AsanThreadContext : public ThreadContextBase {
public:
explicit AsanThreadContext(int tid)
- : ThreadContextBase(tid),
- announced(false),
- destructor_iterations(kPthreadDestructorIterations),
- stack_id(0),
- thread(0) {
- }
+ : ThreadContextBase(tid), announced(false),
+ destructor_iterations(GetPthreadDestructorIterations()), stack_id(0),
+ thread(0) {}
bool announced;
u8 destructor_iterations;
u32 stack_id;
AsanThread *thread;
- void OnCreated(void *arg);
- void OnFinished();
+ void OnCreated(void *arg) override;
+ void OnFinished() override;
};
// AsanThreadContext objects are never freed, so we need many of them.
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index 693f0bcdee8e..addb3d40a696 100644
--- a/lib/asan/asan_win.cc
+++ b/lib/asan/asan_win.cc
@@ -22,21 +22,134 @@
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_report.h"
+#include "asan_stack.h"
#include "asan_thread.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_mutex.h"
+using namespace __asan; // NOLINT
+
extern "C" {
- SANITIZER_INTERFACE_ATTRIBUTE
- int __asan_should_detect_stack_use_after_return() {
- __asan_init();
- return __asan_option_detect_stack_use_after_return;
- }
+SANITIZER_INTERFACE_ATTRIBUTE
+int __asan_should_detect_stack_use_after_return() {
+ __asan_init();
+ return __asan_option_detect_stack_use_after_return;
+}
+
+// -------------------- A workaround for the abscence of weak symbols ----- {{{
+// We don't have a direct equivalent of weak symbols when using MSVC, but we can
+// use the /alternatename directive to tell the linker to default a specific
+// symbol to a specific value, which works nicely for allocator hooks and
+// __asan_default_options().
+void __sanitizer_default_malloc_hook(void *ptr, uptr size) { }
+void __sanitizer_default_free_hook(void *ptr) { }
+const char* __asan_default_default_options() { return ""; }
+const char* __asan_default_default_suppressions() { return ""; }
+void __asan_default_on_error() {}
+#pragma comment(linker, "/alternatename:___sanitizer_malloc_hook=___sanitizer_default_malloc_hook") // NOLINT
+#pragma comment(linker, "/alternatename:___sanitizer_free_hook=___sanitizer_default_free_hook") // NOLINT
+#pragma comment(linker, "/alternatename:___asan_default_options=___asan_default_default_options") // NOLINT
+#pragma comment(linker, "/alternatename:___asan_default_suppressions=___asan_default_default_suppressions") // NOLINT
+#pragma comment(linker, "/alternatename:___asan_on_error=___asan_default_on_error") // NOLINT
+// }}}
+} // extern "C"
+
+// ---------------------- Windows-specific inteceptors ---------------- {{{
+INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
+ CHECK(REAL(RaiseException));
+ __asan_handle_no_return();
+ REAL(RaiseException)(a, b, c, d);
+}
+
+INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
+ CHECK(REAL(_except_handler3));
+ __asan_handle_no_return();
+ return REAL(_except_handler3)(a, b, c, d);
+}
+
+#if ASAN_DYNAMIC
+// This handler is named differently in -MT and -MD CRTs.
+#define _except_handler4 _except_handler4_common
+#endif
+INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
+ CHECK(REAL(_except_handler4));
+ __asan_handle_no_return();
+ return REAL(_except_handler4)(a, b, c, d);
+}
+
+static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
+ AsanThread *t = (AsanThread*)arg;
+ SetCurrentThread(t);
+ return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr);
+}
+
+INTERCEPTOR_WINAPI(DWORD, CreateThread,
+ void* security, uptr stack_size,
+ DWORD (__stdcall *start_routine)(void*), void* arg,
+ DWORD thr_flags, void* tid) {
+ // Strict init-order checking is thread-hostile.
+ if (flags()->strict_init_order)
+ StopInitOrderChecking();
+ GET_STACK_TRACE_THREAD;
+ // FIXME: The CreateThread interceptor is not the same as a pthread_create
+ // one. This is a bandaid fix for PR22025.
+ bool detached = false; // FIXME: how can we determine it on Windows?
+ u32 current_tid = GetCurrentTidOrInvalid();
+ AsanThread *t =
+ AsanThread::Create(start_routine, arg, current_tid, &stack, detached);
+ return REAL(CreateThread)(security, stack_size,
+ asan_thread_start, t, thr_flags, tid);
+}
+
+namespace {
+BlockingMutex mu_for_thread_tracking(LINKER_INITIALIZED);
+
+void EnsureWorkerThreadRegistered() {
+ // FIXME: GetCurrentThread relies on TSD, which might not play well with
+ // system thread pools. We might want to use something like reference
+ // counting to zero out GetCurrentThread() underlying storage when the last
+ // work item finishes? Or can we disable reclaiming of threads in the pool?
+ BlockingMutexLock l(&mu_for_thread_tracking);
+ if (__asan::GetCurrentThread())
+ return;
+
+ AsanThread *t = AsanThread::Create(
+ /* start_routine */ nullptr, /* arg */ nullptr,
+ /* parent_tid */ -1, /* stack */ nullptr, /* detached */ true);
+ t->Init();
+ asanThreadRegistry().StartThread(t->tid(), 0, 0);
+ SetCurrentThread(t);
+}
+} // namespace
+
+INTERCEPTOR_WINAPI(DWORD, NtWaitForWorkViaWorkerFactory, DWORD a, DWORD b) {
+ // NtWaitForWorkViaWorkerFactory is called from system worker pool threads to
+ // query work scheduled by BindIoCompletionCallback, QueueUserWorkItem, etc.
+ // System worker pool threads are created at arbitraty point in time and
+ // without using CreateThread, so we wrap NtWaitForWorkViaWorkerFactory
+ // instead and don't register a specific parent_tid/stack.
+ EnsureWorkerThreadRegistered();
+ return REAL(NtWaitForWorkViaWorkerFactory)(a, b);
}
+// }}}
+
namespace __asan {
-// ---------------------- TSD ---------------- {{{1
+void InitializePlatformInterceptors() {
+ ASAN_INTERCEPT_FUNC(CreateThread);
+ ASAN_INTERCEPT_FUNC(RaiseException);
+ ASAN_INTERCEPT_FUNC(_except_handler3);
+ ASAN_INTERCEPT_FUNC(_except_handler4);
+
+ // NtWaitForWorkViaWorkerFactory is always linked dynamically.
+ CHECK(::__interception::OverrideFunction(
+ "NtWaitForWorkViaWorkerFactory",
+ (uptr)WRAP(NtWaitForWorkViaWorkerFactory),
+ (uptr *)&REAL(NtWaitForWorkViaWorkerFactory)));
+}
+
+// ---------------------- TSD ---------------- {{{
static bool tsd_key_inited = false;
static __declspec(thread) void *fake_tsd = 0;
@@ -59,7 +172,9 @@ void AsanTSDSet(void *tsd) {
void PlatformTSDDtor(void *tsd) {
AsanThread::TSDDtor(tsd);
}
-// ---------------------- Various stuff ---------------- {{{1
+// }}}
+
+// ---------------------- Various stuff ---------------- {{{
void DisableReexec() {
// No need to re-exec on Windows.
}
@@ -93,23 +208,6 @@ void AsanOnSIGSEGV(int, void *siginfo, void *context) {
static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;
-SignalContext SignalContext::Create(void *siginfo, void *context) {
- EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD*)siginfo;
- CONTEXT *context_record = (CONTEXT*)context;
-
- uptr pc = (uptr)exception_record->ExceptionAddress;
-#ifdef _WIN64
- uptr bp = (uptr)context_record->Rbp;
- uptr sp = (uptr)context_record->Rsp;
-#else
- uptr bp = (uptr)context_record->Ebp;
- uptr sp = (uptr)context_record->Esp;
-#endif
- uptr access_addr = exception_record->ExceptionInformation[1];
-
- return SignalContext(context, access_addr, pc, sp, bp);
-}
-
static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
CONTEXT *context = info->ContextRecord;
@@ -162,7 +260,7 @@ int __asan_set_seh_filter() {
static __declspec(allocate(".CRT$XIZ"))
int (*__intercept_seh)() = __asan_set_seh_filter;
#endif
-
+// }}}
} // namespace __asan
#endif // _WIN32
diff --git a/lib/asan/asan_win_dll_thunk.cc b/lib/asan/asan_win_dll_thunk.cc
index 5d39e33096a8..b77f18168ae5 100644
--- a/lib/asan/asan_win_dll_thunk.cc
+++ b/lib/asan/asan_win_dll_thunk.cc
@@ -303,8 +303,8 @@ INTERFACE_FUNCTION(__sanitizer_cov_init)
INTERFACE_FUNCTION(__sanitizer_cov_module_init)
INTERFACE_FUNCTION(__sanitizer_cov_trace_basic_block)
INTERFACE_FUNCTION(__sanitizer_cov_trace_func_enter)
+INTERFACE_FUNCTION(__sanitizer_cov_trace_cmp)
INTERFACE_FUNCTION(__sanitizer_cov_with_check)
-INTERFACE_FUNCTION(__sanitizer_free_hook)
INTERFACE_FUNCTION(__sanitizer_get_allocated_size)
INTERFACE_FUNCTION(__sanitizer_get_coverage_guards)
INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes)
@@ -314,13 +314,14 @@ INTERFACE_FUNCTION(__sanitizer_get_heap_size)
INTERFACE_FUNCTION(__sanitizer_get_ownership)
INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage)
INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes)
-INTERFACE_FUNCTION(__sanitizer_malloc_hook)
INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file)
INTERFACE_FUNCTION(__sanitizer_print_stack_trace)
INTERFACE_FUNCTION(__sanitizer_ptr_cmp)
INTERFACE_FUNCTION(__sanitizer_ptr_sub)
INTERFACE_FUNCTION(__sanitizer_report_error_summary)
INTERFACE_FUNCTION(__sanitizer_reset_coverage)
+INTERFACE_FUNCTION(__sanitizer_get_number_of_counters)
+INTERFACE_FUNCTION(__sanitizer_update_counter_bitset_and_clear_counters)
INTERFACE_FUNCTION(__sanitizer_sandbox_on_notify)
INTERFACE_FUNCTION(__sanitizer_set_death_callback)
INTERFACE_FUNCTION(__sanitizer_set_report_path)
@@ -380,11 +381,15 @@ INTERCEPT_LIBRARY_FUNCTION(strcat); // NOLINT
INTERCEPT_LIBRARY_FUNCTION(strchr);
INTERCEPT_LIBRARY_FUNCTION(strcmp);
INTERCEPT_LIBRARY_FUNCTION(strcpy); // NOLINT
+INTERCEPT_LIBRARY_FUNCTION(strcspn);
INTERCEPT_LIBRARY_FUNCTION(strlen);
INTERCEPT_LIBRARY_FUNCTION(strncat);
INTERCEPT_LIBRARY_FUNCTION(strncmp);
INTERCEPT_LIBRARY_FUNCTION(strncpy);
INTERCEPT_LIBRARY_FUNCTION(strnlen);
+INTERCEPT_LIBRARY_FUNCTION(strpbrk);
+INTERCEPT_LIBRARY_FUNCTION(strspn);
+INTERCEPT_LIBRARY_FUNCTION(strstr);
INTERCEPT_LIBRARY_FUNCTION(strtol);
INTERCEPT_LIBRARY_FUNCTION(wcslen);
diff --git a/lib/asan/asan_win_dynamic_runtime_thunk.cc b/lib/asan/asan_win_dynamic_runtime_thunk.cc
index 19456141c1ec..d59f9f5768a0 100644
--- a/lib/asan/asan_win_dynamic_runtime_thunk.cc
+++ b/lib/asan/asan_win_dynamic_runtime_thunk.cc
@@ -15,7 +15,8 @@
//
// This includes:
// - forwarding the detect_stack_use_after_return runtime option
-// - installing a custom SEH handler
+// - working around deficiencies of the MD runtime
+// - installing a custom SEH handlerx
//
//===----------------------------------------------------------------------===//
@@ -24,9 +25,13 @@
// simplifies the build procedure.
#ifdef ASAN_DYNAMIC_RUNTIME_THUNK
#include <windows.h>
-#include <psapi.h>
-extern "C" {
+// First, declare CRT sections we'll be using in this file
+#pragma section(".CRT$XID", long, read) // NOLINT
+#pragma section(".CRT$XIZ", long, read) // NOLINT
+#pragma section(".CRT$XTW", long, read) // NOLINT
+#pragma section(".CRT$XTY", long, read) // NOLINT
+
////////////////////////////////////////////////////////////////////////////////
// Define a copy of __asan_option_detect_stack_use_after_return that should be
// used when linking an MD runtime with a set of object files on Windows.
@@ -38,82 +43,55 @@ extern "C" {
// with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows
// just to work around this issue, let's clone the a variable that is
// constant after initialization anyways.
+extern "C" {
__declspec(dllimport) int __asan_should_detect_stack_use_after_return();
int __asan_option_detect_stack_use_after_return =
__asan_should_detect_stack_use_after_return();
}
////////////////////////////////////////////////////////////////////////////////
-// For some reason, the MD CRT doesn't call the C/C++ terminators as MT does.
-// To work around this, for each DLL we schedule a call to
-// UnregisterGlobalsInRange atexit() specifying the address range of the DLL
-// image to unregister globals in that range. We don't do the same
-// for the main module (.exe) as the asan_globals.cc allocator is destroyed
-// by the time UnregisterGlobalsInRange is executed.
-// See PR22545 for the details.
-namespace __asan {
-__declspec(dllimport)
-void UnregisterGlobalsInRange(void *beg, void *end);
-}
+// For some reason, the MD CRT doesn't call the C/C++ terminators during on DLL
+// unload or on exit. ASan relies on LLVM global_dtors to call
+// __asan_unregister_globals on these events, which unfortunately doesn't work
+// with the MD runtime, see PR22545 for the details.
+// To work around this, for each DLL we schedule a call to UnregisterGlobals
+// using atexit() that calls a small subset of C terminators
+// where LLVM global_dtors is placed. Fingers crossed, no other C terminators
+// are there.
+extern "C" void __cdecl _initterm(void *a, void *b);
namespace {
-void *this_module_base, *this_module_end;
+__declspec(allocate(".CRT$XTW")) void* before_global_dtors = 0;
+__declspec(allocate(".CRT$XTY")) void* after_global_dtors = 0;
void UnregisterGlobals() {
- __asan::UnregisterGlobalsInRange(this_module_base, this_module_end);
+ _initterm(&before_global_dtors, &after_global_dtors);
}
int ScheduleUnregisterGlobals() {
- HMODULE this_module = 0;
- // Increments the reference counter of the DLL module, so need to call
- // FreeLibrary later.
- if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
- (LPCTSTR)&UnregisterGlobals, &this_module))
- return 1;
-
- // Skip the main module.
- if (this_module == GetModuleHandle(0))
- return 0;
-
- MODULEINFO mi;
- bool success =
- GetModuleInformation(GetCurrentProcess(), this_module, &mi, sizeof(mi));
- if (!FreeLibrary(this_module))
- return 2;
- if (!success)
- return 3;
-
- this_module_base = mi.lpBaseOfDll;
- this_module_end = (char*)mi.lpBaseOfDll + mi.SizeOfImage;
-
return atexit(UnregisterGlobals);
}
-} // namespace
-
-///////////////////////////////////////////////////////////////////////////////
-// ASan SEH handling.
-extern "C" __declspec(dllimport) int __asan_set_seh_filter();
-static int SetSEHFilter() { return __asan_set_seh_filter(); }
-///////////////////////////////////////////////////////////////////////////////
-// We schedule some work at start-up by placing callbacks to our code to the
-// list of CRT C initializers.
-//
-// First, declare sections we'll be using:
-#pragma section(".CRT$XID", long, read) // NOLINT
-#pragma section(".CRT$XIZ", long, read) // NOLINT
-
-// We need to call 'atexit(UnregisterGlobals);' after atexit() is initialized
-// (.CRT$XIC) but before the C++ constructors (.CRT$XCA).
+// We need to call 'atexit(UnregisterGlobals);' as early as possible, but after
+// atexit() is initialized (.CRT$XIC). As this is executed before C++
+// initializers (think ctors for globals), UnregisterGlobals gets executed after
+// dtors for C++ globals.
__declspec(allocate(".CRT$XID"))
-static int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals;
+int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals;
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// ASan SEH handling.
// We need to set the ASan-specific SEH handler at the end of CRT initialization
// of each module (see also asan_win.cc).
-//
+extern "C" {
+__declspec(dllimport) int __asan_set_seh_filter();
+static int SetSEHFilter() { return __asan_set_seh_filter(); }
+
// Unfortunately, putting a pointer to __asan_set_seh_filter into
// __asan_intercept_seh gets optimized out, so we have to use an extra function.
-extern "C" __declspec(allocate(".CRT$XIZ"))
-int (*__asan_seh_interceptor)() = SetSEHFilter;
+__declspec(allocate(".CRT$XIZ")) int (*__asan_seh_interceptor)() = SetSEHFilter;
+}
#endif // ASAN_DYNAMIC_RUNTIME_THUNK
diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py
index 59fceaaed814..b9d3ad3ad2fe 100755
--- a/lib/asan/scripts/asan_symbolize.py
+++ b/lib/asan/scripts/asan_symbolize.py
@@ -23,6 +23,7 @@ sysroot_path = None
binary_name_filter = None
fix_filename_patterns = None
logfile = sys.stdin
+allow_system_symbolizer = True
# FIXME: merge the code that calls fix_filename().
def fix_filename(file_name):
@@ -392,6 +393,8 @@ class SymbolizationLoop(object):
[BreakpadSymbolizerFactory(binary), self.llvm_symbolizers[binary]])
result = symbolizers[binary].symbolize(addr, binary, offset)
if result is None:
+ if not allow_system_symbolizer:
+ raise Exception('Failed to launch or use llvm-symbolizer.')
# Initialize system symbolizer only if other symbolizers failed.
symbolizers[binary].append_symbolizer(
SystemSymbolizerFactory(self.system, addr, binary))
diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt
index 513d1282f8a6..afdd2adf0887 100644
--- a/lib/asan/tests/CMakeLists.txt
+++ b/lib/asan/tests/CMakeLists.txt
@@ -52,6 +52,11 @@ if(APPLE)
list(APPEND ASAN_UNITTEST_COMMON_LINKFLAGS ${DARWIN_osx_LINKFLAGS})
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}
@@ -94,6 +99,7 @@ append_list_if(ANDROID atomic ASAN_UNITTEST_INSTRUMENTED_LIBS)
set(ASAN_UNITTEST_NOINST_LINKFLAGS ${ASAN_UNITTEST_COMMON_LINKFLAGS})
append_list_if(COMPILER_RT_HAS_LIBM -lm ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBDL -ldl ASAN_UNITTEST_NOINST_LINKFLAGS)
+append_list_if(COMPILER_RT_HAS_LIBRT -lrt ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread ASAN_UNITTEST_NOINST_LINKFLAGS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread
ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS)
@@ -123,7 +129,7 @@ endmacro()
# Link ASan unit test for a given architecture from a set
# of objects in with given linker flags.
macro(add_asan_test test_suite test_name arch kind)
- parse_arguments(TEST "OBJECTS;LINKFLAGS;SUBDIR" "WITH_TEST_RUNTIME" ${ARGN})
+ cmake_parse_arguments(TEST "WITH_TEST_RUNTIME" "" "OBJECTS;LINKFLAGS;SUBDIR" ${ARGN})
get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS)
set(TEST_DEPS ${TEST_OBJECTS})
if(NOT COMPILER_RT_STANDALONE_BUILD)
@@ -214,18 +220,18 @@ macro(add_asan_tests_for_arch_and_kind arch kind)
$<TARGET_OBJECTS:RTAsan.osx>
$<TARGET_OBJECTS:RTInterception.osx>
$<TARGET_OBJECTS:RTSanitizerCommon.osx>
- $<TARGET_OBJECTS:RTLSanCommon.osx>)
+ $<TARGET_OBJECTS:RTLSanCommon.osx>
+ $<TARGET_OBJECTS:RTUbsan.osx>)
else()
set(ASAN_TEST_RUNTIME_OBJECTS
$<TARGET_OBJECTS:RTAsan.${arch}>
$<TARGET_OBJECTS:RTAsan_cxx.${arch}>
$<TARGET_OBJECTS:RTInterception.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
- $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>)
- if(NOT WIN32)
- list(APPEND ASAN_TEST_RUNTIME_OBJECTS
- $<TARGET_OBJECTS:RTLSanCommon.${arch}>)
- endif()
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
+ $<TARGET_OBJECTS:RTLSanCommon.${arch}>
+ $<TARGET_OBJECTS:RTUbsan.${arch}>
+ $<TARGET_OBJECTS:RTUbsan_cxx.${arch}>)
endif()
add_library(${ASAN_TEST_RUNTIME} STATIC ${ASAN_TEST_RUNTIME_OBJECTS})
set_target_properties(${ASAN_TEST_RUNTIME} PROPERTIES
@@ -270,6 +276,8 @@ if(ANDROID)
$<TARGET_OBJECTS:RTInterception.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
+ $<TARGET_OBJECTS:RTUbsan.${arch}>
+ $<TARGET_OBJECTS:RTUbsan_cxx.${arch}>
${COMPILER_RT_GTEST_SOURCE}
${ASAN_NOINST_TEST_SOURCES})
set_target_compile_flags(AsanNoinstTest ${ASAN_UNITTEST_COMMON_CFLAGS})
diff --git a/lib/asan/tests/asan_asm_test.cc b/lib/asan/tests/asan_asm_test.cc
index 1d8b04d611bd..200de2c137a5 100644
--- a/lib/asan/tests/asan_asm_test.cc
+++ b/lib/asan/tests/asan_asm_test.cc
@@ -232,7 +232,7 @@ TEST(AddressSanitizer, asm_flags) {
long magic = 0x1234;
long r = 0x0;
-#if defined(__x86_64__)
+#if defined(__x86_64__) && !defined(__ILP32__)
__asm__("xorq %%rax, %%rax \n\t"
"movq (%[p]), %%rax \n\t"
"sete %%al \n\t"
@@ -248,7 +248,7 @@ TEST(AddressSanitizer, asm_flags) {
: [r] "=r"(r)
: [p] "r"(&magic)
: "eax", "memory");
-#endif // defined(__x86_64__)
+#endif // defined(__x86_64__) && !defined(__ILP32__)
ASSERT_EQ(0x1, r);
}
diff --git a/lib/asan/tests/asan_str_test.cc b/lib/asan/tests/asan_str_test.cc
index 1cd2a08b9021..89b0d3d2d0f9 100644
--- a/lib/asan/tests/asan_str_test.cc
+++ b/lib/asan/tests/asan_str_test.cc
@@ -290,9 +290,6 @@ void RunStrCmpTest(PointerToStrCmp StrCmp) {
Ident(StrCmp(s1, s2));
Ident(StrCmp(s1, s2 + size - 1));
Ident(StrCmp(s1 + size - 1, s2 + size - 1));
- s1[size - 1] = 'z';
- s2[size - 1] = 'x';
- Ident(StrCmp(s1, s2));
// One of arguments points to not allocated memory.
EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBReadMessage(1));
EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBReadMessage(1));
@@ -371,17 +368,14 @@ TEST(AddressSanitizer, StrCatOOBTest) {
// One of arguments points to not allocated memory.
EXPECT_DEATH(strcat(to - 1, from), LeftOOBAccessMessage(1));
EXPECT_DEATH(strcat(to, from - 1), LeftOOBReadMessage(1));
- EXPECT_DEATH(strcat(to + to_size, from), RightOOBWriteMessage(0));
EXPECT_DEATH(strcat(to, from + from_size), RightOOBReadMessage(0));
// "from" is not zero-terminated.
from[from_size - 1] = 'z';
EXPECT_DEATH(strcat(to, from), RightOOBReadMessage(0));
from[from_size - 1] = '\0';
- // "to" is not zero-terminated.
- memset(to, 'z', to_size);
- EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
// "to" is too short to fit "from".
+ memset(to, 'z', to_size);
to[to_size - from_size + 1] = '\0';
EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
// length of "to" is just enough.
@@ -409,7 +403,6 @@ TEST(AddressSanitizer, StrNCatOOBTest) {
// One of arguments points to not allocated memory.
EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBAccessMessage(1));
EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBReadMessage(1));
- EXPECT_DEATH(strncat(to + to_size, from, 2), RightOOBWriteMessage(0));
EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBReadMessage(0));
memset(from, 'z', from_size);
@@ -417,8 +410,6 @@ TEST(AddressSanitizer, StrNCatOOBTest) {
to[0] = '\0';
// "from" is too short.
EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0));
- // "to" is not zero-terminated.
- EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBWriteMessage(0));
// "to" is too short to fit "from".
to[0] = 'z';
to[to_size - from_size + 1] = '\0';
@@ -508,20 +499,15 @@ void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
EXPECT_DEATH(Atoi(array - 1), LeftOOBReadMessage(1));
// Die if a buffer doesn't have terminating NULL.
EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
- // Make last symbol a terminating NULL or other non-digit.
+ // Make last symbol a terminating NULL
array[9] = '\0';
Atoi(array);
- array[9] = 'a';
- Atoi(array);
- Atoi(array + 9);
// Sometimes we need to detect overflow if no digits are found.
memset(array, ' ', 10);
EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
array[9] = '-';
EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0));
- array[8] = '-';
- Atoi(array);
free(array);
}
@@ -546,7 +532,6 @@ typedef void(*PointerToCallStrtol)(const char*, char**, int);
void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
char *array = MallocAndMemsetString(3);
- char *endptr = NULL;
array[0] = '1';
array[1] = '2';
array[2] = '3';
@@ -554,19 +539,12 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBReadMessage(0));
EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBReadMessage(1));
// Buffer overflow if there is no terminating null (depends on base).
- Strtol(array, &endptr, 3);
- EXPECT_EQ(array + 2, endptr);
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
array[2] = 'z';
- Strtol(array, &endptr, 35);
- EXPECT_EQ(array + 2, endptr);
EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBReadMessage(0));
// Add terminating zero to get rid of overflow.
array[2] = '\0';
Strtol(array, NULL, 36);
- // Don't check for overflow if base is invalid.
- Strtol(array - 1, NULL, -1);
- Strtol(array + 3, NULL, 1);
// Sometimes we need to detect overflow if no digits are found.
array[0] = array[1] = array[2] = ' ';
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
@@ -574,13 +552,6 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
array[2] = '-';
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
- array[1] = '+';
- Strtol(array, NULL, 0);
- array[1] = array[2] = 'z';
- Strtol(array, &endptr, 0);
- EXPECT_EQ(array, endptr);
- Strtol(array + 2, NULL, 0);
- EXPECT_EQ(array, endptr);
free(array);
}
diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc
index 952b05e21fe2..07d59e09a72f 100644
--- a/lib/asan/tests/asan_test.cc
+++ b/lib/asan/tests/asan_test.cc
@@ -31,13 +31,13 @@ NOINLINE void free_aaa(void *p) { free_bbb(p); break_optimization(0);}
template<typename T>
NOINLINE void uaf_test(int size, int off) {
- char *p = (char *)malloc_aaa(size);
+ void *p = malloc_aaa(size);
free_aaa(p);
for (int i = 1; i < 100; i++)
free_aaa(malloc_aaa(i));
fprintf(stderr, "writing %ld byte(s) at %p with offset %d\n",
(long)sizeof(T), p, off);
- asan_write((T*)(p + off));
+ asan_write((T *)((char *)p + off));
}
TEST(AddressSanitizer, HasFeatureAddressSanitizerTest) {
@@ -436,10 +436,10 @@ TEST(AddressSanitizer, WrongFreeTest) {
void DoubleFree() {
int *x = (int*)malloc(100 * sizeof(int));
- fprintf(stderr, "DoubleFree: x=%p\n", x);
+ fprintf(stderr, "DoubleFree: x=%p\n", (void *)x);
free(x);
free(x);
- fprintf(stderr, "should have failed in the second free(%p)\n", x);
+ fprintf(stderr, "should have failed in the second free(%p)\n", (void *)x);
abort();
}
@@ -569,7 +569,7 @@ TEST(AddressSanitizer, LongJmpTest) {
}
#if !defined(_WIN32) // Only basic longjmp is available on Windows.
-NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) {
+NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) {
// create three red zones for these two stack objects.
int a;
int b;
@@ -577,10 +577,10 @@ NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) {
int *A = Ident(&a);
int *B = Ident(&b);
*A = *B;
- __builtin_longjmp((void**)buf, 1);
+ _longjmp(buf, 1);
}
-NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) {
+NOINLINE void SigLongJmpFunc1(sigjmp_buf buf) {
// create three red zones for these two stack objects.
int a;
int b;
@@ -588,10 +588,14 @@ NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) {
int *A = Ident(&a);
int *B = Ident(&b);
*A = *B;
- _longjmp(buf, 1);
+ siglongjmp(buf, 1);
}
-NOINLINE void SigLongJmpFunc1(sigjmp_buf buf) {
+#if !defined(__ANDROID__) && !defined(__arm__) && \
+ !defined(__powerpc64__) && !defined(__powerpc__) && \
+ !defined(__aarch64__) && !defined(__mips__) && \
+ !defined(__mips64)
+NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) {
// create three red zones for these two stack objects.
int a;
int b;
@@ -599,12 +603,9 @@ NOINLINE void SigLongJmpFunc1(sigjmp_buf buf) {
int *A = Ident(&a);
int *B = Ident(&b);
*A = *B;
- siglongjmp(buf, 1);
+ __builtin_longjmp((void**)buf, 1);
}
-#if !defined(__ANDROID__) && !defined(__arm__) && \
- !defined(__powerpc64__) && !defined(__powerpc__) && \
- !defined(__aarch64__)
// Does not work on Power and ARM:
// https://code.google.com/p/address-sanitizer/issues/detail?id=185
TEST(AddressSanitizer, BuiltinLongJmpTest) {
@@ -616,7 +617,8 @@ TEST(AddressSanitizer, BuiltinLongJmpTest) {
}
}
#endif // !defined(__ANDROID__) && !defined(__powerpc64__) &&
- // !defined(__powerpc__) && !defined(__arm__)
+ // !defined(__powerpc__) && !defined(__arm__) &&
+ // !defined(__mips__) && !defined(__mips64)
TEST(AddressSanitizer, UnderscopeLongJmpTest) {
static jmp_buf buf;
@@ -1243,7 +1245,7 @@ TEST(AddressSanitizer, DISABLED_DemoTooMuchMemoryTest) {
const size_t kAllocSize = (1 << 28) - 1024;
size_t total_size = 0;
while (true) {
- char *x = (char*)malloc(kAllocSize);
+ void *x = malloc(kAllocSize);
memset(x, 0, kAllocSize);
total_size += kAllocSize;
fprintf(stderr, "total: %ldM %p\n", (long)total_size >> 20, x);
diff --git a/lib/builtins/CMakeLists.txt b/lib/builtins/CMakeLists.txt
index 4d102c615003..98d518a83e2c 100644
--- a/lib/builtins/CMakeLists.txt
+++ b/lib/builtins/CMakeLists.txt
@@ -44,6 +44,7 @@ set(GENERIC_SOURCES
enable_execute_stack.c
eprintf.c
extendsfdf2.c
+ extendhfsf2.c
ffsdi2.c
ffsti2.c
fixdfdi.c
@@ -123,7 +124,9 @@ set(GENERIC_SOURCES
subvti3.c
subtf3.c
trampoline_setup.c
+ truncdfhf2.c
truncdfsf2.c
+ truncsfhf2.c
ucmpdi2.c
ucmpti2.c
udivdi3.c
@@ -151,6 +154,12 @@ set(x86_64_SOURCES
x86_64/floatundixf.S
${GENERIC_SOURCES})
+if(WIN32)
+ set(x86_64_SOURCES
+ ${x86_64_SOURCES}
+ x86_64/chkstk.S)
+endif()
+
set(i386_SOURCES
i386/ashldi3.S
i386/ashrdi3.S
@@ -168,6 +177,12 @@ set(i386_SOURCES
i386/umoddi3.S
${GENERIC_SOURCES})
+if(WIN32)
+ set(i386_SOURCES
+ ${i386_SOURCES}
+ i386/chkstk.S)
+endif()
+
set(i686_SOURCES
${i386_SOURCES})
@@ -257,7 +272,7 @@ set(arm_SOURCES
add_custom_target(builtins)
-if (NOT WIN32)
+if (NOT WIN32 OR MINGW)
foreach (arch x86_64 i386 i686 arm)
if (CAN_TARGET_${arch})
# Filter out generic versions of routines that are re-implemented in
diff --git a/lib/builtins/atomic_flag_clear.c b/lib/builtins/atomic_flag_clear.c
new file mode 100644
index 000000000000..15984cd5267d
--- /dev/null
+++ b/lib/builtins/atomic_flag_clear.c
@@ -0,0 +1,19 @@
+/*===-- atomic_flag_clear.c -------------------------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ *===------------------------------------------------------------------------===
+ *
+ * This file implements atomic_flag_clear from C11's stdatomic.h.
+ *
+ *===------------------------------------------------------------------------===
+ */
+
+#include <stdatomic.h>
+#undef atomic_flag_clear
+void atomic_flag_clear(volatile atomic_flag *object) {
+ return __c11_atomic_store(&(object)->_Value, 0, __ATOMIC_SEQ_CST);
+}
diff --git a/lib/builtins/atomic_flag_clear_explicit.c b/lib/builtins/atomic_flag_clear_explicit.c
new file mode 100644
index 000000000000..0f7859c2cd82
--- /dev/null
+++ b/lib/builtins/atomic_flag_clear_explicit.c
@@ -0,0 +1,20 @@
+/*===-- atomic_flag_clear_explicit.c ----------------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ *===------------------------------------------------------------------------===
+ *
+ * This file implements atomic_flag_clear_explicit from C11's stdatomic.h.
+ *
+ *===------------------------------------------------------------------------===
+ */
+
+#include <stdatomic.h>
+#undef atomic_flag_clear_explicit
+void atomic_flag_clear_explicit(volatile atomic_flag *object,
+ memory_order order) {
+ return __c11_atomic_store(&(object)->_Value, 0, order);
+}
diff --git a/lib/builtins/atomic_flag_test_and_set.c b/lib/builtins/atomic_flag_test_and_set.c
new file mode 100644
index 000000000000..07209fc02d5e
--- /dev/null
+++ b/lib/builtins/atomic_flag_test_and_set.c
@@ -0,0 +1,19 @@
+/*===-- atomic_flag_test_and_set.c ------------------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ *===------------------------------------------------------------------------===
+ *
+ * This file implements atomic_flag_test_and_set from C11's stdatomic.h.
+ *
+ *===------------------------------------------------------------------------===
+ */
+
+#include <stdatomic.h>
+#undef atomic_flag_test_and_set
+_Bool atomic_flag_test_and_set(volatile atomic_flag *object) {
+ return __c11_atomic_exchange(&(object)->_Value, 1, __ATOMIC_SEQ_CST);
+}
diff --git a/lib/builtins/atomic_flag_test_and_set_explicit.c b/lib/builtins/atomic_flag_test_and_set_explicit.c
new file mode 100644
index 000000000000..eaa5be08df46
--- /dev/null
+++ b/lib/builtins/atomic_flag_test_and_set_explicit.c
@@ -0,0 +1,20 @@
+/*===-- atomic_flag_test_and_set_explicit.c ---------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ *===------------------------------------------------------------------------===
+ *
+ * This file implements atomic_flag_test_and_set_explicit from C11's stdatomic.h
+ *
+ *===------------------------------------------------------------------------===
+ */
+
+#include <stdatomic.h>
+#undef atomic_flag_test_and_set_explicit
+_Bool atomic_flag_test_and_set_explicit(volatile atomic_flag *object,
+ memory_order order) {
+ return __c11_atomic_exchange(&(object)->_Value, 1, order);
+}
diff --git a/lib/builtins/atomic_signal_fence.c b/lib/builtins/atomic_signal_fence.c
new file mode 100644
index 000000000000..ad292d2f1c72
--- /dev/null
+++ b/lib/builtins/atomic_signal_fence.c
@@ -0,0 +1,19 @@
+/*===-- atomic_signal_fence.c -----------------------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ *===------------------------------------------------------------------------===
+ *
+ * This file implements atomic_signal_fence from C11's stdatomic.h.
+ *
+ *===------------------------------------------------------------------------===
+ */
+
+#include <stdatomic.h>
+#undef atomic_signal_fence
+void atomic_signal_fence(memory_order order) {
+ __c11_atomic_signal_fence(order);
+}
diff --git a/lib/builtins/atomic_thread_fence.c b/lib/builtins/atomic_thread_fence.c
new file mode 100644
index 000000000000..71f698c9de75
--- /dev/null
+++ b/lib/builtins/atomic_thread_fence.c
@@ -0,0 +1,19 @@
+/*===-- atomic_thread_fence.c -----------------------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ *===------------------------------------------------------------------------===
+ *
+ * This file implements atomic_thread_fence from C11's stdatomic.h.
+ *
+ *===------------------------------------------------------------------------===
+ */
+
+#include <stdatomic.h>
+#undef atomic_thread_fence
+void atomic_thread_fence(memory_order order) {
+ __c11_atomic_thread_fence(order);
+}
diff --git a/lib/builtins/clear_cache.c b/lib/builtins/clear_cache.c
index 8dc0fb1c5907..ede7659a051d 100644
--- a/lib/builtins/clear_cache.c
+++ b/lib/builtins/clear_cache.c
@@ -9,11 +9,12 @@
*/
#include "int_lib.h"
+#include <stddef.h>
#if __APPLE__
#include <libkern/OSCacheControl.h>
#endif
-#if defined(__FreeBSD__) && defined(__arm__)
+#if (defined(__FreeBSD__) || defined(__Bitrig__)) && defined(__arm__)
#include <sys/types.h>
#include <machine/sysarch.h>
#endif
@@ -25,6 +26,7 @@
#if defined(__mips__)
#include <sys/cachectl.h>
#include <sys/syscall.h>
+ #include <unistd.h>
#if defined(__ANDROID__) && defined(__LP64__)
/*
* clear_mips_cache - Invalidates instruction cache for Mips.
@@ -89,7 +91,7 @@ void __clear_cache(void *start, void *end) {
* so there is nothing to do
*/
#elif defined(__arm__) && !defined(__APPLE__)
- #if defined(__FreeBSD__) || defined(__NetBSD__)
+ #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__Bitrig__)
struct arm_sync_icache_args arg;
arg.addr = (uintptr_t)start;
@@ -126,6 +128,7 @@ void __clear_cache(void *start, void *end) {
#elif defined(__aarch64__) && !defined(__APPLE__)
uint64_t xstart = (uint64_t)(uintptr_t) start;
uint64_t xend = (uint64_t)(uintptr_t) end;
+ uint64_t addr;
// Get Cache Type Info
uint64_t ctr_el0;
@@ -136,12 +139,12 @@ void __clear_cache(void *start, void *end) {
* uintptr_t in case this runs in an IPL32 environment.
*/
const size_t dcache_line_size = 4 << ((ctr_el0 >> 16) & 15);
- for (uint64_t addr = xstart; addr < xend; addr += dcache_line_size)
+ for (addr = xstart; addr < xend; addr += dcache_line_size)
__asm __volatile("dc cvau, %0" :: "r"(addr));
__asm __volatile("dsb ish");
const size_t icache_line_size = 4 << ((ctr_el0 >> 0) & 15);
- for (uint64_t addr = xstart; addr < xend; addr += icache_line_size)
+ for (addr = xstart; addr < xend; addr += icache_line_size)
__asm __volatile("ic ivau, %0" :: "r"(addr));
__asm __volatile("isb sy");
#else
diff --git a/lib/builtins/enable_execute_stack.c b/lib/builtins/enable_execute_stack.c
index 63d836b31d17..23e494051adf 100644
--- a/lib/builtins/enable_execute_stack.c
+++ b/lib/builtins/enable_execute_stack.c
@@ -10,7 +10,9 @@
#include "int_lib.h"
+#ifndef _WIN32
#include <sys/mman.h>
+#endif
/* #include "config.h"
* FIXME: CMake - include when cmake system is ready.
@@ -18,9 +20,14 @@
*/
#define HAVE_SYSCONF 1
+#ifdef _WIN32
+#include <windef.h>
+#include <winbase.h>
+#else
#ifndef __APPLE__
#include <unistd.h>
#endif /* __APPLE__ */
+#endif /* _WIN32 */
#if __LP64__
#define TRAMPOLINE_SIZE 48
@@ -40,6 +47,12 @@ COMPILER_RT_ABI void
__enable_execute_stack(void* addr)
{
+#if _WIN32
+ MEMORY_BASIC_INFORMATION mbi;
+ if (!VirtualQuery (addr, &mbi, sizeof(mbi)))
+ return; /* We should probably assert here because there is no return value */
+ VirtualProtect (mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
+#else
#if __APPLE__
/* On Darwin, pagesize is always 4096 bytes */
const uintptr_t pageSize = 4096;
@@ -55,4 +68,5 @@ __enable_execute_stack(void* addr)
unsigned char* endPage = (unsigned char*)((p+TRAMPOLINE_SIZE+pageSize) & pageAlignMask);
size_t length = endPage - startPage;
(void) mprotect((void *)startPage, length, PROT_READ | PROT_WRITE | PROT_EXEC);
+#endif
}
diff --git a/lib/builtins/extendhfsf2.c b/lib/builtins/extendhfsf2.c
new file mode 100644
index 000000000000..7524e2ea7ed6
--- /dev/null
+++ b/lib/builtins/extendhfsf2.c
@@ -0,0 +1,23 @@
+//===-- lib/extendhfsf2.c - half -> single conversion -------------*- C -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+
+#define SRC_HALF
+#define DST_SINGLE
+#include "fp_extend_impl.inc"
+
+// Use a forwarding definition and noinline to implement a poor man's alias,
+// as there isn't a good cross-platform way of defining one.
+COMPILER_RT_ABI __attribute__((noinline)) float __extendhfsf2(uint16_t a) {
+ return __extendXfYf2__(a);
+}
+
+COMPILER_RT_ABI float __gnu_h2f_ieee(uint16_t a) {
+ return __extendhfsf2(a);
+}
diff --git a/lib/builtins/fixdfdi.c b/lib/builtins/fixdfdi.c
index 86f9f6ce8db3..14283ef42e61 100644
--- a/lib/builtins/fixdfdi.c
+++ b/lib/builtins/fixdfdi.c
@@ -6,40 +6,41 @@
* Source Licenses. See LICENSE.TXT for details.
*
* ===----------------------------------------------------------------------===
- *
- * This file implements __fixdfdi for the compiler_rt library.
- *
- * ===----------------------------------------------------------------------===
*/
-#include "int_lib.h"
-
-/* Returns: convert a to a signed long long, rounding toward zero. */
+#define DOUBLE_PRECISION
+#include "fp_lib.h"
+ARM_EABI_FNALIAS(d2lz, fixdfdi)
-/* Assumption: double is a IEEE 64 bit floating point type
- * su_int is a 32 bit integral type
- * value in double is representable in di_int (no range checking performed)
+#ifndef __SOFT_FP__
+/* Support for systems that have hardware floating-point; can set the invalid
+ * flag as a side-effect of computation.
*/
-/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */
-
-ARM_EABI_FNALIAS(d2lz, fixdfdi)
+COMPILER_RT_ABI du_int __fixunsdfdi(double a);
COMPILER_RT_ABI di_int
__fixdfdi(double a)
{
- double_bits fb;
- fb.f = a;
- int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023;
- if (e < 0)
- return 0;
- di_int s = (si_int)(fb.u.s.high & 0x80000000) >> 31;
- dwords r;
- r.s.high = (fb.u.s.high & 0x000FFFFF) | 0x00100000;
- r.s.low = fb.u.s.low;
- if (e > 52)
- r.all <<= (e - 52);
- else
- r.all >>= (52 - e);
- return (r.all ^ s) - s;
-}
+ if (a < 0.0) {
+ return -__fixunsdfdi(-a);
+ }
+ return __fixunsdfdi(a);
+}
+
+#else
+/* Support for systems that don't have hardware floating-point; there are no
+ * flags to set, and we don't want to code-gen to an unknown soft-float
+ * implementation.
+ */
+
+typedef di_int fixint_t;
+typedef du_int fixuint_t;
+#include "fp_fixint_impl.inc"
+
+COMPILER_RT_ABI di_int
+__fixdfdi(fp_t a) {
+ return __fixint(a);
+}
+
+#endif
diff --git a/lib/builtins/fixdfsi.c b/lib/builtins/fixdfsi.c
index 88b2ff5e74a0..704e65bc43a1 100644
--- a/lib/builtins/fixdfsi.c
+++ b/lib/builtins/fixdfsi.c
@@ -1,50 +1,22 @@
-//===-- lib/fixdfsi.c - Double-precision -> integer conversion ----*- C -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is dual licensed under the MIT and the University of Illinois Open
-// Source Licenses. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements double-precision to integer conversion for the
-// compiler-rt library. No range checking is performed; the behavior of this
-// conversion is undefined for out of range values in the C standard.
-//
-//===----------------------------------------------------------------------===//
+/* ===-- fixdfsi.c - Implement __fixdfsi -----------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ */
#define DOUBLE_PRECISION
#include "fp_lib.h"
-
-#include "int_lib.h"
+typedef si_int fixint_t;
+typedef su_int fixuint_t;
+#include "fp_fixint_impl.inc"
ARM_EABI_FNALIAS(d2iz, fixdfsi)
-COMPILER_RT_ABI int
+COMPILER_RT_ABI si_int
__fixdfsi(fp_t a) {
-
- // Break a into sign, exponent, significand
- const rep_t aRep = toRep(a);
- const rep_t aAbs = aRep & absMask;
- const int sign = aRep & signBit ? -1 : 1;
- const int exponent = (aAbs >> significandBits) - exponentBias;
- const rep_t significand = (aAbs & significandMask) | implicitBit;
-
- // If 0 < exponent < significandBits, right shift to get the result.
- if ((unsigned int)exponent < significandBits) {
- return sign * (significand >> (significandBits - exponent));
- }
-
- // If exponent is negative, the result is zero.
- else if (exponent < 0) {
- return 0;
- }
-
- // If significandBits < exponent, left shift to get the result. This shift
- // may end up being larger than the type width, which incurs undefined
- // behavior, but the conversion itself is undefined in that case, so
- // whatever the compiler decides to do is fine.
- else {
- return sign * (significand << (exponent - significandBits));
- }
+ return __fixint(a);
}
diff --git a/lib/builtins/fixdfti.c b/lib/builtins/fixdfti.c
index 2c27f4bae3b9..aaf225e74f86 100644
--- a/lib/builtins/fixdfti.c
+++ b/lib/builtins/fixdfti.c
@@ -6,40 +6,21 @@
* Source Licenses. See LICENSE.TXT for details.
*
* ===----------------------------------------------------------------------===
- *
- * This file implements __fixdfti for the compiler_rt library.
- *
- * ===----------------------------------------------------------------------===
*/
#include "int_lib.h"
#ifdef CRT_HAS_128BIT
+#define DOUBLE_PRECISION
+#include "fp_lib.h"
-/* Returns: convert a to a signed long long, rounding toward zero. */
-
-/* Assumption: double is a IEEE 64 bit floating point type
- * su_int is a 32 bit integral type
- * value in double is representable in ti_int (no range checking performed)
- */
-
-/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */
+typedef ti_int fixint_t;
+typedef tu_int fixuint_t;
+#include "fp_fixint_impl.inc"
COMPILER_RT_ABI ti_int
-__fixdfti(double a)
-{
- double_bits fb;
- fb.f = a;
- int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023;
- if (e < 0)
- return 0;
- ti_int s = (si_int)(fb.u.s.high & 0x80000000) >> 31;
- ti_int r = 0x0010000000000000uLL | (0x000FFFFFFFFFFFFFuLL & fb.u.all);
- if (e > 52)
- r <<= (e - 52);
- else
- r >>= (52 - e);
- return (r ^ s) - s;
+__fixdfti(fp_t a) {
+ return __fixint(a);
}
#endif /* CRT_HAS_128BIT */
diff --git a/lib/builtins/fixsfdi.c b/lib/builtins/fixsfdi.c
index 4f6cfdd7a5c6..fab47e272a25 100644
--- a/lib/builtins/fixsfdi.c
+++ b/lib/builtins/fixsfdi.c
@@ -1,43 +1,47 @@
/* ===-- fixsfdi.c - Implement __fixsfdi -----------------------------------===
*
- * The LLVM Compiler Infrastructure
+ * The LLVM Compiler Infrastructure
*
* This file is dual licensed under the MIT and the University of Illinois Open
* Source Licenses. See LICENSE.TXT for details.
*
* ===----------------------------------------------------------------------===
- *
- * This file implements __fixsfdi for the compiler_rt library.
- *
- * ===----------------------------------------------------------------------===
*/
-#include "int_lib.h"
+#define SINGLE_PRECISION
+#include "fp_lib.h"
-/* Returns: convert a to a signed long long, rounding toward zero. */
+ARM_EABI_FNALIAS(f2lz, fixsfdi)
-/* Assumption: float is a IEEE 32 bit floating point type
- * su_int is a 32 bit integral type
- * value in float is representable in di_int (no range checking performed)
+#ifndef __SOFT_FP__
+/* Support for systems that have hardware floating-point; can set the invalid
+ * flag as a side-effect of computation.
*/
-/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */
-
-ARM_EABI_FNALIAS(f2lz, fixsfdi)
+COMPILER_RT_ABI du_int __fixunssfdi(float a);
COMPILER_RT_ABI di_int
__fixsfdi(float a)
{
- float_bits fb;
- fb.f = a;
- int e = ((fb.u & 0x7F800000) >> 23) - 127;
- if (e < 0)
- return 0;
- di_int s = (si_int)(fb.u & 0x80000000) >> 31;
- di_int r = (fb.u & 0x007FFFFF) | 0x00800000;
- if (e > 23)
- r <<= (e - 23);
- else
- r >>= (23 - e);
- return (r ^ s) - s;
+ if (a < 0.0f) {
+ return -__fixunssfdi(-a);
+ }
+ return __fixunssfdi(a);
}
+
+#else
+/* Support for systems that don't have hardware floating-point; there are no
+ * flags to set, and we don't want to code-gen to an unknown soft-float
+ * implementation.
+ */
+
+typedef di_int fixint_t;
+typedef du_int fixuint_t;
+#include "fp_fixint_impl.inc"
+
+COMPILER_RT_ABI di_int
+__fixsfdi(fp_t a) {
+ return __fixint(a);
+}
+
+#endif
diff --git a/lib/builtins/fixsfsi.c b/lib/builtins/fixsfsi.c
index e3cc42d5255a..f045536d6857 100644
--- a/lib/builtins/fixsfsi.c
+++ b/lib/builtins/fixsfsi.c
@@ -1,47 +1,22 @@
-//===-- lib/fixsfsi.c - Single-precision -> integer conversion ----*- C -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is dual licensed under the MIT and the University of Illinois Open
-// Source Licenses. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file implements single-precision to integer conversion for the
-// compiler-rt library. No range checking is performed; the behavior of this
-// conversion is undefined for out of range values in the C standard.
-//
-//===----------------------------------------------------------------------===//
+/* ===-- fixsfsi.c - Implement __fixsfsi -----------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ */
#define SINGLE_PRECISION
#include "fp_lib.h"
+typedef si_int fixint_t;
+typedef su_int fixuint_t;
+#include "fp_fixint_impl.inc"
ARM_EABI_FNALIAS(f2iz, fixsfsi)
-COMPILER_RT_ABI int
+COMPILER_RT_ABI si_int
__fixsfsi(fp_t a) {
- // Break a into sign, exponent, significand
- const rep_t aRep = toRep(a);
- const rep_t aAbs = aRep & absMask;
- const int sign = aRep & signBit ? -1 : 1;
- const int exponent = (aAbs >> significandBits) - exponentBias;
- const rep_t significand = (aAbs & significandMask) | implicitBit;
-
- // If 0 < exponent < significandBits, right shift to get the result.
- if ((unsigned int)exponent < significandBits) {
- return sign * (significand >> (significandBits - exponent));
- }
-
- // If exponent is negative, the result is zero.
- else if (exponent < 0) {
- return 0;
- }
-
- // If significandBits < exponent, left shift to get the result. This shift
- // may end up being larger than the type width, which incurs undefined
- // behavior, but the conversion itself is undefined in that case, so
- // whatever the compiler decides to do is fine.
- else {
- return sign * (significand << (exponent - significandBits));
- }
+ return __fixint(a);
}
diff --git a/lib/builtins/fixsfti.c b/lib/builtins/fixsfti.c
index 6a1a1c6d5465..3a159b3e18e4 100644
--- a/lib/builtins/fixsfti.c
+++ b/lib/builtins/fixsfti.c
@@ -6,40 +6,21 @@
* Source Licenses. See LICENSE.TXT for details.
*
* ===----------------------------------------------------------------------===
- *
- * This file implements __fixsfti for the compiler_rt library.
- *
- * ===----------------------------------------------------------------------===
*/
#include "int_lib.h"
#ifdef CRT_HAS_128BIT
+#define SINGLE_PRECISION
+#include "fp_lib.h"
-/* Returns: convert a to a signed long long, rounding toward zero. */
-
-/* Assumption: float is a IEEE 32 bit floating point type
- * su_int is a 32 bit integral type
- * value in float is representable in ti_int (no range checking performed)
- */
-
-/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */
+typedef ti_int fixint_t;
+typedef tu_int fixuint_t;
+#include "fp_fixint_impl.inc"
COMPILER_RT_ABI ti_int
-__fixsfti(float a)
-{
- float_bits fb;
- fb.f = a;
- int e = ((fb.u & 0x7F800000) >> 23) - 127;
- if (e < 0)
- return 0;
- ti_int s = (si_int)(fb.u & 0x80000000) >> 31;
- ti_int r = (fb.u & 0x007FFFFF) | 0x00800000;
- if (e > 23)
- r <<= (e - 23);
- else
- r >>= (23 - e);
- return (r ^ s) - s;
+__fixsfti(fp_t a) {
+ return __fixint(a);
}
#endif /* CRT_HAS_128BIT */
diff --git a/lib/builtins/fixtfdi.c b/lib/builtins/fixtfdi.c
new file mode 100644
index 000000000000..bc9dea1f4f81
--- /dev/null
+++ b/lib/builtins/fixtfdi.c
@@ -0,0 +1,23 @@
+/* ===-- fixtfdi.c - Implement __fixtfdi -----------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#define QUAD_PRECISION
+#include "fp_lib.h"
+
+#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT)
+typedef di_int fixint_t;
+typedef du_int fixuint_t;
+#include "fp_fixint_impl.inc"
+
+COMPILER_RT_ABI di_int
+__fixtfdi(fp_t a) {
+ return __fixint(a);
+}
+#endif
diff --git a/lib/builtins/fixtfsi.c b/lib/builtins/fixtfsi.c
new file mode 100644
index 000000000000..feb3de885090
--- /dev/null
+++ b/lib/builtins/fixtfsi.c
@@ -0,0 +1,23 @@
+/* ===-- fixtfsi.c - Implement __fixtfsi -----------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#define QUAD_PRECISION
+#include "fp_lib.h"
+
+#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT)
+typedef si_int fixint_t;
+typedef su_int fixuint_t;
+#include "fp_fixint_impl.inc"
+
+COMPILER_RT_ABI si_int
+__fixtfsi(fp_t a) {
+ return __fixint(a);
+}
+#endif
diff --git a/lib/builtins/fixtfti.c b/lib/builtins/fixtfti.c
new file mode 100644
index 000000000000..ee4ada85cb4a
--- /dev/null
+++ b/lib/builtins/fixtfti.c
@@ -0,0 +1,23 @@
+/* ===-- fixtfti.c - Implement __fixtfti -----------------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#define QUAD_PRECISION
+#include "fp_lib.h"
+
+#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT)
+typedef ti_int fixint_t;
+typedef tu_int fixuint_t;
+#include "fp_fixint_impl.inc"
+
+COMPILER_RT_ABI ti_int
+__fixtfti(fp_t a) {
+ return __fixint(a);
+}
+#endif
diff --git a/lib/builtins/fixunsdfdi.c b/lib/builtins/fixunsdfdi.c
index 9e6371390d5c..2e0d87eacf05 100644
--- a/lib/builtins/fixunsdfdi.c
+++ b/lib/builtins/fixunsdfdi.c
@@ -6,42 +6,39 @@
* Source Licenses. See LICENSE.TXT for details.
*
* ===----------------------------------------------------------------------===
- *
- * This file implements __fixunsdfdi for the compiler_rt library.
- *
- * ===----------------------------------------------------------------------===
*/
-#include "int_lib.h"
+#define DOUBLE_PRECISION
+#include "fp_lib.h"
-/* Returns: convert a to a unsigned long long, rounding toward zero.
- * Negative values all become zero.
- */
+ARM_EABI_FNALIAS(d2ulz, fixunsdfdi)
-/* Assumption: double is a IEEE 64 bit floating point type
- * du_int is a 64 bit integral type
- * value in double is representable in du_int or is negative
- * (no range checking performed)
+#ifndef __SOFT_FP__
+/* Support for systems that have hardware floating-point; can set the invalid
+ * flag as a side-effect of computation.
*/
-/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */
-
-ARM_EABI_FNALIAS(d2ulz, fixunsdfdi)
-
COMPILER_RT_ABI du_int
__fixunsdfdi(double a)
{
- double_bits fb;
- fb.f = a;
- int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023;
- if (e < 0 || (fb.u.s.high & 0x80000000))
- return 0;
- udwords r;
- r.s.high = (fb.u.s.high & 0x000FFFFF) | 0x00100000;
- r.s.low = fb.u.s.low;
- if (e > 52)
- r.all <<= (e - 52);
- else
- r.all >>= (52 - e);
- return r.all;
+ if (a <= 0.0) return 0;
+ su_int high = a/0x1p32f;
+ su_int low = a - (double)high*0x1p32f;
+ return ((du_int)high << 32) | low;
}
+
+#else
+/* Support for systems that don't have hardware floating-point; there are no
+ * flags to set, and we don't want to code-gen to an unknown soft-float
+ * implementation.
+ */
+
+typedef du_int fixuint_t;
+#include "fp_fixuint_impl.inc"
+
+COMPILER_RT_ABI du_int
+__fixunsdfdi(fp_t a) {
+ return __fixuint(a);
+}
+
+#endif
diff --git a/lib/builtins/fixunsdfsi.c b/lib/builtins/fixunsdfsi.c
index c6a3c755e90f..232d342d77da 100644
--- a/lib/builtins/fixunsdfsi.c
+++ b/lib/builtins/fixunsdfsi.c
@@ -6,39 +6,16 @@
* Source Licenses. See LICENSE.TXT for details.
*
* ===----------------------------------------------------------------------===
- *
- * This file implements __fixunsdfsi for the compiler_rt library.
- *
- * ===----------------------------------------------------------------------===
- */
-
-#include "int_lib.h"
-
-/* Returns: convert a to a unsigned int, rounding toward zero.
- * Negative values all become zero.
- */
-
-/* Assumption: double is a IEEE 64 bit floating point type
- * su_int is a 32 bit integral type
- * value in double is representable in su_int or is negative
- * (no range checking performed)
*/
-/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */
+#define DOUBLE_PRECISION
+#include "fp_lib.h"
+typedef su_int fixuint_t;
+#include "fp_fixuint_impl.inc"
ARM_EABI_FNALIAS(d2uiz, fixunsdfsi)
COMPILER_RT_ABI su_int
-__fixunsdfsi(double a)
-{
- double_bits fb;
- fb.f = a;
- int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023;
- if (e < 0 || (fb.u.s.high & 0x80000000))
- return 0;
- return (
- 0x80000000u |
- ((fb.u.s.high & 0x000FFFFF) << 11) |
- (fb.u.s.low >> 21)
- ) >> (31 - e);
+__fixunsdfsi(fp_t a) {
+ return __fixuint(a);
}
diff --git a/lib/builtins/fixunsdfti.c b/lib/builtins/fixunsdfti.c
index cc6c84ff5cb5..f8046a02632b 100644
--- a/lib/builtins/fixunsdfti.c
+++ b/lib/builtins/fixunsdfti.c
@@ -6,42 +6,18 @@
* Source Licenses. See LICENSE.TXT for details.
*
* ===----------------------------------------------------------------------===
- *
- * This file implements __fixunsdfti for the compiler_rt library.
- *
- * ===----------------------------------------------------------------------===
*/
#include "int_lib.h"
#ifdef CRT_HAS_128BIT
-
-/* Returns: convert a to a unsigned long long, rounding toward zero.
- * Negative values all become zero.
- */
-
-/* Assumption: double is a IEEE 64 bit floating point type
- * tu_int is a 64 bit integral type
- * value in double is representable in tu_int or is negative
- * (no range checking performed)
- */
-
-/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */
+#define DOUBLE_PRECISION
+#include "fp_lib.h"
+typedef tu_int fixuint_t;
+#include "fp_fixuint_impl.inc"
COMPILER_RT_ABI tu_int
-__fixunsdfti(double a)
-{
- double_bits fb;
- fb.f = a;
- int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023;
- if (e < 0 || (fb.u.s.high & 0x80000000))
- return 0;
- tu_int r = 0x0010000000000000uLL | (fb.u.all & 0x000FFFFFFFFFFFFFuLL);
- if (e > 52)
- r <<= (e - 52);
- else
- r >>= (52 - e);
- return r;
+__fixunsdfti(fp_t a) {
+ return __fixuint(a);
}
-
#endif /* CRT_HAS_128BIT */
diff --git a/lib/builtins/fixunssfdi.c b/lib/builtins/fixunssfdi.c
index 69d5952e9607..5a154e82cff4 100644
--- a/lib/builtins/fixunssfdi.c
+++ b/lib/builtins/fixunssfdi.c
@@ -6,39 +6,40 @@
* Source Licenses. See LICENSE.TXT for details.
*
* ===----------------------------------------------------------------------===
- *
- * This file implements __fixunssfdi for the compiler_rt library.
- *
- * ===----------------------------------------------------------------------===
- */
-
-#include "int_lib.h"
-/* Returns: convert a to a unsigned long long, rounding toward zero.
- * Negative values all become zero.
- */
-
-/* Assumption: float is a IEEE 32 bit floating point type
- * du_int is a 64 bit integral type
- * value in float is representable in du_int or is negative
- * (no range checking performed)
*/
-/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */
+#define SINGLE_PRECISION
+#include "fp_lib.h"
ARM_EABI_FNALIAS(f2ulz, fixunssfdi)
+#ifndef __SOFT_FP__
+/* Support for systems that have hardware floating-point; can set the invalid
+ * flag as a side-effect of computation.
+ */
+
COMPILER_RT_ABI du_int
__fixunssfdi(float a)
{
- float_bits fb;
- fb.f = a;
- int e = ((fb.u & 0x7F800000) >> 23) - 127;
- if (e < 0 || (fb.u & 0x80000000))
- return 0;
- du_int r = (fb.u & 0x007FFFFF) | 0x00800000;
- if (e > 23)
- r <<= (e - 23);
- else
- r >>= (23 - e);
- return r;
+ if (a <= 0.0f) return 0;
+ double da = a;
+ su_int high = da/0x1p32f;
+ su_int low = da - (double)high*0x1p32f;
+ return ((du_int)high << 32) | low;
+}
+
+#else
+/* Support for systems that don't have hardware floating-point; there are no
+ * flags to set, and we don't want to code-gen to an unknown soft-float
+ * implementation.
+ */
+
+typedef du_int fixuint_t;
+#include "fp_fixuint_impl.inc"
+
+COMPILER_RT_ABI du_int
+__fixunssfdi(fp_t a) {
+ return __fixuint(a);
}
+
+#endif
diff --git a/lib/builtins/fixunssfsi.c b/lib/builtins/fixunssfsi.c
index e034139ea277..cc2b05bd84f8 100644
--- a/lib/builtins/fixunssfsi.c
+++ b/lib/builtins/fixunssfsi.c
@@ -12,34 +12,14 @@
* ===----------------------------------------------------------------------===
*/
-#include "int_lib.h"
-
-/* Returns: convert a to a unsigned int, rounding toward zero.
- * Negative values all become zero.
- */
-
-/* Assumption: float is a IEEE 32 bit floating point type
- * su_int is a 32 bit integral type
- * value in float is representable in su_int or is negative
- * (no range checking performed)
- */
-
-/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */
+#define SINGLE_PRECISION
+#include "fp_lib.h"
+typedef su_int fixuint_t;
+#include "fp_fixuint_impl.inc"
ARM_EABI_FNALIAS(f2uiz, fixunssfsi)
COMPILER_RT_ABI su_int
-__fixunssfsi(float a)
-{
- float_bits fb;
- fb.f = a;
- int e = ((fb.u & 0x7F800000) >> 23) - 127;
- if (e < 0 || (fb.u & 0x80000000))
- return 0;
- su_int r = (fb.u & 0x007FFFFF) | 0x00800000;
- if (e > 23)
- r <<= (e - 23);
- else
- r >>= (23 - e);
- return r;
+__fixunssfsi(fp_t a) {
+ return __fixuint(a);
}
diff --git a/lib/builtins/fixunssfti.c b/lib/builtins/fixunssfti.c
index 4da9e242ad05..862d7bd6c7af 100644
--- a/lib/builtins/fixunssfti.c
+++ b/lib/builtins/fixunssfti.c
@@ -12,36 +12,15 @@
* ===----------------------------------------------------------------------===
*/
-#include "int_lib.h"
+#define SINGLE_PRECISION
+#include "fp_lib.h"
-#ifdef CRT_HAS_128BIT
-
-/* Returns: convert a to a unsigned long long, rounding toward zero.
- * Negative values all become zero.
- */
-
-/* Assumption: float is a IEEE 32 bit floating point type
- * tu_int is a 64 bit integral type
- * value in float is representable in tu_int or is negative
- * (no range checking performed)
- */
-
-/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */
+#if defined(CRT_HAS_128BIT)
+typedef tu_int fixuint_t;
+#include "fp_fixuint_impl.inc"
COMPILER_RT_ABI tu_int
-__fixunssfti(float a)
-{
- float_bits fb;
- fb.f = a;
- int e = ((fb.u & 0x7F800000) >> 23) - 127;
- if (e < 0 || (fb.u & 0x80000000))
- return 0;
- tu_int r = (fb.u & 0x007FFFFF) | 0x00800000;
- if (e > 23)
- r <<= (e - 23);
- else
- r >>= (23 - e);
- return r;
+__fixunssfti(fp_t a) {
+ return __fixuint(a);
}
-
-#endif /* CRT_HAS_128BIT */
+#endif
diff --git a/lib/builtins/fixunstfdi.c b/lib/builtins/fixunstfdi.c
new file mode 100644
index 000000000000..b2995f65834a
--- /dev/null
+++ b/lib/builtins/fixunstfdi.c
@@ -0,0 +1,22 @@
+/* ===-- fixunstfdi.c - Implement __fixunstfdi -----------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#define QUAD_PRECISION
+#include "fp_lib.h"
+
+#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT)
+typedef du_int fixuint_t;
+#include "fp_fixuint_impl.inc"
+
+COMPILER_RT_ABI du_int
+__fixunstfdi(fp_t a) {
+ return __fixuint(a);
+}
+#endif
diff --git a/lib/builtins/fixunstfsi.c b/lib/builtins/fixunstfsi.c
new file mode 100644
index 000000000000..b5d3f6a7d38d
--- /dev/null
+++ b/lib/builtins/fixunstfsi.c
@@ -0,0 +1,22 @@
+/* ===-- fixunstfsi.c - Implement __fixunstfsi -----------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#define QUAD_PRECISION
+#include "fp_lib.h"
+
+#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT)
+typedef su_int fixuint_t;
+#include "fp_fixuint_impl.inc"
+
+COMPILER_RT_ABI su_int
+__fixunstfsi(fp_t a) {
+ return __fixuint(a);
+}
+#endif
diff --git a/lib/builtins/fixunstfti.c b/lib/builtins/fixunstfti.c
new file mode 100644
index 000000000000..22ff9dfc0339
--- /dev/null
+++ b/lib/builtins/fixunstfti.c
@@ -0,0 +1,22 @@
+/* ===-- fixunstfsi.c - Implement __fixunstfsi -----------------------------===
+ *
+ * The LLVM Compiler Infrastructure
+ *
+ * This file is dual licensed under the MIT and the University of Illinois Open
+ * Source Licenses. See LICENSE.TXT for details.
+ *
+ * ===----------------------------------------------------------------------===
+ */
+
+#define QUAD_PRECISION
+#include "fp_lib.h"
+
+#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT)
+typedef tu_int fixuint_t;
+#include "fp_fixuint_impl.inc"
+
+COMPILER_RT_ABI tu_int
+__fixunstfti(fp_t a) {
+ return __fixuint(a);
+}
+#endif
diff --git a/lib/builtins/fixunsxfdi.c b/lib/builtins/fixunsxfdi.c
index 7224d467e7c9..075304e78dc9 100644
--- a/lib/builtins/fixunsxfdi.c
+++ b/lib/builtins/fixunsxfdi.c
@@ -38,6 +38,8 @@ __fixunsxfdi(long double a)
int e = (fb.u.high.s.low & 0x00007FFF) - 16383;
if (e < 0 || (fb.u.high.s.low & 0x00008000))
return 0;
+ if ((unsigned)e > sizeof(du_int) * CHAR_BIT)
+ return ~(du_int)0;
return fb.u.low.all >> (63 - e);
}
diff --git a/lib/builtins/fixunsxfsi.c b/lib/builtins/fixunsxfsi.c
index df0a18e2c1ab..c3c70f743de8 100644
--- a/lib/builtins/fixunsxfsi.c
+++ b/lib/builtins/fixunsxfsi.c
@@ -23,7 +23,6 @@
/* Assumption: long double is an intel 80 bit floating point type padded with 6 bytes
* su_int is a 32 bit integral type
* value in long double is representable in su_int or is negative
- * (no range checking performed)
*/
/* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee |
@@ -38,6 +37,8 @@ __fixunsxfsi(long double a)
int e = (fb.u.high.s.low & 0x00007FFF) - 16383;
if (e < 0 || (fb.u.high.s.low & 0x00008000))
return 0;
+ if ((unsigned)e > sizeof(su_int) * CHAR_BIT)
+ return ~(su_int)0;
return fb.u.low.s.high >> (31 - e);
}
diff --git a/lib/builtins/fixunsxfti.c b/lib/builtins/fixunsxfti.c
index 42e507304718..fb39d00ff5b2 100644
--- a/lib/builtins/fixunsxfti.c
+++ b/lib/builtins/fixunsxfti.c
@@ -21,9 +21,8 @@
*/
/* Assumption: long double is an intel 80 bit floating point type padded with 6 bytes
- * tu_int is a 64 bit integral type
+ * tu_int is a 128 bit integral type
* value in long double is representable in tu_int or is negative
- * (no range checking performed)
*/
/* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee |
@@ -38,6 +37,8 @@ __fixunsxfti(long double a)
int e = (fb.u.high.s.low & 0x00007FFF) - 16383;
if (e < 0 || (fb.u.high.s.low & 0x00008000))
return 0;
+ if ((unsigned)e > sizeof(tu_int) * CHAR_BIT)
+ return ~(tu_int)0;
tu_int r = fb.u.low.all;
if (e > 63)
r <<= (e - 63);
diff --git a/lib/builtins/fixxfdi.c b/lib/builtins/fixxfdi.c
index afc79d8664d8..011787f9e4b4 100644
--- a/lib/builtins/fixxfdi.c
+++ b/lib/builtins/fixxfdi.c
@@ -19,7 +19,7 @@
/* Returns: convert a to a signed long long, rounding toward zero. */
/* Assumption: long double is an intel 80 bit floating point type padded with 6 bytes
- * su_int is a 32 bit integral type
+ * di_int is a 64 bit integral type
* value in long double is representable in di_int (no range checking performed)
*/
@@ -30,11 +30,15 @@
COMPILER_RT_ABI di_int
__fixxfdi(long double a)
{
+ const di_int di_max = (di_int)((~(du_int)0) / 2);
+ const di_int di_min = -di_max - 1;
long_double_bits fb;
fb.f = a;
int e = (fb.u.high.s.low & 0x00007FFF) - 16383;
if (e < 0)
return 0;
+ if ((unsigned)e >= sizeof(di_int) * CHAR_BIT)
+ return a > 0 ? di_max : di_min;
di_int s = -(si_int)((fb.u.high.s.low & 0x00008000) >> 15);
di_int r = fb.u.low.all;
r = (du_int)r >> (63 - e);
diff --git a/lib/builtins/fixxfti.c b/lib/builtins/fixxfti.c
index 3d0a279b3842..968a4f0d5eea 100644
--- a/lib/builtins/fixxfti.c
+++ b/lib/builtins/fixxfti.c
@@ -19,8 +19,8 @@
/* Returns: convert a to a signed long long, rounding toward zero. */
/* Assumption: long double is an intel 80 bit floating point type padded with 6 bytes
- * su_int is a 32 bit integral type
- * value in long double is representable in ti_int (no range checking performed)
+ * ti_int is a 128 bit integral type
+ * value in long double is representable in ti_int
*/
/* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee |
@@ -30,6 +30,8 @@
COMPILER_RT_ABI ti_int
__fixxfti(long double a)
{
+ const ti_int ti_max = (ti_int)((~(tu_int)0) / 2);
+ const ti_int ti_min = -ti_max - 1;
long_double_bits fb;
fb.f = a;
int e = (fb.u.high.s.low & 0x00007FFF) - 16383;
@@ -37,6 +39,8 @@ __fixxfti(long double a)
return 0;
ti_int s = -(si_int)((fb.u.high.s.low & 0x00008000) >> 15);
ti_int r = fb.u.low.all;
+ if ((unsigned)e >= sizeof(ti_int) * CHAR_BIT)
+ return a > 0 ? ti_max : ti_min;
if (e > 63)
r <<= (e - 63);
else
diff --git a/lib/builtins/fp_extend.h b/lib/builtins/fp_extend.h
index fff676eeca8c..5c2b92310df1 100644
--- a/lib/builtins/fp_extend.h
+++ b/lib/builtins/fp_extend.h
@@ -39,11 +39,24 @@ static inline int src_rep_t_clz(src_rep_t a) {
#endif
}
+#elif defined SRC_HALF
+typedef uint16_t src_t;
+typedef uint16_t src_rep_t;
+#define SRC_REP_C UINT16_C
+static const int srcSigBits = 10;
+#define src_rep_t_clz __builtin_clz
+
#else
-#error Source should be single precision or double precision!
+#error Source should be half, single, or double precision!
#endif //end source precision
-#if defined DST_DOUBLE
+#if defined DST_SINGLE
+typedef float dst_t;
+typedef uint32_t dst_rep_t;
+#define DST_REP_C UINT32_C
+static const int dstSigBits = 23;
+
+#elif defined DST_DOUBLE
typedef double dst_t;
typedef uint64_t dst_rep_t;
#define DST_REP_C UINT64_C
@@ -56,7 +69,7 @@ typedef __uint128_t dst_rep_t;
static const int dstSigBits = 112;
#else
-#error Destination should be double precision or quad precision!
+#error Destination should be single, double, or quad precision!
#endif //end destination precision
// End of specialization parameters. Two helper routines for conversion to and
diff --git a/lib/builtins/fp_extend_impl.inc b/lib/builtins/fp_extend_impl.inc
index f6953ff4bfae..edcfa8d2329d 100644
--- a/lib/builtins/fp_extend_impl.inc
+++ b/lib/builtins/fp_extend_impl.inc
@@ -66,7 +66,9 @@ static inline dst_t __extendXfYf2__(src_t a) {
const src_rep_t sign = aRep & srcSignMask;
dst_rep_t absResult;
- if (aAbs - srcMinNormal < srcInfinity - srcMinNormal) {
+ // If sizeof(src_rep_t) < sizeof(int), the subtraction result is promoted
+ // to (signed) int. To avoid that, explicitly cast to src_rep_t.
+ if ((src_rep_t)(aAbs - srcMinNormal) < srcInfinity - srcMinNormal) {
// a is a normal number.
// Extend to the destination type by shifting the significand and
// exponent into the proper position and rebiasing the exponent.
diff --git a/lib/builtins/fp_fixint_impl.inc b/lib/builtins/fp_fixint_impl.inc
new file mode 100644
index 000000000000..035e87ca10e0
--- /dev/null
+++ b/lib/builtins/fp_fixint_impl.inc
@@ -0,0 +1,41 @@
+//===-- lib/fixdfsi.c - Double-precision -> integer conversion ----*- C -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements float to integer conversion for the
+// compiler-rt library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "fp_lib.h"
+
+static inline fixint_t __fixint(fp_t a) {
+ const fixint_t fixint_max = (fixint_t)((~(fixuint_t)0) / 2);
+ const fixint_t fixint_min = -fixint_max - 1;
+ // Break a into sign, exponent, significand
+ const rep_t aRep = toRep(a);
+ const rep_t aAbs = aRep & absMask;
+ const fixint_t sign = aRep & signBit ? -1 : 1;
+ const int exponent = (aAbs >> significandBits) - exponentBias;
+ const rep_t significand = (aAbs & significandMask) | implicitBit;
+
+ // If exponent is negative, the result is zero.
+ if (exponent < 0)
+ return 0;
+
+ // If the value is too large for the integer type, saturate.
+ if ((unsigned)exponent >= sizeof(fixint_t) * CHAR_BIT)
+ return sign == 1 ? fixint_max : fixint_min;
+
+ // If 0 <= exponent < significandBits, right shift to get the result.
+ // Otherwise, shift left.
+ if (exponent < significandBits)
+ return sign * (significand >> (significandBits - exponent));
+ else
+ return sign * ((fixint_t)significand << (exponent - significandBits));
+}
diff --git a/lib/builtins/fp_fixuint_impl.inc b/lib/builtins/fp_fixuint_impl.inc
new file mode 100644
index 000000000000..5fefab0e2d8a
--- /dev/null
+++ b/lib/builtins/fp_fixuint_impl.inc
@@ -0,0 +1,39 @@
+//===-- lib/fixdfsi.c - Double-precision -> integer conversion ----*- C -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements float to unsigned integer conversion for the
+// compiler-rt library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "fp_lib.h"
+
+static inline fixuint_t __fixuint(fp_t a) {
+ // Break a into sign, exponent, significand
+ const rep_t aRep = toRep(a);
+ const rep_t aAbs = aRep & absMask;
+ const int sign = aRep & signBit ? -1 : 1;
+ const int exponent = (aAbs >> significandBits) - exponentBias;
+ const rep_t significand = (aAbs & significandMask) | implicitBit;
+
+ // If either the value or the exponent is negative, the result is zero.
+ if (sign == -1 || exponent < 0)
+ return 0;
+
+ // If the value is too large for the integer type, saturate.
+ if ((unsigned)exponent > sizeof(fixuint_t) * CHAR_BIT)
+ return ~(fixuint_t)0;
+
+ // If 0 <= exponent < significandBits, right shift to get the result.
+ // Otherwise, shift left.
+ if (exponent < significandBits)
+ return significand >> (significandBits - exponent);
+ else
+ return (fixuint_t)significand << (exponent - significandBits);
+}
diff --git a/lib/builtins/fp_trunc.h b/lib/builtins/fp_trunc.h
index 49a9aebbc67d..373ba1b0411d 100644
--- a/lib/builtins/fp_trunc.h
+++ b/lib/builtins/fp_trunc.h
@@ -16,7 +16,13 @@
#include "int_lib.h"
-#if defined SRC_DOUBLE
+#if defined SRC_SINGLE
+typedef float src_t;
+typedef uint32_t src_rep_t;
+#define SRC_REP_C UINT32_C
+static const int srcSigBits = 23;
+
+#elif defined SRC_DOUBLE
typedef double src_t;
typedef uint64_t src_rep_t;
#define SRC_REP_C UINT64_C
@@ -44,6 +50,12 @@ typedef uint32_t dst_rep_t;
#define DST_REP_C UINT32_C
static const int dstSigBits = 23;
+#elif defined DST_HALF
+typedef uint16_t dst_t;
+typedef uint16_t dst_rep_t;
+#define DST_REP_C UINT16_C
+static const int dstSigBits = 10;
+
#else
#error Destination should be single precision or double precision!
#endif //end destination precision
diff --git a/lib/builtins/fp_trunc_impl.inc b/lib/builtins/fp_trunc_impl.inc
index 21bffae02bf4..372e8d6014dd 100644
--- a/lib/builtins/fp_trunc_impl.inc
+++ b/lib/builtins/fp_trunc_impl.inc
@@ -99,7 +99,7 @@ static inline dst_t __truncXfYf2__(src_t a) {
absResult |= dstQNaN;
absResult |= ((aAbs & srcNaNCode) >> (srcSigBits - dstSigBits)) & dstNaNCode;
}
- else if (aAbs > overflow) {
+ else if (aAbs >= overflow) {
// a overflows to infinity.
absResult = (dst_rep_t)dstInfExp << dstSigBits;
}
diff --git a/lib/builtins/i386/chkstk.S b/lib/builtins/i386/chkstk.S
new file mode 100644
index 000000000000..3733d722ef19
--- /dev/null
+++ b/lib/builtins/i386/chkstk.S
@@ -0,0 +1,34 @@
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+
+#include "../assembly.h"
+
+// _chkstk routine
+// This routine is windows specific
+// http://msdn.microsoft.com/en-us/library/ms648426.aspx
+
+#ifdef __i386__
+
+.text
+.balign 4
+DEFINE_COMPILERRT_FUNCTION(__chkstk_ms)
+ push %ecx
+ push %eax
+ cmp $0x1000,%eax
+ lea 12(%esp),%ecx
+ jb 1f
+2:
+ sub $0x1000,%ecx
+ orl $0,(%ecx)
+ sub $0x1000,%eax
+ cmp $0x1000,%eax
+ ja 2b
+1:
+ sub %eax,%ecx
+ orl $0,(%ecx)
+ pop %eax
+ pop %ecx
+ ret
+END_COMPILERRT_FUNCTION(__chkstk_ms)
+
+#endif // __i386__
diff --git a/lib/builtins/int_endianness.h b/lib/builtins/int_endianness.h
index 4b35bde079e8..7995ddbb953c 100644
--- a/lib/builtins/int_endianness.h
+++ b/lib/builtins/int_endianness.h
@@ -16,6 +16,20 @@
#ifndef INT_ENDIANNESS_H
#define INT_ENDIANNESS_H
+#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
+ defined(__ORDER_LITTLE_ENDIAN__)
+
+/* Clang and GCC provide built-in endianness definitions. */
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define _YUGA_LITTLE_ENDIAN 0
+#define _YUGA_BIG_ENDIAN 1
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define _YUGA_LITTLE_ENDIAN 1
+#define _YUGA_BIG_ENDIAN 0
+#endif /* __BYTE_ORDER__ */
+
+#else /* Compilers other than Clang or GCC. */
+
#if defined(__SVR4) && defined(__sun)
#include <sys/byteorder.h>
@@ -84,18 +98,6 @@
/* .. */
-#if defined(__linux__)
-
-#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-#define _YUGA_LITTLE_ENDIAN 0
-#define _YUGA_BIG_ENDIAN 1
-#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define _YUGA_LITTLE_ENDIAN 1
-#define _YUGA_BIG_ENDIAN 0
-#endif /* __BYTE_ORDER__ */
-
-#endif /* GNU/Linux */
-
#if defined(_WIN32)
#define _YUGA_LITTLE_ENDIAN 1
@@ -103,6 +105,8 @@
#endif /* Windows */
+#endif /* Clang or GCC. */
+
/* . */
#if !defined(_YUGA_LITTLE_ENDIAN) || !defined(_YUGA_BIG_ENDIAN)
diff --git a/lib/builtins/int_lib.h b/lib/builtins/int_lib.h
index ff314da986f1..bca5d81d4414 100644
--- a/lib/builtins/int_lib.h
+++ b/lib/builtins/int_lib.h
@@ -28,7 +28,11 @@
# define COMPILER_RT_ABI __attribute__((pcs("aapcs")))
#else
# define ARM_EABI_FNALIAS(aeabi_name, name)
-# define COMPILER_RT_ABI
+# if defined(__arm__) && defined(_WIN32)
+# define COMPILER_RT_ABI __attribute__((pcs("aapcs")))
+# else
+# define COMPILER_RT_ABI
+# endif
#endif
#if defined(__NetBSD__) && (defined(_KERNEL) || defined(_STANDALONE))
diff --git a/lib/builtins/truncdfhf2.c b/lib/builtins/truncdfhf2.c
new file mode 100644
index 000000000000..0852df369625
--- /dev/null
+++ b/lib/builtins/truncdfhf2.c
@@ -0,0 +1,16 @@
+//===-- lib/truncdfhf2.c - double -> half conversion --------------*- C -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#define SRC_DOUBLE
+#define DST_HALF
+#include "fp_trunc_impl.inc"
+
+COMPILER_RT_ABI uint16_t __truncdfhf2(double a) {
+ return __truncXfYf2__(a);
+}
diff --git a/lib/builtins/truncsfhf2.c b/lib/builtins/truncsfhf2.c
new file mode 100644
index 000000000000..381e590c342f
--- /dev/null
+++ b/lib/builtins/truncsfhf2.c
@@ -0,0 +1,22 @@
+//===-- lib/truncsfhf2.c - single -> half conversion --------------*- C -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#define SRC_SINGLE
+#define DST_HALF
+#include "fp_trunc_impl.inc"
+
+// Use a forwarding definition and noinline to implement a poor man's alias,
+// as there isn't a good cross-platform way of defining one.
+COMPILER_RT_ABI __attribute__((noinline)) uint16_t __truncsfhf2(float a) {
+ return __truncXfYf2__(a);
+}
+
+COMPILER_RT_ABI uint16_t __gnu_f2h_ieee(float a) {
+ return __truncsfhf2(a);
+}
diff --git a/lib/builtins/x86_64/chkstk.S b/lib/builtins/x86_64/chkstk.S
new file mode 100644
index 000000000000..5759e84498c6
--- /dev/null
+++ b/lib/builtins/x86_64/chkstk.S
@@ -0,0 +1,39 @@
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+
+#include "../assembly.h"
+
+// _chkstk routine
+// This routine is windows specific
+// http://msdn.microsoft.com/en-us/library/ms648426.aspx
+
+// Notes from r227519
+// MSVC x64s __chkstk and cygmings ___chkstk_ms do not adjust %rsp
+// themselves. It also does not clobber %rax so we can reuse it when
+// adjusting %rsp.
+
+#ifdef __x86_64__
+
+.text
+.balign 4
+DEFINE_COMPILERRT_FUNCTION(___chkstk_ms)
+ push %rcx
+ push %rax
+ cmp $0x1000,%rax
+ lea 24(%rsp),%rcx
+ jb 1f
+2:
+ sub $0x1000,%rcx
+ orl $0,(%rcx)
+ sub $0x1000,%rax
+ cmp $0x1000,%rax
+ ja 2b
+1:
+ sub %rax,%rcx
+ orl $0,(%rcx)
+ pop %rax
+ pop %rcx
+ ret
+END_COMPILERRT_FUNCTION(___chkstk_ms)
+
+#endif // __x86_64__
diff --git a/lib/dfsan/dfsan.cc b/lib/dfsan/dfsan.cc
index de5b2ce107b4..d2e137e129c1 100644
--- a/lib/dfsan/dfsan.cc
+++ b/lib/dfsan/dfsan.cc
@@ -302,12 +302,12 @@ dfsan_dump_labels(int fd) {
char buf[64];
internal_snprintf(buf, sizeof(buf), "%u %u %u ", l,
__dfsan_label_info[l].l1, __dfsan_label_info[l].l2);
- internal_write(fd, buf, internal_strlen(buf));
+ WriteToFile(fd, buf, internal_strlen(buf));
if (__dfsan_label_info[l].l1 == 0 && __dfsan_label_info[l].desc) {
- internal_write(fd, __dfsan_label_info[l].desc,
- internal_strlen(__dfsan_label_info[l].desc));
+ WriteToFile(fd, __dfsan_label_info[l].desc,
+ internal_strlen(__dfsan_label_info[l].desc));
}
- internal_write(fd, "\n", 1);
+ WriteToFile(fd, "\n", 1);
}
}
@@ -333,7 +333,7 @@ static void InitializeFlags() {
static void dfsan_fini() {
if (internal_strcmp(flags().dump_labels_at_exit, "") != 0) {
- fd_t fd = OpenFile(flags().dump_labels_at_exit, true /* write */);
+ fd_t fd = OpenFile(flags().dump_labels_at_exit, WrOnly);
if (fd == kInvalidFd) {
Report("WARNING: DataFlowSanitizer: unable to open output file %s\n",
flags().dump_labels_at_exit);
@@ -343,7 +343,7 @@ static void dfsan_fini() {
Report("INFO: DataFlowSanitizer: dumping labels to %s\n",
flags().dump_labels_at_exit);
dfsan_dump_labels(fd);
- internal_close(fd);
+ CloseFile(fd);
}
}
@@ -361,7 +361,7 @@ static void dfsan_init(int argc, char **argv, char **envp) {
// case by disabling memory protection when ASLR is disabled.
uptr init_addr = (uptr)&dfsan_init;
if (!(init_addr >= kUnusedAddr && init_addr < kAppAddr))
- Mprotect(kUnusedAddr, kAppAddr - kUnusedAddr);
+ MmapNoAccess(kUnusedAddr, kAppAddr - kUnusedAddr);
InitializeFlags();
InitializeInterceptors();
diff --git a/lib/dfsan/dfsan_custom.cc b/lib/dfsan/dfsan_custom.cc
index 318ecd6fb317..c58b471db53c 100644
--- a/lib/dfsan/dfsan_custom.cc
+++ b/lib/dfsan/dfsan_custom.cc
@@ -82,11 +82,20 @@ SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_strchr(const char *s, int c,
}
}
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void
+dfsan_weak_hook_memcmp(uptr caller_pc, const void *s1, const void *s2, size_t n,
+ dfsan_label s1_label, dfsan_label s2_label,
+ dfsan_label n_label);
+
SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_memcmp(const void *s1, const void *s2,
size_t n, dfsan_label s1_label,
dfsan_label s2_label,
dfsan_label n_label,
dfsan_label *ret_label) {
+ if (dfsan_weak_hook_memcmp)
+ dfsan_weak_hook_memcmp(GET_CALLER_PC(), s1, s2, n, s1_label, s2_label,
+ n_label);
const char *cs1 = (const char *) s1, *cs2 = (const char *) s2;
for (size_t i = 0; i != n; ++i) {
if (cs1[i] != cs2[i]) {
@@ -847,266 +856,247 @@ __dfsw_write(int fd, const void *buf, size_t count,
*ret_label = 0;
return write(fd, buf, count);
}
+}
// Type used to extract a dfsan_label with va_arg()
typedef int dfsan_label_va;
-// A chunk of data representing the output of formatting either a constant
-// string or a single format directive.
-struct Chunk {
- // Address of the beginning of the formatted string
- const char *ptr;
- // Size of the formatted string
- size_t size;
+// Formats a chunk either a constant string or a single format directive (e.g.,
+// '%.3f').
+struct Formatter {
+ Formatter(char *str_, const char *fmt_, size_t size_)
+ : str(str_), str_off(0), size(size_), fmt_start(fmt_), fmt_cur(fmt_),
+ width(-1) {}
+
+ int format() {
+ char *tmp_fmt = build_format_string();
+ int retval =
+ snprintf(str + str_off, str_off < size ? size - str_off : 0, tmp_fmt,
+ 0 /* used only to avoid warnings */);
+ free(tmp_fmt);
+ return retval;
+ }
- // Type of DFSan label (depends on the format directive)
- enum {
- // Constant string, no argument and thus no label
- NONE = 0,
- // Label for an argument of '%n'
- IGNORED,
- // Label for a '%s' argument
- STRING,
- // Label for any other type of argument
- NUMERIC,
- } label_type;
-
- // Value of the argument (if label_type == STRING)
- const char *arg;
-};
+ template <typename T> int format(T arg) {
+ char *tmp_fmt = build_format_string();
+ int retval;
+ if (width >= 0) {
+ retval = snprintf(str + str_off, str_off < size ? size - str_off : 0,
+ tmp_fmt, width, arg);
+ } else {
+ retval = snprintf(str + str_off, str_off < size ? size - str_off : 0,
+ tmp_fmt, arg);
+ }
+ free(tmp_fmt);
+ return retval;
+ }
-// Formats the input. The output is stored in 'str' starting from offset
-// 'off'. The format directive is represented by the first 'format_size' bytes
-// of 'format'. If 'has_size' is true, 'size' bounds the number of output
-// bytes. Returns the return value of the vsnprintf call used to format the
-// input.
-static int format_chunk(char *str, size_t off, bool has_size, size_t size,
- const char *format, size_t format_size, ...) {
- char *chunk_format = (char *) malloc(format_size + 1);
- assert(chunk_format);
- internal_memcpy(chunk_format, format, format_size);
- chunk_format[format_size] = '\0';
+ char *build_format_string() {
+ size_t fmt_size = fmt_cur - fmt_start + 1;
+ char *new_fmt = (char *)malloc(fmt_size + 1);
+ assert(new_fmt);
+ internal_memcpy(new_fmt, fmt_start, fmt_size);
+ new_fmt[fmt_size] = '\0';
+ return new_fmt;
+ }
- va_list ap;
- va_start(ap, format_size);
- int r = 0;
- if (has_size) {
- r = vsnprintf(str + off, off < size ? size - off : 0, chunk_format, ap);
- } else {
- r = vsprintf(str + off, chunk_format, ap);
+ char *str_cur() { return str + str_off; }
+
+ size_t num_written_bytes(int retval) {
+ if (retval < 0) {
+ return 0;
+ }
+
+ size_t num_avail = str_off < size ? size - str_off : 0;
+ if (num_avail == 0) {
+ return 0;
+ }
+
+ size_t num_written = retval;
+ // A return value of {v,}snprintf of size or more means that the output was
+ // truncated.
+ if (num_written >= num_avail) {
+ num_written -= num_avail;
+ }
+
+ return num_written;
}
- va_end(ap);
- free(chunk_format);
- return r;
-}
+ char *str;
+ size_t str_off;
+ size_t size;
+ const char *fmt_start;
+ const char *fmt_cur;
+ int width;
+};
// Formats the input and propagates the input labels to the output. The output
-// is stored in 'str'. If 'has_size' is true, 'size' bounds the number of
-// output bytes. 'format' and 'ap' are the format string and the list of
-// arguments for formatting. Returns the return value vsnprintf would return.
+// is stored in 'str'. 'size' bounds the number of output bytes. 'format' and
+// 'ap' are the format string and the list of arguments for formatting. Returns
+// the return value vsnprintf would return.
//
// The function tokenizes the format string in chunks representing either a
// constant string or a single format directive (e.g., '%.3f') and formats each
// chunk independently into the output string. This approach allows to figure
// out which bytes of the output string depends on which argument and thus to
// propagate labels more precisely.
-static int format_buffer(char *str, bool has_size, size_t size,
- const char *format, dfsan_label *va_labels,
- dfsan_label *ret_label, va_list ap) {
- InternalMmapVector<Chunk> chunks(8);
- size_t off = 0;
-
- while (*format) {
- chunks.push_back(Chunk());
- Chunk& chunk = chunks.back();
- chunk.ptr = str + off;
- chunk.arg = nullptr;
-
- int status = 0;
-
- if (*format != '%') {
+//
+// WARNING: This implementation does not support conversion specifiers with
+// positional arguments.
+static int format_buffer(char *str, size_t size, const char *fmt,
+ dfsan_label *va_labels, dfsan_label *ret_label,
+ va_list ap) {
+ Formatter formatter(str, fmt, size);
+
+ while (*formatter.fmt_cur) {
+ formatter.fmt_start = formatter.fmt_cur;
+ formatter.width = -1;
+ int retval = 0;
+
+ if (*formatter.fmt_cur != '%') {
// Ordinary character. Consume all the characters until a '%' or the end
// of the string.
- size_t format_size = 0;
- for (; *format && *format != '%'; ++format, ++format_size) {}
- status = format_chunk(str, off, has_size, size, format - format_size,
- format_size);
- chunk.label_type = Chunk::NONE;
+ for (; *(formatter.fmt_cur + 1) && *(formatter.fmt_cur + 1) != '%';
+ ++formatter.fmt_cur) {}
+ retval = formatter.format();
+ dfsan_set_label(0, formatter.str_cur(),
+ formatter.num_written_bytes(retval));
} else {
// Conversion directive. Consume all the characters until a conversion
// specifier or the end of the string.
- bool end_format = false;
-#define FORMAT_CHUNK(t) \
- format_chunk(str, off, has_size, size, format - format_size, \
- format_size + 1, va_arg(ap, t))
-
- for (size_t format_size = 1; *++format && !end_format; ++format_size) {
- switch (*format) {
- case 'd':
- case 'i':
- case 'o':
- case 'u':
- case 'x':
- case 'X':
- switch (*(format - 1)) {
- case 'h':
- // Also covers the 'hh' case (since the size of the arg is still
- // an int).
- status = FORMAT_CHUNK(int);
- break;
- case 'l':
- if (format_size >= 2 && *(format - 2) == 'l') {
- status = FORMAT_CHUNK(long long int);
- } else {
- status = FORMAT_CHUNK(long int);
- }
- break;
- case 'q':
- status = FORMAT_CHUNK(long long int);
- break;
- case 'j':
- status = FORMAT_CHUNK(intmax_t);
- break;
- case 'z':
- status = FORMAT_CHUNK(size_t);
- break;
- case 't':
- status = FORMAT_CHUNK(size_t);
- break;
- default:
- status = FORMAT_CHUNK(int);
- }
- chunk.label_type = Chunk::NUMERIC;
- end_format = true;
+ bool end_fmt = false;
+ for (; *formatter.fmt_cur && !end_fmt; ) {
+ switch (*++formatter.fmt_cur) {
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ switch (*(formatter.fmt_cur - 1)) {
+ case 'h':
+ // Also covers the 'hh' case (since the size of the arg is still
+ // an int).
+ retval = formatter.format(va_arg(ap, int));
break;
-
- case 'a':
- case 'A':
- case 'e':
- case 'E':
- case 'f':
- case 'F':
- case 'g':
- case 'G':
- if (*(format - 1) == 'L') {
- status = FORMAT_CHUNK(long double);
+ case 'l':
+ if (formatter.fmt_cur - formatter.fmt_start >= 2 &&
+ *(formatter.fmt_cur - 2) == 'l') {
+ retval = formatter.format(va_arg(ap, long long int));
} else {
- status = FORMAT_CHUNK(double);
+ retval = formatter.format(va_arg(ap, long int));
}
- chunk.label_type = Chunk::NUMERIC;
- end_format = true;
break;
-
- case 'c':
- status = FORMAT_CHUNK(int);
- chunk.label_type = Chunk::NUMERIC;
- end_format = true;
+ case 'q':
+ retval = formatter.format(va_arg(ap, long long int));
break;
-
- case 's':
- chunk.arg = va_arg(ap, char *);
- status =
- format_chunk(str, off, has_size, size,
- format - format_size, format_size + 1,
- chunk.arg);
- chunk.label_type = Chunk::STRING;
- end_format = true;
+ case 'j':
+ retval = formatter.format(va_arg(ap, intmax_t));
break;
-
- case 'p':
- status = FORMAT_CHUNK(void *);
- chunk.label_type = Chunk::NUMERIC;
- end_format = true;
+ case 'z':
+ case 't':
+ retval = formatter.format(va_arg(ap, size_t));
break;
+ default:
+ retval = formatter.format(va_arg(ap, int));
+ }
+ dfsan_set_label(*va_labels++, formatter.str_cur(),
+ formatter.num_written_bytes(retval));
+ end_fmt = true;
+ break;
+
+ case 'a':
+ case 'A':
+ case 'e':
+ case 'E':
+ case 'f':
+ case 'F':
+ case 'g':
+ case 'G':
+ if (*(formatter.fmt_cur - 1) == 'L') {
+ retval = formatter.format(va_arg(ap, long double));
+ } else {
+ retval = formatter.format(va_arg(ap, double));
+ }
+ dfsan_set_label(*va_labels++, formatter.str_cur(),
+ formatter.num_written_bytes(retval));
+ end_fmt = true;
+ break;
+
+ case 'c':
+ retval = formatter.format(va_arg(ap, int));
+ dfsan_set_label(*va_labels++, formatter.str_cur(),
+ formatter.num_written_bytes(retval));
+ end_fmt = true;
+ break;
+
+ case 's': {
+ char *arg = va_arg(ap, char *);
+ retval = formatter.format(arg);
+ va_labels++;
+ internal_memcpy(shadow_for(formatter.str_cur()), shadow_for(arg),
+ sizeof(dfsan_label) *
+ formatter.num_written_bytes(retval));
+ end_fmt = true;
+ break;
+ }
- case 'n':
- *(va_arg(ap, int *)) = (int)off;
- chunk.label_type = Chunk::IGNORED;
- end_format = true;
- break;
+ case 'p':
+ retval = formatter.format(va_arg(ap, void *));
+ dfsan_set_label(*va_labels++, formatter.str_cur(),
+ formatter.num_written_bytes(retval));
+ end_fmt = true;
+ break;
+
+ case 'n': {
+ int *ptr = va_arg(ap, int *);
+ *ptr = (int)formatter.str_off;
+ va_labels++;
+ dfsan_set_label(0, ptr, sizeof(ptr));
+ end_fmt = true;
+ break;
+ }
- case '%':
- status = format_chunk(str, off, has_size, size,
- format - format_size, format_size + 1);
- chunk.label_type = Chunk::NONE;
- end_format = true;
- break;
+ case '%':
+ retval = formatter.format();
+ dfsan_set_label(0, formatter.str_cur(),
+ formatter.num_written_bytes(retval));
+ end_fmt = true;
+ break;
- default:
- break;
+ case '*':
+ formatter.width = va_arg(ap, int);
+ va_labels++;
+ break;
+
+ default:
+ break;
}
}
-#undef FORMAT_CHUNK
}
- if (status < 0) {
- return status;
+ if (retval < 0) {
+ return retval;
}
- // A return value of {v,}snprintf of size or more means that the output was
- // truncated.
- if (has_size) {
- if (off < size) {
- size_t ustatus = (size_t) status;
- chunk.size = ustatus >= (size - off) ?
- ustatus - (size - off) : ustatus;
- } else {
- chunk.size = 0;
- }
- } else {
- chunk.size = status;
- }
- off += status;
- }
-
- // TODO(martignlo): Decide how to combine labels (e.g., whether to ignore or
- // not the label of the format string).
-
- // Label each output chunk according to the label supplied as argument to the
- // function. We need to go through all the chunks and arguments even if the
- // string was only partially printed ({v,}snprintf case).
- for (size_t i = 0; i < chunks.size(); ++i) {
- const Chunk& chunk = chunks[i];
- void *chunk_ptr = const_cast<char *>(chunk.ptr);
-
- switch (chunk.label_type) {
- case Chunk::NONE:
- dfsan_set_label(0, chunk_ptr, chunk.size);
- break;
- case Chunk::IGNORED:
- va_labels++;
- dfsan_set_label(0, chunk_ptr, chunk.size);
- break;
- case Chunk::NUMERIC: {
- dfsan_label label = *va_labels++;
- dfsan_set_label(label, chunk_ptr, chunk.size);
- break;
- }
- case Chunk::STRING: {
- // Consume the label of the pointer to the string
- va_labels++;
- internal_memcpy(shadow_for(chunk_ptr),
- shadow_for(chunk.arg),
- sizeof(dfsan_label) * (strlen(chunk.arg)));
- break;
- }
- }
+ formatter.fmt_cur++;
+ formatter.str_off += retval;
}
*ret_label = 0;
// Number of bytes written in total.
- return off;
+ return formatter.str_off;
}
+extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
int __dfsw_sprintf(char *str, const char *format, dfsan_label str_label,
dfsan_label format_label, dfsan_label *va_labels,
dfsan_label *ret_label, ...) {
va_list ap;
va_start(ap, ret_label);
- int ret = format_buffer(str, false, 0, format, va_labels, ret_label, ap);
+ int ret = format_buffer(str, ~0ul, format, va_labels, ret_label, ap);
va_end(ap);
return ret;
}
@@ -1118,7 +1108,7 @@ int __dfsw_snprintf(char *str, size_t size, const char *format,
dfsan_label *ret_label, ...) {
va_list ap;
va_start(ap, ret_label);
- int ret = format_buffer(str, true, size, format, va_labels, ret_label, ap);
+ int ret = format_buffer(str, size, format, va_labels, ret_label, ap);
va_end(ap);
return ret;
}
diff --git a/lib/dfsan/done_abilist.txt b/lib/dfsan/done_abilist.txt
index 15c6d1bb2e39..e6c077ff1208 100644
--- a/lib/dfsan/done_abilist.txt
+++ b/lib/dfsan/done_abilist.txt
@@ -258,3 +258,18 @@ fun:reflect.MakeFuncStubGo=uninstrumented
fun:reflect.MakeFuncStubGo=discard
fun:reflect.makeFuncStub=uninstrumented
fun:reflect.makeFuncStub=discard
+
+
+###############################################################################
+# lib/Fuzzer
+###############################################################################
+# Replaces __sanitizer_cov_trace_cmp with __dfsw___sanitizer_cov_trace_cmp
+fun:__sanitizer_cov_trace_cmp=custom
+fun:__sanitizer_cov_trace_cmp=uninstrumented
+
+# Ignores all other __sanitizer callbacks.
+fun:__sanitizer_*=uninstrumented
+fun:__sanitizer_*=discard
+
+# Don't add extra parameters to the Fuzzer callback.
+fun:LLVMFuzzerTestOneInput=uninstrumented
diff --git a/lib/dfsan/scripts/check_custom_wrappers.sh b/lib/dfsan/scripts/check_custom_wrappers.sh
index 50bc85d4aef5..99bf50cbbd08 100755
--- a/lib/dfsan/scripts/check_custom_wrappers.sh
+++ b/lib/dfsan/scripts/check_custom_wrappers.sh
@@ -17,8 +17,10 @@ on_exit() {
rm -f ${DIFF_B} 2> /dev/null
}
+# Ignore __sanitizer_cov_trace* because they are implemented elsewhere.
trap on_exit EXIT
-grep -E "^fun:.*=custom" ${DFSAN_ABI_LIST} | grep -v "dfsan_get_label" \
+grep -E "^fun:.*=custom" ${DFSAN_ABI_LIST} \
+ | grep -v "dfsan_get_label\|__sanitizer_cov_trace" \
| sed "s/^fun:\(.*\)=custom.*/\1/" | sort > $DIFF_A
grep -E "__dfsw.*\(" ${DFSAN_CUSTOM_WRAPPERS} \
| sed "s/.*__dfsw_\(.*\)(.*/\1/" | sort > $DIFF_B
@@ -32,7 +34,7 @@ fi
grep -E __dfsw_ ${DFSAN_CUSTOM_WRAPPERS} \
| sed "s/.*__dfsw_\([^(]*\).*/\1/" | sort > $DIFF_A
-grep -E "^\\s*test_.*\(\);" ${DFSAN_CUSTOM_TESTS} \
+grep -E "^[[:space:]]*test_.*\(\);" ${DFSAN_CUSTOM_TESTS} \
| sed "s/.*test_\(.*\)();/\1/" | sort > $DIFF_B
diff -u $DIFF_A $DIFF_B > ${DIFFOUT}
if [ $? -ne 0 ]
diff --git a/lib/interception/CMakeLists.txt b/lib/interception/CMakeLists.txt
index b77f2d1562b7..16b41c976d6b 100644
--- a/lib/interception/CMakeLists.txt
+++ b/lib/interception/CMakeLists.txt
@@ -12,18 +12,8 @@ include_directories(..)
set(INTERCEPTION_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_no_rtti_flag(INTERCEPTION_CFLAGS)
-if(APPLE)
- # Build universal binary on APPLE.
- foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS})
- add_compiler_rt_darwin_object_library(RTInterception ${os}
- ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH}
- SOURCES ${INTERCEPTION_SOURCES}
- CFLAGS ${INTERCEPTION_CFLAGS})
- endforeach()
-else()
- # Otherwise, build separate libraries for each target.
- foreach(arch ${SANITIZER_COMMON_SUPPORTED_ARCH})
- add_compiler_rt_object_library(RTInterception ${arch}
- SOURCES ${INTERCEPTION_SOURCES} CFLAGS ${INTERCEPTION_CFLAGS})
- endforeach()
-endif()
+add_compiler_rt_object_libraries(RTInterception
+ OS ${SANITIZER_COMMON_SUPPORTED_OS}
+ ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH}
+ SOURCES ${INTERCEPTION_SOURCES}
+ CFLAGS ${INTERCEPTION_CFLAGS})
diff --git a/lib/interception/interception.h b/lib/interception/interception.h
index 52573258bf98..9e9aca215c4d 100644
--- a/lib/interception/interception.h
+++ b/lib/interception/interception.h
@@ -219,7 +219,6 @@ const interpose_substitution substitution_##func_name[] \
namespace __interception { \
FUNC_TYPE(func) PTR_TO_REAL(func); \
} \
- DECLARE_WRAPPER_WINAPI(ret_type, func, __VA_ARGS__) \
extern "C" \
INTERCEPTOR_ATTRIBUTE \
ret_type __stdcall WRAP(func)(__VA_ARGS__)
diff --git a/lib/interception/interception_win.cc b/lib/interception/interception_win.cc
index cd241c3d23c3..19cf184948b9 100644
--- a/lib/interception/interception_win.cc
+++ b/lib/interception/interception_win.cc
@@ -84,6 +84,7 @@ static size_t RoundUpToInstrBoundary(size_t size, char *code) {
cursor += 2;
continue;
case '\xE9': // E9 XX YY ZZ WW = jmp WWZZYYXX
+ case '\xB8': // B8 XX YY ZZ WW = mov eax, WWZZYYXX
cursor += 5;
continue;
}
@@ -182,10 +183,14 @@ bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
}
static const void **InterestingDLLsAvailable() {
- const char *InterestingDLLs[] = {"kernel32.dll",
- "msvcr110.dll", // VS2012
- "msvcr120.dll", // VS2013
- NULL};
+ const char *InterestingDLLs[] = {
+ "kernel32.dll",
+ "msvcr110.dll", // VS2012
+ "msvcr120.dll", // VS2013
+ // NTDLL should go last as it exports some functions that we should override
+ // in the CRT [presumably only used internally].
+ "ntdll.dll", NULL
+ };
static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
if (!result[0]) {
for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {
diff --git a/lib/lsan/CMakeLists.txt b/lib/lsan/CMakeLists.txt
index 2ea765de1bc7..37f794e2e11b 100644
--- a/lib/lsan/CMakeLists.txt
+++ b/lib/lsan/CMakeLists.txt
@@ -18,20 +18,13 @@ set(LSAN_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR})
add_custom_target(lsan)
-if(APPLE)
- foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS})
- add_compiler_rt_darwin_object_library(RTLSanCommon ${os}
- ARCH ${LSAN_COMMON_SUPPORTED_ARCH}
- SOURCES ${LSAN_COMMON_SOURCES}
- CFLAGS ${LSAN_CFLAGS})
- endforeach()
-else()
- foreach(arch ${LSAN_COMMON_SUPPORTED_ARCH})
- add_compiler_rt_object_library(RTLSanCommon ${arch}
- SOURCES ${LSAN_COMMON_SOURCES}
- CFLAGS ${LSAN_CFLAGS})
- endforeach()
+add_compiler_rt_object_libraries(RTLSanCommon
+ OS ${SANITIZER_COMMON_SUPPORTED_OS}
+ ARCHS ${LSAN_COMMON_SUPPORTED_ARCH}
+ SOURCES ${LSAN_COMMON_SOURCES}
+ CFLAGS ${LSAN_CFLAGS})
+if(COMPILER_RT_HAS_LSAN)
foreach(arch ${LSAN_SUPPORTED_ARCH})
add_compiler_rt_runtime(clang_rt.lsan-${arch} ${arch} STATIC
SOURCES ${LSAN_SOURCES}
diff --git a/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc
index b9e2a1104326..0ffba505cc70 100644
--- a/lib/lsan/lsan_common.cc
+++ b/lib/lsan/lsan_common.cc
@@ -21,7 +21,6 @@
#include "sanitizer_common/sanitizer_procmaps.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "sanitizer_common/sanitizer_stoptheworld.h"
#include "sanitizer_common/sanitizer_suppressions.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
@@ -127,13 +126,14 @@ static inline bool CanBeAHeapPointer(uptr p) {
// Scans the memory range, looking for byte patterns that point into allocator
// chunks. Marks those chunks with |tag| and adds them to |frontier|.
-// There are two usage modes for this function: finding reachable or ignored
-// chunks (|tag| = kReachable or kIgnored) and finding indirectly leaked chunks
+// There are two usage modes for this function: finding reachable chunks
+// (|tag| = kReachable) and finding indirectly leaked chunks
// (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill,
// so |frontier| = 0.
void ScanRangeForPointers(uptr begin, uptr end,
Frontier *frontier,
const char *region_type, ChunkTag tag) {
+ CHECK(tag == kReachable || tag == kIndirectlyLeaked);
const uptr alignment = flags()->pointer_alignment();
LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, begin, end);
uptr pp = begin;
@@ -147,9 +147,7 @@ void ScanRangeForPointers(uptr begin, uptr end,
// Pointers to self don't count. This matters when tag == kIndirectlyLeaked.
if (chunk == begin) continue;
LsanMetadata m(chunk);
- // Reachable beats ignored beats leaked.
- if (m.tag() == kReachable) continue;
- if (m.tag() == kIgnored && tag != kReachable) continue;
+ if (m.tag() == kReachable || m.tag() == kIgnored) continue;
// Do this check relatively late so we can log only the interesting cases.
if (!flags()->use_poisoned && WordIsPoisoned(pp)) {
@@ -288,7 +286,7 @@ static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) {
LsanMetadata m(chunk);
if (m.allocated() && m.tag() != kReachable) {
ScanRangeForPointers(chunk, chunk + m.requested_size(),
- /* frontier */ 0, "HEAP", kIndirectlyLeaked);
+ /* frontier */ nullptr, "HEAP", kIndirectlyLeaked);
}
}
@@ -298,8 +296,11 @@ static void CollectIgnoredCb(uptr chunk, void *arg) {
CHECK(arg);
chunk = GetUserBegin(chunk);
LsanMetadata m(chunk);
- if (m.allocated() && m.tag() == kIgnored)
+ if (m.allocated() && m.tag() == kIgnored) {
+ LOG_POINTERS("Ignored: chunk %p-%p of size %zu.\n",
+ chunk, chunk + m.requested_size(), m.requested_size());
reinterpret_cast<Frontier *>(arg)->push_back(chunk);
+ }
}
// Sets the appropriate tag on each chunk.
@@ -307,26 +308,33 @@ static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) {
// Holds the flood fill frontier.
Frontier frontier(1);
+ ForEachChunk(CollectIgnoredCb, &frontier);
ProcessGlobalRegions(&frontier);
ProcessThreads(suspended_threads, &frontier);
ProcessRootRegions(&frontier);
FloodFillTag(&frontier, kReachable);
+
// The check here is relatively expensive, so we do this in a separate flood
// fill. That way we can skip the check for chunks that are reachable
// otherwise.
LOG_POINTERS("Processing platform-specific allocations.\n");
+ CHECK_EQ(0, frontier.size());
ProcessPlatformSpecificAllocations(&frontier);
FloodFillTag(&frontier, kReachable);
- LOG_POINTERS("Scanning ignored chunks.\n");
- CHECK_EQ(0, frontier.size());
- ForEachChunk(CollectIgnoredCb, &frontier);
- FloodFillTag(&frontier, kIgnored);
-
// Iterate over leaked chunks and mark those that are reachable from other
// leaked chunks.
LOG_POINTERS("Scanning leaked chunks.\n");
- ForEachChunk(MarkIndirectlyLeakedCb, 0 /* arg */);
+ ForEachChunk(MarkIndirectlyLeakedCb, nullptr);
+}
+
+// ForEachChunk callback. Resets the tags to pre-leak-check state.
+static void ResetTagsCb(uptr chunk, void *arg) {
+ (void)arg;
+ chunk = GetUserBegin(chunk);
+ LsanMetadata m(chunk);
+ if (m.allocated() && m.tag() != kIgnored)
+ m.set_tag(kDirectlyLeaked);
}
static void PrintStackTraceById(u32 stack_trace_id) {
@@ -372,35 +380,33 @@ static void PrintMatchedSuppressions() {
Printf("%s\n\n", line);
}
-struct DoLeakCheckParam {
+struct CheckForLeaksParam {
bool success;
LeakReport leak_report;
};
-static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads,
- void *arg) {
- DoLeakCheckParam *param = reinterpret_cast<DoLeakCheckParam *>(arg);
+static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads,
+ void *arg) {
+ CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam *>(arg);
CHECK(param);
CHECK(!param->success);
ClassifyAllChunks(suspended_threads);
ForEachChunk(CollectLeaksCb, &param->leak_report);
+ // Clean up for subsequent leak checks. This assumes we did not overwrite any
+ // kIgnored tags.
+ ForEachChunk(ResetTagsCb, nullptr);
param->success = true;
}
-void DoLeakCheck() {
- EnsureMainThreadIDIsCorrect();
- BlockingMutexLock l(&global_mutex);
- static bool already_done;
- if (already_done) return;
- already_done = true;
+static bool CheckForLeaks() {
if (&__lsan_is_turned_off && __lsan_is_turned_off())
- return;
-
- DoLeakCheckParam param;
+ return false;
+ EnsureMainThreadIDIsCorrect();
+ CheckForLeaksParam param;
param.success = false;
LockThreadRegistry();
LockAllocator();
- StopTheWorld(DoLeakCheckCallback, &param);
+ DoStopTheWorld(CheckForLeaksCallback, &param);
UnlockAllocator();
UnlockThreadRegistry();
@@ -424,25 +430,42 @@ void DoLeakCheck() {
PrintMatchedSuppressions();
if (unsuppressed_count > 0) {
param.leak_report.PrintSummary();
- if (flags()->exitcode) {
- if (common_flags()->coverage)
- __sanitizer_cov_dump();
- internal__exit(flags()->exitcode);
- }
+ return true;
}
+ return false;
+}
+
+void DoLeakCheck() {
+ BlockingMutexLock l(&global_mutex);
+ static bool already_done;
+ if (already_done) return;
+ already_done = true;
+ bool have_leaks = CheckForLeaks();
+ if (!have_leaks) {
+ return;
+ }
+ if (flags()->exitcode) {
+ if (common_flags()->coverage)
+ __sanitizer_cov_dump();
+ internal__exit(flags()->exitcode);
+ }
+}
+
+static int DoRecoverableLeakCheck() {
+ BlockingMutexLock l(&global_mutex);
+ bool have_leaks = CheckForLeaks();
+ return have_leaks ? 1 : 0;
}
static Suppression *GetSuppressionForAddr(uptr addr) {
Suppression *s = nullptr;
// Suppress by module name.
- const char *module_name;
- uptr module_offset;
SuppressionContext *suppressions = GetSuppressionContext();
- if (Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(addr, &module_name,
- &module_offset) &&
- suppressions->Match(module_name, kSuppressionLeak, &s))
- return s;
+ if (const char *module_name =
+ Symbolizer::GetOrInit()->GetModuleNameForPc(addr))
+ if (suppressions->Match(module_name, kSuppressionLeak, &s))
+ return s;
// Suppress by file or function name.
SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr);
@@ -679,6 +702,15 @@ void __lsan_do_leak_check() {
#endif // CAN_SANITIZE_LEAKS
}
+SANITIZER_INTERFACE_ATTRIBUTE
+int __lsan_do_recoverable_leak_check() {
+#if CAN_SANITIZE_LEAKS
+ if (common_flags()->detect_leaks)
+ return __lsan::DoRecoverableLeakCheck();
+#endif // CAN_SANITIZE_LEAKS
+ return 0;
+}
+
#if !SANITIZER_SUPPORTS_WEAK_HOOKS
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
int __lsan_is_turned_off() {
diff --git a/lib/lsan/lsan_common.h b/lib/lsan/lsan_common.h
index 2c3a12ab6bd8..4f9d24fb3ab9 100644
--- a/lib/lsan/lsan_common.h
+++ b/lib/lsan/lsan_common.h
@@ -19,6 +19,7 @@
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_platform.h"
+#include "sanitizer_common/sanitizer_stoptheworld.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips64)) \
@@ -99,6 +100,8 @@ typedef InternalMmapVector<uptr> Frontier;
void InitializePlatformSpecificModules();
void ProcessGlobalRegions(Frontier *frontier);
void ProcessPlatformSpecificAllocations(Frontier *frontier);
+// Run stoptheworld while holding any platform-specific locks.
+void DoStopTheWorld(StopTheWorldCallback callback, void* argument);
void ScanRangeForPointers(uptr begin, uptr end,
Frontier *frontier,
diff --git a/lib/lsan/lsan_common_linux.cc b/lib/lsan/lsan_common_linux.cc
index ba51868c76e8..2955343e1f0b 100644
--- a/lib/lsan/lsan_common_linux.cc
+++ b/lib/lsan/lsan_common_linux.cc
@@ -85,10 +85,6 @@ static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size,
// Scans global variables for heap pointers.
void ProcessGlobalRegions(Frontier *frontier) {
if (!flags()->use_globals) return;
- // FIXME: dl_iterate_phdr acquires a linker lock, so we run a risk of
- // deadlocking by running this under StopTheWorld. However, the lock is
- // reentrant, so we should be able to fix this by acquiring the lock before
- // suspending threads.
dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
}
@@ -114,7 +110,7 @@ static void ProcessPlatformSpecificAllocationsCb(uptr chunk, void *arg) {
reinterpret_cast<ProcessPlatformAllocParam *>(arg);
chunk = GetUserBegin(chunk);
LsanMetadata m(chunk);
- if (m.allocated() && m.tag() != kReachable) {
+ if (m.allocated() && m.tag() != kReachable && m.tag() != kIgnored) {
u32 stack_id = m.stack_trace_id();
uptr caller_pc = 0;
if (stack_id > 0)
@@ -153,5 +149,30 @@ void ProcessPlatformSpecificAllocations(Frontier *frontier) {
ForEachChunk(ProcessPlatformSpecificAllocationsCb, &arg);
}
+struct DoStopTheWorldParam {
+ StopTheWorldCallback callback;
+ void *argument;
+};
+
+static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size,
+ void *data) {
+ DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data);
+ StopTheWorld(param->callback, param->argument);
+ return 1;
+}
+
+// LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one
+// of the threads is frozen while holding the libdl lock, the tracer will hang
+// in dl_iterate_phdr() forever.
+// Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the
+// tracer task and the thread that spawned it. Thus, if we run the tracer task
+// while holding the libdl lock in the parent thread, we can safely reenter it
+// in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr()
+// callback in the parent thread.
+void DoStopTheWorld(StopTheWorldCallback callback, void *argument) {
+ DoStopTheWorldParam param = {callback, argument};
+ dl_iterate_phdr(DoStopTheWorldCallback, &param);
+}
+
} // namespace __lsan
#endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX
diff --git a/lib/lsan/lsan_interceptors.cc b/lib/lsan/lsan_interceptors.cc
index ba2519dcb25d..61a92154d95e 100644
--- a/lib/lsan/lsan_interceptors.cc
+++ b/lib/lsan/lsan_interceptors.cc
@@ -208,7 +208,7 @@ extern "C" void *__lsan_thread_start_func(void *arg) {
// Wait until the last iteration to maximize the chance that we are the last
// destructor to run.
if (pthread_setspecific(g_thread_finalize_key,
- (void*)kPthreadDestructorIterations)) {
+ (void*)GetPthreadDestructorIterations())) {
Report("LeakSanitizer: failed to set thread key.\n");
Die();
}
diff --git a/lib/lsan/lsan_thread.h b/lib/lsan/lsan_thread.h
index 4641b32ed6e6..99e2c1d5e64b 100644
--- a/lib/lsan/lsan_thread.h
+++ b/lib/lsan/lsan_thread.h
@@ -22,8 +22,8 @@ namespace __lsan {
class ThreadContext : public ThreadContextBase {
public:
explicit ThreadContext(int tid);
- void OnStarted(void *arg);
- void OnFinished();
+ void OnStarted(void *arg) override;
+ void OnFinished() override;
uptr stack_begin() { return stack_begin_; }
uptr stack_end() { return stack_end_; }
uptr tls_begin() { return tls_begin_; }
diff --git a/lib/msan/CMakeLists.txt b/lib/msan/CMakeLists.txt
index ccf47fc45cf3..de5980e5644b 100644
--- a/lib/msan/CMakeLists.txt
+++ b/lib/msan/CMakeLists.txt
@@ -7,12 +7,15 @@ set(MSAN_RTL_SOURCES
msan_chained_origin_depot.cc
msan_interceptors.cc
msan_linux.cc
- msan_new_delete.cc
msan_report.cc
msan_thread.cc
msan_poisoning.cc
)
+set(MSAN_RTL_CXX_SOURCES
+ msan_new_delete.cc)
+
+
set(MSAN_RTL_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_no_rtti_flag(MSAN_RTL_CFLAGS)
append_list_if(COMPILER_RT_HAS_FPIE_FLAG -fPIE MSAN_RTL_CFLAGS)
@@ -29,12 +32,21 @@ foreach(arch ${MSAN_SUPPORTED_ARCH})
$<TARGET_OBJECTS:RTInterception.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
+ $<TARGET_OBJECTS:RTUbsan.${arch}>
+ CFLAGS ${MSAN_RTL_CFLAGS})
+ add_compiler_rt_runtime(clang_rt.msan_cxx-${arch} ${arch} STATIC
+ SOURCES ${MSAN_RTL_CXX_SOURCES}
+ $<TARGET_OBJECTS:RTUbsan_cxx.${arch}>
CFLAGS ${MSAN_RTL_CFLAGS})
- add_dependencies(msan clang_rt.msan-${arch})
- list(APPEND MSAN_RUNTIME_LIBRARIES clang_rt.msan-${arch})
+ add_dependencies(msan clang_rt.msan-${arch}
+ clang_rt.msan_cxx-${arch})
+ list(APPEND MSAN_RUNTIME_LIBRARIES clang_rt.msan-${arch}
+ clang_rt.msan_cxx-${arch})
if(UNIX)
add_sanitizer_rt_symbols(clang_rt.msan-${arch} msan.syms.extra)
- add_dependencies(msan clang_rt.msan-${arch}-symbols)
+ add_sanitizer_rt_symbols(clang_rt.msan_cxx-${arch} msan.syms.extra)
+ add_dependencies(msan clang_rt.msan-${arch}-symbols
+ clang_rt.msan_cxx-${arch}-symbols)
endif()
endforeach()
diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc
index ed6efbdd682f..163d59dabfa8 100644
--- a/lib/msan/msan.cc
+++ b/lib/msan/msan.cc
@@ -26,6 +26,8 @@
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "ubsan/ubsan_flags.h"
+#include "ubsan/ubsan_init.h"
// ACHTUNG! No system header includes in this file.
@@ -133,11 +135,6 @@ static void RegisterMsanFlags(FlagParser *parser, Flags *f) {
}
static void InitializeFlags() {
- Flags *f = flags();
- FlagParser parser;
- RegisterMsanFlags(&parser, f);
- RegisterCommonFlags(&parser);
-
SetCommonFlagsDefaults();
{
CommonFlags cf;
@@ -151,14 +148,35 @@ static void InitializeFlags() {
OverrideCommonFlags(cf);
}
+ Flags *f = flags();
f->SetDefaults();
+ FlagParser parser;
+ RegisterMsanFlags(&parser, f);
+ RegisterCommonFlags(&parser);
+
+#if MSAN_CONTAINS_UBSAN
+ __ubsan::Flags *uf = __ubsan::flags();
+ uf->SetDefaults();
+
+ FlagParser ubsan_parser;
+ __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
+ RegisterCommonFlags(&ubsan_parser);
+#endif
+
// Override from user-specified string.
if (__msan_default_options)
parser.ParseString(__msan_default_options());
+#if MSAN_CONTAINS_UBSAN
+ const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
+ ubsan_parser.ParseString(ubsan_default_options);
+#endif
const char *msan_options = GetEnv("MSAN_OPTIONS");
parser.ParseString(msan_options);
+#if MSAN_CONTAINS_UBSAN
+ ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
+#endif
VPrintf(1, "MSAN_OPTIONS: %s\n", msan_options ? msan_options : "<empty>");
SetVerbosity(common_flags()->verbosity);
@@ -355,13 +373,12 @@ void __msan_init() {
InitTlsSize();
InitializeFlags();
+ CacheBinaryName();
__sanitizer_set_report_path(common_flags()->log_path);
InitializeInterceptors();
InstallAtExitHandler(); // Needs __cxa_atexit interceptor.
- if (MSAN_REPLACE_OPERATORS_NEW_AND_DELETE)
- ReplaceOperatorsNewAndDelete();
DisableCoreDumperIfNecessary();
if (StackSizeIsUnlimited()) {
VPrintf(1, "Unlimited stack, doing reexec\n");
@@ -374,7 +391,7 @@ void __msan_init() {
__msan_clear_on_return();
if (__msan_get_track_origins())
VPrintf(1, "msan_track_origins\n");
- if (!InitShadow(/* map_shadow */ true, __msan_get_track_origins())) {
+ if (!InitShadow(__msan_get_track_origins())) {
Printf("FATAL: MemorySanitizer can not mmap the shadow memory.\n");
Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
Printf("FATAL: Disabling ASLR is known to cause this error.\n");
@@ -394,6 +411,10 @@ void __msan_init() {
SetCurrentThread(main_thread);
main_thread->ThreadStart();
+#if MSAN_CONTAINS_UBSAN
+ __ubsan::InitAsPlugin();
+#endif
+
VPrintf(1, "MemorySanitizer init done\n");
msan_init_is_running = 0;
@@ -543,6 +564,13 @@ u32 __msan_get_origin(const void *a) {
return *(u32*)origin_ptr;
}
+int __msan_origin_is_descendant_or_same(u32 this_id, u32 prev_id) {
+ Origin o = Origin::FromRawId(this_id);
+ while (o.raw_id() != prev_id && o.isChainedOrigin())
+ o = o.getNextChainedOrigin(nullptr);
+ return o.raw_id() == prev_id;
+}
+
u32 __msan_get_umr_origin() {
return __msan_origin_tls;
}
diff --git a/lib/msan/msan.h b/lib/msan/msan.h
index ed18f21d0282..cd8bc19f51ef 100644
--- a/lib/msan/msan.h
+++ b/lib/msan/msan.h
@@ -20,11 +20,16 @@
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "msan_interface_internal.h"
#include "msan_flags.h"
+#include "ubsan/ubsan_platform.h"
#ifndef MSAN_REPLACE_OPERATORS_NEW_AND_DELETE
# define MSAN_REPLACE_OPERATORS_NEW_AND_DELETE 1
#endif
+#ifndef MSAN_CONTAINS_UBSAN
+# define MSAN_CONTAINS_UBSAN CAN_SANITIZE_UB
+#endif
+
struct MappingDesc {
uptr start;
uptr end;
@@ -47,6 +52,25 @@ const MappingDesc kMemoryLayout[] = {
#define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x4000000000ULL)
#define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x002000000000)
+#elif SANITIZER_LINUX && defined(__powerpc64__)
+
+const MappingDesc kMemoryLayout[] = {
+ {0x000000000000ULL, 0x000100000000ULL, MappingDesc::APP, "low memory"},
+ {0x000100000000ULL, 0x080000000000ULL, MappingDesc::INVALID, "invalid"},
+ {0x080000000000ULL, 0x180100000000ULL, MappingDesc::SHADOW, "shadow"},
+ {0x180100000000ULL, 0x1C0000000000ULL, MappingDesc::INVALID, "invalid"},
+ {0x1C0000000000ULL, 0x2C0100000000ULL, MappingDesc::ORIGIN, "origin"},
+ {0x2C0100000000ULL, 0x300000000000ULL, MappingDesc::INVALID, "invalid"},
+ {0x300000000000ULL, 0x400000000000ULL, MappingDesc::APP, "high memory"}};
+
+// Maps low and high app ranges to contiguous space with zero base:
+// Low: 0000 0000 0000 - 0000 ffff ffff -> 1000 0000 0000 - 1000 ffff ffff
+// High: 3000 0000 0000 - 3fff ffff ffff -> 0000 0000 0000 - 0fff ffff ffff
+#define LINEARIZE_MEM(mem) \
+ (((uptr)(mem) & ~0x200000000000ULL) ^ 0x100000000000ULL)
+#define MEM_TO_SHADOW(mem) (LINEARIZE_MEM((mem)) + 0x080000000000ULL)
+#define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x140000000000ULL)
+
#elif SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 64
// Low memory: main binary, MAP_32BIT mappings and modules
@@ -120,7 +144,7 @@ extern bool msan_init_is_running;
extern int msan_report_count;
bool ProtectRange(uptr beg, uptr end);
-bool InitShadow(bool map_shadow, bool init_origins);
+bool InitShadow(bool init_origins);
char *GetProcSelfMaps();
void InitializeInterceptors();
@@ -131,7 +155,6 @@ void *MsanReallocate(StackTrace *stack, void *oldp, uptr size,
void MsanDeallocate(StackTrace *stack, void *ptr);
void InstallTrapHandler();
void InstallAtExitHandler();
-void ReplaceOperatorsNewAndDelete();
const char *GetStackOriginDescr(u32 id, uptr *pc);
diff --git a/lib/msan/msan.syms.extra b/lib/msan/msan.syms.extra
index aad41cf1124e..950e6f4ad5ad 100644
--- a/lib/msan/msan.syms.extra
+++ b/lib/msan/msan.syms.extra
@@ -1 +1,2 @@
__msan_*
+__ubsan_*
diff --git a/lib/msan/msan_allocator.cc b/lib/msan/msan_allocator.cc
index 698b6cddd30b..6df35664279f 100644
--- a/lib/msan/msan_allocator.cc
+++ b/lib/msan/msan_allocator.cc
@@ -58,6 +58,15 @@ struct MsanMapUnmapCallback {
typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, kMetadataSize,
DefaultSizeClassMap,
MsanMapUnmapCallback> PrimaryAllocator;
+#elif defined(__powerpc64__)
+ static const uptr kAllocatorSpace = 0x300000000000;
+ static const uptr kAllocatorSize = 0x020000000000; // 2T
+ static const uptr kMetadataSize = sizeof(Metadata);
+ static const uptr kMaxAllowedMallocSize = 2UL << 30; // 2G
+
+ typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, kMetadataSize,
+ DefaultSizeClassMap,
+ MsanMapUnmapCallback> PrimaryAllocator;
#endif
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
typedef LargeMmapAllocator<MsanMapUnmapCallback> SecondaryAllocator;
diff --git a/lib/msan/msan_interceptors.cc b/lib/msan/msan_interceptors.cc
index 4a243941b8a3..6d5a056a3bb3 100644
--- a/lib/msan/msan_interceptors.cc
+++ b/lib/msan/msan_interceptors.cc
@@ -94,6 +94,13 @@ bool IsInInterceptorScope() {
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) )
+
+#define CHECK_UNPOISONED_STRING(x, n) \
+ CHECK_UNPOISONED_STRING_OF_LEN((x), internal_strlen(x), (n))
+
INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) {
ENSURE_MSAN_INITED();
SIZE_T res = REAL(fread)(ptr, size, nmemb, file);
@@ -118,6 +125,7 @@ INTERCEPTOR(SIZE_T, fread_unlocked, void *ptr, SIZE_T size, SIZE_T nmemb,
INTERCEPTOR(SSIZE_T, readlink, const char *path, char *buf, SIZE_T bufsiz) {
ENSURE_MSAN_INITED();
+ CHECK_UNPOISONED_STRING(path, 0)
SSIZE_T res = REAL(readlink)(path, buf, bufsiz);
if (res > 0)
__msan_unpoison(buf, res);
@@ -283,13 +291,11 @@ INTERCEPTOR(SIZE_T, strnlen, const char *s, SIZE_T n) {
return res;
}
-// FIXME: Add stricter shadow checks in str* interceptors (ex.: strcpy should
-// check the shadow of the terminating \0 byte).
-
INTERCEPTOR(char *, strcpy, char *dest, const char *src) { // NOLINT
ENSURE_MSAN_INITED();
GET_STORE_STACK_TRACE;
SIZE_T n = REAL(strlen)(src);
+ CHECK_UNPOISONED_STRING(src + n, 0);
char *res = REAL(strcpy)(dest, src); // NOLINT
CopyShadowAndOrigin(dest, src, n + 1, &stack);
return res;
@@ -311,6 +317,7 @@ INTERCEPTOR(char *, stpcpy, char *dest, const char *src) { // NOLINT
ENSURE_MSAN_INITED();
GET_STORE_STACK_TRACE;
SIZE_T n = REAL(strlen)(src);
+ CHECK_UNPOISONED_STRING(src + n, 0);
char *res = REAL(stpcpy)(dest, src); // NOLINT
CopyShadowAndOrigin(dest, src, n + 1, &stack);
return res;
@@ -322,6 +329,7 @@ INTERCEPTOR(char *, strdup, char *src) {
// On FreeBSD strdup() leverages strlen().
InterceptorScope interceptor_scope;
SIZE_T n = REAL(strlen)(src);
+ CHECK_UNPOISONED_STRING(src + n, 0);
char *res = REAL(strdup)(src);
CopyShadowAndOrigin(res, src, n + 1, &stack);
return res;
@@ -332,6 +340,7 @@ INTERCEPTOR(char *, __strdup, char *src) {
ENSURE_MSAN_INITED();
GET_STORE_STACK_TRACE;
SIZE_T n = REAL(strlen)(src);
+ CHECK_UNPOISONED_STRING(src + n, 0);
char *res = REAL(__strdup)(src);
CopyShadowAndOrigin(res, src, n + 1, &stack);
return res;
@@ -381,6 +390,8 @@ INTERCEPTOR(char *, strcat, char *dest, const char *src) { // NOLINT
GET_STORE_STACK_TRACE;
SIZE_T src_size = REAL(strlen)(src);
SIZE_T dest_size = REAL(strlen)(dest);
+ CHECK_UNPOISONED_STRING(src + src_size, 0);
+ CHECK_UNPOISONED_STRING(dest + dest_size, 0);
char *res = REAL(strcat)(dest, src); // NOLINT
CopyShadowAndOrigin(dest + dest_size, src, src_size + 1, &stack);
return res;
@@ -391,6 +402,7 @@ INTERCEPTOR(char *, strncat, char *dest, const char *src, SIZE_T n) { // NOLINT
GET_STORE_STACK_TRACE;
SIZE_T dest_size = REAL(strlen)(dest);
SIZE_T copy_size = REAL(strnlen)(src, n);
+ CHECK_UNPOISONED_STRING(dest + dest_size, 0);
char *res = REAL(strncat)(dest, src, n); // NOLINT
CopyShadowAndOrigin(dest + dest_size, src, copy_size, &stack);
__msan_unpoison(dest + dest_size + copy_size, 1); // \0
@@ -667,6 +679,7 @@ static void UnpoisonEnviron() {
INTERCEPTOR(int, setenv, const char *name, const char *value, int overwrite) {
ENSURE_MSAN_INITED();
+ CHECK_UNPOISONED_STRING(name, 0)
int res = REAL(setenv)(name, value, overwrite);
if (!res) UnpoisonEnviron();
return res;
@@ -1384,6 +1397,14 @@ int OnExit() {
if (map) ForEachMappedRegion(map, __msan_unpoison); \
} while (false)
+#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
+ if (MsanThread *t = GetCurrentThread()) { \
+ *begin = t->tls_begin(); \
+ *end = t->tls_end(); \
+ } else { \
+ *begin = *end = 0; \
+ }
+
#include "sanitizer_common/sanitizer_common_interceptors.inc"
#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s)
@@ -1420,7 +1441,8 @@ void __msan_clear_and_unpoison(void *a, uptr size) {
void *__msan_memcpy(void *dest, const void *src, SIZE_T n) {
if (!msan_inited) return internal_memcpy(dest, src, n);
- if (msan_init_is_running) return REAL(memcpy)(dest, src, n);
+ if (msan_init_is_running || __msan::IsInSymbolizer())
+ return REAL(memcpy)(dest, src, n);
ENSURE_MSAN_INITED();
GET_STORE_STACK_TRACE;
void *res = REAL(memcpy)(dest, src, n);
diff --git a/lib/msan/msan_interface_internal.h b/lib/msan/msan_interface_internal.h
index 8641f814bc5f..f4d37d96c5b5 100644
--- a/lib/msan/msan_interface_internal.h
+++ b/lib/msan/msan_interface_internal.h
@@ -96,6 +96,13 @@ u32 __msan_chain_origin(u32 id);
SANITIZER_INTERFACE_ATTRIBUTE
u32 __msan_get_origin(const void *a);
+// Test that this_id is a descendant of prev_id (or they are simply equal).
+// "descendant" here means that are part of the same chain, created with
+// __msan_chain_origin.
+SANITIZER_INTERFACE_ATTRIBUTE
+int __msan_origin_is_descendant_or_same(u32 this_id, u32 prev_id);
+
+
SANITIZER_INTERFACE_ATTRIBUTE
void __msan_clear_on_return();
diff --git a/lib/msan/msan_linux.cc b/lib/msan/msan_linux.cc
index 6c185165fc50..7025ef6c812d 100644
--- a/lib/msan/msan_linux.cc
+++ b/lib/msan/msan_linux.cc
@@ -53,10 +53,19 @@ static bool CheckMemoryRangeAvailability(uptr beg, uptr size) {
return true;
}
-static bool ProtectMemoryRange(uptr beg, uptr size) {
+static bool ProtectMemoryRange(uptr beg, uptr size, const char *name) {
if (size > 0) {
- uptr end = beg + size - 1;
- if (!Mprotect(beg, size)) {
+ void *addr = MmapNoAccess(beg, size, name);
+ if (beg == 0 && addr != 0) {
+ // Depending on the kernel configuration, we may not be able to protect
+ // the page at address zero.
+ uptr gap = 16 * GetPageSizeCached();
+ beg += gap;
+ size -= gap;
+ addr = MmapNoAccess(beg, size, name);
+ }
+ if ((uptr)addr != beg) {
+ uptr end = beg + size - 1;
Printf("FATAL: Cannot protect memory range %p - %p.\n", beg, end);
return false;
}
@@ -95,7 +104,7 @@ static void CheckMemoryLayoutSanity() {
}
}
-bool InitShadow(bool map_shadow, bool init_origins) {
+bool InitShadow(bool init_origins) {
// Let user know mapping parameters first.
VPrintf(1, "__msan_init %p\n", &__msan_init);
for (unsigned i = 0; i < kMemoryLayoutSize; ++i)
@@ -115,15 +124,27 @@ bool InitShadow(bool map_shadow, bool init_origins) {
uptr end = kMemoryLayout[i].end;
uptr size= end - start;
MappingDesc::Type type = kMemoryLayout[i].type;
- if ((map_shadow && type == MappingDesc::SHADOW) ||
- (init_origins && type == MappingDesc::ORIGIN)) {
- if (!CheckMemoryRangeAvailability(start, size)) return false;
- if ((uptr)MmapFixedNoReserve(start, size) != start) return false;
+
+ bool map = type == MappingDesc::SHADOW ||
+ (init_origins && type == MappingDesc::ORIGIN);
+ bool protect = type == MappingDesc::INVALID ||
+ (!init_origins && type == MappingDesc::ORIGIN);
+ CHECK(!(map && protect));
+ if (!map && !protect)
+ CHECK(type == MappingDesc::APP);
+ if (map) {
+ if (!CheckMemoryRangeAvailability(start, size))
+ return false;
+ if ((uptr)MmapFixedNoReserve(start, size, kMemoryLayout[i].name) != start)
+ return false;
if (common_flags()->use_madv_dontdump)
DontDumpShadowMemory(start, size);
- } else if (type == MappingDesc::INVALID) {
- if (!CheckMemoryRangeAvailability(start, size)) return false;
- if (!ProtectMemoryRange(start, size)) return false;
+ }
+ if (protect) {
+ if (!CheckMemoryRangeAvailability(start, size))
+ return false;
+ if (!ProtectMemoryRange(start, size, kMemoryLayout[i].name))
+ return false;
}
}
diff --git a/lib/msan/msan_new_delete.cc b/lib/msan/msan_new_delete.cc
index 9a8e56e4a28a..c8bc0651b507 100644
--- a/lib/msan/msan_new_delete.cc
+++ b/lib/msan/msan_new_delete.cc
@@ -19,13 +19,6 @@
#include <stddef.h>
-namespace __msan {
-// This function is a no-op. We need it to make sure that object file
-// with our replacements will actually be loaded from static MSan
-// run-time library at link-time.
-void ReplaceOperatorsNewAndDelete() { }
-}
-
using namespace __msan; // NOLINT
// Fake std::nothrow_t to avoid including <new>.
diff --git a/lib/msan/msan_origin.h b/lib/msan/msan_origin.h
index 1284c01bf2af..36c168b8521c 100644
--- a/lib/msan/msan_origin.h
+++ b/lib/msan/msan_origin.h
@@ -87,7 +87,7 @@ class Origin {
CHECK(isChainedOrigin());
u32 prev_id;
u32 stack_id = ChainedOriginDepotGet(getChainedId(), &prev_id);
- *stack = StackDepotGet(stack_id);
+ if (stack) *stack = StackDepotGet(stack_id);
return Origin(prev_id);
}
diff --git a/lib/msan/msan_poisoning.cc b/lib/msan/msan_poisoning.cc
index 96411fdbc31b..92134f6a15b8 100644
--- a/lib/msan/msan_poisoning.cc
+++ b/lib/msan/msan_poisoning.cc
@@ -122,7 +122,7 @@ void CopyMemory(void *dst, const void *src, uptr size, StackTrace *stack) {
void SetShadow(const void *ptr, uptr size, u8 value) {
uptr PageSize = GetPageSizeCached();
uptr shadow_beg = MEM_TO_SHADOW(ptr);
- uptr shadow_end = MEM_TO_SHADOW((uptr)ptr + size);
+ uptr shadow_end = shadow_beg + size;
if (value ||
shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) {
REAL(memset)((void *)shadow_beg, value, shadow_end - shadow_beg);
diff --git a/lib/msan/msan_report.cc b/lib/msan/msan_report.cc
index 33c28b2fba0e..ddb8070282a9 100644
--- a/lib/msan/msan_report.cc
+++ b/lib/msan/msan_report.cc
@@ -103,7 +103,7 @@ void ReportUMR(StackTrace *stack, u32 origin) {
Decorator d;
Printf("%s", d.Warning());
- Report(" WARNING: MemorySanitizer: use-of-uninitialized-value\n");
+ Report("WARNING: MemorySanitizer: use-of-uninitialized-value\n");
Printf("%s", d.End());
stack->Print();
if (origin) {
@@ -115,7 +115,7 @@ void ReportUMR(StackTrace *stack, u32 origin) {
void ReportExpectedUMRNotFound(StackTrace *stack) {
SpinMutexLock l(&CommonSanitizerReportMutex);
- Printf(" WARNING: Expected use of uninitialized value not found\n");
+ Printf("WARNING: Expected use of uninitialized value not found\n");
stack->Print();
}
diff --git a/lib/msan/msan_thread.cc b/lib/msan/msan_thread.cc
index e15a247c6bb8..0ba499350064 100644
--- a/lib/msan/msan_thread.cc
+++ b/lib/msan/msan_thread.cc
@@ -14,7 +14,7 @@ MsanThread *MsanThread::Create(thread_callback_t start_routine,
MsanThread *thread = (MsanThread*)MmapOrDie(size, __func__);
thread->start_routine_ = start_routine;
thread->arg_ = arg;
- thread->destructor_iterations_ = kPthreadDestructorIterations;
+ thread->destructor_iterations_ = GetPthreadDestructorIterations();
return thread;
}
diff --git a/lib/msan/tests/CMakeLists.txt b/lib/msan/tests/CMakeLists.txt
index e008bd329cb4..bf16a16bcf20 100644
--- a/lib/msan/tests/CMakeLists.txt
+++ b/lib/msan/tests/CMakeLists.txt
@@ -33,6 +33,7 @@ set(MSAN_UNITTEST_COMMON_CFLAGS
-Wno-deprecated-declarations
-Wno-unused-variable
-Wno-zero-length-array
+ -Wno-uninitialized
-Werror=sign-compare
)
set(MSAN_UNITTEST_INSTRUMENTED_CFLAGS
@@ -70,7 +71,7 @@ macro(msan_compile obj_list source arch kind)
endmacro()
macro(msan_link_shared so_list so_name arch kind)
- parse_arguments(SOURCE "OBJECTS;LINKFLAGS;DEPS" "" ${ARGN})
+ cmake_parse_arguments(SOURCE "" "" "OBJECTS;LINKFLAGS;DEPS" ${ARGN})
set(output_so "${CMAKE_CURRENT_BINARY_DIR}/${so_name}.${arch}${kind}.so")
get_target_flags_for_arch(${arch} TARGET_LINKFLAGS)
if(NOT COMPILER_RT_STANDALONE_BUILD)
diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc
index 317f70cbc32e..00dd20a3d775 100644
--- a/lib/msan/tests/msan_test.cc
+++ b/lib/msan/tests/msan_test.cc
@@ -134,9 +134,12 @@ static bool TrackingOrigins() {
__msan_set_origin(&x, sizeof(x), 0x1234);
U4 origin = __msan_get_origin(&x);
__msan_set_origin(&x, sizeof(x), 0);
- return origin == 0x1234;
+ return __msan_origin_is_descendant_or_same(origin, 0x1234);
}
+#define EXPECT_ORIGIN(expected, origin) \
+ EXPECT_TRUE(__msan_origin_is_descendant_or_same((origin), (expected)))
+
#define EXPECT_UMR(action) \
do { \
__msan_set_expect_umr(1); \
@@ -144,14 +147,13 @@ static bool TrackingOrigins() {
__msan_set_expect_umr(0); \
} while (0)
-#define EXPECT_UMR_O(action, origin) \
- do { \
- __msan_set_expect_umr(1); \
- action; \
- __msan_set_expect_umr(0); \
- if (TrackingOrigins()) \
- EXPECT_EQ(origin, __msan_get_umr_origin()); \
- } while (0)
+#define EXPECT_UMR_O(action, origin) \
+ do { \
+ __msan_set_expect_umr(1); \
+ action; \
+ __msan_set_expect_umr(0); \
+ if (TrackingOrigins()) EXPECT_ORIGIN(origin, __msan_get_umr_origin()); \
+ } while (0)
#define EXPECT_POISONED(x) ExpectPoisoned(x)
@@ -166,8 +168,7 @@ void ExpectPoisoned(const T& t) {
template<typename T>
void ExpectPoisonedWithOrigin(const T& t, unsigned origin) {
EXPECT_NE(-1, __msan_test_shadow((void*)&t, sizeof(t)));
- if (TrackingOrigins())
- EXPECT_EQ(origin, __msan_get_origin((void*)&t));
+ if (TrackingOrigins()) EXPECT_ORIGIN(origin, __msan_get_origin((void *)&t));
}
#define EXPECT_NOT_POISONED(x) EXPECT_EQ(true, TestForNotPoisoned((x)))
@@ -3902,15 +3903,15 @@ TEST(VectorMaddTest, mmx_pmadd_wd) {
#endif // defined(__clang__)
TEST(MemorySanitizerOrigins, SetGet) {
- EXPECT_EQ(TrackingOrigins(), __msan_get_track_origins());
+ EXPECT_EQ(TrackingOrigins(), !!__msan_get_track_origins());
if (!TrackingOrigins()) return;
int x;
__msan_set_origin(&x, sizeof(x), 1234);
- EXPECT_EQ(1234U, __msan_get_origin(&x));
+ EXPECT_ORIGIN(1234U, __msan_get_origin(&x));
__msan_set_origin(&x, sizeof(x), 5678);
- EXPECT_EQ(5678U, __msan_get_origin(&x));
+ EXPECT_ORIGIN(5678U, __msan_get_origin(&x));
__msan_set_origin(&x, sizeof(x), 0);
- EXPECT_EQ(0U, __msan_get_origin(&x));
+ EXPECT_ORIGIN(0U, __msan_get_origin(&x));
}
namespace {
@@ -3926,12 +3927,12 @@ TEST(MemorySanitizerOrigins, InitializedStoreDoesNotChangeOrigin) {
S s;
U4 origin = rand(); // NOLINT
s.a = *GetPoisonedO<U2>(0, origin);
- EXPECT_EQ(origin, __msan_get_origin(&s.a));
- EXPECT_EQ(origin, __msan_get_origin(&s.b));
+ EXPECT_ORIGIN(origin, __msan_get_origin(&s.a));
+ EXPECT_ORIGIN(origin, __msan_get_origin(&s.b));
s.b = 42;
- EXPECT_EQ(origin, __msan_get_origin(&s.a));
- EXPECT_EQ(origin, __msan_get_origin(&s.b));
+ EXPECT_ORIGIN(origin, __msan_get_origin(&s.a));
+ EXPECT_ORIGIN(origin, __msan_get_origin(&s.b));
}
} // namespace
@@ -3947,7 +3948,8 @@ void BinaryOpOriginTest(BinaryOp op) {
*z = op(*x, *y);
U4 origin = __msan_get_origin(z);
EXPECT_POISONED_O(*z, origin);
- EXPECT_EQ(true, origin == ox || origin == oy);
+ EXPECT_EQ(true, __msan_origin_is_descendant_or_same(origin, ox) ||
+ __msan_origin_is_descendant_or_same(origin, oy));
// y is poisoned, x is not.
*x = 10101;
@@ -3956,7 +3958,7 @@ void BinaryOpOriginTest(BinaryOp op) {
__msan_set_origin(z, sizeof(*z), 0);
*z = op(*x, *y);
EXPECT_POISONED_O(*z, oy);
- EXPECT_EQ(__msan_get_origin(z), oy);
+ EXPECT_ORIGIN(oy, __msan_get_origin(z));
// x is poisoned, y is not.
*x = *GetPoisonedO<T>(0, ox);
@@ -3965,7 +3967,7 @@ void BinaryOpOriginTest(BinaryOp op) {
__msan_set_origin(z, sizeof(*z), 0);
*z = op(*x, *y);
EXPECT_POISONED_O(*z, ox);
- EXPECT_EQ(__msan_get_origin(z), ox);
+ EXPECT_ORIGIN(ox, __msan_get_origin(z));
}
template<class T> INLINE T XOR(const T &a, const T&b) { return a ^ b; }
diff --git a/lib/profile/CMakeLists.txt b/lib/profile/CMakeLists.txt
index 420d7660ee36..d03409fc45b7 100644
--- a/lib/profile/CMakeLists.txt
+++ b/lib/profile/CMakeLists.txt
@@ -7,11 +7,12 @@ set(PROFILE_SOURCES
InstrProfilingFile.c
InstrProfilingPlatformDarwin.c
InstrProfilingPlatformOther.c
- InstrProfilingRuntime.cc)
+ InstrProfilingRuntime.cc
+ InstrProfilingUtil.c)
if(APPLE)
add_compiler_rt_osx_static_runtime(clang_rt.profile_osx
- ARCH ${PROFILE_SUPPORTED_ARCH}
+ ARCHS ${PROFILE_SUPPORTED_ARCH}
SOURCES ${PROFILE_SOURCES})
add_dependencies(profile clang_rt.profile_osx)
else()
diff --git a/lib/profile/GCDAProfiling.c b/lib/profile/GCDAProfiling.c
index 45fbd07e544b..aec232856e74 100644
--- a/lib/profile/GCDAProfiling.c
+++ b/lib/profile/GCDAProfiling.c
@@ -20,23 +20,18 @@
|*
\*===----------------------------------------------------------------------===*/
+#include "InstrProfilingUtil.h"
+
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
-#ifdef _WIN32
-#include <direct.h>
-#endif
+#include <sys/file.h>
#define I386_FREEBSD (defined(__FreeBSD__) && defined(__i386__))
-#if !I386_FREEBSD
-#include <sys/stat.h>
-#include <sys/types.h>
-#endif
-
#if !defined(_MSC_VER) && !I386_FREEBSD
#include <stdint.h>
#endif
@@ -51,7 +46,6 @@ typedef unsigned long long uint64_t;
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
-int mkdir(const char*, unsigned short);
#endif
/* #define DEBUG_GCDAPROFILING */
@@ -208,21 +202,6 @@ static char *mangle_filename(const char *orig_filename) {
return new_filename;
}
-static void recursive_mkdir(char *path) {
- int i;
-
- for (i = 1; path[i] != '\0'; ++i) {
- if (path[i] != '/') continue;
- path[i] = '\0';
-#ifdef _WIN32
- _mkdir(path);
-#else
- mkdir(path, 0755); /* Some of these will fail, ignore it. */
-#endif
- path[i] = '/';
- }
-}
-
static int map_file() {
fseek(output_file, 0L, SEEK_END);
file_size = ftell(output_file);
@@ -282,7 +261,7 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4],
fd = open(filename, O_RDWR | O_CREAT, 0644);
if (fd == -1) {
/* Try creating the directories first then opening the file. */
- recursive_mkdir(filename);
+ __llvm_profile_recursive_mkdir(filename);
fd = open(filename, O_RDWR | O_CREAT, 0644);
if (fd == -1) {
/* Bah! It's hopeless. */
@@ -294,6 +273,11 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4],
}
}
+ /* Try to flock the file to serialize concurrent processes writing out to the
+ * same GCDA. This can fail if the filesystem doesn't support it, but in that
+ * case we'll just carry on with the old racy behaviour and hope for the best.
+ */
+ flock(fd, LOCK_EX);
output_file = fdopen(fd, mode);
/* Initialize the write buffer. */
@@ -493,6 +477,7 @@ void llvm_gcda_end_file() {
}
fclose(output_file);
+ flock(fd, LOCK_UN);
output_file = NULL;
write_buffer = NULL;
}
diff --git a/lib/profile/InstrProfiling.h b/lib/profile/InstrProfiling.h
index 2b1bd003668e..3778a88893e6 100644
--- a/lib/profile/InstrProfiling.h
+++ b/lib/profile/InstrProfiling.h
@@ -62,7 +62,9 @@ uint64_t *__llvm_profile_end_counters(void);
*
* Writes to the file with the last name given to \a __llvm_profile_set_filename(),
* or if it hasn't been called, the \c LLVM_PROFILE_FILE environment variable,
- * or if that's not set, \c "default.profdata".
+ * or if that's not set, the last name given to
+ * \a __llvm_profile_override_default_filename(), or if that's not set,
+ * \c "default.profraw".
*/
int __llvm_profile_write_file(void);
@@ -77,6 +79,19 @@ int __llvm_profile_write_file(void);
*/
void __llvm_profile_set_filename(const char *Name);
+/*!
+ * \brief Set the filename for writing instrumentation data, unless the
+ * \c LLVM_PROFILE_FILE environment variable was set.
+ *
+ * Unless overridden, sets the filename to be used for subsequent calls to
+ * \a __llvm_profile_write_file().
+ *
+ * \c Name is not copied, so it must remain valid. Passing NULL resets the
+ * filename logic to the default behaviour (unless the \c LLVM_PROFILE_FILE
+ * was set in which case it has no effect).
+ */
+void __llvm_profile_override_default_filename(const char *Name);
+
/*! \brief Register to write instrumentation data to file at exit. */
int __llvm_profile_register_write_file_atexit(void);
diff --git a/lib/profile/InstrProfilingFile.c b/lib/profile/InstrProfilingFile.c
index 346665fd5b3e..68e8c7b07871 100644
--- a/lib/profile/InstrProfilingFile.c
+++ b/lib/profile/InstrProfilingFile.c
@@ -8,10 +8,11 @@
\*===----------------------------------------------------------------------===*/
#include "InstrProfiling.h"
+#include "InstrProfilingUtil.h"
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/errno.h>
#define UNCONST(ptr) ((void *)(uintptr_t)(ptr))
@@ -76,40 +77,61 @@ static int writeFileWithName(const char *OutputName) {
__attribute__((weak)) int __llvm_profile_OwnsFilename = 0;
__attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL;
-static void setFilename(const char *Filename, int OwnsFilename) {
- if (__llvm_profile_OwnsFilename)
- free(UNCONST(__llvm_profile_CurrentFilename));
-
- __llvm_profile_CurrentFilename = Filename;
- __llvm_profile_OwnsFilename = OwnsFilename;
-}
-
static void truncateCurrentFile(void) {
- const char *Filename = __llvm_profile_CurrentFilename;
+ const char *Filename;
+ FILE *File;
+
+ Filename = __llvm_profile_CurrentFilename;
if (!Filename || !Filename[0])
return;
+ /* Create the directory holding the file, if needed. */
+ if (strchr(Filename, '/')) {
+ char *Copy = malloc(strlen(Filename) + 1);
+ strcpy(Copy, Filename);
+ __llvm_profile_recursive_mkdir(Copy);
+ free(Copy);
+ }
+
/* Truncate the file. Later we'll reopen and append. */
- FILE *File = fopen(Filename, "w");
+ File = fopen(Filename, "w");
if (!File)
return;
fclose(File);
}
-static void setDefaultFilename(void) { setFilename("default.profraw", 0); }
+static void setFilename(const char *Filename, int OwnsFilename) {
+ /* Check if this is a new filename and therefore needs truncation. */
+ int NewFile = !__llvm_profile_CurrentFilename ||
+ (Filename && strcmp(Filename, __llvm_profile_CurrentFilename));
+ if (__llvm_profile_OwnsFilename)
+ free(UNCONST(__llvm_profile_CurrentFilename));
-int getpid(void);
-static int setFilenameFromEnvironment(void) {
- const char *Filename = getenv("LLVM_PROFILE_FILE");
- if (!Filename || !Filename[0])
- return -1;
+ __llvm_profile_CurrentFilename = Filename;
+ __llvm_profile_OwnsFilename = OwnsFilename;
- /* Check the filename for "%p", which indicates a pid-substitution. */
+ /* If not a new file, append to support profiling multiple shared objects. */
+ if (NewFile)
+ truncateCurrentFile();
+}
+
+static void resetFilenameToDefault(void) { setFilename("default.profraw", 0); }
+
+int getpid(void);
+static int setFilenamePossiblyWithPid(const char *Filename) {
#define MAX_PID_SIZE 16
char PidChars[MAX_PID_SIZE] = {0};
- int NumPids = 0;
- int PidLength = 0;
- int I;
+ int NumPids = 0, PidLength = 0;
+ char *Allocated;
+ int I, J;
+
+ /* Reset filename on NULL, except with env var which is checked by caller. */
+ if (!Filename) {
+ resetFilenameToDefault();
+ return 0;
+ }
+
+ /* Check the filename for "%p", which indicates a pid-substitution. */
for (I = 0; Filename[I]; ++I)
if (Filename[I] == '%' && Filename[++I] == 'p')
if (!NumPids++) {
@@ -123,12 +145,11 @@ static int setFilenameFromEnvironment(void) {
}
/* Allocate enough space for the substituted filename. */
- char *Allocated = (char*)malloc(I + NumPids*(PidLength - 2) + 1);
+ Allocated = malloc(I + NumPids*(PidLength - 2) + 1);
if (!Allocated)
return -1;
/* Construct the new filename. */
- int J;
for (I = 0, J = 0; Filename[I]; ++I)
if (Filename[I] == '%') {
if (Filename[++I] == 'p') {
@@ -145,11 +166,20 @@ static int setFilenameFromEnvironment(void) {
return 0;
}
+static int setFilenameFromEnvironment(void) {
+ const char *Filename = getenv("LLVM_PROFILE_FILE");
+
+ if (!Filename || !Filename[0])
+ return -1;
+
+ return setFilenamePossiblyWithPid(Filename);
+}
+
static void setFilenameAutomatically(void) {
if (!setFilenameFromEnvironment())
return;
- setDefaultFilename();
+ resetFilenameToDefault();
}
__attribute__((visibility("hidden")))
@@ -160,23 +190,32 @@ void __llvm_profile_initialize_file(void) {
/* Detect the filename and truncate. */
setFilenameAutomatically();
- truncateCurrentFile();
}
__attribute__((visibility("hidden")))
void __llvm_profile_set_filename(const char *Filename) {
- setFilename(Filename, 0);
- truncateCurrentFile();
+ setFilenamePossiblyWithPid(Filename);
+}
+
+__attribute__((visibility("hidden")))
+void __llvm_profile_override_default_filename(const char *Filename) {
+ /* If the env var is set, skip setting filename from argument. */
+ const char *Env_Filename = getenv("LLVM_PROFILE_FILE");
+ if (Env_Filename && Env_Filename[0])
+ return;
+ setFilenamePossiblyWithPid(Filename);
}
__attribute__((visibility("hidden")))
int __llvm_profile_write_file(void) {
+ int rc;
+
/* Check the filename. */
if (!__llvm_profile_CurrentFilename)
return -1;
/* Write the file. */
- int rc = writeFileWithName(__llvm_profile_CurrentFilename);
+ rc = writeFileWithName(__llvm_profile_CurrentFilename);
if (rc && getenv("LLVM_PROFILE_VERBOSE_ERRORS"))
fprintf(stderr, "LLVM Profile: Failed to write file \"%s\": %s\n",
__llvm_profile_CurrentFilename, strerror(errno));
diff --git a/lib/profile/InstrProfilingUtil.c b/lib/profile/InstrProfilingUtil.c
new file mode 100644
index 000000000000..e146dfca83c8
--- /dev/null
+++ b/lib/profile/InstrProfilingUtil.c
@@ -0,0 +1,35 @@
+/*===- InstrProfilingUtil.c - Support library for PGO instrumentation -----===*\
+|*
+|* The LLVM Compiler Infrastructure
+|*
+|* This file is distributed under the University of Illinois Open Source
+|* License. See LICENSE.TXT for details.
+|*
+\*===----------------------------------------------------------------------===*/
+
+#include "InstrProfilingUtil.h"
+
+#ifdef _WIN32
+#include <direct.h>
+#elif I386_FREEBSD
+int mkdir(const char*, unsigned short);
+#else
+#include <sys/stat.h>
+#include <sys/types.h>
+#endif
+
+__attribute__((visibility("hidden")))
+void __llvm_profile_recursive_mkdir(char *path) {
+ int i;
+
+ for (i = 1; path[i] != '\0'; ++i) {
+ if (path[i] != '/') continue;
+ path[i] = '\0';
+#ifdef _WIN32
+ _mkdir(path);
+#else
+ mkdir(path, 0755); /* Some of these will fail, ignore it. */
+#endif
+ path[i] = '/';
+ }
+}
diff --git a/lib/profile/InstrProfilingUtil.h b/lib/profile/InstrProfilingUtil.h
new file mode 100644
index 000000000000..756b18e7c56d
--- /dev/null
+++ b/lib/profile/InstrProfilingUtil.h
@@ -0,0 +1,16 @@
+/*===- InstrProfilingUtil.h - Support library for PGO instrumentation -----===*\
+|*
+|* The LLVM Compiler Infrastructure
+|*
+|* This file is distributed under the University of Illinois Open Source
+|* License. See LICENSE.TXT for details.
+|*
+\*===----------------------------------------------------------------------===*/
+
+#ifndef PROFILE_INSTRPROFILINGUTIL_H
+#define PROFILE_INSTRPROFILINGUTIL_H
+
+/*! \brief Create a directory tree. */
+void __llvm_profile_recursive_mkdir(char *Pathname);
+
+#endif /* PROFILE_INSTRPROFILINGUTIL_H */
diff --git a/lib/safestack/CMakeLists.txt b/lib/safestack/CMakeLists.txt
new file mode 100644
index 000000000000..1c15d079dbb5
--- /dev/null
+++ b/lib/safestack/CMakeLists.txt
@@ -0,0 +1,28 @@
+add_custom_target(safestack)
+
+set(SAFESTACK_SOURCES safestack.cc)
+
+include_directories(..)
+
+set(SAFESTACK_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+
+if(APPLE)
+ # Build universal binary on APPLE.
+ add_compiler_rt_osx_static_runtime(clang_rt.safestack_osx
+ ARCH ${SAFESTACK_SUPPORTED_ARCH}
+ SOURCES ${SAFESTACK_SOURCES}
+ $<TARGET_OBJECTS:RTInterception.osx>
+ $<TARGET_OBJECTS:RTSanitizerCommon.osx>
+ CFLAGS ${SAFESTACK_CFLAGS})
+ add_dependencies(safestack clang_rt.safestack_osx)
+else()
+ # Otherwise, build separate libraries for each target.
+ foreach(arch ${SAFESTACK_SUPPORTED_ARCH})
+ add_compiler_rt_runtime(clang_rt.safestack-${arch} ${arch} STATIC
+ SOURCES ${SAFESTACK_SOURCES}
+ $<TARGET_OBJECTS:RTInterception.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+ CFLAGS ${SAFESTACK_CFLAGS})
+ add_dependencies(safestack clang_rt.safestack-${arch})
+ endforeach()
+endif()
diff --git a/lib/safestack/safestack.cc b/lib/safestack/safestack.cc
new file mode 100644
index 000000000000..504bd3cd0d99
--- /dev/null
+++ b/lib/safestack/safestack.cc
@@ -0,0 +1,246 @@
+//===-- safestack.cc ------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the runtime support for the safe stack protection
+// mechanism. The runtime manages allocation/deallocation of the unsafe stack
+// for the main thread, as well as all pthreads that are created/destroyed
+// during program execution.
+//
+//===----------------------------------------------------------------------===//
+
+#include <limits.h>
+#include <pthread.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/user.h>
+
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+// TODO: The runtime library does not currently protect the safe stack beyond
+// relying on the system-enforced ASLR. The protection of the (safe) stack can
+// be provided by three alternative features:
+//
+// 1) Protection via hardware segmentation on x86-32 and some x86-64
+// architectures: the (safe) stack segment (implicitly accessed via the %ss
+// segment register) can be separated from the data segment (implicitly
+// accessed via the %ds segment register). Dereferencing a pointer to the safe
+// segment would result in a segmentation fault.
+//
+// 2) Protection via software fault isolation: memory writes that are not meant
+// to access the safe stack can be prevented from doing so through runtime
+// instrumentation. One way to do it is to allocate the safe stack(s) in the
+// upper half of the userspace and bitmask the corresponding upper bit of the
+// memory addresses of memory writes that are not meant to access the safe
+// stack.
+//
+// 3) Protection via information hiding on 64 bit architectures: the location
+// of the safe stack(s) can be randomized through secure mechanisms, and the
+// leakage of the stack pointer can be prevented. Currently, libc can leak the
+// stack pointer in several ways (e.g. in longjmp, signal handling, user-level
+// context switching related functions, etc.). These can be fixed in libc and
+// in other low-level libraries, by either eliminating the escaping/dumping of
+// the stack pointer (i.e., %rsp) when that's possible, or by using
+// encryption/PTR_MANGLE (XOR-ing the dumped stack pointer with another secret
+// we control and protect better, as is already done for setjmp in glibc.)
+// Furthermore, a static machine code level verifier can be ran after code
+// generation to make sure that the stack pointer is never written to memory,
+// or if it is, its written on the safe stack.
+//
+// Finally, while the Unsafe Stack pointer is currently stored in a thread
+// local variable, with libc support it could be stored in the TCB (thread
+// control block) as well, eliminating another level of indirection and making
+// such accesses faster. Alternatively, dedicating a separate register for
+// storing it would also be possible.
+
+/// Minimum stack alignment for the unsafe stack.
+const unsigned kStackAlign = 16;
+
+/// Default size of the unsafe stack. This value is only used if the stack
+/// size rlimit is set to infinity.
+const unsigned kDefaultUnsafeStackSize = 0x2800000;
+
+// TODO: To make accessing the unsafe stack pointer faster, we plan to
+// eventually store it directly in the thread control block data structure on
+// platforms where this structure is pointed to by %fs or %gs. This is exactly
+// the same mechanism as currently being used by the traditional stack
+// protector pass to store the stack guard (see getStackCookieLocation()
+// function above). Doing so requires changing the tcbhead_t struct in glibc
+// on Linux and tcb struct in libc on FreeBSD.
+//
+// For now, store it in a thread-local variable.
+extern "C" {
+__attribute__((visibility(
+ "default"))) __thread void *__safestack_unsafe_stack_ptr = nullptr;
+}
+
+// Per-thread unsafe stack information. It's not frequently accessed, so there
+// it can be kept out of the tcb in normal thread-local variables.
+static __thread void *unsafe_stack_start = nullptr;
+static __thread size_t unsafe_stack_size = 0;
+static __thread size_t unsafe_stack_guard = 0;
+
+static inline void *unsafe_stack_alloc(size_t size, size_t guard) {
+ CHECK_GE(size + guard, size);
+ void *addr = MmapOrDie(size + guard, "unsafe_stack_alloc");
+ MprotectNoAccess((uptr)addr, (uptr)guard);
+ return (char *)addr + guard;
+}
+
+static inline void unsafe_stack_setup(void *start, size_t size, size_t guard) {
+ CHECK_GE((char *)start + size, (char *)start);
+ CHECK_GE((char *)start + guard, (char *)start);
+ void *stack_ptr = (char *)start + size;
+ CHECK_EQ((((size_t)stack_ptr) & (kStackAlign - 1)), 0);
+
+ __safestack_unsafe_stack_ptr = stack_ptr;
+ unsafe_stack_start = start;
+ unsafe_stack_size = size;
+ unsafe_stack_guard = guard;
+}
+
+static void unsafe_stack_free() {
+ if (unsafe_stack_start) {
+ UnmapOrDie((char *)unsafe_stack_start - unsafe_stack_guard,
+ unsafe_stack_size + unsafe_stack_guard);
+ }
+ unsafe_stack_start = nullptr;
+}
+
+/// Thread data for the cleanup handler
+static pthread_key_t thread_cleanup_key;
+
+/// Safe stack per-thread information passed to the thread_start function
+struct tinfo {
+ void *(*start_routine)(void *);
+ void *start_routine_arg;
+
+ void *unsafe_stack_start;
+ size_t unsafe_stack_size;
+ size_t unsafe_stack_guard;
+};
+
+/// Wrap the thread function in order to deallocate the unsafe stack when the
+/// thread terminates by returning from its main function.
+static void *thread_start(void *arg) {
+ struct tinfo *tinfo = (struct tinfo *)arg;
+
+ void *(*start_routine)(void *) = tinfo->start_routine;
+ void *start_routine_arg = tinfo->start_routine_arg;
+
+ // Setup the unsafe stack; this will destroy tinfo content
+ unsafe_stack_setup(tinfo->unsafe_stack_start, tinfo->unsafe_stack_size,
+ tinfo->unsafe_stack_guard);
+
+ // Make sure out thread-specific destructor will be called
+ // FIXME: we can do this only any other specific key is set by
+ // intercepting the pthread_setspecific function itself
+ pthread_setspecific(thread_cleanup_key, (void *)1);
+
+ return start_routine(start_routine_arg);
+}
+
+/// Thread-specific data destructor
+static void thread_cleanup_handler(void *_iter) {
+ // We want to free the unsafe stack only after all other destructors
+ // have already run. We force this function to be called multiple times.
+ // User destructors that might run more then PTHREAD_DESTRUCTOR_ITERATIONS-1
+ // times might still end up executing after the unsafe stack is deallocated.
+ size_t iter = (size_t)_iter;
+ if (iter < PTHREAD_DESTRUCTOR_ITERATIONS) {
+ pthread_setspecific(thread_cleanup_key, (void *)(iter + 1));
+ } else {
+ // This is the last iteration
+ unsafe_stack_free();
+ }
+}
+
+/// Intercept thread creation operation to allocate and setup the unsafe stack
+INTERCEPTOR(int, pthread_create, pthread_t *thread,
+ const pthread_attr_t *attr,
+ void *(*start_routine)(void*), void *arg) {
+
+ size_t size = 0;
+ size_t guard = 0;
+
+ if (attr != NULL) {
+ pthread_attr_getstacksize(attr, &size);
+ pthread_attr_getguardsize(attr, &guard);
+ } else {
+ // get pthread default stack size
+ pthread_attr_t tmpattr;
+ pthread_attr_init(&tmpattr);
+ pthread_attr_getstacksize(&tmpattr, &size);
+ pthread_attr_getguardsize(&tmpattr, &guard);
+ pthread_attr_destroy(&tmpattr);
+ }
+
+ CHECK_NE(size, 0);
+ CHECK_EQ((size & (kStackAlign - 1)), 0);
+ CHECK_EQ((guard & (PAGE_SIZE - 1)), 0);
+
+ void *addr = unsafe_stack_alloc(size, guard);
+ struct tinfo *tinfo =
+ (struct tinfo *)(((char *)addr) + size - sizeof(struct tinfo));
+ tinfo->start_routine = start_routine;
+ tinfo->start_routine_arg = arg;
+ tinfo->unsafe_stack_start = addr;
+ tinfo->unsafe_stack_size = size;
+ tinfo->unsafe_stack_guard = guard;
+
+ return REAL(pthread_create)(thread, attr, thread_start, tinfo);
+}
+
+extern "C" __attribute__((visibility("default")))
+#if !SANITIZER_CAN_USE_PREINIT_ARRAY
+// On ELF platforms, the constructor is invoked using .preinit_array (see below)
+__attribute__((constructor(0)))
+#endif
+void __safestack_init() {
+ // Determine the stack size for the main thread.
+ size_t size = kDefaultUnsafeStackSize;
+ size_t guard = 4096;
+
+ struct rlimit limit;
+ if (getrlimit(RLIMIT_STACK, &limit) == 0 && limit.rlim_cur != RLIM_INFINITY)
+ size = limit.rlim_cur;
+
+ // Allocate unsafe stack for main thread
+ void *addr = unsafe_stack_alloc(size, guard);
+
+ unsafe_stack_setup(addr, size, guard);
+
+ // Initialize pthread interceptors for thread allocation
+ INTERCEPT_FUNCTION(pthread_create);
+
+ // Setup the cleanup handler
+ pthread_key_create(&thread_cleanup_key, thread_cleanup_handler);
+}
+
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
+// On ELF platforms, run safestack initialization before any other constructors.
+// On other platforms we use the constructor attribute to arrange to run our
+// initialization early.
+extern "C" {
+__attribute__((section(".preinit_array"),
+ used)) void (*__safestack_preinit)(void) = __safestack_init;
+}
+#endif
+
+extern "C"
+ __attribute__((visibility("default"))) void *__get_unsafe_stack_start() {
+ return unsafe_stack_start;
+}
+
+extern "C"
+ __attribute__((visibility("default"))) void *__get_unsafe_stack_ptr() {
+ return __safestack_unsafe_stack_ptr;
+}
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index 6eb6ca8fc900..f604c9f201d4 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -27,6 +27,7 @@ set(SANITIZER_SOURCES
sanitizer_suppressions.cc
sanitizer_symbolizer.cc
sanitizer_symbolizer_libbacktrace.cc
+ sanitizer_symbolizer_mac.cc
sanitizer_symbolizer_win.cc
sanitizer_tls_get_addr.cc
sanitizer_thread_registry.cc
@@ -42,7 +43,8 @@ set(SANITIZER_LIBCDEP_SOURCES
sanitizer_stoptheworld_linux_libcdep.cc
sanitizer_symbolizer_libcdep.cc
sanitizer_symbolizer_posix_libcdep.cc
- sanitizer_unwind_posix_libcdep.cc)
+ sanitizer_symbolizer_process_libcdep.cc
+ sanitizer_unwind_linux_libcdep.cc)
# Explicitly list all sanitizer_common headers. Not all of these are
# included in sanitizer_common source files, but we need to depend on
@@ -81,6 +83,7 @@ set(SANITIZER_HEADERS
sanitizer_platform.h
sanitizer_platform_interceptors.h
sanitizer_platform_limits_posix.h
+ sanitizer_posix.h
sanitizer_procmaps.h
sanitizer_quarantine.h
sanitizer_report_decorator.h
@@ -91,7 +94,10 @@ set(SANITIZER_HEADERS
sanitizer_stoptheworld.h
sanitizer_suppressions.h
sanitizer_symbolizer.h
+ sanitizer_symbolizer_internal.h
sanitizer_symbolizer_libbacktrace.h
+ sanitizer_symbolizer_mac.h
+ sanitizer_symbolizer_win.h
sanitizer_syscall_generic.inc
sanitizer_syscall_linux_x86_64.inc
sanitizer_thread_registry.h)
@@ -106,10 +112,14 @@ else()
SANITIZER_NEEDS_SEGV=1)
endif()
+include(CheckIncludeFile)
+append_have_file_definition(rpc/xdr.h HAVE_RPC_XDR_H SANITIZER_COMMON_DEFINITIONS)
+append_have_file_definition(tirpc/rpc/xdr.h HAVE_TIRPC_RPC_XDR_H SANITIZER_COMMON_DEFINITIONS)
+
set(SANITIZER_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_no_rtti_flag(SANITIZER_CFLAGS)
-append_list_if(SANITIZER_LIMIT_FRAME_SIZE -Wframe-larger-than=512
+append_list_if(SANITIZER_LIMIT_FRAME_SIZE -Wframe-larger-than=570
SANITIZER_CFLAGS)
append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors
SANITIZER_CFLAGS)
@@ -118,31 +128,30 @@ add_custom_target(sanitizer_common)
set(SANITIZER_RUNTIME_LIBRARIES)
if(APPLE)
# Build universal binary on APPLE.
- foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS})
- add_compiler_rt_darwin_object_library(RTSanitizerCommon ${os}
- ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH}
- SOURCES ${SANITIZER_SOURCES} ${SANITIZER_LIBCDEP_SOURCES}
- CFLAGS ${SANITIZER_CFLAGS}
- DEFS ${SANITIZER_COMMON_DEFINITIONS})
+
+ add_compiler_rt_object_libraries(RTSanitizerCommon
+ OS ${SANITIZER_COMMON_SUPPORTED_OS}
+ ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH}
+ SOURCES ${SANITIZER_SOURCES} ${SANITIZER_LIBCDEP_SOURCES}
+ CFLAGS ${SANITIZER_CFLAGS}
+ DEFS ${SANITIZER_COMMON_DEFINITIONS})
+ foreach(os ${SANITIZER_COMMON_SUPPORTED_OS})
list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.${os})
endforeach()
else()
# Otherwise, build separate libraries for each target.
+
+ add_compiler_rt_object_libraries(RTSanitizerCommon
+ ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH}
+ SOURCES ${SANITIZER_SOURCES} CFLAGS ${SANITIZER_CFLAGS}
+ DEFS ${SANITIZER_COMMON_DEFINITIONS})
+ add_compiler_rt_object_libraries(RTSanitizerCommonLibc
+ ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH}
+ SOURCES ${SANITIZER_LIBCDEP_SOURCES} CFLAGS ${SANITIZER_CFLAGS}
+ DEFS ${SANITIZER_COMMON_DEFINITIONS})
foreach(arch ${SANITIZER_COMMON_SUPPORTED_ARCH})
- add_compiler_rt_object_library(RTSanitizerCommon ${arch}
- SOURCES ${SANITIZER_SOURCES} CFLAGS ${SANITIZER_CFLAGS}
- DEFS ${SANITIZER_COMMON_DEFINITIONS})
- add_compiler_rt_object_library(RTSanitizerCommonLibc ${arch}
- SOURCES ${SANITIZER_LIBCDEP_SOURCES} CFLAGS ${SANITIZER_CFLAGS}
- DEFS ${SANITIZER_COMMON_DEFINITIONS})
list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.${arch}
RTSanitizerCommonLibc.${arch})
- add_compiler_rt_runtime(clang_rt.san-${arch} ${arch} STATIC
- SOURCES $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
- $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
- CFLAGS ${SANITIZER_CFLAGS}
- DEFS ${SANITIZER_COMMON_DEFINITIONS})
- add_dependencies(sanitizer_common clang_rt.san-${arch})
endforeach()
endif()
diff --git a/lib/sanitizer_common/sanitizer_allocator.h b/lib/sanitizer_common/sanitizer_allocator.h
index b5105f8c2555..deaffef7150d 100644
--- a/lib/sanitizer_common/sanitizer_allocator.h
+++ b/lib/sanitizer_common/sanitizer_allocator.h
@@ -323,7 +323,7 @@ class SizeClassAllocator64 {
void Init() {
CHECK_EQ(kSpaceBeg,
- reinterpret_cast<uptr>(Mprotect(kSpaceBeg, kSpaceSize)));
+ reinterpret_cast<uptr>(MmapNoAccess(kSpaceBeg, kSpaceSize)));
MapWithCallback(kSpaceEnd, AdditionalSize());
}
diff --git a/lib/sanitizer_common/sanitizer_atomic.h b/lib/sanitizer_common/sanitizer_atomic.h
index 6643c5494393..7e3374aadd0c 100644
--- a/lib/sanitizer_common/sanitizer_atomic.h
+++ b/lib/sanitizer_common/sanitizer_atomic.h
@@ -55,7 +55,7 @@ struct atomic_uintptr_t {
} // namespace __sanitizer
-#if defined(__GNUC__)
+#if defined(__clang__) || defined(__GNUC__)
# include "sanitizer_atomic_clang.h"
#elif defined(_MSC_VER)
# include "sanitizer_atomic_msvc.h"
diff --git a/lib/sanitizer_common/sanitizer_atomic_msvc.h b/lib/sanitizer_common/sanitizer_atomic_msvc.h
index 12ffef3ba105..24d6f0f34424 100644
--- a/lib/sanitizer_common/sanitizer_atomic_msvc.h
+++ b/lib/sanitizer_common/sanitizer_atomic_msvc.h
@@ -21,6 +21,15 @@ extern "C" void _mm_mfence();
#pragma intrinsic(_mm_mfence)
extern "C" void _mm_pause();
#pragma intrinsic(_mm_pause)
+extern "C" char _InterlockedExchange8( // NOLINT
+ char volatile *Addend, char Value); // NOLINT
+#pragma intrinsic(_InterlockedExchange8)
+extern "C" short _InterlockedExchange16( // NOLINT
+ short volatile *Addend, short Value); // NOLINT
+#pragma intrinsic(_InterlockedExchange16)
+extern "C" long _InterlockedExchange( // NOLINT
+ long volatile *Addend, long Value); // NOLINT
+#pragma intrinsic(_InterlockedExchange)
extern "C" long _InterlockedExchangeAdd( // NOLINT
long volatile * Addend, long Value); // NOLINT
#pragma intrinsic(_InterlockedExchangeAdd)
@@ -145,28 +154,25 @@ INLINE u8 atomic_exchange(volatile atomic_uint8_t *a,
u8 v, memory_order mo) {
(void)mo;
DCHECK(!((uptr)a % sizeof(*a)));
- __asm {
- mov eax, a
- mov cl, v
- xchg [eax], cl // NOLINT
- mov v, cl
- }
- return v;
+ return (u8)_InterlockedExchange8((volatile char*)&a->val_dont_use, v);
}
INLINE u16 atomic_exchange(volatile atomic_uint16_t *a,
u16 v, memory_order mo) {
(void)mo;
DCHECK(!((uptr)a % sizeof(*a)));
- __asm {
- mov eax, a
- mov cx, v
- xchg [eax], cx // NOLINT
- mov v, cx
- }
- return v;
+ return (u16)_InterlockedExchange16((volatile short*)&a->val_dont_use, v);
+}
+
+INLINE u32 atomic_exchange(volatile atomic_uint32_t *a,
+ u32 v, memory_order mo) {
+ (void)mo;
+ DCHECK(!((uptr)a % sizeof(*a)));
+ return (u32)_InterlockedExchange((volatile long*)&a->val_dont_use, v);
}
+#ifndef _WIN64
+
INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a,
u8 *cmp,
u8 xchgv,
@@ -188,6 +194,8 @@ INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a,
return false;
}
+#endif
+
INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a,
uptr *cmp,
uptr xchg,
diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc
index 4be3c7abf756..d14e98824d99 100644
--- a/lib/sanitizer_common/sanitizer_common.cc
+++ b/lib/sanitizer_common/sanitizer_common.cc
@@ -16,6 +16,8 @@
#include "sanitizer_flags.h"
#include "sanitizer_libc.h"
#include "sanitizer_placement_new.h"
+#include "sanitizer_stacktrace_printer.h"
+#include "sanitizer_symbolizer.h"
namespace __sanitizer {
@@ -52,18 +54,23 @@ void ReportFile::ReopenIfNecessary() {
if (fd_pid == pid)
return;
else
- internal_close(fd);
+ CloseFile(fd);
}
- internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
- uptr openrv = OpenFile(full_path, true);
- if (internal_iserror(openrv)) {
+ const char *exe_name = GetBinaryBasename();
+ if (common_flags()->log_exe_name && exe_name) {
+ internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix,
+ exe_name, pid);
+ } else {
+ internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
+ }
+ fd = OpenFile(full_path, WrOnly);
+ if (fd == kInvalidFd) {
const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
- internal_write(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
- internal_write(kStderrFd, full_path, internal_strlen(full_path));
+ WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
+ WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
Die();
}
- fd = openrv;
fd_pid = pid;
}
@@ -80,7 +87,7 @@ void ReportFile::SetReportPath(const char *path) {
SpinMutexLock l(mu);
if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
- internal_close(fd);
+ CloseFile(fd);
fd = kInvalidFd;
if (internal_strcmp(path, "stdout") == 0) {
fd = kStdoutFd;
@@ -134,7 +141,7 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond,
}
uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
- uptr max_len, int *errno_p) {
+ uptr max_len, error_t *errno_p) {
uptr PageSize = GetPageSizeCached();
uptr kMinFileLen = PageSize;
uptr read_len = 0;
@@ -142,9 +149,8 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
*buff_size = 0;
// The files we usually open are not seekable, so try different buffer sizes.
for (uptr size = kMinFileLen; size <= max_len; size *= 2) {
- uptr openrv = OpenFile(file_name, /*write*/ false);
- if (internal_iserror(openrv, errno_p)) return 0;
- fd_t fd = openrv;
+ fd_t fd = OpenFile(file_name, RdOnly, errno_p);
+ if (fd == kInvalidFd) return 0;
UnmapOrDie(*buff, *buff_size);
*buff = (char*)MmapOrDie(size, __func__);
*buff_size = size;
@@ -152,8 +158,8 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
read_len = 0;
bool reached_eof = false;
while (read_len + PageSize <= size) {
- uptr just_read = internal_read(fd, *buff + read_len, PageSize);
- if (internal_iserror(just_read, errno_p)) {
+ uptr just_read;
+ if (!ReadFromFile(fd, *buff + read_len, PageSize, &just_read, errno_p)) {
UnmapOrDie(*buff, *buff_size);
return 0;
}
@@ -163,7 +169,7 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
}
read_len += just_read;
}
- internal_close(fd);
+ CloseFile(fd);
if (reached_eof) // We've read the whole file.
break;
}
@@ -206,19 +212,26 @@ const char *StripPathPrefix(const char *filepath,
const char *strip_path_prefix) {
if (filepath == 0) return 0;
if (strip_path_prefix == 0) return filepath;
- const char *pos = internal_strstr(filepath, strip_path_prefix);
- if (pos == 0) return filepath;
- pos += internal_strlen(strip_path_prefix);
- if (pos[0] == '.' && pos[1] == '/')
- pos += 2;
- return pos;
+ const char *res = filepath;
+ if (const char *pos = internal_strstr(filepath, strip_path_prefix))
+ res = pos + internal_strlen(strip_path_prefix);
+ if (res[0] == '.' && res[1] == '/')
+ res += 2;
+ return res;
}
const char *StripModuleName(const char *module) {
if (module == 0)
return 0;
- if (const char *slash_pos = internal_strrchr(module, '/'))
+ if (SANITIZER_WINDOWS) {
+ // On Windows, both slash and backslash are possible.
+ // Pick the one that goes last.
+ if (const char *bslash_pos = internal_strrchr(module, '\\'))
+ return StripModuleName(bslash_pos + 1);
+ }
+ if (const char *slash_pos = internal_strrchr(module, '/')) {
return slash_pos + 1;
+ }
return module;
}
@@ -230,26 +243,27 @@ void ReportErrorSummary(const char *error_message) {
__sanitizer_report_error_summary(buff.data());
}
-void ReportErrorSummary(const char *error_type, const char *file,
- int line, const char *function) {
+#ifndef SANITIZER_GO
+void ReportErrorSummary(const char *error_type, const AddressInfo &info) {
if (!common_flags()->print_summary)
return;
InternalScopedString buff(kMaxSummaryLength);
- buff.append("%s %s:%d %s", error_type,
- file ? StripPathPrefix(file, common_flags()->strip_path_prefix)
- : "??",
- line, function ? function : "??");
+ buff.append("%s ", error_type);
+ RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
ReportErrorSummary(buff.data());
}
+#endif
-LoadedModule::LoadedModule(const char *module_name, uptr base_address) {
+void LoadedModule::set(const char *module_name, uptr base_address) {
+ clear();
full_name_ = internal_strdup(module_name);
base_address_ = base_address;
- ranges_.clear();
}
void LoadedModule::clear() {
InternalFree(full_name_);
+ full_name_ = nullptr;
while (!ranges_.empty()) {
AddressRange *r = ranges_.front();
ranges_.pop_front();
@@ -330,6 +344,32 @@ bool TemplateMatch(const char *templ, const char *str) {
return true;
}
+static char binary_name_cache_str[kMaxPathLength];
+static const char *binary_basename_cache_str;
+
+const char *GetBinaryBasename() {
+ return binary_basename_cache_str;
+}
+
+// Call once to make sure that binary_name_cache_str is initialized
+void CacheBinaryName() {
+ if (binary_name_cache_str[0] != '\0')
+ return;
+ ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
+ binary_basename_cache_str = StripModuleName(binary_name_cache_str);
+}
+
+uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
+ CacheBinaryName();
+ uptr name_len = internal_strlen(binary_name_cache_str);
+ name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
+ if (buf_len == 0)
+ return 0;
+ internal_memcpy(buf, binary_name_cache_str, name_len);
+ buf[name_len] = '\0';
+ return name_len;
+}
+
} // namespace __sanitizer
using namespace __sanitizer; // NOLINT
diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h
index ff13ef164045..2c5a8dbe1238 100644
--- a/lib/sanitizer_common/sanitizer_common.h
+++ b/lib/sanitizer_common/sanitizer_common.h
@@ -23,8 +23,14 @@
#include "sanitizer_list.h"
#include "sanitizer_mutex.h"
+#ifdef _MSC_VER
+extern "C" void _ReadWriteBarrier();
+#pragma intrinsic(_ReadWriteBarrier)
+#endif
+
namespace __sanitizer {
struct StackTrace;
+struct AddressInfo;
// Constants.
const uptr kWordSize = SANITIZER_WORDSIZE / 8;
@@ -38,8 +44,15 @@ const uptr kWordSizeInBits = 8 * kWordSize;
const uptr kMaxPathLength = 4096;
+// 16K loaded modules should be enough for everyone.
+static const uptr kMaxNumberOfModules = 1 << 14;
+
const uptr kMaxThreadStackSize = 1 << 30; // 1Gb
+// Denotes fake PC values that come from JIT/JAVA/etc.
+// For such PC values __tsan_symbolize_external() will be called.
+const u64 kExternalPCBit = 1ULL << 60;
+
extern const char *SanitizerToolName; // Can be changed by the tool.
extern atomic_uint32_t current_verbosity;
@@ -65,12 +78,17 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
// Memory management
void *MmapOrDie(uptr size, const char *mem_type);
void UnmapOrDie(void *addr, uptr size);
-void *MmapFixedNoReserve(uptr fixed_addr, uptr size);
+void *MmapFixedNoReserve(uptr fixed_addr, uptr size,
+ const char *name = nullptr);
void *MmapNoReserveOrDie(uptr size, const char *mem_type);
void *MmapFixedOrDie(uptr fixed_addr, uptr size);
-void *Mprotect(uptr fixed_addr, uptr size);
+void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name = nullptr);
// Map aligned chunk of address space; size and alignment are powers of two.
void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type);
+// Disallow access to a memory range. Use MmapNoAccess to allocate an
+// unaccessible memory.
+bool MprotectNoAccess(uptr addr, uptr size);
+
// Used to check if we can map shadow memory to a fixed location.
bool MemoryRangeIsAvailable(uptr range_start, uptr range_end);
void FlushUnneededShadowMemory(uptr addr, uptr size);
@@ -159,7 +177,7 @@ extern StaticSpinMutex CommonSanitizerReportMutex;
struct ReportFile {
void Write(const char *buffer, uptr length);
- bool PrintsToTty();
+ bool SupportsColors();
void SetReportPath(const char *path);
// Don't use fields directly. They are only declared public to allow
@@ -186,18 +204,39 @@ extern ReportFile report_file;
extern uptr stoptheworld_tracer_pid;
extern uptr stoptheworld_tracer_ppid;
-uptr OpenFile(const char *filename, bool write);
+enum FileAccessMode {
+ RdOnly,
+ WrOnly,
+ RdWr
+};
+
+// Returns kInvalidFd on error.
+fd_t OpenFile(const char *filename, FileAccessMode mode,
+ error_t *errno_p = nullptr);
+void CloseFile(fd_t);
+
+// Return true on success, false on error.
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size,
+ uptr *bytes_read = nullptr, error_t *error_p = nullptr);
+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);
+
+bool SupportsColoredOutput(fd_t fd);
+
// Opens the file 'file_name" and reads up to 'max_len' bytes.
// The resulting buffer is mmaped and stored in '*buff'.
// The size of the mmaped region is stored in '*buff_size',
// Returns the number of read bytes or 0 if file can not be opened.
uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
- uptr max_len, int *errno_p = nullptr);
+ uptr max_len, error_t *errno_p = nullptr);
// Maps given file to virtual memory, and returns pointer to it
-// (or NULL if the mapping failes). Stores the size of mmaped region
+// (or NULL if mapping fails). Stores the size of mmaped region
// in '*buff_size'.
void *MapFileToMemory(const char *file_name, uptr *buff_size);
-void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset);
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset);
bool IsAccessibleMemoryRange(uptr beg, uptr size);
@@ -208,6 +247,10 @@ const char *StripPathPrefix(const char *filepath,
const char *StripModuleName(const char *module);
// OS
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len);
+uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len);
+const char *GetBinaryBasename();
+void CacheBinaryName();
void DisableCoreDumperIfNecessary();
void DumpProcessMap();
bool FileExists(const char *filename);
@@ -215,6 +258,9 @@ const char *GetEnv(const char *name);
bool SetEnv(const char *name, const char *value);
const char *GetPwd();
char *FindPathToBinary(const char *name);
+bool IsPathSeparator(const char c);
+bool IsAbsolutePath(const char *path);
+
u32 GetUid();
void ReExec();
bool StackSizeIsUnlimited();
@@ -288,9 +334,9 @@ const int kMaxSummaryLength = 1024;
// and pass it to __sanitizer_report_error_summary.
void ReportErrorSummary(const char *error_message);
// Same as above, but construct error_message as:
-// error_type file:line function
-void ReportErrorSummary(const char *error_type, const char *file,
- int line, const char *function);
+// error_type file:line[:column][ function]
+void ReportErrorSummary(const char *error_type, const AddressInfo &info);
+// Same as above, but obtains AddressInfo by symbolizing top stack trace frame.
void ReportErrorSummary(const char *error_type, StackTrace *trace);
// Math
@@ -309,7 +355,11 @@ INLINE uptr MostSignificantSetBitIndex(uptr x) {
CHECK_NE(x, 0U);
unsigned long up; // NOLINT
#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
+# ifdef _WIN64
+ up = SANITIZER_WORDSIZE - 1 - __builtin_clzll(x);
+# else
up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x);
+# endif
#elif defined(_WIN64)
_BitScanReverse64(&up, x);
#else
@@ -322,7 +372,11 @@ INLINE uptr LeastSignificantSetBitIndex(uptr x) {
CHECK_NE(x, 0U);
unsigned long up; // NOLINT
#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
+# ifdef _WIN64
+ up = __builtin_ctzll(x);
+# else
up = __builtin_ctzl(x);
+# endif
#elif defined(_WIN64)
_BitScanForward64(&up, x);
#else
@@ -342,7 +396,7 @@ INLINE uptr RoundUpToPowerOfTwo(uptr size) {
uptr up = MostSignificantSetBitIndex(size);
CHECK(size < (1ULL << (up + 1)));
CHECK(size > (1ULL << up));
- return 1UL << (up + 1);
+ return 1ULL << (up + 1);
}
INLINE uptr RoundUpTo(uptr size, uptr boundary) {
@@ -360,17 +414,7 @@ INLINE bool IsAligned(uptr a, uptr alignment) {
INLINE uptr Log2(uptr x) {
CHECK(IsPowerOfTwo(x));
-#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__)
- return __builtin_ctzl(x);
-#elif defined(_WIN64)
- unsigned long ret; // NOLINT
- _BitScanForward64(&ret, x);
- return ret;
-#else
- unsigned long ret; // NOLINT
- _BitScanForward(&ret, x);
- return ret;
-#endif
+ return LeastSignificantSetBitIndex(x);
}
// Don't use std::min, std::max or std::swap, to minimize dependency
@@ -439,11 +483,15 @@ class InternalMmapVectorNoCtor {
const T *data() const {
return data_;
}
+ T *data() {
+ return data_;
+ }
uptr capacity() const {
return capacity_;
}
void clear() { size_ = 0; }
+ bool empty() const { return size() == 0; }
private:
void Resize(uptr new_capacity) {
@@ -532,7 +580,8 @@ uptr InternalBinarySearch(const Container &v, uptr first, uptr last,
// executable or a shared object).
class LoadedModule {
public:
- LoadedModule(const char *module_name, uptr base_address);
+ LoadedModule() : full_name_(nullptr), base_address_(0) { ranges_.clear(); }
+ void set(const char *module_name, uptr base_address);
void clear();
void addAddressRange(uptr beg, uptr end, bool executable);
bool containsAddress(uptr address) const;
@@ -567,28 +616,41 @@ typedef bool (*string_predicate_t)(const char *);
uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
string_predicate_t filter);
-#if SANITIZER_POSIX
-const uptr kPthreadDestructorIterations = 4;
-#else
-// Unused on Windows.
-const uptr kPthreadDestructorIterations = 0;
-#endif
-
// Callback type for iterating over a set of memory ranges.
typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg);
+enum AndroidApiLevel {
+ ANDROID_NOT_ANDROID = 0,
+ ANDROID_KITKAT = 19,
+ ANDROID_LOLLIPOP_MR1 = 22,
+ ANDROID_POST_LOLLIPOP = 23
+};
+
#if SANITIZER_ANDROID
// Initialize Android logging. Any writes before this are silently lost.
void AndroidLogInit();
void AndroidLogWrite(const char *buffer);
void GetExtraActivationFlags(char *buf, uptr size);
void SanitizerInitializeUnwinder();
+AndroidApiLevel AndroidGetApiLevel();
#else
INLINE void AndroidLogInit() {}
INLINE void AndroidLogWrite(const char *buffer_unused) {}
INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; }
INLINE void SanitizerInitializeUnwinder() {}
+INLINE AndroidApiLevel AndroidGetApiLevel() { return ANDROID_NOT_ANDROID; }
+#endif
+
+INLINE uptr GetPthreadDestructorIterations() {
+#if SANITIZER_ANDROID
+ return (AndroidGetApiLevel() == ANDROID_LOLLIPOP_MR1) ? 8 : 4;
+#elif SANITIZER_POSIX
+ return 4;
+#else
+// Unused on Windows.
+ return 0;
#endif
+}
void *internal_start_thread(void(*func)(void*), void *arg);
void internal_join_thread(void *th);
@@ -599,14 +661,30 @@ void MaybeStartBackgroudThread();
// compiler from recognising it and turning it into an actual call to
// memset/memcpy/etc.
static inline void SanitizerBreakOptimization(void *arg) {
-#if _MSC_VER
- // FIXME: make sure this is actually enough.
- __asm;
+#if _MSC_VER && !defined(__clang__)
+ _ReadWriteBarrier();
#else
__asm__ __volatile__("" : : "r" (arg) : "memory");
#endif
}
+struct SignalContext {
+ void *context;
+ uptr addr;
+ uptr pc;
+ uptr sp;
+ uptr bp;
+
+ SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp) :
+ context(context), addr(addr), pc(pc), sp(sp), bp(bp) {
+ }
+
+ // Creates signal context in a platform-specific manner.
+ static SignalContext Create(void *siginfo, void *context);
+};
+
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
+
} // namespace __sanitizer
inline void *operator new(__sanitizer::operator_new_size_type size,
diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc
index 87c33e186320..a7772b7394a5 100644
--- a/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -22,6 +22,7 @@
// COMMON_INTERCEPTOR_FD_RELEASE
// COMMON_INTERCEPTOR_FD_ACCESS
// COMMON_INTERCEPTOR_SET_THREAD_NAME
+// COMMON_INTERCEPTOR_ON_DLOPEN
// COMMON_INTERCEPTOR_ON_EXIT
// COMMON_INTERCEPTOR_MUTEX_LOCK
// COMMON_INTERCEPTOR_MUTEX_UNLOCK
@@ -46,6 +47,7 @@
#define pthread_setname_np pthread_set_name_np
#define inet_aton __inet_aton
#define inet_pton __inet_pton
+#define iconv __bsd_iconv
#endif
#ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE
@@ -101,6 +103,21 @@
#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0)
#endif
+#define COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, n) \
+ COMMON_INTERCEPTOR_READ_RANGE((ctx), (s), \
+ common_flags()->strict_string_checks ? (len) + 1 : (n) )
+
+#define COMMON_INTERCEPTOR_READ_STRING(ctx, s, n) \
+ COMMON_INTERCEPTOR_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n))
+
+#ifndef COMMON_INTERCEPTOR_ON_DLOPEN
+#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) {}
+#endif
+
+#ifndef COMMON_INTERCEPTOR_GET_TLS_RANGE
+#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) *begin = *end = 0;
+#endif
+
struct FileMetadata {
// For open_memstream().
char **addr;
@@ -154,7 +171,8 @@ UNUSED static void DeleteInterceptorMetadata(void *addr) {
INTERCEPTOR(char*, textdomain, const char *domainname) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, textdomain, domainname);
- char* domain = REAL(textdomain)(domainname);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, domainname, 0);
+ char *domain = REAL(textdomain)(domainname);
if (domain) {
COMMON_INTERCEPTOR_INITIALIZE_RANGE(domain, REAL(strlen)(domain) + 1);
}
@@ -180,8 +198,8 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) {
c2 = (unsigned char)s2[i];
if (c1 != c2 || c1 == '\0') break;
}
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1);
return CharCmpX(c1, c2);
}
@@ -226,8 +244,8 @@ INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) {
c2 = (unsigned char)s2[i];
if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break;
}
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1);
return CharCaseCmp(c1, c2);
}
@@ -253,6 +271,97 @@ INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T n) {
#define INIT_STRNCASECMP
#endif
+#if SANITIZER_INTERCEPT_STRSTR || SANITIZER_INTERCEPT_STRCASESTR
+static inline void StrstrCheck(void *ctx, char *r, const char *s1,
+ const char *s2) {
+ uptr len1 = REAL(strlen)(s1);
+ uptr len2 = REAL(strlen)(s2);
+ COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s1, len1,
+ r ? r - s1 + len2 : len1 + 1);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, len2 + 1);
+}
+#endif
+
+#if SANITIZER_INTERCEPT_STRSTR
+INTERCEPTOR(char*, strstr, const char *s1, const char *s2) {
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_strstr(s1, s2);
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strstr, s1, s2);
+ char *r = REAL(strstr)(s1, s2);
+ if (common_flags()->intercept_strstr)
+ StrstrCheck(ctx, r, s1, s2);
+ return r;
+}
+
+#define INIT_STRSTR COMMON_INTERCEPT_FUNCTION(strstr);
+#else
+#define INIT_STRSTR
+#endif
+
+#if SANITIZER_INTERCEPT_STRCASESTR
+INTERCEPTOR(char*, strcasestr, const char *s1, const char *s2) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strcasestr, s1, s2);
+ char *r = REAL(strcasestr)(s1, s2);
+ if (common_flags()->intercept_strstr)
+ StrstrCheck(ctx, r, s1, s2);
+ return r;
+}
+
+#define INIT_STRCASESTR COMMON_INTERCEPT_FUNCTION(strcasestr);
+#else
+#define INIT_STRCASESTR
+#endif
+
+#if SANITIZER_INTERCEPT_STRSPN
+INTERCEPTOR(SIZE_T, strspn, const char *s1, const char *s2) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strspn, s1, s2);
+ SIZE_T r = REAL(strspn)(s1, s2);
+ if (common_flags()->intercept_strspn) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1);
+ }
+ return r;
+}
+
+INTERCEPTOR(SIZE_T, strcspn, const char *s1, const char *s2) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strcspn, s1, s2);
+ SIZE_T r = REAL(strcspn)(s1, s2);
+ if (common_flags()->intercept_strspn) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1);
+ }
+ return r;
+}
+
+#define INIT_STRSPN \
+ COMMON_INTERCEPT_FUNCTION(strspn); \
+ COMMON_INTERCEPT_FUNCTION(strcspn);
+#else
+#define INIT_STRSPN
+#endif
+
+#if SANITIZER_INTERCEPT_STRPBRK
+INTERCEPTOR(char *, strpbrk, const char *s1, const char *s2) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strpbrk, s1, s2);
+ char *r = REAL(strpbrk)(s1, s2);
+ if (common_flags()->intercept_strpbrk) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s1,
+ r ? r - s1 + 1 : REAL(strlen)(s1) + 1);
+ }
+ return r;
+}
+
+#define INIT_STRPBRK COMMON_INTERCEPT_FUNCTION(strpbrk);
+#else
+#define INIT_STRPBRK
+#endif
+
#if SANITIZER_INTERCEPT_MEMCHR
INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) {
void *ctx;
@@ -722,12 +831,12 @@ INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) {
// its metadata. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
char *res = REAL(strptime)(s, format, tm);
- if (res) {
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s, res - s);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, s, res ? res - s : 0);
+ if (res && tm) {
// Do not call unpoison_tm here, because strptime does not, in fact,
// initialize the entire struct tm. For example, tm_zone pointer is left
// uninitialized.
- if (tm) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm));
}
return res;
}
@@ -1029,6 +1138,12 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size,
#if SANITIZER_INTERCEPT_IOCTL
#include "sanitizer_common_interceptors_ioctl.inc"
INTERCEPTOR(int, ioctl, int d, unsigned long request, ...) {
+ // We need a frame pointer, because we call into ioctl_common_[pre|post] which
+ // can trigger a report and we need to be able to unwind through this
+ // function. On Mac in debug mode we might not have a frame pointer, because
+ // ioctl_common_[pre|post] doesn't get inlined here.
+ ENABLE_FRAME_POINTER;
+
void *ctx;
va_list ap;
va_start(ap, request);
@@ -1502,6 +1617,7 @@ INTERCEPTOR(int, glob, const char *pattern, int flags,
__sanitizer_glob_t *pglob) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
__sanitizer_glob_t glob_copy = {
0, 0, 0,
0, wrapped_gl_closedir, wrapped_gl_readdir,
@@ -1532,6 +1648,7 @@ INTERCEPTOR(int, glob64, const char *pattern, int flags,
__sanitizer_glob_t *pglob) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0);
__sanitizer_glob_t glob_copy = {
0, 0, 0,
0, wrapped_gl_closedir, wrapped_gl_readdir,
@@ -1678,6 +1795,7 @@ INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) {
INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, inet_pton, af, src, dst);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, src, 0);
// FIXME: figure out read size based on the address family.
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
@@ -2348,6 +2466,37 @@ INTERCEPTOR(char *, get_current_dir_name, int fake) {
#define INIT_GET_CURRENT_DIR_NAME
#endif
+UNUSED static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) {
+ CHECK(endptr);
+ if (nptr == *endptr) {
+ // No digits were found at strtol call, we need to find out the last
+ // symbol accessed by strtoll on our own.
+ // We get this symbol by skipping leading blanks and optional +/- sign.
+ while (IsSpace(*nptr)) nptr++;
+ if (*nptr == '+' || *nptr == '-') nptr++;
+ *endptr = const_cast<char *>(nptr);
+ }
+ CHECK(*endptr >= nptr);
+}
+
+UNUSED static inline void StrtolFixAndCheck(void *ctx, const char *nptr,
+ char **endptr, char *real_endptr, int base) {
+ if (endptr != 0) {
+ *endptr = real_endptr;
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
+ }
+ // If base has unsupported value, strtol can exit with EINVAL
+ // without reading any characters. So do additional checks only
+ // if base is valid.
+ bool is_valid_base = (base == 0) || (2 <= base && base <= 36);
+ if (is_valid_base) {
+ FixRealStrtolEndptr(nptr, &real_endptr);
+ }
+ COMMON_INTERCEPTOR_READ_STRING(ctx, nptr, is_valid_base ?
+ (real_endptr - nptr) + 1 : 0);
+}
+
+
#if SANITIZER_INTERCEPT_STRTOIMAX
INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
void *ctx;
@@ -2355,8 +2504,9 @@ INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
- INTMAX_T res = REAL(strtoimax)(nptr, endptr, base);
- if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
+ char *real_endptr;
+ INTMAX_T res = REAL(strtoimax)(nptr, &real_endptr, base);
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return res;
}
@@ -2366,8 +2516,9 @@ INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) {
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
- INTMAX_T res = REAL(strtoumax)(nptr, endptr, base);
- if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr));
+ char *real_endptr;
+ INTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base);
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
return res;
}
@@ -3631,6 +3782,7 @@ INTERCEPTOR(char *, tempnam, char *dir, char *pfx) {
INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name);
+ COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0);
COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name);
return REAL(pthread_setname_np)(thread, name);
}
@@ -3847,25 +3999,33 @@ INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) {
}
return res;
}
-INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim,
- void *stream) {
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, __getdelim, lineptr, n, delim, stream);
- // FIXME: under ASan the call below may write to freed memory and corrupt
- // its metadata. See
- // https://code.google.com/p/address-sanitizer/issues/detail?id=321.
- SSIZE_T res = REAL(__getdelim)(lineptr, n, delim, stream);
- if (res > 0) {
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr));
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n));
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1);
+
+// FIXME: under ASan the call below may write to freed memory and corrupt its
+// metadata. See
+// https://code.google.com/p/address-sanitizer/issues/detail?id=321.
+#define GETDELIM_INTERCEPTOR_IMPL(vname) \
+ { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, vname, lineptr, n, delim, stream); \
+ SSIZE_T res = REAL(vname)(lineptr, n, delim, stream); \
+ if (res > 0) { \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); \
+ } \
+ return res; \
}
- return res;
-}
+
+INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim,
+ void *stream)
+GETDELIM_INTERCEPTOR_IMPL(__getdelim)
+
+// There's no __getdelim() on FreeBSD so we supply the getdelim() interceptor
+// with its own body.
INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim,
- void *stream) {
- return __getdelim(lineptr, n, delim, stream);
-}
+ void *stream)
+GETDELIM_INTERCEPTOR_IMPL(getdelim)
+
#define INIT_GETLINE \
COMMON_INTERCEPT_FUNCTION(getline); \
COMMON_INTERCEPT_FUNCTION(__getdelim); \
@@ -3931,7 +4091,9 @@ INTERCEPTOR(void *, __tls_get_addr, void *arg) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr, arg);
void *res = REAL(__tls_get_addr)(arg);
- DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res);
+ uptr tls_begin, tls_end;
+ COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end);
+ DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, tls_begin, tls_end);
if (dtv) {
// New DTLS block has been allocated.
COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size);
@@ -4688,6 +4850,8 @@ INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) {
INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
void *ctx;
COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag);
+ if (filename) COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0);
+ COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag);
void *res = REAL(dlopen)(filename, flag);
COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res);
return res;
@@ -4792,6 +4956,63 @@ INTERCEPTOR(int, munlockall, void) {
#define INIT_MLOCKX
#endif // SANITIZER_INTERCEPT_MLOCKX
+#if SANITIZER_INTERCEPT_FOPENCOOKIE
+struct WrappedCookie {
+ void *real_cookie;
+ __sanitizer_cookie_io_functions_t real_io_funcs;
+};
+
+static uptr wrapped_read(void *cookie, char *buf, uptr size) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+ __sanitizer_cookie_io_read real_read = wrapped_cookie->real_io_funcs.read;
+ return real_read ? real_read(wrapped_cookie->real_cookie, buf, size) : 0;
+}
+
+static uptr wrapped_write(void *cookie, const char *buf, uptr size) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+ __sanitizer_cookie_io_write real_write = wrapped_cookie->real_io_funcs.write;
+ return real_write ? real_write(wrapped_cookie->real_cookie, buf, size) : size;
+}
+
+static int wrapped_seek(void *cookie, u64 *offset, int whence) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(offset, sizeof(*offset));
+ WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+ __sanitizer_cookie_io_seek real_seek = wrapped_cookie->real_io_funcs.seek;
+ return real_seek ? real_seek(wrapped_cookie->real_cookie, offset, whence)
+ : -1;
+}
+
+static int wrapped_close(void *cookie) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie;
+ __sanitizer_cookie_io_close real_close = wrapped_cookie->real_io_funcs.close;
+ int res = real_close ? real_close(wrapped_cookie->real_cookie) : 0;
+ InternalFree(wrapped_cookie);
+ return res;
+}
+
+INTERCEPTOR(__sanitizer_FILE *, fopencookie, void *cookie, const char *mode,
+ __sanitizer_cookie_io_functions_t io_funcs) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fopencookie, cookie, mode, io_funcs);
+ WrappedCookie *wrapped_cookie =
+ (WrappedCookie *)InternalAlloc(sizeof(WrappedCookie));
+ wrapped_cookie->real_cookie = cookie;
+ wrapped_cookie->real_io_funcs = io_funcs;
+ __sanitizer_FILE *res =
+ REAL(fopencookie)(wrapped_cookie, mode, {wrapped_read, wrapped_write,
+ wrapped_seek, wrapped_close});
+ return res;
+}
+
+#define INIT_FOPENCOOKIE COMMON_INTERCEPT_FUNCTION(fopencookie);
+#else
+#define INIT_FOPENCOOKIE
+#endif // SANITIZER_INTERCEPT_FOPENCOOKIE
+
static void InitializeCommonInterceptors() {
static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1];
interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap();
@@ -4801,6 +5022,10 @@ static void InitializeCommonInterceptors() {
INIT_STRNCMP;
INIT_STRCASECMP;
INIT_STRNCASECMP;
+ INIT_STRSTR;
+ INIT_STRCASESTR;
+ INIT_STRSPN;
+ INIT_STRPBRK;
INIT_MEMCHR;
INIT_MEMRCHR;
INIT_READ;
@@ -4955,4 +5180,5 @@ static void InitializeCommonInterceptors() {
INIT_GETPASS;
INIT_TIMERFD;
INIT_MLOCKX;
+ INIT_FOPENCOOKIE;
}
diff --git a/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc b/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc
index 69b7ca9eaa2e..b94c21c96aa0 100755
--- a/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc
+++ b/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc
@@ -578,14 +578,10 @@ static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d,
}
if (desc->type != ioctl_desc::CUSTOM)
return;
- switch (request) {
- case 0x00008912: { // SIOCGIFCONF
- struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
- COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len));
- break;
- }
+ if (request == IOCTL_SIOCGIFCONF) {
+ struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len));
}
- return;
}
static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d,
@@ -597,12 +593,8 @@ static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d,
}
if (desc->type != ioctl_desc::CUSTOM)
return;
- switch (request) {
- case 0x00008912: { // SIOCGIFCONF
- struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len);
- break;
- }
+ if (request == IOCTL_SIOCGIFCONF) {
+ struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg;
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len);
}
- return;
}
diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc
index 17ef6897ba26..1b65bced75d5 100644
--- a/lib/sanitizer_common/sanitizer_common_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc
@@ -17,12 +17,16 @@
#include "sanitizer_stacktrace.h"
#include "sanitizer_symbolizer.h"
+#if SANITIZER_POSIX
+#include "sanitizer_posix.h"
+#endif
+
namespace __sanitizer {
-bool ReportFile::PrintsToTty() {
+bool ReportFile::SupportsColors() {
SpinMutexLock l(mu);
ReopenIfNecessary();
- return internal_isatty(fd) != 0;
+ return SupportsColoredOutput(fd);
}
bool ColorizeReports() {
@@ -33,7 +37,7 @@ bool ColorizeReports() {
const char *flag = common_flags()->color;
return internal_strcmp(flag, "always") == 0 ||
- (internal_strcmp(flag, "auto") == 0 && report_file.PrintsToTty());
+ (internal_strcmp(flag, "auto") == 0 && report_file.SupportsColors());
}
static void (*sandboxing_callback)();
@@ -44,20 +48,16 @@ void SetSandboxingCallback(void (*f)()) {
void ReportErrorSummary(const char *error_type, StackTrace *stack) {
if (!common_flags()->print_summary)
return;
-#if !SANITIZER_GO
- if (stack->size > 0 && Symbolizer::GetOrInit()->CanReturnFileLineInfo()) {
- // Currently, we include the first stack frame into the report summary.
- // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
- uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
- SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
- const AddressInfo &ai = frame->info;
- ReportErrorSummary(error_type, ai.file, ai.line, ai.function);
- frame->ClearAll();
+ if (stack->size == 0) {
+ ReportErrorSummary(error_type);
+ return;
}
-#else
- AddressInfo ai;
- ReportErrorSummary(error_type, ai.file, ai.line, ai.function);
-#endif
+ // Currently, we include the first stack frame into the report summary.
+ // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
+ uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
+ SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
+ ReportErrorSummary(error_type, frame->info);
+ frame->ClearAll();
}
static void (*SoftRssLimitExceededCallback)(bool exceeded);
@@ -117,12 +117,13 @@ void BackgroundThread(void *arg) {
}
void MaybeStartBackgroudThread() {
- if (!SANITIZER_LINUX) return; // Need to implement/test on other platforms.
+#if SANITIZER_LINUX // Need to implement/test on other platforms.
// Start the background thread if one of the rss limits is given.
if (!common_flags()->hard_rss_limit_mb &&
!common_flags()->soft_rss_limit_mb) return;
if (!&real_pthread_create) return; // Can't spawn the thread anyway.
internal_start_thread(BackgroundThread, nullptr);
+#endif
}
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_common_syscalls.inc b/lib/sanitizer_common/sanitizer_common_syscalls.inc
index 7e15d51ff35b..5d4840f961ef 100644
--- a/lib/sanitizer_common/sanitizer_common_syscalls.inc
+++ b/lib/sanitizer_common/sanitizer_common_syscalls.inc
@@ -1443,6 +1443,7 @@ PRE_SYSCALL(fchown)(long fd, long user, long group) {}
POST_SYSCALL(fchown)(long res, long fd, long user, long group) {}
+#if SANITIZER_USES_UID16_SYSCALLS
PRE_SYSCALL(chown16)(const void *filename, long user, long group) {
if (filename)
PRE_READ(filename,
@@ -1552,6 +1553,7 @@ POST_SYSCALL(getgid16)(long res) {}
PRE_SYSCALL(getegid16)() {}
POST_SYSCALL(getegid16)(long res) {}
+#endif // SANITIZER_USES_UID16_SYSCALLS
PRE_SYSCALL(utime)(void *filename, void *times) {}
@@ -2298,7 +2300,8 @@ POST_SYSCALL(ni_syscall)(long res) {}
PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {
#if !SANITIZER_ANDROID && \
- (defined(__i386) || defined(__x86_64) || defined(__mips64))
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__))
if (data) {
if (request == ptrace_setregs) {
PRE_READ((void *)data, struct_user_regs_struct_sz);
@@ -2318,7 +2321,8 @@ PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {
POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) {
#if !SANITIZER_ANDROID && \
- (defined(__i386) || defined(__x86_64) || defined(__mips64))
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__))
if (res >= 0 && data) {
// Note that this is different from the interceptor in
// sanitizer_common_interceptors.inc.
diff --git a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
index 49887b1e91a9..f511c996d8c2 100644
--- a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
@@ -24,8 +24,12 @@
// and atomically set Guard to -Guard.
// - __sanitizer_cov_dump: dump the coverage data to disk.
// For every module of the current process that has coverage data
-// this will create a file module_name.PID.sancov. The file format is simple:
-// it's just a sorted sequence of 4-byte offsets in the module.
+// this will create a file module_name.PID.sancov.
+//
+// The file format is simple: the first 8 bytes is the magic,
+// one of 0xC0BFFFFFFFFFFF64 and 0xC0BFFFFFFFFFFF32. The last byte of the
+// magic defines the size of the following offsets.
+// The rest of the data is the offsets in the module.
//
// Eventually, this coverage implementation should be obsoleted by a more
// powerful general purpose Clang/LLVM coverage instrumentation.
@@ -43,6 +47,9 @@
#include "sanitizer_symbolizer.h"
#include "sanitizer_flags.h"
+static const u64 kMagic64 = 0xC0BFFFFFFFFFFF64ULL;
+static const u64 kMagic32 = 0xC0BFFFFFFFFFFF32ULL;
+
static atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once.
static atomic_uintptr_t coverage_counter;
@@ -56,7 +63,7 @@ static atomic_uintptr_t coverage_counter;
// dump current memory layout to another file.
static bool cov_sandboxed = false;
-static int cov_fd = kInvalidFd;
+static fd_t cov_fd = kInvalidFd;
static unsigned int cov_max_block_size = 0;
static bool coverage_enabled = false;
static const char *coverage_dir;
@@ -77,21 +84,34 @@ class CoverageData {
uptr cache_size);
void DumpCallerCalleePairs();
void DumpTrace();
+ void DumpAsBitSet();
+ void DumpCounters();
+ void DumpOffsets();
+ void DumpAll();
ALWAYS_INLINE
void TraceBasicBlock(s32 *id);
void InitializeGuardArray(s32 *guards);
- void InitializeGuards(s32 *guards, uptr n, const char *module_name);
+ void InitializeGuards(s32 *guards, uptr n, const char *module_name,
+ uptr caller_pc);
+ void InitializeCounters(u8 *counters, uptr n);
void ReinitializeGuards();
+ uptr GetNumberOf8bitCounters();
+ uptr Update8bitCounterBitsetAndClearCounters(u8 *bitset);
uptr *data();
uptr size();
private:
+ void DirectOpen();
+ void UpdateModuleNameVec(uptr caller_pc, uptr range_beg, uptr range_end);
+
// Maximal size pc array may ever grow.
// We MmapNoReserve this space to ensure that the array is contiguous.
- static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << 24, 1 << 27);
+ static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(
+ 1 << (SANITIZER_ANDROID ? 24 : (SANITIZER_WINDOWS ? 27 : 26)),
+ 1 << 27);
// The amount file mapping for the pc array is grown by.
static const uptr kPcArrayMmapSize = 64 * 1024;
@@ -105,13 +125,27 @@ class CoverageData {
// Current file mapped size of the pc array.
uptr pc_array_mapped_size;
// Descriptor of the file mapped pc array.
- int pc_fd;
+ fd_t pc_fd;
// Vector of coverage guard arrays, protected by mu.
InternalMmapVectorNoCtor<s32*> guard_array_vec;
- // Vector of module (compilation unit) names.
- InternalMmapVectorNoCtor<const char*> comp_unit_name_vec;
+ struct NamedPcRange {
+ const char *copied_module_name;
+ uptr beg, end; // elements [beg,end) in pc_array.
+ };
+
+ // Vector of module and compilation unit pc ranges.
+ InternalMmapVectorNoCtor<NamedPcRange> comp_unit_name_vec;
+ InternalMmapVectorNoCtor<NamedPcRange> module_name_vec;
+
+ struct CounterAndSize {
+ u8 *counters;
+ uptr n;
+ };
+
+ InternalMmapVectorNoCtor<CounterAndSize> counters_vec;
+ uptr num_8bit_counters;
// Caller-Callee (cc) array, size and current index.
static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24);
@@ -133,8 +167,6 @@ class CoverageData {
static const uptr kTrPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27);
StaticSpinMutex mu;
-
- void DirectOpen();
};
static CoverageData coverage_data;
@@ -145,9 +177,9 @@ void CoverageData::DirectOpen() {
InternalScopedString path(kMaxPathLength);
internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.raw",
coverage_dir, internal_getpid());
- pc_fd = OpenFile(path.data(), true);
- if (internal_iserror(pc_fd)) {
- Report(" Coverage: failed to open %s for writing\n", path.data());
+ pc_fd = OpenFile(path.data(), RdWr);
+ if (pc_fd == kInvalidFd) {
+ Report("Coverage: failed to open %s for reading/writing\n", path.data());
Die();
}
@@ -180,10 +212,13 @@ void CoverageData::Enable() {
tr_event_array = reinterpret_cast<u32 *>(MmapNoReserveOrDie(
sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + GetMmapGranularity(),
"CovInit::tr_event_array"));
- Mprotect(reinterpret_cast<uptr>(&tr_event_array[kTrEventArrayMaxSize]),
- GetMmapGranularity());
+ MprotectNoAccess(
+ reinterpret_cast<uptr>(&tr_event_array[kTrEventArrayMaxSize]),
+ GetMmapGranularity());
tr_event_array_size = kTrEventArrayMaxSize;
tr_event_pointer = tr_event_array;
+
+ num_8bit_counters = 0;
}
void CoverageData::InitializeGuardArray(s32 *guards) {
@@ -197,22 +232,22 @@ void CoverageData::InitializeGuardArray(s32 *guards) {
void CoverageData::Disable() {
if (pc_array) {
- internal_munmap(pc_array, sizeof(uptr) * kPcArrayMaxSize);
+ UnmapOrDie(pc_array, sizeof(uptr) * kPcArrayMaxSize);
pc_array = nullptr;
}
if (cc_array) {
- internal_munmap(cc_array, sizeof(uptr *) * kCcArrayMaxSize);
+ UnmapOrDie(cc_array, sizeof(uptr *) * kCcArrayMaxSize);
cc_array = nullptr;
}
if (tr_event_array) {
- internal_munmap(tr_event_array,
- sizeof(tr_event_array[0]) * kTrEventArrayMaxSize +
- GetMmapGranularity());
+ UnmapOrDie(tr_event_array,
+ sizeof(tr_event_array[0]) * kTrEventArrayMaxSize +
+ GetMmapGranularity());
tr_event_array = nullptr;
tr_event_pointer = nullptr;
}
if (pc_fd != kInvalidFd) {
- internal_close(pc_fd);
+ CloseFile(pc_fd);
pc_fd = kInvalidFd;
}
}
@@ -231,8 +266,9 @@ void CoverageData::ReInit() {
// In memory-mapped mode we must extend the new file to the known array
// size.
uptr size = atomic_load(&pc_array_size, memory_order_relaxed);
+ uptr npcs = size / sizeof(uptr);
Enable();
- if (size) Extend(size);
+ if (size) Extend(npcs);
if (coverage_enabled) CovUpdateMapping(coverage_dir);
} else {
Enable();
@@ -289,16 +325,69 @@ void CoverageData::Extend(uptr npcs) {
atomic_store(&pc_array_size, size, memory_order_release);
}
+void CoverageData::InitializeCounters(u8 *counters, uptr n) {
+ if (!counters) return;
+ CHECK_EQ(reinterpret_cast<uptr>(counters) % 16, 0);
+ n = RoundUpTo(n, 16); // The compiler must ensure that counters is 16-aligned.
+ SpinMutexLock l(&mu);
+ counters_vec.push_back({counters, n});
+ num_8bit_counters += n;
+}
+
+void CoverageData::UpdateModuleNameVec(uptr caller_pc, uptr range_beg,
+ uptr range_end) {
+ auto sym = Symbolizer::GetOrInit();
+ if (!sym)
+ return;
+ const char *module_name = sym->GetModuleNameForPc(caller_pc);
+ if (!module_name) return;
+ if (module_name_vec.empty() ||
+ module_name_vec.back().copied_module_name != module_name)
+ module_name_vec.push_back({module_name, range_beg, range_end});
+ else
+ module_name_vec.back().end = range_end;
+}
+
void CoverageData::InitializeGuards(s32 *guards, uptr n,
- const char *module_name) {
+ const char *comp_unit_name,
+ uptr caller_pc) {
// The array 'guards' has n+1 elements, we use the element zero
// to store 'n'.
CHECK_LT(n, 1 << 30);
guards[0] = static_cast<s32>(n);
InitializeGuardArray(guards);
SpinMutexLock l(&mu);
- comp_unit_name_vec.push_back(module_name);
+ uptr range_end = atomic_load(&pc_array_index, memory_order_relaxed);
+ uptr range_beg = range_end - n;
+ comp_unit_name_vec.push_back({comp_unit_name, range_beg, range_end});
guard_array_vec.push_back(guards);
+ UpdateModuleNameVec(caller_pc, range_beg, range_end);
+}
+
+static const uptr kBundleCounterBits = 16;
+
+// When coverage_order_pcs==true and SANITIZER_WORDSIZE==64
+// we insert the global counter into the first 16 bits of the PC.
+uptr BundlePcAndCounter(uptr pc, uptr counter) {
+ if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+ return pc;
+ static const uptr kMaxCounter = (1 << kBundleCounterBits) - 1;
+ if (counter > kMaxCounter)
+ counter = kMaxCounter;
+ CHECK_EQ(0, pc >> (SANITIZER_WORDSIZE - kBundleCounterBits));
+ return pc | (counter << (SANITIZER_WORDSIZE - kBundleCounterBits));
+}
+
+uptr UnbundlePc(uptr bundle) {
+ if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+ return bundle;
+ return (bundle << kBundleCounterBits) >> kBundleCounterBits;
+}
+
+uptr UnbundleCounter(uptr bundle) {
+ if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs)
+ return 0;
+ return bundle >> (SANITIZER_WORDSIZE - kBundleCounterBits);
}
// If guard is negative, atomically set it to -guard and store the PC in
@@ -316,8 +405,8 @@ void CoverageData::Add(uptr pc, u32 *guard) {
return; // May happen after fork when pc_array_index becomes 0.
CHECK_LT(idx * sizeof(uptr),
atomic_load(&pc_array_size, memory_order_acquire));
- pc_array[idx] = pc;
- atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
+ uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed);
+ pc_array[idx] = BundlePcAndCounter(pc, counter);
}
// Registers a pair caller=>callee.
@@ -354,6 +443,64 @@ void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[],
}
}
+uptr CoverageData::GetNumberOf8bitCounters() {
+ return num_8bit_counters;
+}
+
+// Map every 8bit counter to a 8-bit bitset and clear the counter.
+uptr CoverageData::Update8bitCounterBitsetAndClearCounters(u8 *bitset) {
+ uptr num_new_bits = 0;
+ uptr cur = 0;
+ // For better speed we map 8 counters to 8 bytes of bitset at once.
+ static const uptr kBatchSize = 8;
+ CHECK_EQ(reinterpret_cast<uptr>(bitset) % kBatchSize, 0);
+ for (uptr i = 0, len = counters_vec.size(); i < len; i++) {
+ u8 *c = counters_vec[i].counters;
+ uptr n = counters_vec[i].n;
+ CHECK_EQ(n % 16, 0);
+ CHECK_EQ(cur % kBatchSize, 0);
+ CHECK_EQ(reinterpret_cast<uptr>(c) % kBatchSize, 0);
+ if (!bitset) {
+ internal_bzero_aligned16(c, n);
+ cur += n;
+ continue;
+ }
+ for (uptr j = 0; j < n; j += kBatchSize, cur += kBatchSize) {
+ CHECK_LT(cur, num_8bit_counters);
+ u64 *pc64 = reinterpret_cast<u64*>(c + j);
+ u64 *pb64 = reinterpret_cast<u64*>(bitset + cur);
+ u64 c64 = *pc64;
+ u64 old_bits_64 = *pb64;
+ u64 new_bits_64 = old_bits_64;
+ if (c64) {
+ *pc64 = 0;
+ for (uptr k = 0; k < kBatchSize; k++) {
+ u64 x = (c64 >> (8 * k)) & 0xff;
+ if (x) {
+ u64 bit = 0;
+ /**/ if (x >= 128) bit = 128;
+ else if (x >= 32) bit = 64;
+ else if (x >= 16) bit = 32;
+ else if (x >= 8) bit = 16;
+ else if (x >= 4) bit = 8;
+ else if (x >= 3) bit = 4;
+ else if (x >= 2) bit = 2;
+ else if (x >= 1) bit = 1;
+ u64 mask = bit << (8 * k);
+ if (!(new_bits_64 & mask)) {
+ num_new_bits++;
+ new_bits_64 |= mask;
+ }
+ }
+ }
+ *pb64 = new_bits_64;
+ }
+ }
+ }
+ CHECK_EQ(cur, num_8bit_counters);
+ return num_new_bits;
+}
+
uptr *CoverageData::data() {
return pc_array;
}
@@ -372,15 +519,15 @@ struct CovHeader {
static void CovWritePacked(int pid, const char *module, const void *blob,
unsigned int blob_size) {
- if (cov_fd < 0) return;
+ if (cov_fd == kInvalidFd) return;
unsigned module_name_length = internal_strlen(module);
CovHeader header = {pid, module_name_length, blob_size};
if (cov_max_block_size == 0) {
// Writing to a file. Just go ahead.
- internal_write(cov_fd, &header, sizeof(header));
- internal_write(cov_fd, module, module_name_length);
- internal_write(cov_fd, blob, blob_size);
+ WriteToFile(cov_fd, &header, sizeof(header));
+ WriteToFile(cov_fd, module, module_name_length);
+ WriteToFile(cov_fd, blob, blob_size);
} else {
// Writing to a socket. We want to split the data into appropriately sized
// blocks.
@@ -403,8 +550,7 @@ static void CovWritePacked(int pid, const char *module, const void *blob,
internal_memcpy(block_data_begin, blob_pos, payload_size);
blob_pos += payload_size;
((CovHeader *)block.data())->data_length = payload_size;
- internal_write(cov_fd, block.data(),
- header_size_with_module + payload_size);
+ WriteToFile(cov_fd, block.data(), header_size_with_module + payload_size);
}
}
}
@@ -413,25 +559,25 @@ static void CovWritePacked(int pid, const char *module, const void *blob,
// If packed = true and name == 0: <pid>.<sancov>.<packed>.
// If packed = true and name != 0: <name>.<sancov>.<packed> (name is
// user-supplied).
-static int CovOpenFile(bool packed, const char *name,
- const char *extension = "sancov") {
- InternalScopedString path(kMaxPathLength);
+static fd_t CovOpenFile(InternalScopedString *path, bool packed,
+ const char *name, const char *extension = "sancov") {
+ path->clear();
if (!packed) {
CHECK(name);
- path.append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(),
+ path->append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(),
extension);
} else {
if (!name)
- path.append("%s/%zd.%s.packed", coverage_dir, internal_getpid(),
+ path->append("%s/%zd.%s.packed", coverage_dir, internal_getpid(),
extension);
else
- path.append("%s/%s.%s.packed", coverage_dir, name, extension);
- }
- uptr fd = OpenFile(path.data(), true);
- if (internal_iserror(fd)) {
- Report(" SanitizerCoverage: failed to open %s for writing\n", path.data());
- return -1;
+ path->append("%s/%s.%s.packed", coverage_dir, name, extension);
}
+ error_t err;
+ fd_t fd = OpenFile(path->data(), WrOnly, &err);
+ if (fd == kInvalidFd)
+ Report("SanitizerCoverage: failed to open %s for writing (reason: %d)\n",
+ path->data(), err);
return fd;
}
@@ -446,38 +592,40 @@ void CoverageData::DumpTrace() {
for (uptr i = 0, n = size(); i < n; i++) {
const char *module_name = "<unknown>";
uptr module_address = 0;
- sym->GetModuleNameAndOffsetForPC(pc_array[i], &module_name,
+ sym->GetModuleNameAndOffsetForPC(UnbundlePc(pc_array[i]), &module_name,
&module_address);
out.append("%s 0x%zx\n", module_name, module_address);
}
- int fd = CovOpenFile(false, "trace-points");
- if (fd < 0) return;
- internal_write(fd, out.data(), out.length());
- internal_close(fd);
+ InternalScopedString path(kMaxPathLength);
+ fd_t fd = CovOpenFile(&path, false, "trace-points");
+ if (fd == kInvalidFd) return;
+ WriteToFile(fd, out.data(), out.length());
+ CloseFile(fd);
- fd = CovOpenFile(false, "trace-compunits");
- if (fd < 0) return;
+ fd = CovOpenFile(&path, false, "trace-compunits");
+ if (fd == kInvalidFd) return;
out.clear();
for (uptr i = 0; i < comp_unit_name_vec.size(); i++)
- out.append("%s\n", comp_unit_name_vec[i]);
- internal_write(fd, out.data(), out.length());
- internal_close(fd);
+ out.append("%s\n", comp_unit_name_vec[i].copied_module_name);
+ WriteToFile(fd, out.data(), out.length());
+ CloseFile(fd);
- fd = CovOpenFile(false, "trace-events");
- if (fd < 0) return;
+ fd = CovOpenFile(&path, false, "trace-events");
+ if (fd == kInvalidFd) return;
uptr bytes_to_write = max_idx * sizeof(tr_event_array[0]);
u8 *event_bytes = reinterpret_cast<u8*>(tr_event_array);
// The trace file could be huge, and may not be written with a single syscall.
while (bytes_to_write) {
- uptr actually_written = internal_write(fd, event_bytes, bytes_to_write);
- if (actually_written <= bytes_to_write) {
+ uptr actually_written;
+ if (WriteToFile(fd, event_bytes, bytes_to_write, &actually_written) &&
+ actually_written <= bytes_to_write) {
bytes_to_write -= actually_written;
event_bytes += actually_written;
} else {
break;
}
}
- internal_close(fd);
+ CloseFile(fd);
VReport(1, " CovDump: Trace: %zd PCs written\n", size());
VReport(1, " CovDump: Trace: %zd Events written\n", max_idx);
}
@@ -514,10 +662,11 @@ void CoverageData::DumpCallerCalleePairs() {
callee_module_address);
}
}
- int fd = CovOpenFile(false, "caller-callee");
- if (fd < 0) return;
- internal_write(fd, out.data(), out.length());
- internal_close(fd);
+ InternalScopedString path(kMaxPathLength);
+ fd_t fd = CovOpenFile(&path, false, "caller-callee");
+ if (fd == kInvalidFd) return;
+ WriteToFile(fd, out.data(), out.length());
+ CloseFile(fd);
VReport(1, " CovDump: %zd caller-callee pairs written\n", total);
}
@@ -534,86 +683,123 @@ void CoverageData::TraceBasicBlock(s32 *id) {
tr_event_pointer++;
}
-static void CovDumpAsBitSet() {
+void CoverageData::DumpCounters() {
+ if (!common_flags()->coverage_counters) return;
+ uptr n = coverage_data.GetNumberOf8bitCounters();
+ if (!n) return;
+ InternalScopedBuffer<u8> bitset(n);
+ coverage_data.Update8bitCounterBitsetAndClearCounters(bitset.data());
+ InternalScopedString path(kMaxPathLength);
+
+ for (uptr m = 0; m < module_name_vec.size(); m++) {
+ auto r = module_name_vec[m];
+ CHECK(r.copied_module_name);
+ CHECK_LE(r.beg, r.end);
+ CHECK_LE(r.end, size());
+ const char *base_name = StripModuleName(r.copied_module_name);
+ fd_t fd =
+ CovOpenFile(&path, /* packed */ false, base_name, "counters-sancov");
+ if (fd == kInvalidFd) return;
+ WriteToFile(fd, bitset.data() + r.beg, r.end - r.beg);
+ CloseFile(fd);
+ VReport(1, " CovDump: %zd counters written for '%s'\n", r.end - r.beg,
+ base_name);
+ }
+}
+
+void CoverageData::DumpAsBitSet() {
if (!common_flags()->coverage_bitset) return;
- if (!coverage_data.size()) return;
- int fd = CovOpenFile(/* packed */false, "combined", "bitset-sancov");
- if (fd < 0) return;
- uptr n = coverage_data.size();
- uptr n_set_bits = 0;
- InternalScopedBuffer<char> out(n);
- for (uptr i = 0; i < n; i++) {
- uptr pc = coverage_data.data()[i];
- out[i] = pc ? '1' : '0';
- if (pc)
- n_set_bits++;
+ if (!size()) return;
+ InternalScopedBuffer<char> out(size());
+ InternalScopedString path(kMaxPathLength);
+ for (uptr m = 0; m < module_name_vec.size(); m++) {
+ uptr n_set_bits = 0;
+ auto r = module_name_vec[m];
+ CHECK(r.copied_module_name);
+ CHECK_LE(r.beg, r.end);
+ CHECK_LE(r.end, size());
+ for (uptr i = r.beg; i < r.end; i++) {
+ uptr pc = UnbundlePc(pc_array[i]);
+ out[i] = pc ? '1' : '0';
+ if (pc)
+ n_set_bits++;
+ }
+ const char *base_name = StripModuleName(r.copied_module_name);
+ fd_t fd = CovOpenFile(&path, /* packed */false, base_name, "bitset-sancov");
+ if (fd == kInvalidFd) return;
+ WriteToFile(fd, out.data() + r.beg, r.end - r.beg);
+ CloseFile(fd);
+ VReport(1,
+ " CovDump: bitset of %zd bits written for '%s', %zd bits are set\n",
+ r.end - r.beg, base_name, n_set_bits);
}
- internal_write(fd, out.data(), n);
- internal_close(fd);
- VReport(1, " CovDump: bitset of %zd bits written, %zd bits are set\n", n,
- n_set_bits);
}
-// Dump the coverage on disk.
-static void CovDump() {
- if (!coverage_enabled || common_flags()->coverage_direct) return;
-#if !SANITIZER_WINDOWS
- if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed))
- return;
- CovDumpAsBitSet();
- coverage_data.DumpTrace();
+void CoverageData::DumpOffsets() {
+ auto sym = Symbolizer::GetOrInit();
if (!common_flags()->coverage_pcs) return;
- uptr size = coverage_data.size();
- InternalMmapVector<u32> offsets(size);
- uptr *vb = coverage_data.data();
- uptr *ve = vb + size;
- SortArray(vb, size);
- MemoryMappingLayout proc_maps(/*cache_enabled*/true);
- uptr mb, me, off, prot;
- InternalScopedString module(kMaxPathLength);
+ CHECK_NE(sym, nullptr);
+ InternalMmapVector<uptr> offsets(0);
InternalScopedString path(kMaxPathLength);
- for (int i = 0;
- proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot);
- i++) {
- if ((prot & MemoryMappingLayout::kProtectionExecute) == 0)
- continue;
- while (vb < ve && *vb < mb) vb++;
- if (vb >= ve) break;
- if (*vb < me) {
- offsets.clear();
- const uptr *old_vb = vb;
- CHECK_LE(off, *vb);
- for (; vb < ve && *vb < me; vb++) {
- uptr diff = *vb - (i ? mb : 0) + off;
- CHECK_LE(diff, 0xffffffffU);
- offsets.push_back(static_cast<u32>(diff));
- }
- const char *module_name = StripModuleName(module.data());
- if (cov_sandboxed) {
- if (cov_fd >= 0) {
- CovWritePacked(internal_getpid(), module_name, offsets.data(),
- offsets.size() * sizeof(u32));
- VReport(1, " CovDump: %zd PCs written to packed file\n", vb - old_vb);
- }
- } else {
- // One file per module per process.
- path.clear();
- path.append("%s/%s.%zd.sancov", coverage_dir, module_name,
- internal_getpid());
- int fd = CovOpenFile(false /* packed */, module_name);
- if (fd > 0) {
- internal_write(fd, offsets.data(), offsets.size() * sizeof(u32));
- internal_close(fd);
- VReport(1, " CovDump: %s: %zd PCs written\n", path.data(),
- vb - old_vb);
- }
+ for (uptr m = 0; m < module_name_vec.size(); m++) {
+ offsets.clear();
+ uptr num_words_for_magic = SANITIZER_WORDSIZE == 64 ? 1 : 2;
+ for (uptr i = 0; i < num_words_for_magic; i++)
+ offsets.push_back(0);
+ auto r = module_name_vec[m];
+ CHECK(r.copied_module_name);
+ CHECK_LE(r.beg, r.end);
+ CHECK_LE(r.end, size());
+ for (uptr i = r.beg; i < r.end; i++) {
+ uptr pc = UnbundlePc(pc_array[i]);
+ uptr counter = UnbundleCounter(pc_array[i]);
+ if (!pc) continue; // Not visited.
+ uptr offset = 0;
+ sym->GetModuleNameAndOffsetForPC(pc, nullptr, &offset);
+ offsets.push_back(BundlePcAndCounter(offset, counter));
+ }
+
+ CHECK_GE(offsets.size(), num_words_for_magic);
+ SortArray(offsets.data(), offsets.size());
+ for (uptr i = 0; i < offsets.size(); i++)
+ offsets[i] = UnbundlePc(offsets[i]);
+
+ uptr num_offsets = offsets.size() - num_words_for_magic;
+ u64 *magic_p = reinterpret_cast<u64*>(offsets.data());
+ CHECK_EQ(*magic_p, 0ULL);
+ // FIXME: we may want to write 32-bit offsets even in 64-mode
+ // if all the offsets are small enough.
+ *magic_p = SANITIZER_WORDSIZE == 64 ? kMagic64 : kMagic32;
+
+ const char *module_name = StripModuleName(r.copied_module_name);
+ if (cov_sandboxed) {
+ if (cov_fd != kInvalidFd) {
+ CovWritePacked(internal_getpid(), module_name, offsets.data(),
+ offsets.size() * sizeof(offsets[0]));
+ VReport(1, " CovDump: %zd PCs written to packed file\n", num_offsets);
}
+ } else {
+ // One file per module per process.
+ fd_t fd = CovOpenFile(&path, false /* packed */, module_name);
+ if (fd == kInvalidFd) continue;
+ WriteToFile(fd, offsets.data(), offsets.size() * sizeof(offsets[0]));
+ CloseFile(fd);
+ VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), num_offsets);
}
}
- if (cov_fd >= 0)
- internal_close(cov_fd);
- coverage_data.DumpCallerCalleePairs();
-#endif // !SANITIZER_WINDOWS
+ if (cov_fd != kInvalidFd)
+ CloseFile(cov_fd);
+}
+
+void CoverageData::DumpAll() {
+ if (!coverage_enabled || common_flags()->coverage_direct) return;
+ if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed))
+ return;
+ DumpAsBitSet();
+ DumpCounters();
+ DumpTrace();
+ DumpOffsets();
+ DumpCallerCalleePairs();
}
void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
@@ -621,17 +807,21 @@ void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
if (!coverage_enabled) return;
cov_sandboxed = args->coverage_sandboxed;
if (!cov_sandboxed) return;
- cov_fd = args->coverage_fd;
cov_max_block_size = args->coverage_max_block_size;
- if (cov_fd < 0)
+ if (args->coverage_fd >= 0) {
+ cov_fd = (fd_t)args->coverage_fd;
+ } else {
+ InternalScopedString path(kMaxPathLength);
// Pre-open the file now. The sandbox won't allow us to do it later.
- cov_fd = CovOpenFile(true /* packed */, 0);
+ cov_fd = CovOpenFile(&path, true /* packed */, 0);
+ }
}
-int MaybeOpenCovFile(const char *name) {
+fd_t MaybeOpenCovFile(const char *name) {
CHECK(name);
- if (!coverage_enabled) return -1;
- return CovOpenFile(true /* packed */, name);
+ if (!coverage_enabled) return kInvalidFd;
+ InternalScopedString path(kMaxPathLength);
+ return CovOpenFile(&path, true /* packed */, name);
}
void CovBeforeFork() {
@@ -649,9 +839,7 @@ void InitializeCoverage(bool enabled, const char *dir) {
coverage_dir = dir;
coverage_data.Init();
if (enabled) coverage_data.Enable();
-#if !SANITIZER_WINDOWS
if (!common_flags()->coverage_direct) Atexit(__sanitizer_cov_dump);
-#endif
}
void ReInitializeCoverage(bool enabled, const char *dir) {
@@ -674,7 +862,8 @@ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u32 *guard) {
}
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_with_check(u32 *guard) {
atomic_uint32_t *atomic_guard = reinterpret_cast<atomic_uint32_t*>(guard);
- if (__sanitizer::atomic_load(atomic_guard, memory_order_relaxed))
+ if (static_cast<s32>(
+ __sanitizer::atomic_load(atomic_guard, memory_order_relaxed)) < 0)
__sanitizer_cov(guard);
}
SANITIZER_INTERFACE_ATTRIBUTE void
@@ -687,10 +876,14 @@ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() {
coverage_dir = common_flags()->coverage_dir;
coverage_data.Init();
}
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); }
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() {
+ coverage_data.DumpAll();
+}
SANITIZER_INTERFACE_ATTRIBUTE void
-__sanitizer_cov_module_init(s32 *guards, uptr npcs, const char *module_name) {
- coverage_data.InitializeGuards(guards, npcs, module_name);
+__sanitizer_cov_module_init(s32 *guards, uptr npcs, u8 *counters,
+ const char *comp_unit_name) {
+ coverage_data.InitializeGuards(guards, npcs, comp_unit_name, GET_CALLER_PC());
+ coverage_data.InitializeCounters(counters, npcs);
if (!common_flags()->coverage_direct) return;
if (SANITIZER_ANDROID && coverage_enabled) {
// dlopen/dlclose interceptors do not work on Android, so we rely on
@@ -701,7 +894,7 @@ __sanitizer_cov_module_init(s32 *guards, uptr npcs, const char *module_name) {
}
SANITIZER_INTERFACE_ATTRIBUTE
sptr __sanitizer_maybe_open_cov_file(const char *name) {
- return MaybeOpenCovFile(name);
+ return (sptr)MaybeOpenCovFile(name);
}
SANITIZER_INTERFACE_ATTRIBUTE
uptr __sanitizer_get_total_unique_coverage() {
@@ -728,4 +921,17 @@ uptr __sanitizer_get_coverage_guards(uptr **data) {
*data = coverage_data.data();
return coverage_data.size();
}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_number_of_counters() {
+ return coverage_data.GetNumberOf8bitCounters();
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_update_counter_bitset_and_clear_counters(u8 *bitset) {
+ return coverage_data.Update8bitCounterBitsetAndClearCounters(bitset);
+}
+// Default empty implementation (weak). Users should redefine it.
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void __sanitizer_cov_trace_cmp() {}
} // extern "C"
diff --git a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
index 6b5e91fbc018..a3d75abc0476 100644
--- a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc
@@ -35,7 +35,6 @@
namespace __sanitizer {
-static const uptr kMaxNumberOfModules = 1 << 14;
static const uptr kMaxTextSize = 64 * 1024;
struct CachedMapping {
@@ -96,32 +95,30 @@ void CovUpdateMapping(const char *coverage_dir, uptr caller_pc) {
}
}
- int err;
+ error_t err;
InternalScopedString tmp_path(64 + internal_strlen(coverage_dir));
uptr res = internal_snprintf((char *)tmp_path.data(), tmp_path.size(),
"%s/%zd.sancov.map.tmp", coverage_dir,
internal_getpid());
CHECK_LE(res, tmp_path.size());
- uptr map_fd = OpenFile(tmp_path.data(), true);
- if (internal_iserror(map_fd, &err)) {
- Report(" Coverage: failed to open %s for writing: %d\n", tmp_path.data(),
+ fd_t map_fd = OpenFile(tmp_path.data(), WrOnly, &err);
+ if (map_fd == kInvalidFd) {
+ Report("Coverage: failed to open %s for writing: %d\n", tmp_path.data(),
err);
Die();
}
- res = internal_write(map_fd, text.data(), text.length());
- if (internal_iserror(res, &err)) {
+ if (!WriteToFile(map_fd, text.data(), text.length(), nullptr, &err)) {
Printf("sancov.map write failed: %d\n", err);
Die();
}
- internal_close(map_fd);
+ CloseFile(map_fd);
InternalScopedString path(64 + internal_strlen(coverage_dir));
res = internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.map",
coverage_dir, internal_getpid());
CHECK_LE(res, path.size());
- res = internal_rename(tmp_path.data(), path.data());
- if (internal_iserror(res, &err)) {
+ if (!RenameFile(tmp_path.data(), path.data(), &err)) {
Printf("sancov.map rename failed: %d\n", err);
Die();
}
diff --git a/lib/sanitizer_common/sanitizer_deadlock_detector1.cc b/lib/sanitizer_common/sanitizer_deadlock_detector1.cc
index b931edc7eae5..5890b54b933b 100644
--- a/lib/sanitizer_common/sanitizer_deadlock_detector1.cc
+++ b/lib/sanitizer_common/sanitizer_deadlock_detector1.cc
@@ -40,19 +40,20 @@ struct DD : public DDetector {
explicit DD(const DDFlags *flags);
- DDPhysicalThread* CreatePhysicalThread();
- void DestroyPhysicalThread(DDPhysicalThread *pt);
+ DDPhysicalThread *CreatePhysicalThread() override;
+ void DestroyPhysicalThread(DDPhysicalThread *pt) override;
- DDLogicalThread* CreateLogicalThread(u64 ctx);
- void DestroyLogicalThread(DDLogicalThread *lt);
+ DDLogicalThread *CreateLogicalThread(u64 ctx) override;
+ void DestroyLogicalThread(DDLogicalThread *lt) override;
- void MutexInit(DDCallback *cb, DDMutex *m);
- void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock);
- void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock);
- void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock);
- void MutexDestroy(DDCallback *cb, DDMutex *m);
+ void MutexInit(DDCallback *cb, DDMutex *m) override;
+ void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) override;
+ void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
+ bool trylock) override;
+ void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) override;
+ void MutexDestroy(DDCallback *cb, DDMutex *m) override;
- DDReport *GetReport(DDCallback *cb);
+ DDReport *GetReport(DDCallback *cb) override;
void MutexEnsureID(DDLogicalThread *lt, DDMutex *m);
void ReportDeadlock(DDCallback *cb, DDMutex *m);
diff --git a/lib/sanitizer_common/sanitizer_flags.cc b/lib/sanitizer_common/sanitizer_flags.cc
index e835b46a24fc..01098a3d6b87 100644
--- a/lib/sanitizer_common/sanitizer_flags.cc
+++ b/lib/sanitizer_common/sanitizer_flags.cc
@@ -54,7 +54,7 @@ class FlagHandlerInclude : public FlagHandlerBase {
bool Parse(const char *value) final {
char *data;
uptr data_mapped_size;
- int err;
+ error_t err;
uptr len =
ReadFileToBuffer(value, &data, &data_mapped_size,
Max(kMaxIncludeSize, GetPageSizeCached()), &err);
diff --git a/lib/sanitizer_common/sanitizer_flags.inc b/lib/sanitizer_common/sanitizer_flags.inc
index 58f7f372228f..bbb39c73e2d0 100644
--- a/lib/sanitizer_common/sanitizer_flags.inc
+++ b/lib/sanitizer_common/sanitizer_flags.inc
@@ -51,6 +51,10 @@ COMMON_FLAG(
"Write logs to \"log_path.pid\". The special values are \"stdout\" and "
"\"stderr\". The default is \"stderr\".")
COMMON_FLAG(
+ bool, log_exe_name, false,
+ "Mention name of executable when reporting error and "
+ "append executable name to logs (as in \"log_path.exe_name.pid\").")
+COMMON_FLAG(
int, verbosity, 0,
"Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).")
COMMON_FLAG(bool, detect_leaks, true, "Enable memory leak detection.")
@@ -67,8 +71,9 @@ COMMON_FLAG(bool, print_summary, true,
"reports.")
COMMON_FLAG(bool, check_printf, true, "Check printf arguments.")
COMMON_FLAG(bool, handle_segv, SANITIZER_NEEDS_SEGV,
- "If set, registers the tool's custom SEGV handler (both SIGBUS and "
- "SIGSEGV on OSX).")
+ "If set, registers the tool's custom SIGSEGV/SIGBUS handler.")
+COMMON_FLAG(bool, handle_abort, false,
+ "If set, registers the tool's custom SIGABRT handler.")
COMMON_FLAG(bool, allow_user_segv_handler, false,
"If set, allows user to register a SEGV handler even if the tool "
"registers one.")
@@ -111,13 +116,18 @@ COMMON_FLAG(
bool, coverage, false,
"If set, coverage information will be dumped at program shutdown (if the "
"coverage instrumentation was enabled at compile time).")
-// On by default, but works only if coverage == true.
COMMON_FLAG(bool, coverage_pcs, true,
"If set (and if 'coverage' is set too), the coverage information "
"will be dumped as a set of PC offsets for every module.")
+COMMON_FLAG(bool, coverage_order_pcs, false,
+ "If true, the PCs will be dumped in the order they've"
+ " appeared during the execution.")
COMMON_FLAG(bool, coverage_bitset, false,
"If set (and if 'coverage' is set too), the coverage information "
"will also be dumped as a bitset to a separate file.")
+COMMON_FLAG(bool, coverage_counters, false,
+ "If set (and if 'coverage' is set too), the bitmap that corresponds"
+ " to coverage counters will be dumped.")
COMMON_FLAG(bool, coverage_direct, SANITIZER_ANDROID,
"If set, coverage information will be dumped directly to a memory "
"mapped file. This way data is not lost even if the process is "
@@ -140,9 +150,26 @@ COMMON_FLAG(bool, use_madv_dontdump, true,
"in core file.")
COMMON_FLAG(bool, symbolize_inline_frames, true,
"Print inlined frames in stacktraces. Defaults to true.")
+COMMON_FLAG(bool, symbolize_vs_style, false,
+ "Print file locations in Visual Studio style (e.g: "
+ " file(10,42): ...")
COMMON_FLAG(const char *, stack_trace_format, "DEFAULT",
"Format string used to render stack frames. "
"See sanitizer_stacktrace_printer.h for the format description. "
"Use DEFAULT to get default format.")
COMMON_FLAG(bool, no_huge_pages_for_shadow, true,
"If true, the shadow is not allowed to use huge pages. ")
+COMMON_FLAG(bool, strict_string_checks, false,
+ "If set check that string arguments are properly null-terminated")
+COMMON_FLAG(bool, intercept_strstr, true,
+ "If set, uses custom wrappers for strstr and strcasestr functions "
+ "to find more errors.")
+COMMON_FLAG(bool, intercept_strspn, true,
+ "If set, uses custom wrappers for strspn and strcspn function "
+ "to find more errors.")
+COMMON_FLAG(bool, intercept_strpbrk, true,
+ "If set, uses custom wrappers for strpbrk function "
+ "to find more errors.")
+COMMON_FLAG(bool, decorate_proc_maps, false, "If set, decorate sanitizer "
+ "mappings in /proc/self/maps with "
+ "user-readable names")
diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h
index a969f305cd1a..b76c6023aba2 100644
--- a/lib/sanitizer_common/sanitizer_internal_defs.h
+++ b/lib/sanitizer_common/sanitizer_internal_defs.h
@@ -32,7 +32,7 @@
# define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak))
#endif
-#if SANITIZER_LINUX && !defined(SANITIZER_GO)
+#if (SANITIZER_LINUX || SANITIZER_WINDOWS) && !defined(SANITIZER_GO)
# define SANITIZER_SUPPORTS_WEAK_HOOKS 1
#else
# define SANITIZER_SUPPORTS_WEAK_HOOKS 0
@@ -80,7 +80,15 @@ typedef signed char s8;
typedef signed short s16; // NOLINT
typedef signed int s32;
typedef signed long long s64; // NOLINT
+#if SANITIZER_WINDOWS
+// On Windows, files are HANDLE, which is a synonim of void*.
+// Use void* to avoid including <windows.h> everywhere.
+typedef void* fd_t;
+typedef unsigned error_t;
+#else
typedef int fd_t;
+typedef int error_t;
+#endif
// WARNING: OFF_T may be different from OS type off_t, depending on the value of
// _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls
@@ -295,6 +303,7 @@ extern "C" void* _ReturnAddress(void);
do { \
volatile uptr enable_fp; \
enable_fp = GET_CURRENT_FRAME(); \
+ (void)enable_fp; \
} while (0)
#endif // SANITIZER_DEFS_H
diff --git a/lib/sanitizer_common/sanitizer_libc.h b/lib/sanitizer_common/sanitizer_libc.h
index c086b8a9139e..ae4e938f4af9 100644
--- a/lib/sanitizer_common/sanitizer_libc.h
+++ b/lib/sanitizer_common/sanitizer_libc.h
@@ -11,6 +11,7 @@
// run-time libraries.
// These tools can not use some of the libc functions directly because those
// functions are intercepted. Instead, we implement a tiny subset of libc here.
+// FIXME: Some of functions declared in this file are in fact POSIX, not libc.
//===----------------------------------------------------------------------===//
#ifndef SANITIZER_LIBC_H
#define SANITIZER_LIBC_H
@@ -56,74 +57,26 @@ int internal_snprintf(char *buffer, uptr length, const char *format, ...);
// Optimized for the case when the result is true.
bool mem_is_zero(const char *mem, uptr size);
-
-// Memory
-uptr internal_mmap(void *addr, uptr length, int prot, int flags,
- int fd, u64 offset);
-uptr internal_munmap(void *addr, uptr length);
-
// I/O
-const fd_t kInvalidFd = -1;
+const fd_t kInvalidFd = (fd_t)-1;
const fd_t kStdinFd = 0;
-const fd_t kStdoutFd = 1;
-const fd_t kStderrFd = 2;
-uptr internal_close(fd_t fd);
-int internal_isatty(fd_t fd);
+const fd_t kStdoutFd = (fd_t)1;
+const fd_t kStderrFd = (fd_t)2;
-// Use __sanitizer::OpenFile() instead.
-uptr internal_open(const char *filename, int flags);
-uptr internal_open(const char *filename, int flags, u32 mode);
-
-uptr internal_read(fd_t fd, void *buf, uptr count);
-uptr internal_write(fd_t fd, const void *buf, uptr count);
uptr internal_ftruncate(fd_t fd, uptr size);
// OS
-uptr internal_filesize(fd_t fd); // -1 on error.
-uptr internal_stat(const char *path, void *buf);
-uptr internal_lstat(const char *path, void *buf);
-uptr internal_fstat(fd_t fd, void *buf);
-uptr internal_dup2(int oldfd, int newfd);
-uptr internal_readlink(const char *path, char *buf, uptr bufsize);
-uptr internal_unlink(const char *path);
-uptr internal_rename(const char *oldpath, const char *newpath);
void NORETURN internal__exit(int exitcode);
-uptr internal_lseek(fd_t fd, OFF_T offset, int whence);
-uptr internal_ptrace(int request, int pid, void *addr, void *data);
-uptr internal_waitpid(int pid, int *status, int options);
uptr internal_getpid();
uptr internal_getppid();
-int internal_fork();
-
// Threading
uptr internal_sched_yield();
-// These functions call appropriate pthread_ functions directly, bypassing
-// the interceptor. They are weak and may not be present in some tools.
-SANITIZER_WEAK_ATTRIBUTE
-int real_pthread_create(void *th, void *attr, void *(*callback)(void *),
- void *param);
-SANITIZER_WEAK_ATTRIBUTE
-int real_pthread_join(void *th, void **ret);
-
-#define DEFINE_REAL_PTHREAD_FUNCTIONS \
- namespace __sanitizer { \
- int real_pthread_create(void *th, void *attr, void *(*callback)(void *), \
- void *param) { \
- return REAL(pthread_create)(th, attr, callback, param); \
- } \
- int real_pthread_join(void *th, void **ret) { \
- return REAL(pthread_join(th, ret)); \
- } \
- } // namespace __sanitizer
-
// Error handling
bool internal_iserror(uptr retval, int *rverrno = 0);
-int internal_sigaction(int signum, const void *act, void *oldact);
-
} // namespace __sanitizer
#endif // SANITIZER_LIBC_H
diff --git a/lib/sanitizer_common/sanitizer_libignore.cc b/lib/sanitizer_common/sanitizer_libignore.cc
index cefb1dc97a17..8c4aeffda45e 100644
--- a/lib/sanitizer_common/sanitizer_libignore.cc
+++ b/lib/sanitizer_common/sanitizer_libignore.cc
@@ -12,6 +12,7 @@
#include "sanitizer_libignore.h"
#include "sanitizer_flags.h"
+#include "sanitizer_posix.h"
#include "sanitizer_procmaps.h"
namespace __sanitizer {
diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc
index 8029181a5173..98e5d122a0f9 100644
--- a/lib/sanitizer_common/sanitizer_linux.cc
+++ b/lib/sanitizer_common/sanitizer_linux.cc
@@ -36,6 +36,7 @@
// access stat from asm/stat.h, without conflicting with definition in
// sys/stat.h, we use this trick.
#if defined(__mips64)
+#include <asm/unistd.h>
#include <sys/types.h>
#define stat kernel_stat
#include <asm/stat.h>
@@ -45,9 +46,7 @@
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
-#if !SANITIZER_ANDROID
#include <link.h>
-#endif
#include <pthread.h>
#include <sched.h>
#include <sys/mman.h>
@@ -57,6 +56,7 @@
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
+#include <ucontext.h>
#include <unistd.h>
#if SANITIZER_FREEBSD
@@ -110,7 +110,7 @@ namespace __sanitizer {
// --------------- sanitizer_libc.h
uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
- u64 offset) {
+ OFF_T offset) {
#if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd,
offset);
@@ -126,6 +126,10 @@ uptr internal_munmap(void *addr, uptr length) {
return internal_syscall(SYSCALL(munmap), (uptr)addr, length);
}
+int internal_mprotect(void *addr, uptr length, int prot) {
+ return internal_syscall(SYSCALL(mprotect), (uptr)addr, length, prot);
+}
+
uptr internal_close(fd_t fd) {
return internal_syscall(SYSCALL(close), fd);
}
@@ -147,11 +151,6 @@ uptr internal_open(const char *filename, int flags, u32 mode) {
#endif
}
-uptr OpenFile(const char *filename, bool write) {
- return internal_open(filename,
- write ? O_RDWR | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660);
-}
-
uptr internal_read(fd_t fd, void *buf, uptr count) {
sptr res;
HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(read), fd, (uptr)buf,
@@ -168,7 +167,8 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) {
uptr internal_ftruncate(fd_t fd, uptr size) {
sptr res;
- HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd, size));
+ HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd,
+ (OFF_T)size));
return res;
}
@@ -558,6 +558,7 @@ int internal_fork() {
}
#if SANITIZER_LINUX
+#define SA_RESTORER 0x04000000
// Doesn't set sa_restorer, use with caution (see below).
int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
__sanitizer_kernel_sigaction_t k_act, k_oldact;
@@ -570,7 +571,8 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
k_act.sigaction = u_act->sigaction;
internal_memcpy(&k_act.sa_mask, &u_act->sa_mask,
sizeof(__sanitizer_kernel_sigset_t));
- k_act.sa_flags = u_act->sa_flags;
+ // Without SA_RESTORER kernel ignores the calls (probably returns EINVAL).
+ k_act.sa_flags = u_act->sa_flags | SA_RESTORER;
// FIXME: most often sa_restorer is unset, however the kernel requires it
// to point to a valid signal restorer that calls the rt_sigreturn syscall.
// If sa_restorer passed to the kernel is NULL, the program may crash upon
@@ -704,47 +706,32 @@ uptr GetPageSize() {
#endif
}
-static char proc_self_exe_cache_str[kMaxPathLength];
-static uptr proc_self_exe_cache_len = 0;
-
uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
- if (proc_self_exe_cache_len > 0) {
- // If available, use the cached module name.
- uptr module_name_len =
- internal_snprintf(buf, buf_len, "%s", proc_self_exe_cache_str);
- CHECK_LT(module_name_len, buf_len);
- return module_name_len;
- }
#if SANITIZER_FREEBSD
- const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+ const int Mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+ const char *default_module_name = "kern.proc.pathname";
size_t Size = buf_len;
- bool IsErr = (sysctl(Mib, 4, buf, &Size, NULL, 0) != 0);
+ bool IsErr = (sysctl(Mib, ARRAY_SIZE(Mib), buf, &Size, NULL, 0) != 0);
int readlink_error = IsErr ? errno : 0;
uptr module_name_len = Size;
#else
+ const char *default_module_name = "/proc/self/exe";
uptr module_name_len = internal_readlink(
- "/proc/self/exe", buf, buf_len);
+ default_module_name, buf, buf_len);
int readlink_error;
bool IsErr = internal_iserror(module_name_len, &readlink_error);
#endif
if (IsErr) {
- // We can't read /proc/self/exe for some reason, assume the name of the
- // binary is unknown.
- Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, "
+ // We can't read binary name for some reason, assume it's unknown.
+ Report("WARNING: reading executable name failed with errno %d, "
"some stack frames may not be symbolized\n", readlink_error);
- module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe");
+ module_name_len = internal_snprintf(buf, buf_len, "%s",
+ default_module_name);
CHECK_LT(module_name_len, buf_len);
}
return module_name_len;
}
-void CacheBinaryName() {
- if (!proc_self_exe_cache_len) {
- proc_self_exe_cache_len =
- ReadBinaryName(proc_self_exe_cache_str, kMaxPathLength);
- }
-}
-
// Match full names of the form /path/to/base_name{-,.}*
bool LibraryNameIs(const char *full_name, const char *base_name) {
const char *name = full_name;
@@ -861,11 +848,70 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
return res;
}
#elif defined(__mips__)
-// TODO(sagarthakur): clone function is to be rewritten in assembly.
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr) {
- return clone(fn, child_stack, flags, arg, parent_tidptr,
- newtls, child_tidptr);
+ long long res;
+ if (!fn || !child_stack)
+ return -EINVAL;
+ CHECK_EQ(0, (uptr)child_stack % 16);
+ child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
+ ((unsigned long long *)child_stack)[0] = (uptr)fn;
+ ((unsigned long long *)child_stack)[1] = (uptr)arg;
+ register void *a3 __asm__("$7") = newtls;
+ register int *a4 __asm__("$8") = child_tidptr;
+ // We don't have proper CFI directives here because it requires alot of code
+ // for very marginal benefits.
+ __asm__ __volatile__(
+ /* $v0 = syscall($v0 = __NR_clone,
+ * $a0 = flags,
+ * $a1 = child_stack,
+ * $a2 = parent_tidptr,
+ * $a3 = new_tls,
+ * $a4 = child_tidptr)
+ */
+ ".cprestore 16;\n"
+ "move $4,%1;\n"
+ "move $5,%2;\n"
+ "move $6,%3;\n"
+ "move $7,%4;\n"
+ /* Store the fifth argument on stack
+ * if we are using 32-bit abi.
+ */
+#if SANITIZER_WORDSIZE == 32
+ "lw %5,16($29);\n"
+#else
+ "move $8,%5;\n"
+#endif
+ "li $2,%6;\n"
+ "syscall;\n"
+
+ /* if ($v0 != 0)
+ * return;
+ */
+ "bnez $2,1f;\n"
+
+ /* Call "fn(arg)". */
+ "ld $25,0($29);\n"
+ "ld $4,8($29);\n"
+ "jal $25;\n"
+
+ /* Call _exit($v0). */
+ "move $4,$2;\n"
+ "li $2,%7;\n"
+ "syscall;\n"
+
+ /* Return to parent. */
+ "1:\n"
+ : "=r" (res)
+ : "r"(flags),
+ "r"(child_stack),
+ "r"(parent_tidptr),
+ "r"(a3),
+ "r"(a4),
+ "i"(__NR_clone),
+ "i"(__NR_exit)
+ : "memory", "$29" );
+ return res;
}
#endif // defined(__x86_64__) && SANITIZER_LINUX
@@ -901,9 +947,52 @@ void GetExtraActivationFlags(char *buf, uptr size) {
CHECK(size > PROP_VALUE_MAX);
__system_property_get("asan.options", buf);
}
+
+#if __ANDROID_API__ < 21
+extern "C" __attribute__((weak)) int dl_iterate_phdr(
+ int (*)(struct dl_phdr_info *, size_t, void *), void *);
+#endif
+
+static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size,
+ void *data) {
+ // Any name starting with "lib" indicates a bug in L where library base names
+ // are returned instead of paths.
+ if (info->dlpi_name && info->dlpi_name[0] == 'l' &&
+ info->dlpi_name[1] == 'i' && info->dlpi_name[2] == 'b') {
+ *(bool *)data = true;
+ return 1;
+ }
+ return 0;
+}
+
+static atomic_uint32_t android_api_level;
+
+static AndroidApiLevel AndroidDetectApiLevel() {
+ if (!&dl_iterate_phdr)
+ return ANDROID_KITKAT; // K or lower
+ bool base_name_seen = false;
+ dl_iterate_phdr(dl_iterate_phdr_test_cb, &base_name_seen);
+ if (base_name_seen)
+ return ANDROID_LOLLIPOP_MR1; // L MR1
+ return ANDROID_POST_LOLLIPOP; // post-L
+ // Plain L (API level 21) is completely broken wrt ASan and not very
+ // interesting to detect.
+}
+
+AndroidApiLevel AndroidGetApiLevel() {
+ AndroidApiLevel level =
+ (AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed);
+ if (level) return level;
+ level = AndroidDetectApiLevel();
+ atomic_store(&android_api_level, level, memory_order_relaxed);
+ return level;
+}
+
#endif
bool IsDeadlySignal(int signum) {
+ if (common_flags()->handle_abort && signum == SIGABRT)
+ return true;
return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv;
}
@@ -912,6 +1001,11 @@ void *internal_start_thread(void(*func)(void *arg), void *arg) {
// Start the thread with signals blocked, otherwise it can steal user signals.
__sanitizer_sigset_t set, old;
internal_sigfillset(&set);
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ // Glibc uses SIGSETXID signal during setuid call. If this signal is blocked
+ // on any thread, setuid call hangs (see test/tsan/setuid.c).
+ internal_sigdelset(&set, 33);
+#endif
internal_sigprocmask(SIG_SETMASK, &set, &old);
void *th;
real_pthread_create(&th, 0, (void*(*)(void *arg))func, arg);
@@ -928,6 +1022,78 @@ void *internal_start_thread(void (*func)(void *), void *arg) { return 0; }
void internal_join_thread(void *th) {}
#endif
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
+#if defined(__arm__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.arm_pc;
+ *bp = ucontext->uc_mcontext.arm_fp;
+ *sp = ucontext->uc_mcontext.arm_sp;
+#elif defined(__aarch64__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.pc;
+ *bp = ucontext->uc_mcontext.regs[29];
+ *sp = ucontext->uc_mcontext.sp;
+#elif defined(__hppa__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.sc_iaoq[0];
+ /* GCC uses %r3 whenever a frame pointer is needed. */
+ *bp = ucontext->uc_mcontext.sc_gr[3];
+ *sp = ucontext->uc_mcontext.sc_gr[30];
+#elif defined(__x86_64__)
+# if SANITIZER_FREEBSD
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.mc_rip;
+ *bp = ucontext->uc_mcontext.mc_rbp;
+ *sp = ucontext->uc_mcontext.mc_rsp;
+# else
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.gregs[REG_RIP];
+ *bp = ucontext->uc_mcontext.gregs[REG_RBP];
+ *sp = ucontext->uc_mcontext.gregs[REG_RSP];
+# endif
+#elif defined(__i386__)
+# if SANITIZER_FREEBSD
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.mc_eip;
+ *bp = ucontext->uc_mcontext.mc_ebp;
+ *sp = ucontext->uc_mcontext.mc_esp;
+# else
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.gregs[REG_EIP];
+ *bp = ucontext->uc_mcontext.gregs[REG_EBP];
+ *sp = ucontext->uc_mcontext.gregs[REG_ESP];
+# endif
+#elif defined(__powerpc__) || defined(__powerpc64__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.regs->nip;
+ *sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
+ // The powerpc{,64}-linux ABIs do not specify r31 as the frame
+ // pointer, but GCC always uses r31 when we need a frame pointer.
+ *bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
+#elif defined(__sparc__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ uptr *stk_ptr;
+# if defined (__arch64__)
+ *pc = ucontext->uc_mcontext.mc_gregs[MC_PC];
+ *sp = ucontext->uc_mcontext.mc_gregs[MC_O6];
+ stk_ptr = (uptr *) (*sp + 2047);
+ *bp = stk_ptr[15];
+# else
+ *pc = ucontext->uc_mcontext.gregs[REG_PC];
+ *sp = ucontext->uc_mcontext.gregs[REG_O6];
+ stk_ptr = (uptr *) *sp;
+ *bp = stk_ptr[15];
+# endif
+#elif defined(__mips__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.pc;
+ *bp = ucontext->uc_mcontext.gregs[30];
+ *sp = ucontext->uc_mcontext.gregs[29];
+#else
+# error "Unsupported arch"
+#endif
+}
+
} // namespace __sanitizer
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/lib/sanitizer_common/sanitizer_linux.h b/lib/sanitizer_common/sanitizer_linux.h
index b2e603d3a23e..e9fc4ad448d8 100644
--- a/lib/sanitizer_common/sanitizer_linux.h
+++ b/lib/sanitizer_common/sanitizer_linux.h
@@ -17,6 +17,7 @@
#if SANITIZER_FREEBSD || SANITIZER_LINUX
#include "sanitizer_common.h"
#include "sanitizer_internal_defs.h"
+#include "sanitizer_posix.h"
#include "sanitizer_platform_limits_posix.h"
struct link_map; // Opaque type returned by dlopen().
@@ -80,11 +81,6 @@ uptr ThreadSelfOffset();
// information).
bool LibraryNameIs(const char *full_name, const char *base_name);
-// Read the name of the current binary from /proc/self/exe.
-uptr ReadBinaryName(/*out*/char *buf, uptr buf_len);
-// Cache the value of /proc/self/exe.
-void CacheBinaryName();
-
// Call cb for each region mapped by map.
void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr));
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
index c71b6257ebc3..39eb1d216b2e 100644
--- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
@@ -15,6 +15,7 @@
#include "sanitizer_platform.h"
#if SANITIZER_FREEBSD || SANITIZER_LINUX
+#include "sanitizer_atomic.h"
#include "sanitizer_common.h"
#include "sanitizer_flags.h"
#include "sanitizer_freebsd.h"
@@ -22,13 +23,12 @@
#include "sanitizer_placement_new.h"
#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
-#include "sanitizer_atomic.h"
-#include "sanitizer_symbolizer.h"
#if SANITIZER_ANDROID || SANITIZER_FREEBSD
#include <dlfcn.h> // for dlsym()
#endif
+#include <link.h>
#include <pthread.h>
#include <signal.h>
#include <sys/resource.h>
@@ -43,9 +43,12 @@
#include <sys/prctl.h>
#endif
+#if SANITIZER_ANDROID
+#include <android/api-level.h>
+#endif
+
#if !SANITIZER_ANDROID
#include <elf.h>
-#include <link.h>
#include <unistd.h>
#endif
@@ -398,13 +401,6 @@ void AdjustStackSize(void *attr_) {
}
}
-#if SANITIZER_ANDROID
-uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
- string_predicate_t filter) {
- MemoryMappingLayout memory_mapping(false);
- return memory_mapping.DumpListOfModules(modules, max_modules, filter);
-}
-#else // SANITIZER_ANDROID
# if !SANITIZER_FREEBSD
typedef ElfW(Phdr) Elf_Phdr;
# elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2
@@ -429,7 +425,7 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
if (data->first) {
data->first = false;
// First module is the binary itself.
- ReadBinaryName(module_name.data(), module_name.size());
+ ReadBinaryNameCached(module_name.data(), module_name.size());
} else if (info->dlpi_name) {
module_name.append("%s", info->dlpi_name);
}
@@ -437,9 +433,8 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
return 0;
if (data->filter && !data->filter(module_name.data()))
return 0;
- void *mem = &data->modules[data->current_n];
- LoadedModule *cur_module = new(mem) LoadedModule(module_name.data(),
- info->dlpi_addr);
+ LoadedModule *cur_module = &data->modules[data->current_n];
+ cur_module->set(module_name.data(), info->dlpi_addr);
data->current_n++;
for (int i = 0; i < info->dlpi_phnum; i++) {
const Elf_Phdr *phdr = &info->dlpi_phdr[i];
@@ -453,27 +448,28 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
return 0;
}
+#if SANITIZER_ANDROID && __ANDROID_API__ < 21
+extern "C" __attribute__((weak)) int dl_iterate_phdr(
+ int (*)(struct dl_phdr_info *, size_t, void *), void *);
+#endif
+
uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
string_predicate_t filter) {
+#if SANITIZER_ANDROID && __ANDROID_API__ < 21
+ u32 api_level = AndroidGetApiLevel();
+ // Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken.
+ // The runtime check allows the same library to work with
+ // both K and L (and future) Android releases.
+ if (api_level <= ANDROID_LOLLIPOP_MR1) { // L or earlier
+ MemoryMappingLayout memory_mapping(false);
+ return memory_mapping.DumpListOfModules(modules, max_modules, filter);
+ }
+#endif
CHECK(modules);
DlIteratePhdrData data = {modules, 0, true, max_modules, filter};
dl_iterate_phdr(dl_iterate_phdr_cb, &data);
return data.current_n;
}
-#endif // SANITIZER_ANDROID
-
-void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
- // Some kinds of sandboxes may forbid filesystem access, so we won't be able
- // to read the file mappings from /proc/self/maps. Luckily, neither the
- // process will be able to load additional libraries, so it's fine to use the
- // cached mappings.
- MemoryMappingLayout::CacheMemoryMappings();
- // Same for /proc/self/exe in the symbolizer.
-#if !SANITIZER_GO
- Symbolizer::GetOrInit()->PrepareForSandboxing();
- CovPrepareForSandboxing(args);
-#endif
-}
// getrusage does not give us the current RSS, only the max RSS.
// Still, this is better than nothing if /proc/self/statm is not available
@@ -488,8 +484,8 @@ static uptr GetRSSFromGetrusage() {
uptr GetRSS() {
if (!common_flags()->can_use_proc_maps_statm)
return GetRSSFromGetrusage();
- uptr fd = OpenFile("/proc/self/statm", false);
- if ((sptr)fd < 0)
+ fd_t fd = OpenFile("/proc/self/statm", RdOnly);
+ if (fd == kInvalidFd)
return GetRSSFromGetrusage();
char buf[64];
uptr len = internal_read(fd, buf, sizeof(buf) - 1);
diff --git a/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc
index 39a5c7e8d24f..dddce1c1583c 100644
--- a/lib/sanitizer_common/sanitizer_mac.cc
+++ b/lib/sanitizer_common/sanitizer_mac.cc
@@ -27,21 +27,30 @@
#include "sanitizer_libc.h"
#include "sanitizer_mac.h"
#include "sanitizer_placement_new.h"
+#include "sanitizer_platform_limits_posix.h"
#include "sanitizer_procmaps.h"
+#if !SANITIZER_IOS
#include <crt_externs.h> // for _NSGetEnviron
+#else
+extern char **environ;
+#endif
+
+#include <errno.h>
#include <fcntl.h>
+#include <libkern/OSAtomic.h>
+#include <mach-o/dyld.h>
+#include <mach/mach.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
+#include <stdlib.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <unistd.h>
-#include <libkern/OSAtomic.h>
-#include <errno.h>
namespace __sanitizer {
@@ -57,6 +66,10 @@ uptr internal_munmap(void *addr, uptr length) {
return munmap(addr, length);
}
+int internal_mprotect(void *addr, uptr length, int prot) {
+ return mprotect(addr, length, prot);
+}
+
uptr internal_close(fd_t fd) {
return close(fd);
}
@@ -69,11 +82,6 @@ uptr internal_open(const char *filename, int flags, u32 mode) {
return open(filename, flags, mode);
}
-uptr OpenFile(const char *filename, bool write) {
- return internal_open(filename,
- write ? O_WRONLY | O_CREAT : O_RDONLY, 0660);
-}
-
uptr internal_read(fd_t fd, void *buf, uptr count) {
return read(fd, buf, count);
}
@@ -130,6 +138,13 @@ int internal_sigaction(int signum, const void *act, void *oldact) {
(struct sigaction *)act, (struct sigaction *)oldact);
}
+void internal_sigfillset(__sanitizer_sigset_t *set) { sigfillset(set); }
+
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+ __sanitizer_sigset_t *oldset) {
+ return sigprocmask(how, set, oldset);
+}
+
int internal_fork() {
// TODO(glider): this may call user's pthread_atfork() handlers which is bad.
return fork();
@@ -180,7 +195,8 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
*stack_bottom = *stack_top - stacksize;
}
-const char *GetEnv(const char *name) {
+char **GetEnviron() {
+#if !SANITIZER_IOS
char ***env_ptr = _NSGetEnviron();
if (!env_ptr) {
Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is "
@@ -188,29 +204,45 @@ const char *GetEnv(const char *name) {
CHECK(env_ptr);
}
char **environ = *env_ptr;
+#endif
CHECK(environ);
+ return environ;
+}
+
+const char *GetEnv(const char *name) {
+ char **env = GetEnviron();
uptr name_len = internal_strlen(name);
- while (*environ != 0) {
- uptr len = internal_strlen(*environ);
+ while (*env != 0) {
+ uptr len = internal_strlen(*env);
if (len > name_len) {
- const char *p = *environ;
+ const char *p = *env;
if (!internal_memcmp(p, name, name_len) &&
p[name_len] == '=') { // Match.
- return *environ + name_len + 1; // String starting after =.
+ return *env + name_len + 1; // String starting after =.
}
}
- environ++;
+ env++;
}
return 0;
}
-void ReExec() {
- UNIMPLEMENTED();
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+ CHECK_LE(kMaxPathLength, buf_len);
+
+ // On OS X the executable path is saved to the stack by dyld. Reading it
+ // from there is much faster than calling dladdr, especially for large
+ // binaries with symbols.
+ InternalScopedString exe_path(kMaxPathLength);
+ uint32_t size = exe_path.size();
+ if (_NSGetExecutablePath(exe_path.data(), &size) == 0 &&
+ realpath(exe_path.data(), buf) != 0) {
+ return internal_strlen(buf);
+ }
+ return 0;
}
-void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
- (void)args;
- // Nothing here for now.
+void ReExec() {
+ UNIMPLEMENTED();
}
uptr GetPageSize() {
@@ -322,12 +354,47 @@ MacosVersion GetMacosVersion() {
}
uptr GetRSS() {
- return 0;
+ struct task_basic_info info;
+ unsigned count = TASK_BASIC_INFO_COUNT;
+ kern_return_t result =
+ task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count);
+ if (UNLIKELY(result != KERN_SUCCESS)) {
+ Report("Cannot get task info. Error: %d\n", result);
+ Die();
+ }
+ return info.resident_size;
}
void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; }
void internal_join_thread(void *th) { }
+void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
+ ucontext_t *ucontext = (ucontext_t*)context;
+# if defined(__aarch64__)
+ *pc = ucontext->uc_mcontext->__ss.__pc;
+# if defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
+ *bp = ucontext->uc_mcontext->__ss.__fp;
+# else
+ *bp = ucontext->uc_mcontext->__ss.__lr;
+# endif
+ *sp = ucontext->uc_mcontext->__ss.__sp;
+# elif defined(__x86_64__)
+ *pc = ucontext->uc_mcontext->__ss.__rip;
+ *bp = ucontext->uc_mcontext->__ss.__rbp;
+ *sp = ucontext->uc_mcontext->__ss.__rsp;
+# elif defined(__arm__)
+ *pc = ucontext->uc_mcontext->__ss.__pc;
+ *bp = ucontext->uc_mcontext->__ss.__r[7];
+ *sp = ucontext->uc_mcontext->__ss.__sp;
+# elif defined(__i386__)
+ *pc = ucontext->uc_mcontext->__ss.__eip;
+ *bp = ucontext->uc_mcontext->__ss.__ebp;
+ *sp = ucontext->uc_mcontext->__ss.__esp;
+# else
+# error "Unknown architecture"
+# endif
+}
+
} // namespace __sanitizer
#endif // SANITIZER_MAC
diff --git a/lib/sanitizer_common/sanitizer_mac.h b/lib/sanitizer_common/sanitizer_mac.h
index 9eed905187ec..50dbe93226c2 100644
--- a/lib/sanitizer_common/sanitizer_mac.h
+++ b/lib/sanitizer_common/sanitizer_mac.h
@@ -15,6 +15,7 @@
#include "sanitizer_platform.h"
#if SANITIZER_MAC
+#include "sanitizer_posix.h"
namespace __sanitizer {
@@ -32,6 +33,8 @@ enum MacosVersion {
MacosVersion GetMacosVersion();
+char **GetEnviron();
+
} // namespace __sanitizer
#endif // SANITIZER_MAC
diff --git a/lib/sanitizer_common/sanitizer_platform.h b/lib/sanitizer_common/sanitizer_platform.h
index 6f8cd30bf4d9..b47281b589e9 100644
--- a/lib/sanitizer_common/sanitizer_platform.h
+++ b/lib/sanitizer_common/sanitizer_platform.h
@@ -38,9 +38,15 @@
# else
# define SANITIZER_IOS 0
# endif
+# if TARGET_IPHONE_SIMULATOR
+# define SANITIZER_IOSSIM 1
+# else
+# define SANITIZER_IOSSIM 0
+# endif
#else
# define SANITIZER_MAC 0
# define SANITIZER_IOS 0
+# define SANITIZER_IOSSIM 0
#endif
#if defined(_WIN32)
@@ -111,10 +117,29 @@
# endif
#endif
+// udi16 syscalls can only be used when the following conditions are
+// met:
+// * target is one of arm32, x86-32, sparc32, sh or m68k
+// * libc version is libc5, glibc-2.0, glibc-2.1 or glibc-2.2 to 2.15
+// built against > linux-2.2 kernel headers
+// Since we don't want to include libc headers here, we check the
+// target only.
+#if defined(__arm__) || SANITIZER_X32 || defined(__sparc__)
+#define SANITIZER_USES_UID16_SYSCALLS 1
+#else
+#define SANITIZER_USES_UID16_SYSCALLS 0
+#endif
+
#ifdef __mips__
# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10)
#else
# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12)
#endif
+// Assume obsolete RPC headers are available by default
+#if !defined(HAVE_RPC_XDR_H) && !defined(HAVE_TIRPC_RPC_XDR_H)
+# define HAVE_RPC_XDR_H (SANITIZER_LINUX && !SANITIZER_ANDROID)
+# define HAVE_TIRPC_RPC_XDR_H 0
+#endif
+
#endif // SANITIZER_PLATFORM_H
diff --git a/lib/sanitizer_common/sanitizer_platform_interceptors.h b/lib/sanitizer_common/sanitizer_platform_interceptors.h
index 438ecbaa2ec8..77cc84cd03af 100644
--- a/lib/sanitizer_common/sanitizer_platform_interceptors.h
+++ b/lib/sanitizer_common/sanitizer_platform_interceptors.h
@@ -54,6 +54,10 @@
#endif
#define SANITIZER_INTERCEPT_STRCMP 1
+#define SANITIZER_INTERCEPT_STRSTR 1
+#define SANITIZER_INTERCEPT_STRCASESTR SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_STRSPN 1
+#define SANITIZER_INTERCEPT_STRPBRK 1
#define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_MEMCHR 1
@@ -127,7 +131,8 @@
#define SANITIZER_INTERCEPT_READDIR SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \
- (defined(__i386) || defined (__x86_64) || defined (__mips64)) // NOLINT
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__))
#define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID
@@ -147,7 +152,8 @@
#define SANITIZER_INTERCEPT_STRERROR SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_STRERROR_R SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_XPG_STRERROR_R SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_SCANDIR SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_SCANDIR \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SCANDIR64 SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_GETGROUPS SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_POLL SI_NOT_WINDOWS
@@ -161,7 +167,7 @@
SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS
-#define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_BACKTRACE SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX
#define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_STATFS SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
@@ -203,12 +209,13 @@
#define SANITIZER_INTERCEPT_LGAMMA_R SI_FREEBSD || SI_LINUX
#define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_RAND_R SI_MAC || SI_LINUX_NOT_ANDROID
-#define SANITIZER_INTERCEPT_ICONV SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_RAND_R \
+ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_ICONV SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_TIMES SI_NOT_WINDOWS
// FIXME: getline seems to be available on OSX 10.7
-#define SANITIZER_INTERCEPT_GETLINE SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_GETLINE SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD
@@ -222,12 +229,14 @@
#define SANITIZER_INTERCEPT_LISTXATTR SI_LINUX
#define SANITIZER_INTERCEPT_GETXATTR SI_LINUX
#define SANITIZER_INTERCEPT_GETRESID SI_LINUX
-#define SANITIZER_INTERCEPT_GETIFADDRS SI_LINUX_NOT_ANDROID || SI_MAC
-#define SANITIZER_INTERCEPT_IF_INDEXTONAME SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_GETIFADDRS \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC
+#define SANITIZER_INTERCEPT_IF_INDEXTONAME \
+ SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC
#define SANITIZER_INTERCEPT_CAPGET SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_AEABI_MEM SI_LINUX && defined(__arm__)
#define SANITIZER_INTERCEPT___BZERO SI_MAC
-#define SANITIZER_INTERCEPT_FTIME SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_FTIME !SI_FREEBSD && SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_XDR SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_TSEARCH SI_LINUX_NOT_ANDROID || SI_MAC
#define SANITIZER_INTERCEPT_LIBIO_INTERNALS SI_LINUX_NOT_ANDROID
@@ -243,5 +252,6 @@
#define SANITIZER_INTERCEPT_TIMERFD SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_MLOCKX SI_NOT_WINDOWS
+#define SANITIZER_INTERCEPT_FOPENCOOKIE SI_LINUX_NOT_ANDROID
#endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
index 8824c8088f2d..aaa37ed02ebd 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
@@ -34,8 +34,6 @@
#include <grp.h>
#include <limits.h>
#include <net/if.h>
-#include <net/if_arp.h>
-#include <net/route.h>
#include <netdb.h>
#include <poll.h>
#include <pthread.h>
@@ -54,6 +52,10 @@
#include <time.h>
#include <wchar.h>
+#if !SANITIZER_IOS
+#include <net/route.h>
+#endif
+
#if !SANITIZER_ANDROID
#include <sys/mount.h>
#include <sys/timeb.h>
@@ -75,6 +77,7 @@
#include <linux/sysctl.h>
#include <linux/utsname.h>
#include <linux/posix_types.h>
+#include <net/if_arp.h>
#endif
#if SANITIZER_FREEBSD
@@ -135,7 +138,11 @@
#include <netax25/ax25.h>
#include <netipx/ipx.h>
#include <netrom/netrom.h>
-#include <rpc/xdr.h>
+#if HAVE_RPC_XDR_H
+# include <rpc/xdr.h>
+#elif HAVE_TIRPC_RPC_XDR_H
+# include <tirpc/rpc/xdr.h>
+#endif
#include <scsi/scsi.h>
#include <sys/mtio.h>
#include <sys/kd.h>
@@ -289,19 +296,20 @@ namespace __sanitizer {
#endif
#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
- (defined(__i386) || defined(__x86_64) || defined(__mips64))
-#if defined(__mips64)
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__))
+#if defined(__mips64) || defined(__powerpc64__)
unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs);
unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t);
#else
unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct);
unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct);
-#endif // __mips64
-#if (defined(__x86_64) || defined(__mips64))
+#endif // __mips64 || __powerpc64__
+#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__)
unsigned struct_user_fpxregs_struct_sz = 0;
#else
unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct);
-#endif // __x86_64 || __mips64
+#endif // __x86_64 || __mips64 || __powerpc64__
int ptrace_peektext = PTRACE_PEEKTEXT;
int ptrace_peekdata = PTRACE_PEEKDATA;
@@ -310,8 +318,13 @@ namespace __sanitizer {
int ptrace_setregs = PTRACE_SETREGS;
int ptrace_getfpregs = PTRACE_GETFPREGS;
int ptrace_setfpregs = PTRACE_SETFPREGS;
+#if defined(PTRACE_GETFPXREGS) && defined(PTRACE_SETFPXREGS)
int ptrace_getfpxregs = PTRACE_GETFPXREGS;
int ptrace_setfpxregs = PTRACE_SETFPXREGS;
+#else
+ int ptrace_getfpxregs = -1;
+ int ptrace_setfpxregs = -1;
+#endif // PTRACE_GETFPXREGS/PTRACE_SETFPXREGS
int ptrace_geteventmsg = PTRACE_GETEVENTMSG;
#if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \
(defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO))
@@ -333,12 +346,12 @@ namespace __sanitizer {
unsigned path_max = PATH_MAX;
// ioctl arguments
- unsigned struct_arpreq_sz = sizeof(struct arpreq);
unsigned struct_ifreq_sz = sizeof(struct ifreq);
unsigned struct_termios_sz = sizeof(struct termios);
unsigned struct_winsize_sz = sizeof(struct winsize);
#if SANITIZER_LINUX
+ unsigned struct_arpreq_sz = sizeof(struct arpreq);
unsigned struct_cdrom_msf_sz = sizeof(struct cdrom_msf);
unsigned struct_cdrom_multisession_sz = sizeof(struct cdrom_multisession);
unsigned struct_cdrom_read_audio_sz = sizeof(struct cdrom_read_audio);
@@ -1016,7 +1029,7 @@ CHECK_SIZE_AND_OFFSET(__sysctl_args, newlen);
CHECK_TYPE_SIZE(__kernel_uid_t);
CHECK_TYPE_SIZE(__kernel_gid_t);
-#if !defined(__aarch64__)
+#if SANITIZER_USES_UID16_SYSCALLS
CHECK_TYPE_SIZE(__kernel_old_uid_t);
CHECK_TYPE_SIZE(__kernel_old_gid_t);
#endif
@@ -1159,7 +1172,7 @@ CHECK_SIZE_AND_OFFSET(group, gr_passwd);
CHECK_SIZE_AND_OFFSET(group, gr_gid);
CHECK_SIZE_AND_OFFSET(group, gr_mem);
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
+#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);
@@ -1200,6 +1213,12 @@ CHECK_SIZE_AND_OFFSET(obstack, chunk_size);
CHECK_SIZE_AND_OFFSET(obstack, chunk);
CHECK_SIZE_AND_OFFSET(obstack, object_base);
CHECK_SIZE_AND_OFFSET(obstack, next_free);
+
+CHECK_TYPE_SIZE(cookie_io_functions_t);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, read);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, write);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, seek);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, close);
#endif
#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
index bd20bea94e93..4da7c70da0a6 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.h
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
@@ -321,7 +321,7 @@ namespace __sanitizer {
long pw_change;
char *pw_class;
#endif
-#if !SANITIZER_ANDROID
+#if !(SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 32))
char *pw_gecos;
#endif
char *pw_dir;
@@ -383,7 +383,7 @@ namespace __sanitizer {
};
#endif
-#if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD
+#if SANITIZER_MAC || SANITIZER_FREEBSD
struct __sanitizer_msghdr {
void *msg_name;
unsigned msg_namelen;
@@ -520,6 +520,27 @@ namespace __sanitizer {
#endif
// Linux system headers define the 'sa_handler' and 'sa_sigaction' macros.
+#if SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 64)
+ struct __sanitizer_sigaction {
+ unsigned sa_flags;
+ union {
+ void (*sigaction)(int sig, void *siginfo, void *uctx);
+ void (*handler)(int sig);
+ };
+ __sanitizer_sigset_t sa_mask;
+ void (*sa_restorer)();
+ };
+#elif SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 32)
+ struct __sanitizer_sigaction {
+ union {
+ void (*sigaction)(int sig, void *siginfo, void *uctx);
+ void (*handler)(int sig);
+ };
+ __sanitizer_sigset_t sa_mask;
+ uptr sa_flags;
+ void (*sa_restorer)();
+ };
+#else // !SANITIZER_ANDROID
struct __sanitizer_sigaction {
#if defined(__mips__) && !SANITIZER_FREEBSD
unsigned int sa_flags;
@@ -544,6 +565,7 @@ namespace __sanitizer {
int sa_resv[1];
#endif
};
+#endif // !SANITIZER_ANDROID
#if SANITIZER_FREEBSD
typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;
@@ -699,7 +721,8 @@ namespace __sanitizer {
#endif
#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
- (defined(__i386) || defined(__x86_64) || defined(__mips64))
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__))
extern unsigned struct_user_regs_struct_sz;
extern unsigned struct_user_fpregs_struct_sz;
extern unsigned struct_user_fpxregs_struct_sz;
@@ -756,6 +779,20 @@ struct __sanitizer_obstack {
char *next_free;
uptr more_fields[7];
};
+
+typedef uptr (*__sanitizer_cookie_io_read)(void *cookie, char *buf, uptr size);
+typedef uptr (*__sanitizer_cookie_io_write)(void *cookie, const char *buf,
+ uptr size);
+typedef int (*__sanitizer_cookie_io_seek)(void *cookie, u64 *offset,
+ int whence);
+typedef int (*__sanitizer_cookie_io_close)(void *cookie);
+
+struct __sanitizer_cookie_io_functions_t {
+ __sanitizer_cookie_io_read read;
+ __sanitizer_cookie_io_write write;
+ __sanitizer_cookie_io_seek seek;
+ __sanitizer_cookie_io_close close;
+};
#endif
#define IOC_NRBITS 8
@@ -792,12 +829,12 @@ struct __sanitizer_obstack {
#define IOC_NR(nr) (((nr) >> IOC_NRSHIFT) & IOC_NRMASK)
#define IOC_SIZE(nr) (((nr) >> IOC_SIZESHIFT) & IOC_SIZEMASK)
- extern unsigned struct_arpreq_sz;
extern unsigned struct_ifreq_sz;
extern unsigned struct_termios_sz;
extern unsigned struct_winsize_sz;
#if SANITIZER_LINUX
+ extern unsigned struct_arpreq_sz;
extern unsigned struct_cdrom_msf_sz;
extern unsigned struct_cdrom_multisession_sz;
extern unsigned struct_cdrom_read_audio_sz;
diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc
index 5bc41c2580fb..abf6738a8daa 100644
--- a/lib/sanitizer_common/sanitizer_posix.cc
+++ b/lib/sanitizer_common/sanitizer_posix.cc
@@ -9,7 +9,7 @@
//
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries and implements POSIX-specific functions from
-// sanitizer_libc.h.
+// sanitizer_posix.h.
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
@@ -17,9 +17,12 @@
#include "sanitizer_common.h"
#include "sanitizer_libc.h"
+#include "sanitizer_posix.h"
#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
+#include <fcntl.h>
+#include <signal.h>
#include <sys/mman.h>
#if SANITIZER_LINUX
@@ -47,7 +50,7 @@ uptr GetMmapGranularity() {
#if SANITIZER_WORDSIZE == 32
// Take care of unusable kernel area in top gigabyte.
static uptr GetKernelAreaSize() {
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_X32
const uptr gbyte = 1UL << 30;
// Firstly check if there are writable segments
@@ -79,7 +82,7 @@ static uptr GetKernelAreaSize() {
return gbyte;
#else
return 0;
-#endif // SANITIZER_LINUX
+#endif // SANITIZER_LINUX && !SANITIZER_X32
}
#endif // SANITIZER_WORDSIZE == 32
@@ -162,22 +165,6 @@ void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
return (void *)p;
}
-void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
- uptr PageSize = GetPageSizeCached();
- uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),
- RoundUpTo(size, PageSize),
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE,
- -1, 0);
- int reserrno;
- if (internal_iserror(p, &reserrno))
- Report("ERROR: %s failed to "
- "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n",
- SanitizerToolName, size, size, fixed_addr, reserrno);
- IncreaseTotalMmap(size);
- return (void *)p;
-}
-
void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
uptr PageSize = GetPageSizeCached();
uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),
@@ -196,17 +183,55 @@ void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
return (void *)p;
}
-void *Mprotect(uptr fixed_addr, uptr size) {
- return (void *)internal_mmap((void*)fixed_addr, size,
- PROT_NONE,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED |
- MAP_NORESERVE, -1, 0);
+bool MprotectNoAccess(uptr addr, uptr size) {
+ return 0 == internal_mprotect((void*)addr, size, PROT_NONE);
+}
+
+fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
+ int flags;
+ switch (mode) {
+ case RdOnly: flags = O_RDONLY; break;
+ case WrOnly: flags = O_WRONLY | O_CREAT; break;
+ case RdWr: flags = O_RDWR | O_CREAT; break;
+ }
+ fd_t res = internal_open(filename, flags, 0660);
+ if (internal_iserror(res, errno_p))
+ return kInvalidFd;
+ return res;
+}
+
+void CloseFile(fd_t fd) {
+ internal_close(fd);
+}
+
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
+ error_t *error_p) {
+ uptr res = internal_read(fd, buff, buff_size);
+ if (internal_iserror(res, error_p))
+ return false;
+ if (bytes_read)
+ *bytes_read = res;
+ return true;
+}
+
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
+ error_t *error_p) {
+ uptr res = internal_write(fd, buff, buff_size);
+ if (internal_iserror(res, error_p))
+ return false;
+ if (bytes_written)
+ *bytes_written = res;
+ 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) {
- uptr openrv = OpenFile(file_name, false);
- CHECK(!internal_iserror(openrv));
- fd_t fd = openrv;
+ fd_t fd = OpenFile(file_name, RdOnly);
+ CHECK(fd != kInvalidFd);
uptr fsize = internal_filesize(fd);
CHECK_NE(fsize, (uptr)-1);
CHECK_GT(fsize, 0);
@@ -215,13 +240,14 @@ void *MapFileToMemory(const char *file_name, uptr *buff_size) {
return internal_iserror(map) ? 0 : (void *)map;
}
-void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset) {
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) {
uptr flags = MAP_SHARED;
if (addr) flags |= MAP_FIXED;
uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset);
- if (internal_iserror(p)) {
- Printf("could not map writable file (%zd, %zu, %zu): %zd\n", fd, offset,
- size, p);
+ int mmap_errno = 0;
+ if (internal_iserror(p, &mmap_errno)) {
+ Printf("could not map writable file (%d, %lld, %zu): %zd, errno: %d\n",
+ fd, (long long)offset, size, p, mmap_errno);
return 0;
}
return (void *)p;
@@ -244,6 +270,7 @@ bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
while (proc_maps.Next(&start, &end,
/*offset*/0, /*filename*/0, /*filename_size*/0,
/*protection*/0)) {
+ if (start == end) continue; // Empty range.
CHECK_NE(0, end);
if (!IntervalsAreSeparate(start, end - 1, range_start, range_end))
return false;
@@ -293,6 +320,14 @@ char *FindPathToBinary(const char *name) {
return 0;
}
+bool IsPathSeparator(const char c) {
+ return c == '/';
+}
+
+bool IsAbsolutePath(const char *path) {
+ return path != nullptr && IsPathSeparator(path[0]);
+}
+
void ReportFile::Write(const char *buffer, uptr length) {
SpinMutexLock l(mu);
static const char *kWriteError =
@@ -319,6 +354,13 @@ bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) {
return false;
}
+SignalContext SignalContext::Create(void *siginfo, void *context) {
+ uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr;
+ uptr pc, sp, bp;
+ GetPcSpBp(context, &pc, &sp, &bp);
+ return SignalContext(context, addr, pc, sp, bp);
+}
+
} // namespace __sanitizer
#endif // SANITIZER_POSIX
diff --git a/lib/sanitizer_common/sanitizer_posix.h b/lib/sanitizer_common/sanitizer_posix.h
new file mode 100644
index 000000000000..5a9e97d5b5a9
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_posix.h
@@ -0,0 +1,81 @@
+//===-- sanitizer_posix.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 shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and declares some useful POSIX-specific functions.
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_POSIX_H
+#define SANITIZER_POSIX_H
+
+// ----------- ATTENTION -------------
+// This header should NOT include any other headers from sanitizer runtime.
+#include "sanitizer_internal_defs.h"
+
+#if !SANITIZER_POSIX
+// Make it hard to accidentally use any of functions declared in this file:
+#error This file should only be included on POSIX
+#endif
+
+namespace __sanitizer {
+
+// I/O
+// Don't use directly, use __sanitizer::OpenFile() instead.
+uptr internal_open(const char *filename, int flags);
+uptr internal_open(const char *filename, int flags, u32 mode);
+uptr internal_close(fd_t fd);
+
+uptr internal_read(fd_t fd, void *buf, uptr count);
+uptr internal_write(fd_t fd, const void *buf, uptr count);
+
+// Memory
+uptr internal_mmap(void *addr, uptr length, int prot, int flags,
+ int fd, OFF_T offset);
+uptr internal_munmap(void *addr, uptr length);
+int internal_mprotect(void *addr, uptr length, int prot);
+
+// OS
+uptr internal_filesize(fd_t fd); // -1 on error.
+uptr internal_stat(const char *path, void *buf);
+uptr internal_lstat(const char *path, void *buf);
+uptr internal_fstat(fd_t fd, void *buf);
+uptr internal_dup2(int oldfd, int newfd);
+uptr internal_readlink(const char *path, char *buf, uptr bufsize);
+uptr internal_unlink(const char *path);
+uptr internal_rename(const char *oldpath, const char *newpath);
+uptr internal_lseek(fd_t fd, OFF_T offset, int whence);
+
+uptr internal_ptrace(int request, int pid, void *addr, void *data);
+uptr internal_waitpid(int pid, int *status, int options);
+
+int internal_fork();
+
+// These functions call appropriate pthread_ functions directly, bypassing
+// the interceptor. They are weak and may not be present in some tools.
+SANITIZER_WEAK_ATTRIBUTE
+int real_pthread_create(void *th, void *attr, void *(*callback)(void *),
+ void *param);
+SANITIZER_WEAK_ATTRIBUTE
+int real_pthread_join(void *th, void **ret);
+
+#define DEFINE_REAL_PTHREAD_FUNCTIONS \
+ namespace __sanitizer { \
+ int real_pthread_create(void *th, void *attr, void *(*callback)(void *), \
+ void *param) { \
+ return REAL(pthread_create)(th, attr, callback, param); \
+ } \
+ int real_pthread_join(void *th, void **ret) { \
+ return REAL(pthread_join(th, ret)); \
+ } \
+ } // namespace __sanitizer
+
+int internal_sigaction(int signum, const void *act, void *oldact);
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_POSIX_H
diff --git a/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_posix_libcdep.cc
index 11828e6cdf51..3f0a4f453cb4 100644
--- a/lib/sanitizer_common/sanitizer_posix_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_posix_libcdep.cc
@@ -18,9 +18,13 @@
#include "sanitizer_common.h"
#include "sanitizer_flags.h"
#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_posix.h"
+#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
#include <errno.h>
+#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
@@ -28,8 +32,16 @@
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include <unistd.h>
+#if SANITIZER_FREEBSD
+// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
+// that, it was never implemented. So just define it to zero.
+#undef MAP_NORESERVE
+#define MAP_NORESERVE 0
+#endif
+
namespace __sanitizer {
u32 GetUid() {
@@ -119,8 +131,8 @@ int Atexit(void (*function)(void)) {
#endif
}
-int internal_isatty(fd_t fd) {
- return isatty(fd);
+bool SupportsColoredOutput(fd_t fd) {
+ return isatty(fd) != 0;
}
#ifndef SANITIZER_GO
@@ -175,6 +187,7 @@ void InstallDeadlySignalHandlers(SignalHandlerType handler) {
if (common_flags()->use_sigaltstack) SetAlternateSignalStack();
MaybeInstallSigaction(SIGSEGV, handler);
MaybeInstallSigaction(SIGBUS, handler);
+ MaybeInstallSigaction(SIGABRT, handler);
}
#endif // SANITIZER_GO
@@ -200,6 +213,68 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) {
return result;
}
+void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
+ // Some kinds of sandboxes may forbid filesystem access, so we won't be able
+ // to read the file mappings from /proc/self/maps. Luckily, neither the
+ // process will be able to load additional libraries, so it's fine to use the
+ // cached mappings.
+ MemoryMappingLayout::CacheMemoryMappings();
+ // Same for /proc/self/exe in the symbolizer.
+#if !SANITIZER_GO
+ Symbolizer::GetOrInit()->PrepareForSandboxing();
+ CovPrepareForSandboxing(args);
+#endif
+}
+
+#if SANITIZER_ANDROID
+int GetNamedMappingFd(const char *name, uptr size) {
+ return -1;
+}
+#else
+int GetNamedMappingFd(const char *name, uptr size) {
+ if (!common_flags()->decorate_proc_maps)
+ return -1;
+ char shmname[200];
+ CHECK(internal_strlen(name) < sizeof(shmname) - 10);
+ internal_snprintf(shmname, sizeof(shmname), "%zu [%s]", internal_getpid(),
+ name);
+ int fd = shm_open(shmname, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
+ CHECK_GE(fd, 0);
+ int res = internal_ftruncate(fd, size);
+ CHECK_EQ(0, res);
+ res = shm_unlink(shmname);
+ CHECK_EQ(0, res);
+ return fd;
+}
+#endif
+
+void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
+ int fd = name ? GetNamedMappingFd(name, size) : -1;
+ unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE;
+ if (fd == -1) flags |= MAP_ANON;
+
+ uptr PageSize = GetPageSizeCached();
+ uptr p = internal_mmap((void *)(fixed_addr & ~(PageSize - 1)),
+ RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE,
+ flags, fd, 0);
+ int reserrno;
+ if (internal_iserror(p, &reserrno))
+ Report("ERROR: %s failed to "
+ "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n",
+ SanitizerToolName, size, size, fixed_addr, reserrno);
+ IncreaseTotalMmap(size);
+ return (void *)p;
+}
+
+void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) {
+ int fd = name ? GetNamedMappingFd(name, size) : -1;
+ unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE;
+ if (fd == -1) flags |= MAP_ANON;
+
+ return (void *)internal_mmap((void *)fixed_addr, size, PROT_NONE, flags, fd,
+ 0);
+}
+
} // namespace __sanitizer
#endif // SANITIZER_POSIX
diff --git a/lib/sanitizer_common/sanitizer_printf.cc b/lib/sanitizer_common/sanitizer_printf.cc
index 3be6723cd660..e4f67f5e0db5 100644
--- a/lib/sanitizer_common/sanitizer_printf.cc
+++ b/lib/sanitizer_common/sanitizer_printf.cc
@@ -251,26 +251,32 @@ static void SharedPrintfCode(bool append_pid, const char *format,
buffer_size = kLen;
}
needed_length = 0;
+ // Check that data fits into the current buffer.
+# define CHECK_NEEDED_LENGTH \
+ if (needed_length >= buffer_size) { \
+ if (!use_mmap) continue; \
+ RAW_CHECK_MSG(needed_length < kLen, \
+ "Buffer in Report is too short!\n"); \
+ }
if (append_pid) {
int pid = internal_getpid();
- needed_length += internal_snprintf(buffer, buffer_size, "==%d==", pid);
- if (needed_length >= buffer_size) {
- // The pid doesn't fit into the current buffer.
- if (!use_mmap)
- continue;
- RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
+ const char *exe_name = GetBinaryBasename();
+ if (common_flags()->log_exe_name && exe_name) {
+ needed_length += internal_snprintf(buffer, buffer_size,
+ "==%s", exe_name);
+ CHECK_NEEDED_LENGTH
}
+ needed_length += internal_snprintf(buffer + needed_length,
+ buffer_size - needed_length,
+ "==%d==", pid);
+ CHECK_NEEDED_LENGTH
}
needed_length += VSNPrintf(buffer + needed_length,
buffer_size - needed_length, format, args);
- if (needed_length >= buffer_size) {
- // The message doesn't fit into the current buffer.
- if (!use_mmap)
- continue;
- RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n");
- }
+ CHECK_NEEDED_LENGTH
// If the message fit into the buffer, print it and exit.
break;
+# undef CHECK_NEEDED_LENGTH
}
RawWrite(buffer);
AndroidLogWrite(buffer);
diff --git a/lib/sanitizer_common/sanitizer_procmaps_common.cc b/lib/sanitizer_common/sanitizer_procmaps_common.cc
index 2ec08d7f127b..2c6ce8e2b211 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_common.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_common.cc
@@ -130,7 +130,6 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
continue;
if (filter && !filter(cur_name))
continue;
- void *mem = &modules[n_modules];
// Don't subtract 'cur_beg' from the first entry:
// * If a binary is compiled w/o -pie, then the first entry in
// process maps is likely the binary itself (all dynamic libs
@@ -143,7 +142,8 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
// shadow memory of the tool), so the module can't be the
// first entry.
uptr base_address = (i ? cur_beg : 0) - cur_offset;
- LoadedModule *cur_module = new(mem) LoadedModule(cur_name, base_address);
+ LoadedModule *cur_module = &modules[n_modules];
+ cur_module->set(cur_name, base_address);
cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
n_modules++;
}
diff --git a/lib/sanitizer_common/sanitizer_procmaps_mac.cc b/lib/sanitizer_common/sanitizer_procmaps_mac.cc
index c0a86149788c..29d699609a6e 100644
--- a/lib/sanitizer_common/sanitizer_procmaps_mac.cc
+++ b/lib/sanitizer_common/sanitizer_procmaps_mac.cc
@@ -171,13 +171,13 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules,
continue;
if (filter && !filter(cur_name))
continue;
- LoadedModule *cur_module = 0;
+ LoadedModule *cur_module = nullptr;
if (n_modules > 0 &&
0 == internal_strcmp(cur_name, modules[n_modules - 1].full_name())) {
cur_module = &modules[n_modules - 1];
} else {
- void *mem = &modules[n_modules];
- cur_module = new(mem) LoadedModule(cur_name, cur_beg);
+ cur_module = &modules[n_modules];
+ cur_module->set(cur_name, cur_beg);
n_modules++;
}
cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute);
diff --git a/lib/sanitizer_common/sanitizer_stacktrace.cc b/lib/sanitizer_common/sanitizer_stacktrace.cc
index 2deadb6e3560..84ff9d9d9e3b 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace.cc
+++ b/lib/sanitizer_common/sanitizer_stacktrace.cc
@@ -20,6 +20,8 @@ namespace __sanitizer {
uptr StackTrace::GetNextInstructionPc(uptr pc) {
#if defined(__mips__)
return pc + 8;
+#elif defined(__powerpc__)
+ return pc + 4;
#else
return pc + 1;
#endif
diff --git a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
index 0f98c7d5af4c..f66fa79f19a5 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc
@@ -35,7 +35,8 @@ void StackTrace::Print() const {
for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
frame_desc.clear();
RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++,
- cur->info, common_flags()->strip_path_prefix);
+ cur->info, common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
Printf("%s\n", frame_desc.data());
}
frames->ClearAll();
@@ -59,10 +60,14 @@ void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context,
return;
}
if (!WillUseFastUnwind(request_fast_unwind)) {
+#if SANITIZER_CAN_SLOW_UNWIND
if (context)
SlowUnwindStackWithContext(pc, context, max_depth);
else
SlowUnwindStack(pc, max_depth);
+#else
+ UNREACHABLE("slow unwind requested but not available");
+#endif
} else {
FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth);
}
diff --git a/lib/sanitizer_common/sanitizer_stacktrace_printer.cc b/lib/sanitizer_common/sanitizer_stacktrace_printer.cc
index 7b37dbcf3860..3574fa3782c6 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace_printer.cc
+++ b/lib/sanitizer_common/sanitizer_stacktrace_printer.cc
@@ -26,8 +26,8 @@ static const char *StripFunctionName(const char *function, const char *prefix) {
static const char kDefaultFormat[] = " #%n %p %F %L";
void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
- const AddressInfo &info, const char *strip_path_prefix,
- const char *strip_func_prefix) {
+ const AddressInfo &info, bool vs_style,
+ const char *strip_path_prefix, const char *strip_func_prefix) {
if (0 == internal_strcmp(format, "DEFAULT"))
format = kDefaultFormat;
for (const char *p = format; *p != '\0'; p++) {
@@ -82,14 +82,14 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
break;
case 'S':
// File/line information.
- RenderSourceLocation(buffer, info.file, info.line, info.column,
+ RenderSourceLocation(buffer, info.file, info.line, info.column, vs_style,
strip_path_prefix);
break;
case 'L':
// Source location, or module location.
if (info.file) {
RenderSourceLocation(buffer, info.file, info.line, info.column,
- strip_path_prefix);
+ vs_style, strip_path_prefix);
} else if (info.module) {
RenderModuleLocation(buffer, info.module, info.module_offset,
strip_path_prefix);
@@ -99,22 +99,33 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
break;
case 'M':
// Module basename and offset, or PC.
- if (info.module)
+ if (info.address & kExternalPCBit)
+ {} // There PCs are not meaningful.
+ else if (info.module)
buffer->append("(%s+%p)", StripModuleName(info.module),
(void *)info.module_offset);
else
buffer->append("(%p)", (void *)info.address);
break;
default:
- Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n",
- *p, *p);
+ Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p,
+ *p);
Die();
}
}
}
void RenderSourceLocation(InternalScopedString *buffer, const char *file,
- int line, int column, const char *strip_path_prefix) {
+ int line, int column, bool vs_style,
+ const char *strip_path_prefix) {
+ if (vs_style && line > 0) {
+ buffer->append("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
+ if (column > 0)
+ buffer->append(",%d", column);
+ buffer->append(")");
+ return;
+ }
+
buffer->append("%s", StripPathPrefix(file, strip_path_prefix));
if (line > 0) {
buffer->append(":%d", line);
diff --git a/lib/sanitizer_common/sanitizer_stacktrace_printer.h b/lib/sanitizer_common/sanitizer_stacktrace_printer.h
index 93569882ba9d..7f6c5c73b85d 100644
--- a/lib/sanitizer_common/sanitizer_stacktrace_printer.h
+++ b/lib/sanitizer_common/sanitizer_stacktrace_printer.h
@@ -48,11 +48,13 @@ namespace __sanitizer {
// module+offset if it is known, or (<unknown module>) string.
// %M - prints module basename and offset, if it is known, or PC.
void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
- const AddressInfo &info, const char *strip_path_prefix = "",
+ const AddressInfo &info, bool vs_style,
+ const char *strip_path_prefix = "",
const char *strip_func_prefix = "");
void RenderSourceLocation(InternalScopedString *buffer, const char *file,
- int line, int column, const char *strip_path_prefix);
+ int line, int column, bool vs_style,
+ const char *strip_path_prefix);
void RenderModuleLocation(InternalScopedString *buffer, const char *module,
uptr offset, const char *strip_path_prefix);
diff --git a/lib/sanitizer_common/sanitizer_stoptheworld.h b/lib/sanitizer_common/sanitizer_stoptheworld.h
index a32646708d45..aa6f5d833a4d 100644
--- a/lib/sanitizer_common/sanitizer_stoptheworld.h
+++ b/lib/sanitizer_common/sanitizer_stoptheworld.h
@@ -59,7 +59,8 @@ typedef void (*StopTheWorldCallback)(
// Suspend all threads in the current process and run the callback on the list
// of suspended threads. This function will resume the threads before returning.
-// The callback should not call any libc functions.
+// The callback should not call any libc functions. The callback must not call
+// exit() nor _exit() and instead return to the caller.
// This function should NOT be called from multiple threads simultaneously.
void StopTheWorld(StopTheWorldCallback callback, void *argument);
diff --git a/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
index ad20e39556d8..47b27e7e5c75 100644
--- a/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
@@ -19,6 +19,7 @@
#include "sanitizer_stoptheworld.h"
#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_atomic.h"
#include <errno.h>
#include <sched.h> // for CLONE_* definitions
@@ -70,11 +71,25 @@
COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t));
namespace __sanitizer {
+
+// Structure for passing arguments into the tracer thread.
+struct TracerThreadArgument {
+ StopTheWorldCallback callback;
+ void *callback_argument;
+ // The tracer thread waits on this mutex while the parent finishes its
+ // preparations.
+ BlockingMutex mutex;
+ // Tracer thread signals its completion by setting done.
+ atomic_uintptr_t done;
+ uptr parent_pid;
+};
+
// This class handles thread suspending/unsuspending in the tracer thread.
class ThreadSuspender {
public:
- explicit ThreadSuspender(pid_t pid)
- : pid_(pid) {
+ explicit ThreadSuspender(pid_t pid, TracerThreadArgument *arg)
+ : arg(arg)
+ , pid_(pid) {
CHECK_GE(pid, 0);
}
bool SuspendAllThreads();
@@ -83,6 +98,7 @@ class ThreadSuspender {
SuspendedThreadsList &suspended_threads_list() {
return suspended_threads_list_;
}
+ TracerThreadArgument *arg;
private:
SuspendedThreadsList suspended_threads_list_;
pid_t pid_;
@@ -103,7 +119,7 @@ bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) {
VReport(1, "Could not attach to thread %d (errno %d).\n", tid, pterrno);
return false;
} else {
- VReport(1, "Attached to thread %d.\n", tid);
+ VReport(2, "Attached to thread %d.\n", tid);
// The thread is not guaranteed to stop before ptrace returns, so we must
// wait on it. Note: if the thread receives a signal concurrently,
// we can get notification about the signal before notification about stop.
@@ -143,7 +159,7 @@ void ThreadSuspender::ResumeAllThreads() {
int pterrno;
if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, NULL, NULL),
&pterrno)) {
- VReport(1, "Detached from thread %d.\n", tid);
+ VReport(2, "Detached from thread %d.\n", tid);
} else {
// Either the thread is dead, or we are already detached.
// The latter case is possible, for instance, if this function was called
@@ -188,25 +204,23 @@ static ThreadSuspender *thread_suspender_instance = NULL;
static const int kSyncSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS,
SIGXCPU, SIGXFSZ };
-// Structure for passing arguments into the tracer thread.
-struct TracerThreadArgument {
- StopTheWorldCallback callback;
- void *callback_argument;
- // The tracer thread waits on this mutex while the parent finishes its
- // preparations.
- BlockingMutex mutex;
- uptr parent_pid;
-};
-
static DieCallbackType old_die_callback;
// Signal handler to wake up suspended threads when the tracer thread dies.
-static void TracerThreadSignalHandler(int signum, void *siginfo, void *) {
- if (thread_suspender_instance != NULL) {
+static void TracerThreadSignalHandler(int signum, void *siginfo, void *uctx) {
+ SignalContext ctx = SignalContext::Create(siginfo, uctx);
+ VPrintf(1, "Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n",
+ signum, ctx.addr, ctx.pc, ctx.sp);
+ ThreadSuspender *inst = thread_suspender_instance;
+ if (inst != NULL) {
if (signum == SIGABRT)
- thread_suspender_instance->KillAllThreads();
+ inst->KillAllThreads();
else
- thread_suspender_instance->ResumeAllThreads();
+ inst->ResumeAllThreads();
+ SetDieCallback(old_die_callback);
+ old_die_callback = NULL;
+ thread_suspender_instance = NULL;
+ atomic_store(&inst->arg->done, 1, memory_order_relaxed);
}
internal__exit((signum == SIGABRT) ? 1 : 2);
}
@@ -218,10 +232,15 @@ static void TracerThreadDieCallback() {
// point. So we correctly handle calls to Die() from within the callback, but
// not those that happen before or after the callback. Hopefully there aren't
// a lot of opportunities for that to happen...
- if (thread_suspender_instance)
- thread_suspender_instance->KillAllThreads();
+ ThreadSuspender *inst = thread_suspender_instance;
+ if (inst != NULL && stoptheworld_tracer_pid == internal_getpid()) {
+ inst->KillAllThreads();
+ thread_suspender_instance = NULL;
+ }
if (old_die_callback)
old_die_callback();
+ SetDieCallback(old_die_callback);
+ old_die_callback = NULL;
}
// Size of alternative stack for signal handlers in the tracer thread.
@@ -244,7 +263,7 @@ static int TracerThread(void* argument) {
old_die_callback = GetDieCallback();
SetDieCallback(TracerThreadDieCallback);
- ThreadSuspender thread_suspender(internal_getppid());
+ ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument);
// Global pointer for the signal handler.
thread_suspender_instance = &thread_suspender;
@@ -276,11 +295,9 @@ static int TracerThread(void* argument) {
thread_suspender.ResumeAllThreads();
exit_code = 0;
}
- // Note, this is a bad race. If TracerThreadDieCallback is already started
- // in another thread and observed that thread_suspender_instance != 0,
- // it can call KillAllThreads on the destroyed variable.
SetDieCallback(old_die_callback);
thread_suspender_instance = NULL;
+ atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed);
return exit_code;
}
@@ -293,7 +310,7 @@ class ScopedStackSpaceWithGuard {
// in the future.
guard_start_ = (uptr)MmapOrDie(stack_size_ + guard_size_,
"ScopedStackWithGuard");
- CHECK_EQ(guard_start_, (uptr)Mprotect((uptr)guard_start_, guard_size_));
+ CHECK(MprotectNoAccess((uptr)guard_start_, guard_size_));
}
~ScopedStackSpaceWithGuard() {
UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_);
@@ -354,6 +371,7 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) {
tracer_thread_argument.callback = callback;
tracer_thread_argument.callback_argument = argument;
tracer_thread_argument.parent_pid = internal_getpid();
+ atomic_store(&tracer_thread_argument.done, 0, memory_order_relaxed);
const uptr kTracerStackSize = 2 * 1024 * 1024;
ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize);
// Block the execution of TracerThread until after we have set ptrace
@@ -402,14 +420,27 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) {
#endif
// Allow the tracer thread to start.
tracer_thread_argument.mutex.Unlock();
- // Since errno is shared between this thread and the tracer thread, we
- // must avoid using errno while the tracer thread is running.
- // At this point, any signal will either be blocked or kill us, so waitpid
- // should never return (and set errno) while the tracer thread is alive.
- uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL);
- if (internal_iserror(waitpid_status, &local_errno))
+ // NOTE: errno is shared between this thread and the tracer thread.
+ // internal_waitpid() may call syscall() which can access/spoil errno,
+ // so we can't call it now. Instead we for the tracer thread to finish using
+ // the spin loop below. Man page for sched_yield() says "In the Linux
+ // implementation, sched_yield() always succeeds", so let's hope it does not
+ // spoil errno. Note that this spin loop runs only for brief periods before
+ // the tracer thread has suspended us and when it starts unblocking threads.
+ while (atomic_load(&tracer_thread_argument.done, memory_order_relaxed) == 0)
+ sched_yield();
+ // Now the tracer thread is about to exit and does not touch errno,
+ // wait for it.
+ for (;;) {
+ uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL);
+ if (!internal_iserror(waitpid_status, &local_errno))
+ break;
+ if (local_errno == EINTR)
+ continue;
VReport(1, "Waiting on the tracer thread failed (errno %d).\n",
local_errno);
+ break;
+ }
}
}
diff --git a/lib/sanitizer_common/sanitizer_suppressions.cc b/lib/sanitizer_common/sanitizer_suppressions.cc
index 2b697e955709..08cb497269bf 100644
--- a/lib/sanitizer_common/sanitizer_suppressions.cc
+++ b/lib/sanitizer_common/sanitizer_suppressions.cc
@@ -30,18 +30,50 @@ SuppressionContext::SuppressionContext(const char *suppression_types[],
internal_memset(has_suppression_type_, 0, suppression_types_num_);
}
+static bool GetPathAssumingFileIsRelativeToExec(const char *file_path,
+ /*out*/char *new_file_path,
+ uptr new_file_path_size) {
+ InternalScopedString exec(kMaxPathLength);
+ if (ReadBinaryNameCached(exec.data(), exec.size())) {
+ const char *file_name_pos = StripModuleName(exec.data());
+ uptr path_to_exec_len = file_name_pos - exec.data();
+ internal_strncat(new_file_path, exec.data(),
+ Min(path_to_exec_len, new_file_path_size - 1));
+ internal_strncat(new_file_path, file_path,
+ new_file_path_size - internal_strlen(new_file_path) - 1);
+ return true;
+ }
+ return false;
+}
+
void SuppressionContext::ParseFromFile(const char *filename) {
if (filename[0] == '\0')
return;
+
+ // If we cannot find the file, check if its location is relative to
+ // the location of the executable.
+ InternalScopedString new_file_path(kMaxPathLength);
+ if (!FileExists(filename) && !IsAbsolutePath(filename) &&
+ GetPathAssumingFileIsRelativeToExec(filename, new_file_path.data(),
+ new_file_path.size())) {
+ filename = new_file_path.data();
+ }
+
+ // Read the file.
char *file_contents;
uptr buffer_size;
- uptr contents_size = ReadFileToBuffer(filename, &file_contents, &buffer_size,
- 1 << 26 /* max_len */);
+ const uptr max_len = 1 << 26;
+ uptr contents_size =
+ ReadFileToBuffer(filename, &file_contents, &buffer_size, max_len);
+ VPrintf(1, "%s: reading suppressions file at %s\n",
+ SanitizerToolName, filename);
+
if (contents_size == 0) {
Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName,
filename);
Die();
}
+
Parse(file_contents);
}
diff --git a/lib/sanitizer_common/sanitizer_symbolizer.cc b/lib/sanitizer_common/sanitizer_symbolizer.cc
index 135720ed5eb1..8b2496a6df2e 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer.cc
@@ -16,7 +16,7 @@
#include "sanitizer_internal_defs.h"
#include "sanitizer_libc.h"
#include "sanitizer_placement_new.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
namespace __sanitizer {
@@ -33,9 +33,7 @@ void AddressInfo::Clear() {
function_offset = kUnknown;
}
-void AddressInfo::FillAddressAndModuleInfo(uptr addr, const char *mod_name,
- uptr mod_offset) {
- address = addr;
+void AddressInfo::FillModuleInfo(const char *mod_name, uptr mod_offset) {
module = internal_strdup(mod_name);
module_offset = mod_offset;
}
@@ -70,13 +68,6 @@ Symbolizer *Symbolizer::symbolizer_;
StaticSpinMutex Symbolizer::init_mu_;
LowLevelAllocator Symbolizer::symbolizer_allocator_;
-Symbolizer *Symbolizer::Disable() {
- CHECK_EQ(0, symbolizer_);
- // Initialize a dummy symbolizer.
- symbolizer_ = new(symbolizer_allocator_) Symbolizer;
- return symbolizer_;
-}
-
void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook,
Symbolizer::EndSymbolizationHook end_hook) {
CHECK(start_hook_ == 0 && end_hook_ == 0);
@@ -84,7 +75,29 @@ void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook,
end_hook_ = end_hook;
}
-Symbolizer::Symbolizer() : start_hook_(0), end_hook_(0) {}
+const char *Symbolizer::ModuleNameOwner::GetOwnedCopy(const char *str) {
+ mu_->CheckLocked();
+
+ // 'str' will be the same string multiple times in a row, optimize this case.
+ if (last_match_ && !internal_strcmp(last_match_, str))
+ return last_match_;
+
+ // FIXME: this is linear search.
+ // We should optimize this further if this turns out to be a bottleneck later.
+ for (uptr i = 0; i < storage_.size(); ++i) {
+ if (!internal_strcmp(storage_[i], str)) {
+ last_match_ = storage_[i];
+ return last_match_;
+ }
+ }
+ last_match_ = internal_strdup(str);
+ storage_.push_back(last_match_);
+ return last_match_;
+}
+
+Symbolizer::Symbolizer(IntrusiveList<SymbolizerTool> tools)
+ : module_names_(&mu_), n_modules_(0), modules_fresh_(false), tools_(tools),
+ start_hook_(0), end_hook_(0) {}
Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym)
: sym_(sym) {
diff --git a/lib/sanitizer_common/sanitizer_symbolizer.h b/lib/sanitizer_common/sanitizer_symbolizer.h
index 3ccfce9995be..92332230f80a 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer.h
+++ b/lib/sanitizer_common/sanitizer_symbolizer.h
@@ -43,8 +43,7 @@ struct AddressInfo {
AddressInfo();
// Deletes all strings and resets all fields.
void Clear();
- void FillAddressAndModuleInfo(uptr addr, const char *mod_name,
- uptr mod_offset);
+ void FillModuleInfo(const char *mod_name, uptr mod_offset);
};
// Linked list of symbolized frames (each frame is described by AddressInfo).
@@ -74,33 +73,35 @@ struct DataInfo {
void Clear();
};
-class Symbolizer {
+class SymbolizerTool;
+
+class Symbolizer final {
public:
/// Initialize and return platform-specific implementation of symbolizer
/// (if it wasn't already initialized).
static Symbolizer *GetOrInit();
// Returns a list of symbolized frames for a given address (containing
// all inlined functions, if necessary).
- virtual SymbolizedStack *SymbolizePC(uptr address) {
- return SymbolizedStack::New(address);
- }
- virtual bool SymbolizeData(uptr address, DataInfo *info) {
- return false;
- }
- virtual bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
- uptr *module_address) {
- return false;
- }
- virtual bool CanReturnFileLineInfo() {
- return false;
+ SymbolizedStack *SymbolizePC(uptr address);
+ bool SymbolizeData(uptr address, DataInfo *info);
+
+ // The module names Symbolizer returns are stable and unique for every given
+ // module. It is safe to store and compare them as pointers.
+ bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
+ uptr *module_address);
+ const char *GetModuleNameForPc(uptr pc) {
+ const char *module_name = nullptr;
+ uptr unused;
+ if (GetModuleNameAndOffsetForPC(pc, &module_name, &unused))
+ return module_name;
+ return nullptr;
}
+
// Release internal caches (if any).
- virtual void Flush() {}
+ void Flush();
// Attempts to demangle the provided C++ mangled name.
- virtual const char *Demangle(const char *name) {
- return name;
- }
- virtual void PrepareForSandboxing() {}
+ const char *Demangle(const char *name);
+ void PrepareForSandboxing();
// Allow user to install hooks that would be called before/after Symbolizer
// does the actual file/line info fetching. Specific sanitizers may need this
@@ -113,16 +114,53 @@ class Symbolizer {
EndSymbolizationHook end_hook);
private:
+ // GetModuleNameAndOffsetForPC has to return a string to the caller.
+ // Since the corresponding module might get unloaded later, we should create
+ // our owned copies of the strings that we can safely return.
+ // ModuleNameOwner does not provide any synchronization, thus calls to
+ // its method should be protected by |mu_|.
+ class ModuleNameOwner {
+ public:
+ explicit ModuleNameOwner(BlockingMutex *synchronized_by)
+ : storage_(kInitialCapacity), last_match_(nullptr),
+ mu_(synchronized_by) {}
+ const char *GetOwnedCopy(const char *str);
+
+ private:
+ static const uptr kInitialCapacity = 1000;
+ InternalMmapVector<const char*> storage_;
+ const char *last_match_;
+
+ BlockingMutex *mu_;
+ } module_names_;
+
/// Platform-specific function for creating a Symbolizer object.
static Symbolizer *PlatformInit();
- /// Initialize the symbolizer in a disabled state. Not thread safe.
- static Symbolizer *Disable();
+
+ bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name,
+ uptr *module_offset);
+ LoadedModule *FindModuleForAddress(uptr address);
+ LoadedModule modules_[kMaxNumberOfModules];
+ uptr n_modules_;
+ // If stale, need to reload the modules before looking up addresses.
+ bool modules_fresh_;
+
+ // Platform-specific default demangler, must not return nullptr.
+ const char *PlatformDemangle(const char *name);
+ void PlatformPrepareForSandboxing();
static Symbolizer *symbolizer_;
static StaticSpinMutex init_mu_;
- protected:
- Symbolizer();
+ // Mutex locked from public methods of |Symbolizer|, so that the internals
+ // (including individual symbolizer tools and platform-specific methods) are
+ // always synchronized.
+ BlockingMutex mu_;
+
+ typedef IntrusiveList<SymbolizerTool>::Iterator Iterator;
+ IntrusiveList<SymbolizerTool> tools_;
+
+ explicit Symbolizer(IntrusiveList<SymbolizerTool> tools);
static LowLevelAllocator symbolizer_allocator_;
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_internal.h b/lib/sanitizer_common/sanitizer_symbolizer_internal.h
new file mode 100644
index 000000000000..66ae809ed53e
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_internal.h
@@ -0,0 +1,109 @@
+//===-- sanitizer_symbolizer_internal.h -------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Header for internal classes and functions to be used by implementations of
+// symbolizers.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_SYMBOLIZER_INTERNAL_H
+#define SANITIZER_SYMBOLIZER_INTERNAL_H
+
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+
+// Parsing helpers, 'str' is searched for delimiter(s) and a string or uptr
+// is extracted. When extracting a string, a newly allocated (using
+// InternalAlloc) and null-terminataed buffer is returned. They return a pointer
+// to the next characted after the found delimiter.
+const char *ExtractToken(const char *str, const char *delims, char **result);
+const char *ExtractInt(const char *str, const char *delims, int *result);
+const char *ExtractUptr(const char *str, const char *delims, uptr *result);
+const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,
+ char **result);
+
+const char *DemangleCXXABI(const char *name);
+
+// SymbolizerTool is an interface that is implemented by individual "tools"
+// that can perform symbolication (external llvm-symbolizer, libbacktrace,
+// Windows DbgHelp symbolizer, etc.).
+class SymbolizerTool {
+ public:
+ // The main |Symbolizer| class implements a "fallback chain" of symbolizer
+ // tools. In a request to symbolize an address, if one tool returns false,
+ // the next tool in the chain will be tried.
+ SymbolizerTool *next;
+
+ SymbolizerTool() : next(nullptr) { }
+
+ // Can't declare pure virtual functions in sanitizer runtimes:
+ // __cxa_pure_virtual might be unavailable.
+
+ // The |stack| parameter is inout. It is pre-filled with the address,
+ // module base and module offset values and is to be used to construct
+ // other stack frames.
+ virtual bool SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ UNIMPLEMENTED();
+ }
+
+ // The |info| parameter is inout. It is pre-filled with the module base
+ // and module offset values.
+ virtual bool SymbolizeData(uptr addr, DataInfo *info) {
+ UNIMPLEMENTED();
+ }
+
+ virtual void Flush() {}
+
+ // Return nullptr to fallback to the default platform-specific demangler.
+ virtual const char *Demangle(const char *name) {
+ return nullptr;
+ }
+};
+
+// SymbolizerProcess encapsulates communication between the tool and
+// external symbolizer program, running in a different subprocess.
+// SymbolizerProcess may not be used from two threads simultaneously.
+class SymbolizerProcess {
+ public:
+ explicit SymbolizerProcess(const char *path, bool use_forkpty = false);
+ const char *SendCommand(const char *command);
+
+ private:
+ bool Restart();
+ const char *SendCommandImpl(const char *command);
+ bool ReadFromSymbolizer(char *buffer, uptr max_length);
+ bool WriteToSymbolizer(const char *buffer, uptr length);
+ bool StartSymbolizerSubprocess();
+
+ virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const {
+ UNIMPLEMENTED();
+ }
+
+ virtual void ExecuteWithDefaultArgs(const char *path_to_binary) const {
+ UNIMPLEMENTED();
+ }
+
+ const char *path_;
+ int input_fd_;
+ int output_fd_;
+
+ static const uptr kBufferSize = 16 * 1024;
+ char buffer_[kBufferSize];
+
+ static const uptr kMaxTimesRestarted = 5;
+ static const int kSymbolizerStartupTimeMillis = 10;
+ uptr times_restarted_;
+ bool failed_to_start_;
+ bool reported_invalid_path_;
+ bool use_forkpty_;
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_SYMBOLIZER_INTERNAL_H
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
index 9317a78eef13..57354668bfbb 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
@@ -33,6 +33,8 @@
namespace __sanitizer {
+static char *DemangleAlloc(const char *name, bool always_alloc);
+
#if SANITIZER_LIBBACKTRACE
namespace {
@@ -86,17 +88,20 @@ char *CplusV3Demangle(const char *name) {
struct SymbolizeCodeCallbackArg {
SymbolizedStack *first;
SymbolizedStack *last;
- const char *module_name;
- uptr module_offset;
-
- void append(SymbolizedStack *f) {
- if (last != nullptr) {
- last->next = f;
- last = f;
- } else {
- first = f;
- last = f;
+ uptr frames_symbolized;
+
+ AddressInfo *get_new_frame(uintptr_t addr) {
+ CHECK(last);
+ if (frames_symbolized > 0) {
+ SymbolizedStack *cur = SymbolizedStack::New(addr);
+ AddressInfo *info = &cur->info;
+ info->FillModuleInfo(first->info.module, first->info.module_offset);
+ last->next = cur;
+ last = cur;
}
+ CHECK_EQ(addr, first->info.address);
+ CHECK_EQ(addr, last->info.address);
+ return &last->info;
}
};
@@ -106,15 +111,12 @@ static int SymbolizeCodePCInfoCallback(void *vdata, uintptr_t addr,
const char *function) {
SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
if (function) {
- SymbolizedStack *cur = SymbolizedStack::New(addr);
- cdata->append(cur);
- AddressInfo *info = &cur->info;
- info->FillAddressAndModuleInfo(addr, cdata->module_name,
- cdata->module_offset);
- info->function = LibbacktraceSymbolizer::Demangle(function, true);
+ AddressInfo *info = cdata->get_new_frame(addr);
+ info->function = DemangleAlloc(function, /*always_alloc*/ true);
if (filename)
info->file = internal_strdup(filename);
info->line = lineno;
+ cdata->frames_symbolized++;
}
return 0;
}
@@ -123,12 +125,9 @@ static void SymbolizeCodeCallback(void *vdata, uintptr_t addr,
const char *symname, uintptr_t, uintptr_t) {
SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
if (symname) {
- SymbolizedStack *cur = SymbolizedStack::New(addr);
- cdata->append(cur);
- AddressInfo *info = &cur->info;
- info->FillAddressAndModuleInfo(addr, cdata->module_name,
- cdata->module_offset);
- info->function = LibbacktraceSymbolizer::Demangle(symname, true);
+ AddressInfo *info = cdata->get_new_frame(addr);
+ info->function = DemangleAlloc(symname, /*always_alloc*/ true);
+ cdata->frames_symbolized++;
}
}
@@ -136,7 +135,7 @@ static void SymbolizeDataCallback(void *vdata, uintptr_t, const char *symname,
uintptr_t symval, uintptr_t symsize) {
DataInfo *info = (DataInfo *)vdata;
if (symname && symval) {
- info->name = LibbacktraceSymbolizer::Demangle(symname, true);
+ info->name = DemangleAlloc(symname, /*always_alloc*/ true);
info->start = symval;
info->size = symsize;
}
@@ -156,21 +155,18 @@ LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) {
return new(*alloc) LibbacktraceSymbolizer(state);
}
-SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr,
- const char *module_name,
- uptr module_offset) {
+bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
SymbolizeCodeCallbackArg data;
- data.first = nullptr;
- data.last = nullptr;
- data.module_name = module_name;
- data.module_offset = module_offset;
+ data.first = stack;
+ data.last = stack;
+ data.frames_symbolized = 0;
backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback,
ErrorCallback, &data);
- if (data.first)
- return data.first;
+ if (data.frames_symbolized > 0)
+ return true;
backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback,
ErrorCallback, &data);
- return data.first;
+ return (data.frames_symbolized > 0);
}
bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
@@ -185,11 +181,9 @@ LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) {
return 0;
}
-SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr,
- const char *module_name,
- uptr module_offset) {
+bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
(void)state_;
- return nullptr;
+ return false;
}
bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
@@ -198,7 +192,7 @@ bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
#endif // SANITIZER_LIBBACKTRACE
-char *LibbacktraceSymbolizer::Demangle(const char *name, bool always_alloc) {
+static char *DemangleAlloc(const char *name, bool always_alloc) {
#if SANITIZER_LIBBACKTRACE && SANITIZER_CP_DEMANGLE
if (char *demangled = CplusV3Demangle(name))
return demangled;
@@ -208,4 +202,8 @@ char *LibbacktraceSymbolizer::Demangle(const char *name, bool always_alloc) {
return 0;
}
+const char *LibbacktraceSymbolizer::Demangle(const char *name) {
+ return DemangleAlloc(name, /*always_alloc*/ false);
+}
+
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h
index 1ff005042a11..00b465a72774 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h
+++ b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h
@@ -16,7 +16,7 @@
#include "sanitizer_platform.h"
#include "sanitizer_common.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
#ifndef SANITIZER_LIBBACKTRACE
# define SANITIZER_LIBBACKTRACE 0
@@ -28,17 +28,16 @@
namespace __sanitizer {
-class LibbacktraceSymbolizer {
+class LibbacktraceSymbolizer : public SymbolizerTool {
public:
static LibbacktraceSymbolizer *get(LowLevelAllocator *alloc);
- SymbolizedStack *SymbolizeCode(uptr addr, const char *module_name,
- uptr module_offset);
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
- bool SymbolizeData(uptr addr, DataInfo *info);
+ bool SymbolizeData(uptr addr, DataInfo *info) override;
// May return NULL if demangling failed.
- static char *Demangle(const char *name, bool always_alloc = false);
+ const char *Demangle(const char *name) override;
private:
explicit LibbacktraceSymbolizer(void *state) : state_(state) {}
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc
index 63c9356a521f..160f55d422ca 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc
@@ -11,18 +11,177 @@
// run-time libraries.
//===----------------------------------------------------------------------===//
+#include "sanitizer_allocator_internal.h"
#include "sanitizer_internal_defs.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
namespace __sanitizer {
+const char *ExtractToken(const char *str, const char *delims, char **result) {
+ uptr prefix_len = internal_strcspn(str, delims);
+ *result = (char*)InternalAlloc(prefix_len + 1);
+ internal_memcpy(*result, str, prefix_len);
+ (*result)[prefix_len] = '\0';
+ const char *prefix_end = str + prefix_len;
+ if (*prefix_end != '\0') prefix_end++;
+ return prefix_end;
+}
+
+const char *ExtractInt(const char *str, const char *delims, int *result) {
+ char *buff;
+ const char *ret = ExtractToken(str, delims, &buff);
+ if (buff != 0) {
+ *result = (int)internal_atoll(buff);
+ }
+ InternalFree(buff);
+ return ret;
+}
+
+const char *ExtractUptr(const char *str, const char *delims, uptr *result) {
+ char *buff;
+ const char *ret = ExtractToken(str, delims, &buff);
+ if (buff != 0) {
+ *result = (uptr)internal_atoll(buff);
+ }
+ InternalFree(buff);
+ return ret;
+}
+
+const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,
+ char **result) {
+ const char *found_delimiter = internal_strstr(str, delimiter);
+ uptr prefix_len =
+ found_delimiter ? found_delimiter - str : internal_strlen(str);
+ *result = (char *)InternalAlloc(prefix_len + 1);
+ internal_memcpy(*result, str, prefix_len);
+ (*result)[prefix_len] = '\0';
+ const char *prefix_end = str + prefix_len;
+ if (*prefix_end != '\0') prefix_end += internal_strlen(delimiter);
+ return prefix_end;
+}
+
+SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
+ BlockingMutexLock l(&mu_);
+ const char *module_name;
+ uptr module_offset;
+ SymbolizedStack *res = SymbolizedStack::New(addr);
+ if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset))
+ return res;
+ // Always fill data about module name and offset.
+ res->info.FillModuleInfo(module_name, module_offset);
+ for (auto iter = Iterator(&tools_); iter.hasNext();) {
+ auto *tool = iter.next();
+ SymbolizerScope sym_scope(this);
+ if (tool->SymbolizePC(addr, res)) {
+ return res;
+ }
+ }
+ return res;
+}
+
+bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ BlockingMutexLock l(&mu_);
+ const char *module_name;
+ uptr module_offset;
+ if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset))
+ return false;
+ info->Clear();
+ info->module = internal_strdup(module_name);
+ info->module_offset = module_offset;
+ for (auto iter = Iterator(&tools_); iter.hasNext();) {
+ auto *tool = iter.next();
+ SymbolizerScope sym_scope(this);
+ if (tool->SymbolizeData(addr, info)) {
+ return true;
+ }
+ }
+ return true;
+}
+
+bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
+ uptr *module_address) {
+ BlockingMutexLock l(&mu_);
+ const char *internal_module_name = nullptr;
+ if (!FindModuleNameAndOffsetForAddress(pc, &internal_module_name,
+ module_address))
+ return false;
+
+ if (module_name)
+ *module_name = module_names_.GetOwnedCopy(internal_module_name);
+ return true;
+}
+
+void Symbolizer::Flush() {
+ BlockingMutexLock l(&mu_);
+ for (auto iter = Iterator(&tools_); iter.hasNext();) {
+ auto *tool = iter.next();
+ SymbolizerScope sym_scope(this);
+ tool->Flush();
+ }
+}
+
+const char *Symbolizer::Demangle(const char *name) {
+ BlockingMutexLock l(&mu_);
+ for (auto iter = Iterator(&tools_); iter.hasNext();) {
+ auto *tool = iter.next();
+ SymbolizerScope sym_scope(this);
+ if (const char *demangled = tool->Demangle(name))
+ return demangled;
+ }
+ return PlatformDemangle(name);
+}
+
+void Symbolizer::PrepareForSandboxing() {
+ BlockingMutexLock l(&mu_);
+ PlatformPrepareForSandboxing();
+}
+
+bool Symbolizer::FindModuleNameAndOffsetForAddress(uptr address,
+ const char **module_name,
+ uptr *module_offset) {
+ LoadedModule *module = FindModuleForAddress(address);
+ if (module == 0)
+ return false;
+ *module_name = module->full_name();
+ *module_offset = address - module->base_address();
+ return true;
+}
+
+LoadedModule *Symbolizer::FindModuleForAddress(uptr address) {
+ bool modules_were_reloaded = false;
+ if (!modules_fresh_) {
+ for (uptr i = 0; i < n_modules_; i++)
+ modules_[i].clear();
+ n_modules_ =
+ GetListOfModules(modules_, kMaxNumberOfModules, /* filter */ nullptr);
+ CHECK_GT(n_modules_, 0);
+ CHECK_LT(n_modules_, kMaxNumberOfModules);
+ modules_fresh_ = true;
+ modules_were_reloaded = true;
+ }
+ for (uptr i = 0; i < n_modules_; i++) {
+ if (modules_[i].containsAddress(address)) {
+ return &modules_[i];
+ }
+ }
+ // Reload the modules and look up again, if we haven't tried it yet.
+ if (!modules_were_reloaded) {
+ // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors.
+ // It's too aggressive to reload the list of modules each time we fail
+ // to find a module for a given address.
+ modules_fresh_ = false;
+ return FindModuleForAddress(address);
+ }
+ return 0;
+}
+
Symbolizer *Symbolizer::GetOrInit() {
SpinMutexLock l(&init_mu_);
if (symbolizer_)
return symbolizer_;
- if ((symbolizer_ = PlatformInit()))
- return symbolizer_;
- return Disable();
+ symbolizer_ = PlatformInit();
+ CHECK(symbolizer_);
+ return symbolizer_;
}
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_mac.cc b/lib/sanitizer_common/sanitizer_symbolizer_mac.cc
new file mode 100644
index 000000000000..9a64192b0353
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_mac.cc
@@ -0,0 +1,148 @@
+//===-- sanitizer_symbolizer_mac.cc ---------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries.
+//
+// Implementation of Mac-specific "atos" symbolizer.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_mac.h"
+#include "sanitizer_symbolizer_mac.h"
+
+namespace __sanitizer {
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <util.h>
+
+bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ Dl_info info;
+ int result = dladdr((const void *)addr, &info);
+ if (!result) return false;
+ const char *demangled = DemangleCXXABI(info.dli_sname);
+ stack->info.function = internal_strdup(demangled);
+ return true;
+}
+
+bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ return false;
+}
+
+class AtosSymbolizerProcess : public SymbolizerProcess {
+ public:
+ explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
+ : SymbolizerProcess(path, /*use_forkpty*/ true),
+ parent_pid_(parent_pid) {}
+
+ private:
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
+ return (length >= 1 && buffer[length - 1] == '\n');
+ }
+
+ void ExecuteWithDefaultArgs(const char *path_to_binary) const override {
+ char pid_str[16];
+ internal_snprintf(pid_str, sizeof(pid_str), "%d", parent_pid_);
+ if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
+ // On Mavericks atos prints a deprecation warning which we suppress by
+ // passing -d. The warning isn't present on other OSX versions, even the
+ // newer ones.
+ execl(path_to_binary, path_to_binary, "-p", pid_str, "-d", (char *)0);
+ } else {
+ execl(path_to_binary, path_to_binary, "-p", pid_str, (char *)0);
+ }
+ }
+
+ pid_t parent_pid_;
+};
+
+static const char *kAtosErrorMessages[] = {
+ "atos cannot examine process",
+ "unable to get permission to examine process",
+ "An admin user name and password is required",
+ "could not load inserted library",
+ "architecture mismatch between analysis process",
+};
+
+static bool IsAtosErrorMessage(const char *str) {
+ for (uptr i = 0; i < ARRAY_SIZE(kAtosErrorMessages); i++) {
+ if (internal_strstr(str, kAtosErrorMessages[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool ParseCommandOutput(const char *str, SymbolizedStack *res) {
+ // Trim ending newlines.
+ char *trim;
+ ExtractTokenUpToDelimiter(str, "\n", &trim);
+
+ // The line from `atos` is in one of these formats:
+ // myfunction (in library.dylib) (sourcefile.c:17)
+ // myfunction (in library.dylib) + 0x1fe
+ // 0xdeadbeef (in library.dylib) + 0x1fe
+ // 0xdeadbeef (in library.dylib)
+ // 0xdeadbeef
+
+ if (IsAtosErrorMessage(trim)) {
+ Report("atos returned an error: %s\n", trim);
+ InternalFree(trim);
+ return false;
+ }
+
+ const char *rest = trim;
+ char *function_name;
+ rest = ExtractTokenUpToDelimiter(rest, " (in ", &function_name);
+ if (internal_strncmp(function_name, "0x", 2) != 0)
+ res->info.function = function_name;
+ else
+ InternalFree(function_name);
+ rest = ExtractTokenUpToDelimiter(rest, ") ", &res->info.module);
+
+ if (rest[0] == '(') {
+ rest++;
+ rest = ExtractTokenUpToDelimiter(rest, ":", &res->info.file);
+ char *extracted_line_number;
+ rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
+ res->info.line = internal_atoll(extracted_line_number);
+ InternalFree(extracted_line_number);
+ }
+
+ InternalFree(trim);
+ return true;
+}
+
+AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
+ : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
+
+bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ if (!process_) return false;
+ char command[32];
+ internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
+ const char *buf = process_->SendCommand(command);
+ if (!buf) return false;
+ if (!ParseCommandOutput(buf, stack)) {
+ process_ = nullptr;
+ return false;
+ }
+ return true;
+}
+
+bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; }
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_MAC
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_mac.h b/lib/sanitizer_common/sanitizer_symbolizer_mac.h
new file mode 100644
index 000000000000..068644de3762
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_mac.h
@@ -0,0 +1,48 @@
+//===-- sanitizer_symbolizer_mac.h ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries.
+//
+// Header for Mac-specific "atos" symbolizer.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_SYMBOLIZER_MAC_H
+#define SANITIZER_SYMBOLIZER_MAC_H
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+class DlAddrSymbolizer : public SymbolizerTool {
+ public:
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+ bool SymbolizeData(uptr addr, DataInfo *info) override;
+};
+
+class AtosSymbolizerProcess;
+
+class AtosSymbolizer : public SymbolizerTool {
+ public:
+ explicit AtosSymbolizer(const char *path, LowLevelAllocator *allocator);
+
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+ bool SymbolizeData(uptr addr, DataInfo *info) override;
+
+ private:
+ AtosSymbolizerProcess *process_;
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_MAC
+
+#endif // SANITIZER_SYMBOLIZER_MAC_H
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
index 69ac18e8426f..cb8455a21ae2 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
@@ -21,12 +21,10 @@
#include "sanitizer_linux.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_procmaps.h"
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_internal.h"
#include "sanitizer_symbolizer_libbacktrace.h"
+#include "sanitizer_symbolizer_mac.h"
-#include <errno.h>
-#include <stdlib.h>
-#include <sys/wait.h>
#include <unistd.h>
// C++ demangling function, as required by Itanium C++ ABI. This is weak,
@@ -41,7 +39,7 @@ namespace __cxxabiv1 {
namespace __sanitizer {
// Attempts to demangle the name via __cxa_demangle from __cxxabiv1.
-static const char *DemangleCXXABI(const char *name) {
+const char *DemangleCXXABI(const char *name) {
// FIXME: __cxa_demangle aggressively insists on allocating memory.
// There's not much we can do about that, short of providing our
// own demangler (libc++abi's implementation could be adapted so that
@@ -55,258 +53,67 @@ static const char *DemangleCXXABI(const char *name) {
return name;
}
-// Extracts the prefix of "str" that consists of any characters not
-// present in "delims" string, and copies this prefix to "result", allocating
-// space for it.
-// Returns a pointer to "str" after skipping extracted prefix and first
-// delimiter char.
-static const char *ExtractToken(const char *str, const char *delims,
- char **result) {
- uptr prefix_len = internal_strcspn(str, delims);
- *result = (char*)InternalAlloc(prefix_len + 1);
- internal_memcpy(*result, str, prefix_len);
- (*result)[prefix_len] = '\0';
- const char *prefix_end = str + prefix_len;
- if (*prefix_end != '\0') prefix_end++;
- return prefix_end;
-}
-
-// Same as ExtractToken, but converts extracted token to integer.
-static const char *ExtractInt(const char *str, const char *delims,
- int *result) {
- char *buff;
- const char *ret = ExtractToken(str, delims, &buff);
- if (buff != 0) {
- *result = (int)internal_atoll(buff);
- }
- InternalFree(buff);
- return ret;
-}
-
-static const char *ExtractUptr(const char *str, const char *delims,
- uptr *result) {
- char *buff;
- const char *ret = ExtractToken(str, delims, &buff);
- if (buff != 0) {
- *result = (uptr)internal_atoll(buff);
- }
- InternalFree(buff);
- return ret;
-}
-
-class ExternalSymbolizerInterface {
- public:
- // Can't declare pure virtual functions in sanitizer runtimes:
- // __cxa_pure_virtual might be unavailable.
- virtual char *SendCommand(bool is_data, const char *module_name,
- uptr module_offset) {
- UNIMPLEMENTED();
- }
-};
-
-// SymbolizerProcess encapsulates communication between the tool and
-// external symbolizer program, running in a different subprocess.
-// SymbolizerProcess may not be used from two threads simultaneously.
-class SymbolizerProcess : public ExternalSymbolizerInterface {
- public:
- explicit SymbolizerProcess(const char *path)
- : path_(path),
- input_fd_(kInvalidFd),
- output_fd_(kInvalidFd),
- times_restarted_(0),
- failed_to_start_(false),
- reported_invalid_path_(false) {
- CHECK(path_);
- CHECK_NE(path_[0], '\0');
- }
-
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
- // Start or restart symbolizer if we failed to send command to it.
- if (char *res = SendCommandImpl(is_data, module_name, module_offset))
- return res;
- Restart();
- }
- if (!failed_to_start_) {
- Report("WARNING: Failed to use and restart external symbolizer!\n");
- failed_to_start_ = true;
- }
- return 0;
- }
-
- private:
- bool Restart() {
- if (input_fd_ != kInvalidFd)
- internal_close(input_fd_);
- if (output_fd_ != kInvalidFd)
- internal_close(output_fd_);
- return StartSymbolizerSubprocess();
- }
-
- char *SendCommandImpl(bool is_data, const char *module_name,
- uptr module_offset) {
- if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
- return 0;
- CHECK(module_name);
- if (!RenderInputCommand(buffer_, kBufferSize, is_data, module_name,
- module_offset))
- return 0;
- if (!writeToSymbolizer(buffer_, internal_strlen(buffer_)))
- return 0;
- if (!readFromSymbolizer(buffer_, kBufferSize))
- return 0;
- return buffer_;
- }
-
- bool readFromSymbolizer(char *buffer, uptr max_length) {
- if (max_length == 0)
- return true;
- uptr read_len = 0;
- while (true) {
- uptr just_read = internal_read(input_fd_, buffer + read_len,
- max_length - read_len - 1);
- // We can't read 0 bytes, as we don't expect external symbolizer to close
- // its stdout.
- if (just_read == 0 || just_read == (uptr)-1) {
- Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
- return false;
- }
- read_len += just_read;
- if (ReachedEndOfOutput(buffer, read_len))
- break;
- }
- buffer[read_len] = '\0';
- return true;
- }
-
- bool writeToSymbolizer(const char *buffer, uptr length) {
- if (length == 0)
- return true;
- uptr write_len = internal_write(output_fd_, buffer, length);
- if (write_len == 0 || write_len == (uptr)-1) {
- Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
- return false;
+// Parses one or more two-line strings in the following format:
+// <function_name>
+// <file_name>:<line_number>[:<column_number>]
+// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of
+// them use the same output format.
+static void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) {
+ bool top_frame = true;
+ SymbolizedStack *last = res;
+ while (true) {
+ char *function_name = 0;
+ str = ExtractToken(str, "\n", &function_name);
+ CHECK(function_name);
+ if (function_name[0] == '\0') {
+ // There are no more frames.
+ InternalFree(function_name);
+ break;
}
- return true;
- }
-
- bool StartSymbolizerSubprocess() {
- if (!FileExists(path_)) {
- if (!reported_invalid_path_) {
- Report("WARNING: invalid path to external symbolizer!\n");
- reported_invalid_path_ = true;
- }
- return false;
+ SymbolizedStack *cur;
+ if (top_frame) {
+ cur = res;
+ top_frame = false;
+ } else {
+ cur = SymbolizedStack::New(res->info.address);
+ cur->info.FillModuleInfo(res->info.module, res->info.module_offset);
+ last->next = cur;
+ last = cur;
}
- int *infd = NULL;
- int *outfd = NULL;
- // The client program may close its stdin and/or stdout and/or stderr
- // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
- // In this case the communication between the forked processes may be
- // broken if either the parent or the child tries to close or duplicate
- // these descriptors. The loop below produces two pairs of file
- // descriptors, each greater than 2 (stderr).
- int sock_pair[5][2];
- for (int i = 0; i < 5; i++) {
- if (pipe(sock_pair[i]) == -1) {
- for (int j = 0; j < i; j++) {
- internal_close(sock_pair[j][0]);
- internal_close(sock_pair[j][1]);
- }
- Report("WARNING: Can't create a socket pair to start "
- "external symbolizer (errno: %d)\n", errno);
- return false;
- } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) {
- if (infd == NULL) {
- infd = sock_pair[i];
- } else {
- outfd = sock_pair[i];
- for (int j = 0; j < i; j++) {
- if (sock_pair[j] == infd) continue;
- internal_close(sock_pair[j][0]);
- internal_close(sock_pair[j][1]);
- }
- break;
- }
- }
- }
- CHECK(infd);
- CHECK(outfd);
-
- // Real fork() may call user callbacks registered with pthread_atfork().
- int pid = internal_fork();
- if (pid == -1) {
- // Fork() failed.
- internal_close(infd[0]);
- internal_close(infd[1]);
- internal_close(outfd[0]);
- internal_close(outfd[1]);
- Report("WARNING: failed to fork external symbolizer "
- " (errno: %d)\n", errno);
- return false;
- } else if (pid == 0) {
- // Child subprocess.
- internal_close(STDOUT_FILENO);
- internal_close(STDIN_FILENO);
- internal_dup2(outfd[0], STDIN_FILENO);
- internal_dup2(infd[1], STDOUT_FILENO);
- internal_close(outfd[0]);
- internal_close(outfd[1]);
- internal_close(infd[0]);
- internal_close(infd[1]);
- for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--)
- internal_close(fd);
- ExecuteWithDefaultArgs(path_);
- internal__exit(1);
+ AddressInfo *info = &cur->info;
+ info->function = function_name;
+ // Parse <file>:<line>:<column> buffer.
+ char *file_line_info = 0;
+ str = ExtractToken(str, "\n", &file_line_info);
+ CHECK(file_line_info);
+ const char *line_info = ExtractToken(file_line_info, ":", &info->file);
+ line_info = ExtractInt(line_info, ":", &info->line);
+ line_info = ExtractInt(line_info, "", &info->column);
+ InternalFree(file_line_info);
+
+ // Functions and filenames can be "??", in which case we write 0
+ // to address info to mark that names are unknown.
+ if (0 == internal_strcmp(info->function, "??")) {
+ InternalFree(info->function);
+ info->function = 0;
}
-
- // Continue execution in parent process.
- internal_close(outfd[0]);
- internal_close(infd[1]);
- input_fd_ = infd[0];
- output_fd_ = outfd[1];
-
- // Check that symbolizer subprocess started successfully.
- int pid_status;
- SleepForMillis(kSymbolizerStartupTimeMillis);
- int exited_pid = waitpid(pid, &pid_status, WNOHANG);
- if (exited_pid != 0) {
- // Either waitpid failed, or child has already exited.
- Report("WARNING: external symbolizer didn't start up correctly!\n");
- return false;
+ if (0 == internal_strcmp(info->file, "??")) {
+ InternalFree(info->file);
+ info->file = 0;
}
-
- return true;
- }
-
- virtual bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,
- const char *module_name,
- uptr module_offset) const {
- UNIMPLEMENTED();
- }
-
- virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const {
- UNIMPLEMENTED();
- }
-
- virtual void ExecuteWithDefaultArgs(const char *path_to_binary) const {
- UNIMPLEMENTED();
}
+}
- const char *path_;
- int input_fd_;
- int output_fd_;
-
- static const uptr kBufferSize = 16 * 1024;
- char buffer_[kBufferSize];
-
- static const uptr kMaxTimesRestarted = 5;
- static const int kSymbolizerStartupTimeMillis = 10;
- uptr times_restarted_;
- bool failed_to_start_;
- bool reported_invalid_path_;
-};
+// Parses a two-line string in the following format:
+// <symbol_name>
+// <start_address> <size>
+// Used by LLVMSymbolizer and InternalSymbolizer.
+static void ParseSymbolizeDataOutput(const char *str, DataInfo *info) {
+ str = ExtractToken(str, "\n", &info->name);
+ str = ExtractUptr(str, " ", &info->start);
+ str = ExtractUptr(str, "\n", &info->size);
+}
// For now we assume the following protocol:
// For each request of the form
@@ -323,21 +130,16 @@ class LLVMSymbolizerProcess : public SymbolizerProcess {
explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {}
private:
- bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,
- const char *module_name, uptr module_offset) const {
- internal_snprintf(buffer, max_length, "%s\"%s\" 0x%zx\n",
- is_data ? "DATA " : "", module_name, module_offset);
- return true;
- }
-
- bool ReachedEndOfOutput(const char *buffer, uptr length) const {
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
// Empty line marks the end of llvm-symbolizer output.
return length >= 2 && buffer[length - 1] == '\n' &&
buffer[length - 2] == '\n';
}
- void ExecuteWithDefaultArgs(const char *path_to_binary) const {
-#if defined(__x86_64__)
+ void ExecuteWithDefaultArgs(const char *path_to_binary) const override {
+#if defined(__x86_64h__)
+ const char* const kSymbolizerArch = "--default-arch=x86_64h";
+#elif defined(__x86_64__)
const char* const kSymbolizerArch = "--default-arch=x86_64";
#elif defined(__i386__)
const char* const kSymbolizerArch = "--default-arch=i386";
@@ -357,6 +159,44 @@ class LLVMSymbolizerProcess : public SymbolizerProcess {
}
};
+class LLVMSymbolizer : public SymbolizerTool {
+ public:
+ explicit LLVMSymbolizer(const char *path, LowLevelAllocator *allocator)
+ : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {}
+
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+ if (const char *buf = SendCommand(/*is_data*/ false, stack->info.module,
+ stack->info.module_offset)) {
+ ParseSymbolizePCOutput(buf, stack);
+ return true;
+ }
+ return false;
+ }
+
+ bool SymbolizeData(uptr addr, DataInfo *info) override {
+ if (const char *buf =
+ SendCommand(/*is_data*/ true, info->module, info->module_offset)) {
+ ParseSymbolizeDataOutput(buf, info);
+ info->start += (addr - info->module_offset); // Add the base address.
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ const char *SendCommand(bool is_data, const char *module_name,
+ uptr module_offset) {
+ CHECK(module_name);
+ internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n",
+ is_data ? "DATA " : "", module_name, module_offset);
+ return symbolizer_process_->SendCommand(buffer_);
+ }
+
+ LLVMSymbolizerProcess *symbolizer_process_;
+ static const uptr kBufferSize = 16 * 1024;
+ char buffer_[kBufferSize];
+};
+
class Addr2LineProcess : public SymbolizerProcess {
public:
Addr2LineProcess(const char *path, const char *module_name)
@@ -365,16 +205,7 @@ class Addr2LineProcess : public SymbolizerProcess {
const char *module_name() const { return module_name_; }
private:
- bool RenderInputCommand(char *buffer, uptr max_length, bool is_data,
- const char *module_name, uptr module_offset) const {
- if (is_data)
- return false;
- CHECK_EQ(0, internal_strcmp(module_name, module_name_));
- internal_snprintf(buffer, max_length, "0x%zx\n", module_offset);
- return true;
- }
-
- bool ReachedEndOfOutput(const char *buffer, uptr length) const {
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
// Output should consist of two lines.
int num_lines = 0;
for (uptr i = 0; i < length; ++i) {
@@ -386,23 +217,35 @@ class Addr2LineProcess : public SymbolizerProcess {
return false;
}
- void ExecuteWithDefaultArgs(const char *path_to_binary) const {
+ void ExecuteWithDefaultArgs(const char *path_to_binary) const override {
execl(path_to_binary, path_to_binary, "-Cfe", module_name_, (char *)0);
}
const char *module_name_; // Owned, leaked.
};
-class Addr2LinePool : public ExternalSymbolizerInterface {
+class Addr2LinePool : public SymbolizerTool {
public:
explicit Addr2LinePool(const char *addr2line_path,
LowLevelAllocator *allocator)
: addr2line_path_(addr2line_path), allocator_(allocator),
addr2line_pool_(16) {}
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- if (is_data)
- return 0;
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+ if (const char *buf =
+ SendCommand(stack->info.module, stack->info.module_offset)) {
+ ParseSymbolizePCOutput(buf, stack);
+ return true;
+ }
+ return false;
+ }
+
+ bool SymbolizeData(uptr addr, DataInfo *info) override {
+ return false;
+ }
+
+ private:
+ const char *SendCommand(const char *module_name, uptr module_offset) {
Addr2LineProcess *addr2line = 0;
for (uptr i = 0; i < addr2line_pool_.size(); ++i) {
if (0 ==
@@ -416,10 +259,13 @@ class Addr2LinePool : public ExternalSymbolizerInterface {
new(*allocator_) Addr2LineProcess(addr2line_path_, module_name);
addr2line_pool_.push_back(addr2line);
}
- return addr2line->SendCommand(is_data, module_name, module_offset);
+ CHECK_EQ(0, internal_strcmp(module_name, addr2line->module_name()));
+ char buffer_[kBufferSize];
+ internal_snprintf(buffer_, kBufferSize, "0x%zx\n", module_offset);
+ return addr2line->SendCommand(buffer_);
}
- private:
+ static const uptr kBufferSize = 32;
const char *addr2line_path_;
LowLevelAllocator *allocator_;
InternalMmapVector<Addr2LineProcess*> addr2line_pool_;
@@ -440,10 +286,8 @@ int __sanitizer_symbolize_demangle(const char *Name, char *Buffer,
int MaxLength);
} // extern "C"
-class InternalSymbolizer {
+class InternalSymbolizer : public SymbolizerTool {
public:
- typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int);
-
static InternalSymbolizer *get(LowLevelAllocator *alloc) {
if (__sanitizer_symbolize_code != 0 &&
__sanitizer_symbolize_data != 0) {
@@ -452,20 +296,29 @@ class InternalSymbolizer {
return 0;
}
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- SanitizerSymbolizeFn symbolize_fn = is_data ? __sanitizer_symbolize_data
- : __sanitizer_symbolize_code;
- if (symbolize_fn(module_name, module_offset, buffer_, kBufferSize))
- return buffer_;
- return 0;
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+ bool result = __sanitizer_symbolize_code(
+ stack->info.module, stack->info.module_offset, buffer_, kBufferSize);
+ if (result) ParseSymbolizePCOutput(buffer_, stack);
+ return result;
}
- void Flush() {
+ bool SymbolizeData(uptr addr, DataInfo *info) override {
+ bool result = __sanitizer_symbolize_data(info->module, info->module_offset,
+ buffer_, kBufferSize);
+ if (result) {
+ ParseSymbolizeDataOutput(buffer_, info);
+ info->start += (addr - info->module_offset); // Add the base address.
+ }
+ return result;
+ }
+
+ void Flush() override {
if (__sanitizer_symbolize_flush)
__sanitizer_symbolize_flush();
}
- const char *Demangle(const char *name) {
+ const char *Demangle(const char *name) override {
if (__sanitizer_symbolize_demangle) {
for (uptr res_length = 1024;
res_length <= InternalSizeClassMap::kMaxSize;) {
@@ -492,273 +345,101 @@ class InternalSymbolizer {
};
#else // SANITIZER_SUPPORTS_WEAK_HOOKS
-class InternalSymbolizer {
+class InternalSymbolizer : public SymbolizerTool {
public:
static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; }
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- return 0;
- }
- void Flush() { }
- const char *Demangle(const char *name) { return name; }
};
#endif // SANITIZER_SUPPORTS_WEAK_HOOKS
-class POSIXSymbolizer : public Symbolizer {
- public:
- POSIXSymbolizer(ExternalSymbolizerInterface *external_symbolizer,
- InternalSymbolizer *internal_symbolizer,
- LibbacktraceSymbolizer *libbacktrace_symbolizer)
- : Symbolizer(),
- external_symbolizer_(external_symbolizer),
- internal_symbolizer_(internal_symbolizer),
- libbacktrace_symbolizer_(libbacktrace_symbolizer) {}
-
- SymbolizedStack *SymbolizePC(uptr addr) override {
- BlockingMutexLock l(&mu_);
- const char *module_name;
- uptr module_offset;
- if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset))
- return SymbolizedStack::New(addr);
- // First, try to use libbacktrace symbolizer (if it's available).
- if (libbacktrace_symbolizer_ != 0) {
- mu_.CheckLocked();
- if (SymbolizedStack *res = libbacktrace_symbolizer_->SymbolizeCode(
- addr, module_name, module_offset))
- return res;
- }
- // Always fill data about module name and offset.
- SymbolizedStack *res = SymbolizedStack::New(addr);
- res->info.FillAddressAndModuleInfo(addr, module_name, module_offset);
-
- const char *str = SendCommand(false, module_name, module_offset);
- if (str == 0) {
- // Symbolizer was not initialized or failed.
- return res;
- }
-
- bool top_frame = true;
- SymbolizedStack *last = res;
- while (true) {
- char *function_name = 0;
- str = ExtractToken(str, "\n", &function_name);
- CHECK(function_name);
- if (function_name[0] == '\0') {
- // There are no more frames.
- break;
- }
- SymbolizedStack *cur;
- if (top_frame) {
- cur = res;
- top_frame = false;
- } else {
- cur = SymbolizedStack::New(addr);
- cur->info.FillAddressAndModuleInfo(addr, module_name, module_offset);
- last->next = cur;
- last = cur;
- }
-
- AddressInfo *info = &cur->info;
- info->function = function_name;
- // Parse <file>:<line>:<column> buffer.
- char *file_line_info = 0;
- str = ExtractToken(str, "\n", &file_line_info);
- CHECK(file_line_info);
- const char *line_info = ExtractToken(file_line_info, ":", &info->file);
- line_info = ExtractInt(line_info, ":", &info->line);
- line_info = ExtractInt(line_info, "", &info->column);
- InternalFree(file_line_info);
-
- // Functions and filenames can be "??", in which case we write 0
- // to address info to mark that names are unknown.
- if (0 == internal_strcmp(info->function, "??")) {
- InternalFree(info->function);
- info->function = 0;
- }
- if (0 == internal_strcmp(info->file, "??")) {
- InternalFree(info->file);
- info->file = 0;
- }
- }
- return res;
- }
-
- bool SymbolizeData(uptr addr, DataInfo *info) override {
- BlockingMutexLock l(&mu_);
- LoadedModule *module = FindModuleForAddress(addr);
- if (module == 0)
- return false;
- const char *module_name = module->full_name();
- uptr module_offset = addr - module->base_address();
- info->Clear();
- info->module = internal_strdup(module_name);
- info->module_offset = module_offset;
- // First, try to use libbacktrace symbolizer (if it's available).
- if (libbacktrace_symbolizer_ != 0) {
- mu_.CheckLocked();
- if (libbacktrace_symbolizer_->SymbolizeData(addr, info))
- return true;
- }
- const char *str = SendCommand(true, module_name, module_offset);
- if (str == 0)
- return true;
- str = ExtractToken(str, "\n", &info->name);
- str = ExtractUptr(str, " ", &info->start);
- str = ExtractUptr(str, "\n", &info->size);
- info->start += module->base_address();
- return true;
- }
-
- bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
- uptr *module_address) override {
- BlockingMutexLock l(&mu_);
- return FindModuleNameAndOffsetForAddress(pc, module_name, module_address);
- }
-
- bool CanReturnFileLineInfo() override {
- return internal_symbolizer_ != 0 || external_symbolizer_ != 0 ||
- libbacktrace_symbolizer_ != 0;
- }
-
- void Flush() override {
- BlockingMutexLock l(&mu_);
- if (internal_symbolizer_ != 0) {
- SymbolizerScope sym_scope(this);
- internal_symbolizer_->Flush();
- }
- }
+const char *Symbolizer::PlatformDemangle(const char *name) {
+ return DemangleCXXABI(name);
+}
- const char *Demangle(const char *name) override {
- BlockingMutexLock l(&mu_);
- // Run hooks even if we don't use internal symbolizer, as cxxabi
- // demangle may call system functions.
- SymbolizerScope sym_scope(this);
- // Try to use libbacktrace demangler (if available).
- if (libbacktrace_symbolizer_ != 0) {
- if (const char *demangled = libbacktrace_symbolizer_->Demangle(name))
- return demangled;
+void Symbolizer::PlatformPrepareForSandboxing() {}
+
+static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) {
+ const char *path = common_flags()->external_symbolizer_path;
+ const char *binary_name = path ? StripModuleName(path) : "";
+ if (path && path[0] == '\0') {
+ VReport(2, "External symbolizer is explicitly disabled.\n");
+ return nullptr;
+ } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) {
+ VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path);
+ return new(*allocator) LLVMSymbolizer(path, allocator);
+ } else if (!internal_strcmp(binary_name, "atos")) {
+#if SANITIZER_MAC
+ VReport(2, "Using atos at user-specified path: %s\n", path);
+ return new(*allocator) AtosSymbolizer(path, allocator);
+#else // SANITIZER_MAC
+ Report("ERROR: Using `atos` is only supported on Darwin.\n");
+ Die();
+#endif // SANITIZER_MAC
+ } else if (!internal_strcmp(binary_name, "addr2line")) {
+ VReport(2, "Using addr2line at user-specified path: %s\n", path);
+ return new(*allocator) Addr2LinePool(path, allocator);
+ } else if (path) {
+ Report("ERROR: External symbolizer path is set to '%s' which isn't "
+ "a known symbolizer. Please set the path to the llvm-symbolizer "
+ "binary or other known tool.\n", path);
+ Die();
+ }
+
+ // Otherwise symbolizer program is unknown, let's search $PATH
+ CHECK(path == nullptr);
+ if (const char *found_path = FindPathToBinary("llvm-symbolizer")) {
+ VReport(2, "Using llvm-symbolizer found at: %s\n", found_path);
+ return new(*allocator) LLVMSymbolizer(found_path, allocator);
+ }
+#if SANITIZER_MAC
+ if (const char *found_path = FindPathToBinary("atos")) {
+ VReport(2, "Using atos found at: %s\n", found_path);
+ return new(*allocator) AtosSymbolizer(found_path, allocator);
+ }
+#endif // SANITIZER_MAC
+ if (common_flags()->allow_addr2line) {
+ if (const char *found_path = FindPathToBinary("addr2line")) {
+ VReport(2, "Using addr2line found at: %s\n", found_path);
+ return new(*allocator) Addr2LinePool(found_path, allocator);
}
- if (internal_symbolizer_ != 0)
- return internal_symbolizer_->Demangle(name);
- return DemangleCXXABI(name);
}
+ return nullptr;
+}
- void PrepareForSandboxing() override {
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
- BlockingMutexLock l(&mu_);
- // Cache /proc/self/exe on Linux.
- CacheBinaryName();
-#endif
+static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
+ LowLevelAllocator *allocator) {
+ if (!common_flags()->symbolize) {
+ VReport(2, "Symbolizer is disabled.\n");
+ return;
}
-
- private:
- char *SendCommand(bool is_data, const char *module_name, uptr module_offset) {
- mu_.CheckLocked();
- // First, try to use internal symbolizer.
- if (internal_symbolizer_) {
- SymbolizerScope sym_scope(this);
- return internal_symbolizer_->SendCommand(is_data, module_name,
- module_offset);
- }
- // Otherwise, fall back to external symbolizer.
- if (external_symbolizer_) {
- SymbolizerScope sym_scope(this);
- return external_symbolizer_->SendCommand(is_data, module_name,
- module_offset);
- }
- return 0;
+ if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) {
+ VReport(2, "Using internal symbolizer.\n");
+ list->push_back(tool);
+ return;
}
-
- LoadedModule *FindModuleForAddress(uptr address) {
- mu_.CheckLocked();
- bool modules_were_reloaded = false;
- if (modules_ == 0 || !modules_fresh_) {
- modules_ = (LoadedModule*)(symbolizer_allocator_.Allocate(
- kMaxNumberOfModuleContexts * sizeof(LoadedModule)));
- CHECK(modules_);
- n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts,
- /* filter */ 0);
- CHECK_GT(n_modules_, 0);
- CHECK_LT(n_modules_, kMaxNumberOfModuleContexts);
- modules_fresh_ = true;
- modules_were_reloaded = true;
- }
- for (uptr i = 0; i < n_modules_; i++) {
- if (modules_[i].containsAddress(address)) {
- return &modules_[i];
- }
- }
- // Reload the modules and look up again, if we haven't tried it yet.
- if (!modules_were_reloaded) {
- // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors.
- // It's too aggressive to reload the list of modules each time we fail
- // to find a module for a given address.
- modules_fresh_ = false;
- return FindModuleForAddress(address);
- }
- return 0;
+ if (SymbolizerTool *tool = LibbacktraceSymbolizer::get(allocator)) {
+ VReport(2, "Using libbacktrace symbolizer.\n");
+ list->push_back(tool);
+ return;
}
- bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name,
- uptr *module_offset) {
- mu_.CheckLocked();
- LoadedModule *module = FindModuleForAddress(address);
- if (module == 0)
- return false;
- *module_name = module->full_name();
- *module_offset = address - module->base_address();
- return true;
+ if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) {
+ list->push_back(tool);
+ } else {
+ VReport(2, "No internal or external symbolizer found.\n");
}
- // 16K loaded modules should be enough for everyone.
- static const uptr kMaxNumberOfModuleContexts = 1 << 14;
- LoadedModule *modules_; // Array of module descriptions is leaked.
- uptr n_modules_;
- // If stale, need to reload the modules before looking up addresses.
- bool modules_fresh_;
- BlockingMutex mu_;
-
- ExternalSymbolizerInterface *external_symbolizer_; // Leaked.
- InternalSymbolizer *const internal_symbolizer_; // Leaked.
- LibbacktraceSymbolizer *libbacktrace_symbolizer_; // Leaked.
-};
+#if SANITIZER_MAC
+ VReport(2, "Using dladdr symbolizer.\n");
+ list->push_back(new(*allocator) DlAddrSymbolizer());
+#endif // SANITIZER_MAC
+}
Symbolizer *Symbolizer::PlatformInit() {
- if (!common_flags()->symbolize) {
- return new(symbolizer_allocator_) POSIXSymbolizer(0, 0, 0);
- }
- InternalSymbolizer* internal_symbolizer =
- InternalSymbolizer::get(&symbolizer_allocator_);
- ExternalSymbolizerInterface *external_symbolizer = 0;
- LibbacktraceSymbolizer *libbacktrace_symbolizer = 0;
-
- if (!internal_symbolizer) {
- libbacktrace_symbolizer =
- LibbacktraceSymbolizer::get(&symbolizer_allocator_);
- if (!libbacktrace_symbolizer) {
- const char *path_to_external = common_flags()->external_symbolizer_path;
- if (path_to_external && path_to_external[0] == '\0') {
- // External symbolizer is explicitly disabled. Do nothing.
- } else {
- // Find path to llvm-symbolizer if it's not provided.
- if (!path_to_external)
- path_to_external = FindPathToBinary("llvm-symbolizer");
- if (path_to_external) {
- external_symbolizer = new(symbolizer_allocator_)
- LLVMSymbolizerProcess(path_to_external);
- } else if (common_flags()->allow_addr2line) {
- // If llvm-symbolizer is not found, try to use addr2line.
- if (const char *addr2line_path = FindPathToBinary("addr2line")) {
- external_symbolizer = new(symbolizer_allocator_)
- Addr2LinePool(addr2line_path, &symbolizer_allocator_);
- }
- }
- }
- }
- }
-
- return new(symbolizer_allocator_) POSIXSymbolizer(
- external_symbolizer, internal_symbolizer, libbacktrace_symbolizer);
+ IntrusiveList<SymbolizerTool> list;
+ list.clear();
+ ChooseSymbolizerTools(&list, &symbolizer_allocator_);
+ return new(symbolizer_allocator_) Symbolizer(list);
}
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc
new file mode 100644
index 000000000000..f1c01a332499
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc
@@ -0,0 +1,229 @@
+//===-- sanitizer_symbolizer_process_libcdep.cc ---------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of SymbolizerProcess used by external symbolizers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_POSIX
+#include "sanitizer_posix.h"
+#include "sanitizer_symbolizer_internal.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#if SANITIZER_MAC
+#include <util.h> // for forkpty()
+#endif // SANITIZER_MAC
+
+namespace __sanitizer {
+
+SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty)
+ : path_(path),
+ input_fd_(kInvalidFd),
+ output_fd_(kInvalidFd),
+ times_restarted_(0),
+ failed_to_start_(false),
+ reported_invalid_path_(false),
+ use_forkpty_(use_forkpty) {
+ CHECK(path_);
+ CHECK_NE(path_[0], '\0');
+}
+
+const char *SymbolizerProcess::SendCommand(const char *command) {
+ for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
+ // Start or restart symbolizer if we failed to send command to it.
+ if (const char *res = SendCommandImpl(command))
+ return res;
+ Restart();
+ }
+ if (!failed_to_start_) {
+ Report("WARNING: Failed to use and restart external symbolizer!\n");
+ failed_to_start_ = true;
+ }
+ return 0;
+}
+
+bool SymbolizerProcess::Restart() {
+ if (input_fd_ != kInvalidFd)
+ internal_close(input_fd_);
+ if (output_fd_ != kInvalidFd)
+ internal_close(output_fd_);
+ return StartSymbolizerSubprocess();
+}
+
+const char *SymbolizerProcess::SendCommandImpl(const char *command) {
+ if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
+ return 0;
+ if (!WriteToSymbolizer(command, internal_strlen(command)))
+ return 0;
+ if (!ReadFromSymbolizer(buffer_, kBufferSize))
+ return 0;
+ return buffer_;
+}
+
+bool SymbolizerProcess::ReadFromSymbolizer(char *buffer, uptr max_length) {
+ if (max_length == 0)
+ return true;
+ uptr read_len = 0;
+ while (true) {
+ uptr just_read = internal_read(input_fd_, buffer + read_len,
+ max_length - read_len - 1);
+ // We can't read 0 bytes, as we don't expect external symbolizer to close
+ // its stdout.
+ if (just_read == 0 || just_read == (uptr)-1) {
+ Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
+ return false;
+ }
+ read_len += just_read;
+ if (ReachedEndOfOutput(buffer, read_len))
+ break;
+ }
+ buffer[read_len] = '\0';
+ return true;
+}
+
+bool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) {
+ if (length == 0)
+ return true;
+ uptr write_len = internal_write(output_fd_, buffer, length);
+ if (write_len == 0 || write_len == (uptr)-1) {
+ Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
+ return false;
+ }
+ return true;
+}
+
+bool SymbolizerProcess::StartSymbolizerSubprocess() {
+ if (!FileExists(path_)) {
+ if (!reported_invalid_path_) {
+ Report("WARNING: invalid path to external symbolizer!\n");
+ reported_invalid_path_ = true;
+ }
+ return false;
+ }
+
+ int pid;
+ if (use_forkpty_) {
+#if SANITIZER_MAC
+ fd_t fd = kInvalidFd;
+ // Use forkpty to disable buffering in the new terminal.
+ pid = forkpty(&fd, 0, 0, 0);
+ if (pid == -1) {
+ // forkpty() failed.
+ Report("WARNING: failed to fork external symbolizer (errno: %d)\n",
+ errno);
+ return false;
+ } else if (pid == 0) {
+ // Child subprocess.
+ ExecuteWithDefaultArgs(path_);
+ internal__exit(1);
+ }
+
+ // Continue execution in parent process.
+ input_fd_ = output_fd_ = fd;
+
+ // Disable echo in the new terminal, disable CR.
+ struct termios termflags;
+ tcgetattr(fd, &termflags);
+ termflags.c_oflag &= ~ONLCR;
+ termflags.c_lflag &= ~ECHO;
+ tcsetattr(fd, TCSANOW, &termflags);
+#else // SANITIZER_MAC
+ UNIMPLEMENTED();
+#endif // SANITIZER_MAC
+ } else {
+ int *infd = NULL;
+ int *outfd = NULL;
+ // The client program may close its stdin and/or stdout and/or stderr
+ // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
+ // In this case the communication between the forked processes may be
+ // broken if either the parent or the child tries to close or duplicate
+ // these descriptors. The loop below produces two pairs of file
+ // descriptors, each greater than 2 (stderr).
+ int sock_pair[5][2];
+ for (int i = 0; i < 5; i++) {
+ if (pipe(sock_pair[i]) == -1) {
+ for (int j = 0; j < i; j++) {
+ internal_close(sock_pair[j][0]);
+ internal_close(sock_pair[j][1]);
+ }
+ Report("WARNING: Can't create a socket pair to start "
+ "external symbolizer (errno: %d)\n", errno);
+ return false;
+ } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) {
+ if (infd == NULL) {
+ infd = sock_pair[i];
+ } else {
+ outfd = sock_pair[i];
+ for (int j = 0; j < i; j++) {
+ if (sock_pair[j] == infd) continue;
+ internal_close(sock_pair[j][0]);
+ internal_close(sock_pair[j][1]);
+ }
+ break;
+ }
+ }
+ }
+ CHECK(infd);
+ CHECK(outfd);
+
+ // Real fork() may call user callbacks registered with pthread_atfork().
+ pid = internal_fork();
+ if (pid == -1) {
+ // Fork() failed.
+ internal_close(infd[0]);
+ internal_close(infd[1]);
+ internal_close(outfd[0]);
+ internal_close(outfd[1]);
+ Report("WARNING: failed to fork external symbolizer "
+ " (errno: %d)\n", errno);
+ return false;
+ } else if (pid == 0) {
+ // Child subprocess.
+ internal_close(STDOUT_FILENO);
+ internal_close(STDIN_FILENO);
+ internal_dup2(outfd[0], STDIN_FILENO);
+ internal_dup2(infd[1], STDOUT_FILENO);
+ internal_close(outfd[0]);
+ internal_close(outfd[1]);
+ internal_close(infd[0]);
+ internal_close(infd[1]);
+ for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--)
+ internal_close(fd);
+ ExecuteWithDefaultArgs(path_);
+ internal__exit(1);
+ }
+
+ // Continue execution in parent process.
+ internal_close(outfd[0]);
+ internal_close(infd[1]);
+ input_fd_ = infd[0];
+ output_fd_ = outfd[1];
+ }
+
+ // Check that symbolizer subprocess started successfully.
+ int pid_status;
+ SleepForMillis(kSymbolizerStartupTimeMillis);
+ int exited_pid = waitpid(pid, &pid_status, WNOHANG);
+ if (exited_pid != 0) {
+ // Either waitpid failed, or child has already exited.
+ Report("WARNING: external symbolizer didn't start up correctly!\n");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_POSIX
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_win.cc b/lib/sanitizer_common/sanitizer_symbolizer_win.cc
index ed96a3a895a8..31f374687e96 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_win.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_win.cc
@@ -18,139 +18,130 @@
#include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib")
-#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_win.h"
+#include "sanitizer_symbolizer_internal.h"
namespace __sanitizer {
-class WinSymbolizer : public Symbolizer {
- public:
- WinSymbolizer() : initialized_(false) {}
-
- SymbolizedStack *SymbolizePC(uptr addr) override {
- SymbolizedStack *frame = SymbolizedStack::New(addr);
-
- BlockingMutexLock l(&dbghelp_mu_);
- InitializeIfNeeded();
-
- // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
- char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
- PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
- symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
- symbol->MaxNameLen = MAX_SYM_NAME;
- DWORD64 offset = 0;
- BOOL got_objname = SymFromAddr(GetCurrentProcess(),
- (DWORD64)addr, &offset, symbol);
- if (!got_objname)
- return frame;
-
- DWORD unused;
- IMAGEHLP_LINE64 line_info;
- line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
- BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
- &unused, &line_info);
- frame->info.function = internal_strdup(symbol->Name);
- frame->info.function_offset = (uptr)offset;
- if (got_fileline) {
- frame->info.file = internal_strdup(line_info.FileName);
- frame->info.line = line_info.LineNumber;
- }
-
- IMAGEHLP_MODULE64 mod_info;
- internal_memset(&mod_info, 0, sizeof(mod_info));
- mod_info.SizeOfStruct = sizeof(mod_info);
- if (SymGetModuleInfo64(GetCurrentProcess(), addr, &mod_info))
- frame->info.FillAddressAndModuleInfo(addr, mod_info.ImageName,
- addr - (uptr)mod_info.BaseOfImage);
- return frame;
- }
+namespace {
- bool CanReturnFileLineInfo() override {
- return true;
- }
+bool is_dbghelp_initialized = false;
- const char *Demangle(const char *name) override {
- CHECK(initialized_);
- static char demangle_buffer[1000];
- if (name[0] == '\01' &&
- UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
- UNDNAME_NAME_ONLY))
- return demangle_buffer;
- else
- return name;
- }
+bool TrySymInitialize() {
+ SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
+ return SymInitialize(GetCurrentProcess(), 0, TRUE);
+ // FIXME: We don't call SymCleanup() on exit yet - should we?
+}
- // FIXME: Implement GetModuleNameAndOffsetForPC().
-
- private:
- void InitializeIfNeeded() {
- if (initialized_)
- return;
- if (!TrySymInitialize()) {
- // OK, maybe the client app has called SymInitialize already.
- // That's a bit unfortunate for us as all the DbgHelp functions are
- // single-threaded and we can't coordinate with the app.
- // FIXME: Can we stop the other threads at this point?
- // Anyways, we have to reconfigure stuff to make sure that SymInitialize
- // has all the appropriate options set.
- // Cross our fingers and reinitialize DbgHelp.
- Report("*** WARNING: Failed to initialize DbgHelp! ***\n");
- Report("*** Most likely this means that the app is already ***\n");
- Report("*** using DbgHelp, possibly with incompatible flags. ***\n");
- Report("*** Due to technical reasons, symbolization might crash ***\n");
- Report("*** or produce wrong results. ***\n");
- SymCleanup(GetCurrentProcess());
- TrySymInitialize();
- }
- initialized_ = true;
-
- // When an executable is run from a location different from the one where it
- // was originally built, we may not see the nearby PDB files.
- // To work around this, let's append the directory of the main module
- // to the symbol search path. All the failures below are not fatal.
- const size_t kSymPathSize = 2048;
- static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH];
- if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) {
- Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
- return;
- }
- size_t sz = wcslen(path_buffer);
- if (sz) {
- CHECK_EQ(0, wcscat_s(path_buffer, L";"));
- sz++;
- }
- DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH);
- if (res == 0 || res == MAX_PATH) {
- Report("*** WARNING: Failed to getting the EXE directory ***\n");
- return;
- }
- // Write the zero character in place of the last backslash to get the
- // directory of the main module at the end of path_buffer.
- wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\');
- CHECK_NE(last_bslash, 0);
- *last_bslash = L'\0';
- if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) {
- Report("*** WARNING: Failed to SymSetSearchPathW\n");
- return;
- }
+// Initializes DbgHelp library, if it's not yet initialized. Calls to this
+// function should be synchronized with respect to other calls to DbgHelp API
+// (e.g. from WinSymbolizerTool).
+void InitializeDbgHelpIfNeeded() {
+ if (is_dbghelp_initialized)
+ return;
+ if (!TrySymInitialize()) {
+ // OK, maybe the client app has called SymInitialize already.
+ // That's a bit unfortunate for us as all the DbgHelp functions are
+ // single-threaded and we can't coordinate with the app.
+ // FIXME: Can we stop the other threads at this point?
+ // Anyways, we have to reconfigure stuff to make sure that SymInitialize
+ // has all the appropriate options set.
+ // Cross our fingers and reinitialize DbgHelp.
+ Report("*** WARNING: Failed to initialize DbgHelp! ***\n");
+ Report("*** Most likely this means that the app is already ***\n");
+ Report("*** using DbgHelp, possibly with incompatible flags. ***\n");
+ Report("*** Due to technical reasons, symbolization might crash ***\n");
+ Report("*** or produce wrong results. ***\n");
+ SymCleanup(GetCurrentProcess());
+ TrySymInitialize();
+ }
+ is_dbghelp_initialized = true;
+
+ // When an executable is run from a location different from the one where it
+ // was originally built, we may not see the nearby PDB files.
+ // To work around this, let's append the directory of the main module
+ // to the symbol search path. All the failures below are not fatal.
+ const size_t kSymPathSize = 2048;
+ static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH];
+ if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) {
+ Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
+ return;
+ }
+ size_t sz = wcslen(path_buffer);
+ if (sz) {
+ CHECK_EQ(0, wcscat_s(path_buffer, L";"));
+ sz++;
+ }
+ DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH);
+ if (res == 0 || res == MAX_PATH) {
+ Report("*** WARNING: Failed to getting the EXE directory ***\n");
+ return;
+ }
+ // Write the zero character in place of the last backslash to get the
+ // directory of the main module at the end of path_buffer.
+ wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\');
+ CHECK_NE(last_bslash, 0);
+ *last_bslash = L'\0';
+ if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) {
+ Report("*** WARNING: Failed to SymSetSearchPathW\n");
+ return;
}
+}
- bool TrySymInitialize() {
- SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
- return SymInitialize(GetCurrentProcess(), 0, TRUE);
- // FIXME: We don't call SymCleanup() on exit yet - should we?
+} // namespace
+
+bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) {
+ InitializeDbgHelpIfNeeded();
+
+ // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
+ char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
+ PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ symbol->MaxNameLen = MAX_SYM_NAME;
+ DWORD64 offset = 0;
+ BOOL got_objname = SymFromAddr(GetCurrentProcess(),
+ (DWORD64)addr, &offset, symbol);
+ if (!got_objname)
+ return false;
+
+ DWORD unused;
+ IMAGEHLP_LINE64 line_info;
+ line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
+ &unused, &line_info);
+ frame->info.function = internal_strdup(symbol->Name);
+ frame->info.function_offset = (uptr)offset;
+ if (got_fileline) {
+ frame->info.file = internal_strdup(line_info.FileName);
+ frame->info.line = line_info.LineNumber;
}
+ return true;
+}
+
+const char *WinSymbolizerTool::Demangle(const char *name) {
+ CHECK(is_dbghelp_initialized);
+ static char demangle_buffer[1000];
+ if (name[0] == '\01' &&
+ UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
+ UNDNAME_NAME_ONLY))
+ return demangle_buffer;
+ else
+ return name;
+}
- // All DbgHelp functions are single threaded, so we should use a mutex to
- // serialize accesses.
- BlockingMutex dbghelp_mu_;
- bool initialized_;
-};
+const char *Symbolizer::PlatformDemangle(const char *name) {
+ return name;
+}
+
+void Symbolizer::PlatformPrepareForSandboxing() {
+ // Do nothing.
+}
Symbolizer *Symbolizer::PlatformInit() {
- static bool called_once = false;
- CHECK(!called_once && "Shouldn't create more than one symbolizer");
- called_once = true;
- return new(symbolizer_allocator_) WinSymbolizer();
+ IntrusiveList<SymbolizerTool> list;
+ list.clear();
+ list.push_back(new(symbolizer_allocator_) WinSymbolizerTool());
+ return new(symbolizer_allocator_) Symbolizer(list);
}
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_win.h b/lib/sanitizer_common/sanitizer_symbolizer_win.h
new file mode 100644
index 000000000000..72ac5e5ee104
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_symbolizer_win.h
@@ -0,0 +1,31 @@
+//===-- sanitizer_symbolizer_win.h ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Header file for the Windows symbolizer tool.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_SYMBOLIZER_WIN_H
+#define SANITIZER_SYMBOLIZER_WIN_H
+
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+class WinSymbolizerTool : public SymbolizerTool {
+ public:
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+ bool SymbolizeData(uptr addr, DataInfo *info) override {
+ return false;
+ }
+ const char *Demangle(const char *name) override;
+};
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_SYMBOLIZER_WIN_H
diff --git a/lib/sanitizer_common/sanitizer_tls_get_addr.cc b/lib/sanitizer_common/sanitizer_tls_get_addr.cc
index 6142ce517db5..ea037159d00b 100644
--- a/lib/sanitizer_common/sanitizer_tls_get_addr.cc
+++ b/lib/sanitizer_common/sanitizer_tls_get_addr.cc
@@ -78,7 +78,8 @@ void DTLS_Destroy() {
DTLS_Deallocate(dtls.dtv, s);
}
-DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res) {
+DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
+ uptr static_tls_begin, uptr static_tls_end) {
if (!common_flags()->intercept_tls_get_addr) return 0;
TlsGetAddrParam *arg = reinterpret_cast<TlsGetAddrParam *>(arg_void);
uptr dso_id = arg->dso_id;
@@ -95,6 +96,11 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res) {
tls_size = dtls.last_memalign_size;
VPrintf(2, "__tls_get_addr: glibc <=2.18 suspected; tls={%p,%p}\n",
tls_beg, tls_size);
+ } else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) {
+ // This is the static TLS block which was initialized / unpoisoned at thread
+ // creation.
+ VPrintf(2, "__tls_get_addr: static tls: %p\n", tls_beg);
+ tls_size = 0;
} else if ((tls_beg % 4096) == sizeof(Glibc_2_19_tls_header)) {
// We may want to check gnu_get_libc_version().
Glibc_2_19_tls_header *header = (Glibc_2_19_tls_header *)tls_beg - 1;
diff --git a/lib/sanitizer_common/sanitizer_tls_get_addr.h b/lib/sanitizer_common/sanitizer_tls_get_addr.h
index 0fc9a2257c68..58d47634d382 100644
--- a/lib/sanitizer_common/sanitizer_tls_get_addr.h
+++ b/lib/sanitizer_common/sanitizer_tls_get_addr.h
@@ -50,7 +50,8 @@ struct DTLS {
// Returns pointer and size of a linker-allocated TLS block.
// Each block is returned exactly once.
-DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res);
+DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res, uptr static_tls_begin,
+ uptr static_tls_end);
void DTLS_on_libc_memalign(void *ptr, uptr size);
DTLS *DTLS_Get();
void DTLS_Destroy(); // Make sure to call this before the thread is destroyed.
diff --git a/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cc
index 7ab2efbd75bd..1082ccfd6a88 100644
--- a/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cc
@@ -1,4 +1,4 @@
-//===-- sanitizer_unwind_posix.cc ----------------------------------------===//
+//===-- sanitizer_unwind_linux_libcdep.cc ---------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -8,11 +8,11 @@
//===----------------------------------------------------------------------===//
//
// This file contains the unwind.h-based (aka "slow") stack unwinding routines
-// available to the tools on Linux, Android, FreeBSD and OS X.
+// available to the tools on Linux, Android, and FreeBSD.
//===----------------------------------------------------------------------===//
#include "sanitizer_platform.h"
-#if SANITIZER_POSIX
+#if SANITIZER_FREEBSD || SANITIZER_LINUX
#include "sanitizer_common.h"
#include "sanitizer_stacktrace.h"
@@ -82,7 +82,7 @@ void SanitizerInitializeUnwinder() {
#endif
uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
-#ifdef __arm__
+#if defined(__arm__) && !SANITIZER_MAC
uptr val;
_Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
15 /* r15 = PC */, _UVRSD_UINT32, &val);
@@ -155,4 +155,4 @@ void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
} // namespace __sanitizer
-#endif // SANITIZER_POSIX
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc
index 335cecabe118..d5bbe4565068 100644
--- a/lib/sanitizer_common/sanitizer_win.cc
+++ b/lib/sanitizer_common/sanitizer_win.cc
@@ -35,7 +35,9 @@ namespace __sanitizer {
// --------------------- sanitizer_common.h
uptr GetPageSize() {
- return 1U << 14; // FIXME: is this configurable?
+ // FIXME: there is an API for getting the system page size (GetSystemInfo or
+ // GetNativeSystemInfo), but if we use it here we get test failures elsewhere.
+ return 1U << 14;
}
uptr GetMmapGranularity() {
@@ -93,6 +95,9 @@ void *MmapOrDie(uptr size, const char *mem_type) {
}
void UnmapOrDie(void *addr, uptr size) {
+ if (!size || !addr)
+ return;
+
if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) {
Report("ERROR: %s failed to "
"deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n",
@@ -101,9 +106,10 @@ void UnmapOrDie(void *addr, uptr size) {
}
}
-void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
+void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
// FIXME: is this really "NoReserve"? On Win32 this does not matter much,
// but on Win64 it does.
+ (void)name; // unsupported
void *p = VirtualAlloc((LPVOID)fixed_addr, size,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (p == 0)
@@ -122,7 +128,8 @@ void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
return MmapOrDie(size, mem_type);
}
-void *Mprotect(uptr fixed_addr, uptr size) {
+void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) {
+ (void)name; // unsupported
void *res = VirtualAlloc((LPVOID)fixed_addr, size,
MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
if (res == 0)
@@ -132,6 +139,12 @@ void *Mprotect(uptr fixed_addr, uptr size) {
return res;
}
+bool MprotectNoAccess(uptr addr, uptr size) {
+ DWORD old_protection;
+ return VirtualProtect((LPVOID)addr, size, PAGE_NOACCESS, &old_protection);
+}
+
+
void FlushUnneededShadowMemory(uptr addr, uptr size) {
// This is almost useless on 32-bits.
// FIXME: add madvise-analog when we move to 64-bits.
@@ -157,7 +170,7 @@ void *MapFileToMemory(const char *file_name, uptr *buff_size) {
UNIMPLEMENTED();
}
-void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset) {
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) {
UNIMPLEMENTED();
}
@@ -206,76 +219,46 @@ u32 GetUid() {
namespace {
struct ModuleInfo {
- HMODULE handle;
+ const char *filepath;
uptr base_address;
uptr end_address;
};
int CompareModulesBase(const void *pl, const void *pr) {
- const ModuleInfo &l = *(ModuleInfo *)pl, &r = *(ModuleInfo *)pr;
- if (l.base_address < r.base_address)
+ const ModuleInfo *l = (ModuleInfo *)pl, *r = (ModuleInfo *)pr;
+ if (l->base_address < r->base_address)
return -1;
- return l.base_address > r.base_address;
+ return l->base_address > r->base_address;
}
} // namespace
#ifndef SANITIZER_GO
void DumpProcessMap() {
Report("Dumping process modules:\n");
- HANDLE cur_process = GetCurrentProcess();
+ InternalScopedBuffer<LoadedModule> modules(kMaxNumberOfModules);
+ uptr num_modules =
+ GetListOfModules(modules.data(), kMaxNumberOfModules, nullptr);
- // Query the list of modules. Start by assuming there are no more than 256
- // modules and retry if that's not sufficient.
- ModuleInfo *modules;
- size_t num_modules;
- {
- HMODULE *hmodules = 0;
- uptr modules_buffer_size = sizeof(HMODULE) * 256;
- DWORD bytes_required;
- while (!hmodules) {
- hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__);
- CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size,
- &bytes_required));
- if (bytes_required > modules_buffer_size) {
- // Either there turned out to be more than 256 hmodules, or new hmodules
- // could have loaded since the last try. Retry.
- UnmapOrDie(hmodules, modules_buffer_size);
- hmodules = 0;
- modules_buffer_size = bytes_required;
- }
- }
-
- num_modules = bytes_required / sizeof(HMODULE);
- modules =
- (ModuleInfo *)MmapOrDie(num_modules * sizeof(ModuleInfo), __FUNCTION__);
- for (size_t i = 0; i < num_modules; ++i) {
- modules[i].handle = hmodules[i];
- MODULEINFO mi;
- if (!GetModuleInformation(cur_process, hmodules[i], &mi, sizeof(mi)))
- continue;
- modules[i].base_address = (uptr)mi.lpBaseOfDll;
- modules[i].end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage;
- }
- UnmapOrDie(hmodules, modules_buffer_size);
+ InternalScopedBuffer<ModuleInfo> module_infos(num_modules);
+ for (size_t i = 0; i < num_modules; ++i) {
+ module_infos[i].filepath = modules[i].full_name();
+ module_infos[i].base_address = modules[i].base_address();
+ module_infos[i].end_address = modules[i].ranges().next()->end;
}
-
- qsort(modules, num_modules, sizeof(ModuleInfo), CompareModulesBase);
+ qsort(module_infos.data(), num_modules, sizeof(ModuleInfo),
+ CompareModulesBase);
for (size_t i = 0; i < num_modules; ++i) {
- const ModuleInfo &mi = modules[i];
- char module_name[MAX_PATH];
- bool got_module_name = GetModuleFileNameA(
- mi.handle, module_name, sizeof(module_name));
+ const ModuleInfo &mi = module_infos[i];
if (mi.end_address != 0) {
Printf("\t%p-%p %s\n", mi.base_address, mi.end_address,
- got_module_name ? module_name : "[no name]");
- } else if (got_module_name) {
- Printf("\t??\?-??? %s\n", module_name);
+ mi.filepath[0] ? mi.filepath : "[no name]");
+ } else if (mi.filepath[0]) {
+ Printf("\t??\?-??? %s\n", mi.filepath);
} else {
Printf("\t???\n");
}
}
- UnmapOrDie(modules, num_modules * sizeof(ModuleInfo));
}
#endif
@@ -288,8 +271,9 @@ void ReExec() {
}
void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
- (void)args;
- // Nothing here for now.
+#if !SANITIZER_GO
+ CovPrepareForSandboxing(args);
+#endif
}
bool StackSizeIsUnlimited() {
@@ -313,6 +297,14 @@ char *FindPathToBinary(const char *name) {
return 0;
}
+bool IsPathSeparator(const char c) {
+ return c == '\\' || c == '/';
+}
+
+bool IsAbsolutePath(const char *path) {
+ UNIMPLEMENTED();
+}
+
void SleepForSeconds(int seconds) {
Sleep(seconds * 1000);
}
@@ -333,117 +325,132 @@ void Abort() {
uptr GetListOfModules(LoadedModule *modules, uptr max_modules,
string_predicate_t filter) {
- UNIMPLEMENTED();
-};
-
-#ifndef SANITIZER_GO
-int Atexit(void (*function)(void)) {
- return atexit(function);
-}
-#endif
+ HANDLE cur_process = GetCurrentProcess();
-// ------------------ sanitizer_libc.h
-uptr internal_mmap(void *addr, uptr length, int prot, int flags,
- int fd, u64 offset) {
- UNIMPLEMENTED();
-}
+ // Query the list of modules. Start by assuming there are no more than 256
+ // modules and retry if that's not sufficient.
+ HMODULE *hmodules = 0;
+ uptr modules_buffer_size = sizeof(HMODULE) * 256;
+ DWORD bytes_required;
+ while (!hmodules) {
+ hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__);
+ CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size,
+ &bytes_required));
+ if (bytes_required > modules_buffer_size) {
+ // Either there turned out to be more than 256 hmodules, or new hmodules
+ // could have loaded since the last try. Retry.
+ UnmapOrDie(hmodules, modules_buffer_size);
+ hmodules = 0;
+ modules_buffer_size = bytes_required;
+ }
+ }
-uptr internal_munmap(void *addr, uptr length) {
- UNIMPLEMENTED();
-}
+ // |num_modules| is the number of modules actually present,
+ // |count| is the number of modules we return.
+ size_t nun_modules = bytes_required / sizeof(HMODULE),
+ count = 0;
+ for (size_t i = 0; i < nun_modules && count < max_modules; ++i) {
+ HMODULE handle = hmodules[i];
+ MODULEINFO mi;
+ if (!GetModuleInformation(cur_process, handle, &mi, sizeof(mi)))
+ continue;
-uptr internal_close(fd_t fd) {
- UNIMPLEMENTED();
-}
+ char module_name[MAX_PATH];
+ bool got_module_name =
+ GetModuleFileNameA(handle, module_name, sizeof(module_name));
+ if (!got_module_name)
+ module_name[0] = '\0';
+
+ if (filter && !filter(module_name))
+ continue;
+
+ uptr base_address = (uptr)mi.lpBaseOfDll;
+ uptr end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage;
+ LoadedModule *cur_module = &modules[count];
+ cur_module->set(module_name, base_address);
+ // We add the whole module as one single address range.
+ cur_module->addAddressRange(base_address, end_address, /*executable*/ true);
+ count++;
+ }
+ UnmapOrDie(hmodules, modules_buffer_size);
-int internal_isatty(fd_t fd) {
- return _isatty(fd);
-}
+ return count;
+};
-uptr internal_open(const char *filename, int flags) {
- UNIMPLEMENTED();
-}
+#ifndef SANITIZER_GO
+// We can't use atexit() directly at __asan_init time as the CRT is not fully
+// initialized at this point. Place the functions into a vector and use
+// atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers).
+InternalMmapVectorNoCtor<void (*)(void)> atexit_functions;
-uptr internal_open(const char *filename, int flags, u32 mode) {
- UNIMPLEMENTED();
+int Atexit(void (*function)(void)) {
+ atexit_functions.push_back(function);
+ return 0;
}
-uptr OpenFile(const char *filename, bool write) {
- UNIMPLEMENTED();
+static int RunAtexit() {
+ int ret = 0;
+ for (uptr i = 0; i < atexit_functions.size(); ++i) {
+ ret |= atexit(atexit_functions[i]);
+ }
+ return ret;
}
-uptr internal_read(fd_t fd, void *buf, uptr count) {
- UNIMPLEMENTED();
-}
+#pragma section(".CRT$XID", long, read) // NOLINT
+static __declspec(allocate(".CRT$XID")) int (*__run_atexit)() = RunAtexit;
+#endif
-uptr internal_write(fd_t fd, const void *buf, uptr count) {
- if (fd != kStderrFd)
+// ------------------ sanitizer_libc.h
+fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *last_error) {
+ if (mode != WrOnly)
UNIMPLEMENTED();
-
- static HANDLE output_stream = 0;
- // Abort immediately if we know printing is not possible.
- if (output_stream == INVALID_HANDLE_VALUE)
- return 0;
-
- // If called for the first time, try to use stderr to output stuff,
- // falling back to stdout if anything goes wrong.
- bool fallback_to_stdout = false;
- if (output_stream == 0) {
- output_stream = GetStdHandle(STD_ERROR_HANDLE);
- // We don't distinguish "no such handle" from error.
- if (output_stream == 0)
- output_stream = INVALID_HANDLE_VALUE;
-
- if (output_stream == INVALID_HANDLE_VALUE) {
- // Retry with stdout?
- output_stream = GetStdHandle(STD_OUTPUT_HANDLE);
- if (output_stream == 0)
- output_stream = INVALID_HANDLE_VALUE;
- if (output_stream == INVALID_HANDLE_VALUE)
- return 0;
- } else {
- // Successfully got an stderr handle. However, if WriteFile() fails,
- // we can still try to fallback to stdout.
- fallback_to_stdout = true;
- }
- }
-
- DWORD ret;
- if (WriteFile(output_stream, buf, count, &ret, 0))
- return ret;
-
- // Re-try with stdout if using a valid stderr handle fails.
- if (fallback_to_stdout) {
- output_stream = GetStdHandle(STD_OUTPUT_HANDLE);
- if (output_stream == 0)
- output_stream = INVALID_HANDLE_VALUE;
- if (output_stream != INVALID_HANDLE_VALUE)
- return internal_write(fd, buf, count);
- }
- return 0;
+ fd_t res = CreateFile(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ CHECK(res != kStdoutFd || kStdoutFd == kInvalidFd);
+ CHECK(res != kStderrFd || kStderrFd == kInvalidFd);
+ if (res == kInvalidFd && last_error)
+ *last_error = GetLastError();
+ return res;
}
-uptr internal_stat(const char *path, void *buf) {
- UNIMPLEMENTED();
+void CloseFile(fd_t fd) {
+ CloseHandle(fd);
}
-uptr internal_lstat(const char *path, void *buf) {
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
+ error_t *error_p) {
UNIMPLEMENTED();
}
-uptr internal_fstat(fd_t fd, void *buf) {
- UNIMPLEMENTED();
+bool SupportsColoredOutput(fd_t fd) {
+ // FIXME: support colored output.
+ return false;
}
-uptr internal_filesize(fd_t fd) {
- UNIMPLEMENTED();
-}
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
+ error_t *error_p) {
+ CHECK(fd != kInvalidFd);
-uptr internal_dup2(int oldfd, int newfd) {
- UNIMPLEMENTED();
+ if (fd == kStdoutFd) {
+ fd = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (fd == 0) fd = kInvalidFd;
+ } else if (fd == kStderrFd) {
+ fd = GetStdHandle(STD_ERROR_HANDLE);
+ if (fd == 0) fd = kInvalidFd;
+ }
+
+ DWORD internal_bytes_written;
+ if (fd == kInvalidFd ||
+ WriteFile(fd, buff, buff_size, &internal_bytes_written, 0)) {
+ if (error_p) *error_p = GetLastError();
+ return false;
+ } else {
+ if (bytes_written) *bytes_written = internal_bytes_written;
+ return true;
+ }
}
-uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
+bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) {
UNIMPLEMENTED();
}
@@ -460,10 +467,6 @@ uptr internal_ftruncate(fd_t fd, uptr size) {
UNIMPLEMENTED();
}
-uptr internal_rename(const char *oldpath, const char *newpath) {
- UNIMPLEMENTED();
-}
-
uptr GetRSS() {
return 0;
}
@@ -587,7 +590,7 @@ void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
void ReportFile::Write(const char *buffer, uptr length) {
SpinMutexLock l(mu);
ReopenIfNecessary();
- if (length != internal_write(fd, buffer, length)) {
+ if (!WriteToFile(fd, buffer, length)) {
// stderr may be closed, but we may be able to print to the debugger
// instead. This is the case when launching a program from Visual Studio,
// and the following routine should write to its console.
@@ -614,10 +617,54 @@ bool IsDeadlySignal(int signum) {
}
bool IsAccessibleMemoryRange(uptr beg, uptr size) {
- // FIXME: Actually implement this function.
+ SYSTEM_INFO si;
+ GetNativeSystemInfo(&si);
+ uptr page_size = si.dwPageSize;
+ uptr page_mask = ~(page_size - 1);
+
+ for (uptr page = beg & page_mask, end = (beg + size - 1) & page_mask;
+ page <= end;) {
+ MEMORY_BASIC_INFORMATION info;
+ if (VirtualQuery((LPCVOID)page, &info, sizeof(info)) != sizeof(info))
+ return false;
+
+ if (info.Protect == 0 || info.Protect == PAGE_NOACCESS ||
+ info.Protect == PAGE_EXECUTE)
+ return false;
+
+ if (info.RegionSize == 0)
+ return false;
+
+ page += info.RegionSize;
+ }
+
return true;
}
+SignalContext SignalContext::Create(void *siginfo, void *context) {
+ EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD*)siginfo;
+ CONTEXT *context_record = (CONTEXT*)context;
+
+ uptr pc = (uptr)exception_record->ExceptionAddress;
+#ifdef _WIN64
+ uptr bp = (uptr)context_record->Rbp;
+ uptr sp = (uptr)context_record->Rsp;
+#else
+ uptr bp = (uptr)context_record->Ebp;
+ uptr sp = (uptr)context_record->Esp;
+#endif
+ uptr access_addr = exception_record->ExceptionInformation[1];
+
+ return SignalContext(context, access_addr, pc, sp, bp);
+}
+
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+ // FIXME: Actually implement this function.
+ CHECK_GT(buf_len, 0);
+ buf[0] = 0;
+ return 0;
+}
+
} // namespace __sanitizer
#endif // _WIN32
diff --git a/lib/sanitizer_common/scripts/check_lint.sh b/lib/sanitizer_common/scripts/check_lint.sh
index 7ed05d73563a..9108a81e26c8 100755
--- a/lib/sanitizer_common/scripts/check_lint.sh
+++ b/lib/sanitizer_common/scripts/check_lint.sh
@@ -17,7 +17,6 @@ fi
# Filters
# TODO: remove some of these filters
-LLVM_LINT_FILTER=-,+whitespace
COMMON_LINT_FILTER=-build/include,-build/header_guard,-legal/copyright,-whitespace/comments,-readability/casting,\
-build/namespaces
ASAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int
@@ -60,9 +59,6 @@ run_lint() {
${LITLINT} "$@" 2>>$ERROR_LOG
}
-run_lint ${LLVM_LINT_FILTER} --filter=${LLVM_LINT_FILTER} \
- lib/Transforms/Instrumentation/*Sanitizer.cpp &
-
if [ "${COMPILER_RT}" = "" ]; then
COMPILER_RT=projects/compiler-rt
fi
diff --git a/lib/sanitizer_common/scripts/cpplint.py b/lib/sanitizer_common/scripts/cpplint.py
index 742459af172f..d45c47f7ed0c 100755
--- a/lib/sanitizer_common/scripts/cpplint.py
+++ b/lib/sanitizer_common/scripts/cpplint.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
#
# Copyright (c) 2009 Google Inc. All rights reserved.
#
diff --git a/lib/sanitizer_common/scripts/gen_dynamic_list.py b/lib/sanitizer_common/scripts/gen_dynamic_list.py
index 7bab230650cb..f055bb44ba21 100755
--- a/lib/sanitizer_common/scripts/gen_dynamic_list.py
+++ b/lib/sanitizer_common/scripts/gen_dynamic_list.py
@@ -14,15 +14,24 @@
# gen_dynamic_list.py libclang_rt.*san*.a [ files ... ]
#
#===------------------------------------------------------------------------===#
+import argparse
import os
import re
import subprocess
import sys
-new_delete = set(['_ZdaPv', '_ZdaPvRKSt9nothrow_t',
- '_ZdlPv', '_ZdlPvRKSt9nothrow_t',
- '_Znam', '_ZnamRKSt9nothrow_t',
- '_Znwm', '_ZnwmRKSt9nothrow_t'])
+new_delete = set([
+ '_Znam', '_ZnamRKSt9nothrow_t', # operator new[](unsigned long)
+ '_Znwm', '_ZnwmRKSt9nothrow_t', # operator new(unsigned long)
+ '_Znaj', '_ZnajRKSt9nothrow_t', # operator new[](unsigned int)
+ '_Znwj', '_ZnwjRKSt9nothrow_t', # operator new(unsigned int)
+ '_ZdaPv', '_ZdaPvRKSt9nothrow_t', # operator delete[](void *)
+ '_ZdlPv', '_ZdlPvRKSt9nothrow_t', # operator delete(void *)
+ '_ZdaPvm', # operator delete[](void*, unsigned long)
+ '_ZdlPvm', # operator delete(void*, unsigned long)
+ '_ZdaPvj', # operator delete[](void*, unsigned int)
+ '_ZdlPvj', # operator delete(void*, unsigned int)
+ ])
versioned_functions = set(['memcpy', 'pthread_attr_getaffinity_np',
'pthread_cond_broadcast',
@@ -49,10 +58,17 @@ def get_global_functions(library):
return functions
def main(argv):
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--version-list', action='store_true')
+ parser.add_argument('--extra', default=[], action='append')
+ parser.add_argument('libraries', default=[], nargs='+')
+ args = parser.parse_args()
+
result = []
- library = argv[1]
- all_functions = get_global_functions(library)
+ all_functions = []
+ for library in args.libraries:
+ all_functions.extend(get_global_functions(library))
function_set = set(all_functions)
for func in all_functions:
# Export new/delete operators.
@@ -66,7 +82,7 @@ def main(argv):
# We have to avoid exporting the interceptors for versioned library
# functions due to gold internal error.
orig_name = match.group(1)
- if orig_name in function_set and orig_name not in versioned_functions:
+ if orig_name in function_set and (args.version_list or orig_name not in versioned_functions):
result.append(orig_name)
continue
# Export sanitizer interface functions.
@@ -74,15 +90,20 @@ def main(argv):
result.append(func)
# Additional exported functions from files.
- for fname in argv[2:]:
+ for fname in args.extra:
f = open(fname, 'r')
for line in f:
result.append(line.rstrip())
# Print the resulting list in the format recognized by ld.
print('{')
+ if args.version_list:
+ print('global:')
result.sort()
for f in result:
- print(' ' + f + ';')
+ print(' ' + f.encode('utf-8') + ';')
+ if args.version_list:
+ print('local:')
+ print(' *;')
print('};')
if __name__ == '__main__':
diff --git a/lib/sanitizer_common/scripts/litlint.py b/lib/sanitizer_common/scripts/litlint.py
index 1e78448b63d4..81b89c214438 100755
--- a/lib/sanitizer_common/scripts/litlint.py
+++ b/lib/sanitizer_common/scripts/litlint.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/env python
#
# litlint
#
diff --git a/lib/sanitizer_common/scripts/sancov.py b/lib/sanitizer_common/scripts/sancov.py
index 566116eb2334..a5ae9574a26a 100755
--- a/lib/sanitizer_common/scripts/sancov.py
+++ b/lib/sanitizer_common/scripts/sancov.py
@@ -4,40 +4,85 @@
# We need to merge these integers into a set and then
# either print them (as hex) or dump them into another file.
import array
-import struct
-import sys
import bisect
+import glob
import os.path
+import struct
+import subprocess
+import sys
-prog_name = "";
+prog_name = ""
def Usage():
print >> sys.stderr, "Usage: \n" + \
- " " + prog_name + " merge file1 [file2 ...] > output\n" \
- " " + prog_name + " print file1 [file2 ...]\n" \
- " " + prog_name + " unpack file1 [file2 ...]\n" \
- " " + prog_name + " rawunpack file1 [file2 ...]\n"
+ " " + prog_name + " merge FILE [FILE...] > OUTPUT\n" \
+ " " + prog_name + " print FILE [FILE...]\n" \
+ " " + prog_name + " unpack FILE [FILE...]\n" \
+ " " + prog_name + " rawunpack FILE [FILE ...]\n" \
+ " " + prog_name + " missing BINARY < LIST_OF_PCS\n"
exit(1)
+def CheckBits(bits):
+ if bits != 32 and bits != 64:
+ raise Exception("Wrong bitness: %d" % bits)
+
+def TypeCodeForBits(bits):
+ CheckBits(bits)
+ return 'L' if bits == 64 else 'I'
+
+kMagic32SecondHalf = 0xFFFFFF32;
+kMagic64SecondHalf = 0xFFFFFF64;
+kMagicFirstHalf = 0xC0BFFFFF;
+
+def MagicForBits(bits):
+ CheckBits(bits)
+ if sys.byteorder == 'little':
+ return [kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf, kMagicFirstHalf]
+ else:
+ return [kMagicFirstHalf, kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf]
+
+def ReadMagicAndReturnBitness(f, path):
+ magic_bytes = f.read(8)
+ magic_words = struct.unpack('II', magic_bytes);
+ bits = 0
+ idx = 1 if sys.byteorder == 'little' else 0
+ if magic_words[idx] == kMagicFirstHalf:
+ if magic_words[1-idx] == kMagic64SecondHalf:
+ bits = 64
+ elif magic_words[1-idx] == kMagic32SecondHalf:
+ bits = 32
+ if bits == 0:
+ raise Exception('Bad magic word in %s' % path)
+ return bits
+
def ReadOneFile(path):
with open(path, mode="rb") as f:
f.seek(0, 2)
size = f.tell()
f.seek(0, 0)
- s = set(array.array('I', f.read(size)))
- print >>sys.stderr, "%s: read %d PCs from %s" % (prog_name, size / 4, path)
+ if size < 8:
+ raise Exception('File %s is short (< 8 bytes)' % path)
+ bits = ReadMagicAndReturnBitness(f, path)
+ size -= 8
+ s = array.array(TypeCodeForBits(bits), f.read(size))
+ print >>sys.stderr, "%s: read %d %d-bit PCs from %s" % (prog_name, size * 8 / bits, bits, path)
return s
def Merge(files):
s = set()
for f in files:
- s = s.union(ReadOneFile(f))
+ s = s.union(set(ReadOneFile(f)))
print >> sys.stderr, "%s: %d files merged; %d PCs total" % \
(prog_name, len(files), len(s))
return sorted(s)
def PrintFiles(files):
- s = Merge(files)
+ if len(files) > 1:
+ s = Merge(files)
+ else: # If there is just on file, print the PCs in order.
+ s = ReadOneFile(files[0])
+ print >> sys.stderr, "%s: 1 file merged; %d PCs total" % \
+ (prog_name, len(s))
for i in s:
print "0x%x" % i
@@ -45,7 +90,11 @@ def MergeAndPrint(files):
if sys.stdout.isatty():
Usage()
s = Merge(files)
- a = array.array('I', s)
+ bits = 32
+ if max(s) > 0xFFFFFFFF:
+ bits = 64
+ array.array('I', MagicForBits(bits)).tofile(sys.stdout)
+ a = array.array(TypeCodeForBits(bits), s)
a.tofile(sys.stdout)
@@ -82,6 +131,8 @@ def UnpackOneRawFile(path, map_path):
with open(map_path, mode="rt") as f_map:
print >> sys.stderr, "%s: reading map %s" % (prog_name, map_path)
bits = int(f_map.readline())
+ if bits != 32 and bits != 64:
+ raise Exception('Wrong bits size in the map')
for line in f_map:
parts = line.rstrip().split()
mem_map.append((int(parts[0], 16),
@@ -97,11 +148,7 @@ def UnpackOneRawFile(path, map_path):
f.seek(0, 2)
size = f.tell()
f.seek(0, 0)
- if bits == 64:
- typecode = 'L'
- else:
- typecode = 'I'
- pcs = array.array(typecode, f.read(size))
+ pcs = array.array(TypeCodeForBits(bits), f.read(size))
mem_map_pcs = [[] for i in range(0, len(mem_map))]
for pc in pcs:
@@ -119,9 +166,10 @@ def UnpackOneRawFile(path, map_path):
assert path.endswith('.sancov.raw')
dst_path = module_path + '.' + os.path.basename(path)[:-4]
print >> sys.stderr, "%s: writing %d PCs to %s" % (prog_name, len(pc_list), dst_path)
- arr = array.array('I')
+ arr = array.array(TypeCodeForBits(bits))
arr.fromlist(sorted(pc_list))
with open(dst_path, 'ab') as f2:
+ array.array('I', MagicForBits(bits)).tofile(f2)
arr.tofile(f2)
def RawUnpack(files):
@@ -131,17 +179,63 @@ def RawUnpack(files):
f_map = f[:-3] + 'map'
UnpackOneRawFile(f, f_map)
+def GetInstrumentedPCs(binary):
+ # This looks scary, but all it does is extract all offsets where we call:
+ # - __sanitizer_cov() or __sanitizer_cov_with_check(),
+ # - with call or callq,
+ # - directly or via PLT.
+ cmd = "objdump -d %s | " \
+ "grep '^\s\+[0-9a-f]\+:.*\scall\(q\|\)\s\+[0-9a-f]\+ <__sanitizer_cov\(_with_check\|\)\(@plt\|\)>' | " \
+ "grep '^\s\+[0-9a-f]\+' -o" % binary
+ proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+ shell=True)
+ proc.stdin.close()
+ # The PCs we get from objdump are off by 4 bytes, as they point to the
+ # beginning of the callq instruction. Empirically this is true on x86 and
+ # x86_64.
+ return set(int(line.strip(), 16) + 4 for line in proc.stdout)
+
+def PrintMissing(binary):
+ if not os.path.isfile(binary):
+ raise Exception('File not found: %s' % binary)
+ instrumented = GetInstrumentedPCs(binary)
+ print >> sys.stderr, "%s: found %d instrumented PCs in %s" % (prog_name,
+ len(instrumented),
+ binary)
+ covered = set(int(line, 16) for line in sys.stdin)
+ print >> sys.stderr, "%s: read %d PCs from stdin" % (prog_name, len(covered))
+ missing = instrumented - covered
+ print >> sys.stderr, "%s: %d PCs missing from coverage" % (prog_name, len(missing))
+ if (len(missing) > len(instrumented) - len(covered)):
+ print >> sys.stderr, \
+ "%s: WARNING: stdin contains PCs not found in binary" % prog_name
+ for pc in sorted(missing):
+ print "0x%x" % pc
+
if __name__ == '__main__':
prog_name = sys.argv[0]
if len(sys.argv) <= 2:
Usage();
+
+ if sys.argv[1] == "missing":
+ if len(sys.argv) != 3:
+ Usage()
+ PrintMissing(sys.argv[2])
+ exit(0)
+
+ file_list = []
+ for f in sys.argv[2:]:
+ file_list += glob.glob(f)
+ if not file_list:
+ Usage()
+
if sys.argv[1] == "print":
- PrintFiles(sys.argv[2:])
+ PrintFiles(file_list)
elif sys.argv[1] == "merge":
- MergeAndPrint(sys.argv[2:])
+ MergeAndPrint(file_list)
elif sys.argv[1] == "unpack":
- Unpack(sys.argv[2:])
+ Unpack(file_list)
elif sys.argv[1] == "rawunpack":
- RawUnpack(sys.argv[2:])
+ RawUnpack(file_list)
else:
Usage()
diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt
index 75008db3b6c9..b0165eac25f0 100644
--- a/lib/sanitizer_common/tests/CMakeLists.txt
+++ b/lib/sanitizer_common/tests/CMakeLists.txt
@@ -28,6 +28,7 @@ set(SANITIZER_UNITTESTS
sanitizer_stacktrace_test.cc
sanitizer_stoptheworld_test.cc
sanitizer_suppressions_test.cc
+ sanitizer_symbolizer_test.cc
sanitizer_test_main.cc
sanitizer_thread_registry_test.cc)
@@ -50,23 +51,33 @@ set(SANITIZER_TEST_CFLAGS_COMMON
-Werror=sign-compare
-Wno-non-virtual-dtor)
+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 ASAN_UNITTEST_COMMON_CFLAGS -gline-tables-only)
+ list(APPEND SANITIZER_TEST_CFLAGS_COMMON -gline-tables-only)
else()
- list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -g)
+ list(APPEND SANITIZER_TEST_CFLAGS_COMMON -g)
endif()
if(NOT MSVC)
list(APPEND SANITIZER_TEST_LINK_FLAGS_COMMON --driver-mode=g++)
endif()
+if(ANDROID)
+ list(APPEND SANITIZER_TEST_LINK_FLAGS_COMMON -pie)
+endif()
+
set(SANITIZER_TEST_LINK_LIBS)
append_list_if(ANDROID log SANITIZER_TEST_LINK_LIBS)
# NDK r10 requires -latomic almost always.
append_list_if(ANDROID atomic SANITIZER_TEST_LINK_LIBS)
append_list_if(COMPILER_RT_HAS_LIBDL -ldl SANITIZER_TEST_LINK_FLAGS_COMMON)
+append_list_if(COMPILER_RT_HAS_LIBRT -lrt SANITIZER_TEST_LINK_FLAGS_COMMON)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread SANITIZER_TEST_LINK_FLAGS_COMMON)
# x86_64 FreeBSD 9.2 additionally requires libc++ to build the tests. Also,
# 'libm' shall be specified explicitly to build i386 tests.
diff --git a/lib/sanitizer_common/tests/sanitizer_libc_test.cc b/lib/sanitizer_common/tests/sanitizer_libc_test.cc
index 8712d2c1b2a5..3252db77653d 100644
--- a/lib/sanitizer_common/tests/sanitizer_libc_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_libc_test.cc
@@ -17,6 +17,7 @@
#if SANITIZER_LINUX || SANITIZER_MAC
# define SANITIZER_TEST_HAS_STAT_H 1
# include <sys/stat.h>
+# include "sanitizer_common/sanitizer_posix.h"
#else
# define SANITIZER_TEST_HAS_STAT_H 0
#endif
@@ -78,16 +79,14 @@ TEST(SanitizerCommon, FileOps) {
char tmpfile[128];
temp_file_name(tmpfile, sizeof(tmpfile), "sanitizer_common.fileops.tmp.");
- uptr openrv = OpenFile(tmpfile, true);
- EXPECT_FALSE(internal_iserror(openrv));
- fd_t fd = openrv;
+ fd_t fd = OpenFile(tmpfile, WrOnly);
+ ASSERT_NE(fd, kInvalidFd);
EXPECT_EQ(len1, internal_write(fd, str1, len1));
EXPECT_EQ(len2, internal_write(fd, str2, len2));
- internal_close(fd);
+ CloseFile(fd);
- openrv = OpenFile(tmpfile, false);
- EXPECT_FALSE(internal_iserror(openrv));
- fd = openrv;
+ fd = OpenFile(tmpfile, RdOnly);
+ ASSERT_NE(fd, kInvalidFd);
uptr fsize = internal_filesize(fd);
EXPECT_EQ(len1 + len2, fsize);
@@ -115,7 +114,7 @@ TEST(SanitizerCommon, FileOps) {
internal_memset(buf, 0, len1);
EXPECT_EQ(len2, internal_read(fd, buf, len2));
EXPECT_EQ(0, internal_memcmp(buf, str2, len2));
- internal_close(fd);
+ CloseFile(fd);
internal_unlink(tmpfile);
}
#endif
@@ -134,12 +133,11 @@ TEST(SanitizerCommon, InternalMmapWithOffset) {
char tmpfile[128];
temp_file_name(tmpfile, sizeof(tmpfile),
"sanitizer_common.internalmmapwithoffset.tmp.");
- uptr res = OpenFile(tmpfile, true);
- ASSERT_FALSE(internal_iserror(res));
- fd_t fd = res;
+ fd_t fd = OpenFile(tmpfile, RdWr);
+ ASSERT_NE(fd, kInvalidFd);
uptr page_size = GetPageSizeCached();
- res = internal_ftruncate(fd, page_size * 2);
+ uptr res = internal_ftruncate(fd, page_size * 2);
ASSERT_FALSE(internal_iserror(res));
res = internal_lseek(fd, page_size, SEEK_SET);
@@ -154,8 +152,8 @@ TEST(SanitizerCommon, InternalMmapWithOffset) {
ASSERT_EQ('A', p[0]);
ASSERT_EQ('B', p[1]);
- internal_close(fd);
- internal_munmap(p, page_size);
+ CloseFile(fd);
+ UnmapOrDie(p, page_size);
internal_unlink(tmpfile);
}
#endif
diff --git a/lib/sanitizer_common/tests/sanitizer_posix_test.cc b/lib/sanitizer_common/tests/sanitizer_posix_test.cc
index 56ce416141fa..03ca449d3622 100644
--- a/lib/sanitizer_common/tests/sanitizer_posix_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_posix_test.cc
@@ -52,9 +52,9 @@ static void SpawnThread(uptr iteration) {
TEST(SanitizerCommon, PthreadDestructorIterations) {
ASSERT_EQ(0, pthread_key_create(&key, &destructor));
- SpawnThread(kPthreadDestructorIterations);
+ SpawnThread(GetPthreadDestructorIterations());
EXPECT_TRUE(destructor_executed);
- SpawnThread(kPthreadDestructorIterations + 1);
+ SpawnThread(GetPthreadDestructorIterations() + 1);
EXPECT_FALSE(destructor_executed);
}
diff --git a/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc b/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc
index abe4ef43093f..12bc9e18193a 100644
--- a/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc
@@ -37,8 +37,7 @@ TEST(MemoryMappingLayout, DumpListOfModules) {
const char *binary_name = last_slash ? last_slash + 1 : argv0;
MemoryMappingLayout memory_mapping(false);
const uptr kMaxModules = 100;
- LoadedModule *modules =
- (LoadedModule *)malloc(kMaxModules * sizeof(LoadedModule));
+ LoadedModule modules[kMaxModules];
uptr n_modules = memory_mapping.DumpListOfModules(modules, kMaxModules, 0);
EXPECT_GT(n_modules, 0U);
bool found = false;
@@ -51,7 +50,6 @@ TEST(MemoryMappingLayout, DumpListOfModules) {
modules[i].clear();
}
EXPECT_TRUE(found);
- free(modules);
}
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc b/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc
index cc9a9edbbb46..05796fcbff77 100644
--- a/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc
@@ -18,20 +18,36 @@ namespace __sanitizer {
TEST(SanitizerStacktracePrinter, RenderSourceLocation) {
InternalScopedString str(128);
- RenderSourceLocation(&str, "/dir/file.cc", 10, 5, "");
+ RenderSourceLocation(&str, "/dir/file.cc", 10, 5, false, "");
EXPECT_STREQ("/dir/file.cc:10:5", str.data());
str.clear();
- RenderSourceLocation(&str, "/dir/file.cc", 11, 0, "");
+ RenderSourceLocation(&str, "/dir/file.cc", 11, 0, false, "");
EXPECT_STREQ("/dir/file.cc:11", str.data());
str.clear();
- RenderSourceLocation(&str, "/dir/file.cc", 0, 0, "");
+ RenderSourceLocation(&str, "/dir/file.cc", 0, 0, false, "");
EXPECT_STREQ("/dir/file.cc", str.data());
str.clear();
- RenderSourceLocation(&str, "/dir/file.cc", 10, 5, "/dir/");
+ RenderSourceLocation(&str, "/dir/file.cc", 10, 5, false, "/dir/");
EXPECT_STREQ("file.cc:10:5", str.data());
+
+ str.clear();
+ RenderSourceLocation(&str, "/dir/file.cc", 10, 5, true, "");
+ EXPECT_STREQ("/dir/file.cc(10,5)", str.data());
+
+ str.clear();
+ RenderSourceLocation(&str, "/dir/file.cc", 11, 0, true, "");
+ EXPECT_STREQ("/dir/file.cc(11)", str.data());
+
+ str.clear();
+ RenderSourceLocation(&str, "/dir/file.cc", 0, 0, true, "");
+ EXPECT_STREQ("/dir/file.cc", str.data());
+
+ str.clear();
+ RenderSourceLocation(&str, "/dir/file.cc", 10, 5, true, "/dir/");
+ EXPECT_STREQ("file.cc(10,5)", str.data());
}
TEST(SanitizerStacktracePrinter, RenderModuleLocation) {
@@ -62,7 +78,7 @@ TEST(SanitizerStacktracePrinter, RenderFrame) {
RenderFrame(&str, "%% Frame:%n PC:%p Module:%m ModuleOffset:%o "
"Function:%f FunctionOffset:%q Source:%s Line:%l "
"Column:%c",
- frame_no, info, "/path/to/", "function_");
+ frame_no, info, false, "/path/to/", "function_");
EXPECT_STREQ("% Frame:42 PC:0x400000 Module:my/module ModuleOffset:0x200 "
"Function:foo FunctionOffset:0x100 Source:my/source Line:10 "
"Column:5",
@@ -72,50 +88,64 @@ TEST(SanitizerStacktracePrinter, RenderFrame) {
// Test special format specifiers.
info.address = 0x400000;
- RenderFrame(&str, "%M", frame_no, info);
+ RenderFrame(&str, "%M", frame_no, info, false);
EXPECT_NE(nullptr, internal_strstr(str.data(), "400000"));
str.clear();
- RenderFrame(&str, "%L", frame_no, info);
+ RenderFrame(&str, "%L", frame_no, info, false);
EXPECT_STREQ("(<unknown module>)", str.data());
str.clear();
info.module = internal_strdup("/path/to/module");
info.module_offset = 0x200;
- RenderFrame(&str, "%M", frame_no, info);
+ RenderFrame(&str, "%M", frame_no, info, false);
EXPECT_NE(nullptr, internal_strstr(str.data(), "(module+0x"));
EXPECT_NE(nullptr, internal_strstr(str.data(), "200"));
str.clear();
- RenderFrame(&str, "%L", frame_no, info);
+ RenderFrame(&str, "%L", frame_no, info, false);
EXPECT_STREQ("(/path/to/module+0x200)", str.data());
str.clear();
info.function = internal_strdup("my_function");
- RenderFrame(&str, "%F", frame_no, info);
+ RenderFrame(&str, "%F", frame_no, info, false);
EXPECT_STREQ("in my_function", str.data());
str.clear();
info.function_offset = 0x100;
- RenderFrame(&str, "%F %S", frame_no, info);
+ RenderFrame(&str, "%F %S", frame_no, info, false);
EXPECT_STREQ("in my_function+0x100 <null>", str.data());
str.clear();
info.file = internal_strdup("my_file");
- RenderFrame(&str, "%F %S", frame_no, info);
+ RenderFrame(&str, "%F %S", frame_no, info, false);
EXPECT_STREQ("in my_function my_file", str.data());
str.clear();
info.line = 10;
- RenderFrame(&str, "%F %S", frame_no, info);
+ RenderFrame(&str, "%F %S", frame_no, info, false);
EXPECT_STREQ("in my_function my_file:10", str.data());
str.clear();
info.column = 5;
- RenderFrame(&str, "%S %L", frame_no, info);
+ RenderFrame(&str, "%S %L", frame_no, info, false);
EXPECT_STREQ("my_file:10:5 my_file:10:5", str.data());
str.clear();
+ RenderFrame(&str, "%S %L", frame_no, info, true);
+ EXPECT_STREQ("my_file(10,5) my_file(10,5)", str.data());
+ str.clear();
+
+ info.column = 0;
+ RenderFrame(&str, "%F %S", frame_no, info, true);
+ EXPECT_STREQ("in my_function my_file(10)", str.data());
+ str.clear();
+
+ info.line = 0;
+ RenderFrame(&str, "%F %S", frame_no, info, true);
+ EXPECT_STREQ("in my_function my_file", str.data());
+ str.clear();
+
info.Clear();
}
diff --git a/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc
index ac820c25a0f8..654ea1db82fb 100644
--- a/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc
@@ -30,11 +30,11 @@ class FastUnwindTest : public ::testing::Test {
}
void *mapping;
- uptr *fake_stack;
+ uhwptr *fake_stack;
const uptr fake_stack_size = 10;
- uptr start_pc;
- uptr fake_top;
- uptr fake_bottom;
+ uhwptr start_pc;
+ uhwptr fake_top;
+ uhwptr fake_bottom;
BufferedStackTrace trace;
};
@@ -45,10 +45,10 @@ static uptr PC(uptr idx) {
void FastUnwindTest::SetUp() {
size_t ps = GetPageSize();
mapping = MmapOrDie(2 * ps, "FastUnwindTest");
- Mprotect((uptr)mapping, ps);
+ MprotectNoAccess((uptr)mapping, ps);
// Unwinder may peek 1 word down from the starting FP.
- fake_stack = (uptr *)((uptr)mapping + ps + sizeof(uptr));
+ fake_stack = (uhwptr *)((uptr)mapping + ps + sizeof(uhwptr));
// Fill an array of pointers with fake fp+retaddr pairs. Frame pointers have
// even indices.
@@ -57,12 +57,12 @@ void FastUnwindTest::SetUp() {
fake_stack[i+1] = PC(i + 1); // retaddr
}
// Mark the last fp point back up to terminate the stack trace.
- fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uptr)&fake_stack[0];
+ fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uhwptr)&fake_stack[0];
// Top is two slots past the end because FastUnwindStack subtracts two.
- fake_top = (uptr)&fake_stack[fake_stack_size + 2];
+ fake_top = (uhwptr)&fake_stack[fake_stack_size + 2];
// Bottom is one slot before the start because FastUnwindStack uses >.
- fake_bottom = (uptr)mapping;
+ fake_bottom = (uhwptr)mapping;
start_pc = PC(0);
}
@@ -85,7 +85,7 @@ TEST_F(FastUnwindTest, Basic) {
// From: http://code.google.com/p/address-sanitizer/issues/detail?id=162
TEST_F(FastUnwindTest, FramePointerLoop) {
// Make one fp point to itself.
- fake_stack[4] = (uptr)&fake_stack[4];
+ fake_stack[4] = (uhwptr)&fake_stack[4];
if (!TryFastUnwind(kStackTraceMax))
return;
// Should get all on-stack retaddrs up to the 4th slot and start_pc.
@@ -114,7 +114,7 @@ TEST_F(FastUnwindTest, OneFrameStackTrace) {
return;
EXPECT_EQ(1U, trace.size);
EXPECT_EQ(start_pc, trace.trace[0]);
- EXPECT_EQ((uptr)&fake_stack[0], trace.top_frame_bp);
+ EXPECT_EQ((uhwptr)&fake_stack[0], trace.top_frame_bp);
}
TEST_F(FastUnwindTest, ZeroFramesStackTrace) {
@@ -127,7 +127,7 @@ TEST_F(FastUnwindTest, ZeroFramesStackTrace) {
TEST_F(FastUnwindTest, FPBelowPrevFP) {
// The next FP points to unreadable memory inside the stack limits, but below
// current FP.
- fake_stack[0] = (uptr)&fake_stack[-50];
+ fake_stack[0] = (uhwptr)&fake_stack[-50];
fake_stack[1] = PC(1);
if (!TryFastUnwind(3))
return;
diff --git a/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc b/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc
index b6786ba8b013..802af392c609 100644
--- a/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc
@@ -189,6 +189,16 @@ TEST(StopTheWorld, SuspendThreadsAdvanced) {
pthread_mutex_destroy(&advanced_incrementer_thread_exit_mutex);
}
+static void SegvCallback(const SuspendedThreadsList &suspended_threads_list,
+ void *argument) {
+ *(volatile int*)0x1234 = 0;
+}
+
+TEST(StopTheWorld, SegvInCallback) {
+ // Test that tracer thread catches SIGSEGV.
+ StopTheWorld(&SegvCallback, NULL);
+}
+
} // namespace __sanitizer
#endif // SANITIZER_LINUX && defined(__x86_64__)
diff --git a/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc b/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc
new file mode 100644
index 000000000000..429ac591e502
--- /dev/null
+++ b/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc
@@ -0,0 +1,58 @@
+//===-- sanitizer_symbolizer_test.cc --------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Tests for sanitizer_symbolizer.h and sanitizer_symbolizer_internal.h
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_allocator_internal.h"
+#include "sanitizer_common/sanitizer_symbolizer_internal.h"
+#include "gtest/gtest.h"
+
+namespace __sanitizer {
+
+TEST(Symbolizer, ExtractToken) {
+ char *token;
+ const char *rest;
+
+ rest = ExtractToken("a;b;c", ";", &token);
+ EXPECT_STREQ("a", token);
+ EXPECT_STREQ("b;c", rest);
+ InternalFree(token);
+
+ rest = ExtractToken("aaa-bbb.ccc", ";.-*", &token);
+ EXPECT_STREQ("aaa", token);
+ EXPECT_STREQ("bbb.ccc", rest);
+ InternalFree(token);
+}
+
+TEST(Symbolizer, ExtractInt) {
+ int token;
+ const char *rest = ExtractInt("123,456;789", ";,", &token);
+ EXPECT_EQ(123, token);
+ EXPECT_STREQ("456;789", rest);
+}
+
+TEST(Symbolizer, ExtractUptr) {
+ uptr token;
+ const char *rest = ExtractUptr("123,456;789", ";,", &token);
+ EXPECT_EQ(123U, token);
+ EXPECT_STREQ("456;789", rest);
+}
+
+TEST(Symbolizer, ExtractTokenUpToDelimiter) {
+ char *token;
+ const char *rest =
+ ExtractTokenUpToDelimiter("aaa-+-bbb-+-ccc", "-+-", &token);
+ EXPECT_STREQ("aaa", token);
+ EXPECT_STREQ("bbb-+-ccc", rest);
+ InternalFree(token);
+}
+
+} // namespace __sanitizer
diff --git a/lib/tsan/CMakeLists.txt b/lib/tsan/CMakeLists.txt
index 9f6d63041a68..90137800ffac 100644
--- a/lib/tsan/CMakeLists.txt
+++ b/lib/tsan/CMakeLists.txt
@@ -41,6 +41,9 @@ set(TSAN_SOURCES
rtl/tsan_symbolize.cc
rtl/tsan_sync.cc)
+set(TSAN_CXX_SOURCES
+ rtl/tsan_new_delete.cc)
+
if(APPLE)
list(APPEND TSAN_SOURCES rtl/tsan_platform_mac.cc)
elseif(UNIX)
@@ -57,6 +60,7 @@ set(TSAN_HEADERS
rtl/tsan_flags.h
rtl/tsan_flags.inc
rtl/tsan_ignoreset.h
+ rtl/tsan_interceptors.h
rtl/tsan_interface_ann.h
rtl/tsan_interface.h
rtl/tsan_interface_inl.h
@@ -78,45 +82,50 @@ set(TSAN_HEADERS
set(TSAN_RUNTIME_LIBRARIES)
add_custom_target(tsan)
-# TSan is currently supported on 64-bit Linux only.
-if(UNIX AND NOT APPLE)
- foreach(arch ${TSAN_SUPPORTED_ARCH})
- if(arch STREQUAL "x86_64")
- set(TSAN_ASM_SOURCES rtl/tsan_rtl_amd64.S)
- # Pass ASM file directly to the C++ compiler.
- set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES
- LANGUAGE C)
- # Sanity check for Go runtime.
- set(BUILDGO_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/go/buildgo.sh)
- add_custom_target(GotsanRuntimeCheck
- COMMAND CC=${CMAKE_C_COMPILER} IN_TMPDIR=1 SILENT=1 ${BUILDGO_SCRIPT}
- DEPENDS clang_rt.tsan-${arch} ${BUILDGO_SCRIPT}
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/go
- COMMENT "Checking TSan Go runtime..."
- VERBATIM)
- else()
- set(TSAN_ASM_SOURCES)
- endif()
- add_compiler_rt_runtime(clang_rt.tsan-${arch} ${arch} STATIC
- SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES}
- $<TARGET_OBJECTS:RTInterception.${arch}>
- $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
- $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
- CFLAGS ${TSAN_RTL_CFLAGS}
- DEFS ${TSAN_COMMON_DEFINITIONS})
- list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch})
- add_sanitizer_rt_symbols(clang_rt.tsan-${arch} rtl/tsan.syms.extra)
- add_dependencies(tsan clang_rt.tsan-${arch}
- clang_rt.tsan-${arch}-symbols)
- endforeach()
-endif()
-add_dependencies(compiler-rt tsan)
+foreach(arch ${TSAN_SUPPORTED_ARCH})
+ if(arch STREQUAL "x86_64")
+ set(TSAN_ASM_SOURCES rtl/tsan_rtl_amd64.S)
+ # Pass ASM file directly to the C++ compiler.
+ set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES
+ LANGUAGE C)
+ # Sanity check for Go runtime.
+ set(BUILDGO_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/go/buildgo.sh)
+ add_custom_target(GotsanRuntimeCheck
+ COMMAND env "CC=${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}"
+ IN_TMPDIR=1 SILENT=1 ${BUILDGO_SCRIPT}
+ DEPENDS clang_rt.tsan-${arch} ${BUILDGO_SCRIPT}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/go
+ COMMENT "Checking TSan Go runtime..."
+ VERBATIM)
+ else()
+ set(TSAN_ASM_SOURCES)
+ endif()
+ add_compiler_rt_runtime(clang_rt.tsan-${arch} ${arch} STATIC
+ SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES}
+ $<TARGET_OBJECTS:RTInterception.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
+ $<TARGET_OBJECTS:RTUbsan.${arch}>
+ CFLAGS ${TSAN_RTL_CFLAGS})
+ add_compiler_rt_runtime(clang_rt.tsan_cxx-${arch} ${arch} STATIC
+ SOURCES ${TSAN_CXX_SOURCES}
+ $<TARGET_OBJECTS:RTUbsan_cxx.${arch}>
+ CFLAGS ${TSAN_RTL_CFLAGS})
+ list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch}
+ clang_rt.tsan_cxx-${arch})
+ add_sanitizer_rt_symbols(clang_rt.tsan-${arch} rtl/tsan.syms.extra)
+ add_sanitizer_rt_symbols(clang_rt.tsan_cxx-${arch} rtl/tsan.syms.extra)
+ add_dependencies(tsan clang_rt.tsan-${arch}
+ clang_rt.tsan_cxx-${arch}
+ clang_rt.tsan-${arch}-symbols
+ clang_rt.tsan_cxx-${arch}-symbols)
+endforeach()
+add_dependencies(compiler-rt tsan)
# Build libcxx instrumented with TSan.
-if(TSAN_SUPPORTED_ARCH AND
- COMPILER_RT_HAS_LIBCXX_SOURCES AND
+if(COMPILER_RT_HAS_LIBCXX_SOURCES AND
COMPILER_RT_TEST_COMPILER_ID STREQUAL "Clang")
set(LIBCXX_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libcxx_tsan)
add_custom_libcxx(libcxx_tsan ${LIBCXX_PREFIX}
diff --git a/lib/tsan/Makefile.old b/lib/tsan/Makefile.old
index 9e0693fa04c4..b2ac912d741d 100644
--- a/lib/tsan/Makefile.old
+++ b/lib/tsan/Makefile.old
@@ -1,7 +1,8 @@
DEBUG=0
LDFLAGS=-ldl -lrt -lpthread -pie
CXXFLAGS = -std=c++11 -fPIE -fno-rtti -g -Wall -Werror \
- -DGTEST_HAS_RTTI=0 -DSANITIZER_DEBUG=$(DEBUG)
+ -DGTEST_HAS_RTTI=0 -DSANITIZER_DEBUG=$(DEBUG) \
+ -DTSAN_CONTAINS_UBSAN=0
CLANG=clang
FILECHECK=FileCheck
# Silence warnings that Clang produces for gtest code.
@@ -11,7 +12,9 @@ ifeq ($(DEBUG), 0)
CXXFLAGS += -O3
endif
ifeq ($(CXX), $(CLANG)++)
- CXXFLAGS+= -Wno-unused-private-field -Wno-static-in-inline -Wgnu
+ CXXFLAGS += -Wno-unused-private-field -Wno-static-in-inline -Wgnu
+else
+ CXXFLAGS += -Wno-maybe-uninitialized
endif
LIBTSAN=rtl/libtsan.a
diff --git a/lib/tsan/dd/CMakeLists.txt b/lib/tsan/dd/CMakeLists.txt
index 981c1fb0bb5e..8130e09c27e8 100644
--- a/lib/tsan/dd/CMakeLists.txt
+++ b/lib/tsan/dd/CMakeLists.txt
@@ -12,6 +12,7 @@ set(DD_SOURCES
set(DD_LINKLIBS)
append_list_if(COMPILER_RT_HAS_LIBDL dl DD_LINKLIBS)
+append_list_if(COMPILER_RT_HAS_LIBRT rt DD_LINKLIBS)
append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread DD_LINKLIBS)
add_custom_target(dd)
@@ -26,7 +27,8 @@ if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE AND NOT ANDROID)
CFLAGS ${DD_CFLAGS})
add_dependencies(dd clang_rt.dd-${arch})
- add_compiler_rt_object_library(RTDD ${arch}
+ add_compiler_rt_object_libraries(RTDD
+ ARCHS ${arch}
SOURCES ${DD_SOURCES} CFLAGS ${DD_CFLAGS})
add_compiler_rt_runtime(clang_rt.dyndd-${arch} ${arch} SHARED
diff --git a/lib/tsan/dd/dd_rtl.h b/lib/tsan/dd/dd_rtl.h
index bb1b20205b54..9abf17da48c8 100644
--- a/lib/tsan/dd/dd_rtl.h
+++ b/lib/tsan/dd/dd_rtl.h
@@ -35,7 +35,7 @@ struct Callback : DDCallback {
Thread *thr;
Callback(Thread *thr);
- virtual u32 Unwind();
+ u32 Unwind() override;
};
typedef AddrHashMap<Mutex, 31051> MutexHashMap;
diff --git a/lib/tsan/go/buildgo.sh b/lib/tsan/go/buildgo.sh
index 5ac60349e081..7193b57684f2 100755
--- a/lib/tsan/go/buildgo.sh
+++ b/lib/tsan/go/buildgo.sh
@@ -36,7 +36,7 @@ SRCS="
if [ "`uname -a | grep Linux`" != "" ]; then
SUFFIX="linux_amd64"
OSCFLAGS="-fPIC -ffreestanding -Wno-maybe-uninitialized -Wno-unused-const-variable -Werror -Wno-unknown-warning-option"
- OSLDFLAGS="-lpthread -fPIC -fpie"
+ OSLDFLAGS="-lpthread -lrt -fPIC -fpie"
SRCS="
$SRCS
../rtl/tsan_platform_linux.cc
diff --git a/lib/tsan/rtl/Makefile.old b/lib/tsan/rtl/Makefile.old
index 150b376343be..ee7095e88d96 100644
--- a/lib/tsan/rtl/Makefile.old
+++ b/lib/tsan/rtl/Makefile.old
@@ -1,4 +1,4 @@
-CXXFLAGS = -std=c++11 -fPIE -g -Wall -Werror -fno-builtin -msse3 -DSANITIZER_DEBUG=$(DEBUG)
+CXXFLAGS = -std=c++11 -fPIE -g -Wall -Werror -fno-builtin -msse3 -DSANITIZER_DEBUG=$(DEBUG) -DTSAN_CONTAINS_UBSAN=0
CLANG=clang
ifeq ($(DEBUG), 0)
CXXFLAGS += -O3
@@ -15,6 +15,7 @@ CXXFLAGS+=$(CFLAGS)
ifeq ($(DEBUG), 0)
CXXFLAGS+=-fomit-frame-pointer
ifeq ($(CXX), g++)
+ CXXFLAGS+=-Wno-maybe-uninitialized
CXXFLAGS+=-Wframe-larger-than=512
endif # CXX=g++
endif # DEBUG=0
diff --git a/lib/tsan/rtl/tsan.syms.extra b/lib/tsan/rtl/tsan.syms.extra
index 49ed6b46629e..1bc1d93298d3 100644
--- a/lib/tsan/rtl/tsan.syms.extra
+++ b/lib/tsan/rtl/tsan.syms.extra
@@ -8,6 +8,7 @@ __tsan_java*
__tsan_unaligned*
__tsan_release
__tsan_acquire
+__ubsan_*
Annotate*
WTFAnnotate*
RunningOnValgrind
diff --git a/lib/tsan/rtl/tsan_defs.h b/lib/tsan/rtl/tsan_defs.h
index 910a483127d5..d869d95e0878 100644
--- a/lib/tsan/rtl/tsan_defs.h
+++ b/lib/tsan/rtl/tsan_defs.h
@@ -17,6 +17,7 @@
#include "sanitizer_common/sanitizer_internal_defs.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "tsan_stat.h"
+#include "ubsan/ubsan_platform.h"
// Setup defaults for compile definitions.
#ifndef TSAN_NO_HISTORY
@@ -27,6 +28,10 @@
# define TSAN_COLLECT_STATS 0
#endif
+#ifndef TSAN_CONTAINS_UBSAN
+# define TSAN_CONTAINS_UBSAN (CAN_SANITIZE_UB && !defined(SANITIZER_GO))
+#endif
+
namespace __tsan {
#ifdef SANITIZER_GO
diff --git a/lib/tsan/rtl/tsan_fd.cc b/lib/tsan/rtl/tsan_fd.cc
index d18502f00540..d84df4a6413c 100644
--- a/lib/tsan/rtl/tsan_fd.cc
+++ b/lib/tsan/rtl/tsan_fd.cc
@@ -91,7 +91,8 @@ static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) {
}
// pd must be already ref'ed.
-static void init(ThreadState *thr, uptr pc, int fd, FdSync *s) {
+static void init(ThreadState *thr, uptr pc, int fd, FdSync *s,
+ bool write = true) {
FdDesc *d = fddesc(thr, pc, fd);
// As a matter of fact, we don't intercept all close calls.
// See e.g. libc __res_iclose().
@@ -109,8 +110,13 @@ static void init(ThreadState *thr, uptr pc, int fd, FdSync *s) {
}
d->creation_tid = thr->tid;
d->creation_stack = CurrentStackId(thr, pc);
- // To catch races between fd usage and open.
- MemoryRangeImitateWrite(thr, pc, (uptr)d, 8);
+ if (write) {
+ // To catch races between fd usage and open.
+ MemoryRangeImitateWrite(thr, pc, (uptr)d, 8);
+ } else {
+ // See the dup-related comment in FdClose.
+ MemoryRead(thr, pc, (uptr)d, kSizeLog8);
+ }
}
void FdInit() {
@@ -181,13 +187,25 @@ void FdAccess(ThreadState *thr, uptr pc, int fd) {
MemoryRead(thr, pc, (uptr)d, kSizeLog8);
}
-void FdClose(ThreadState *thr, uptr pc, int fd) {
+void FdClose(ThreadState *thr, uptr pc, int fd, bool write) {
DPrintf("#%d: FdClose(%d)\n", thr->tid, fd);
if (bogusfd(fd))
return;
FdDesc *d = fddesc(thr, pc, fd);
- // To catch races between fd usage and close.
- MemoryWrite(thr, pc, (uptr)d, kSizeLog8);
+ if (write) {
+ // To catch races between fd usage and close.
+ MemoryWrite(thr, pc, (uptr)d, kSizeLog8);
+ } else {
+ // This path is used only by dup2/dup3 calls.
+ // We do read instead of write because there is a number of legitimate
+ // cases where write would lead to false positives:
+ // 1. Some software dups a closed pipe in place of a socket before closing
+ // the socket (to prevent races actually).
+ // 2. Some daemons dup /dev/null in place of stdin/stdout.
+ // On the other hand we have not seen cases when write here catches real
+ // bugs.
+ MemoryRead(thr, pc, (uptr)d, kSizeLog8);
+ }
// We need to clear it, because if we do not intercept any call out there
// that creates fd, we will hit false postives.
MemoryResetRange(thr, pc, (uptr)d, 8);
@@ -204,15 +222,15 @@ void FdFileCreate(ThreadState *thr, uptr pc, int fd) {
init(thr, pc, fd, &fdctx.filesync);
}
-void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) {
+void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd, bool write) {
DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd);
if (bogusfd(oldfd) || bogusfd(newfd))
return;
// Ignore the case when user dups not yet connected socket.
FdDesc *od = fddesc(thr, pc, oldfd);
MemoryRead(thr, pc, (uptr)od, kSizeLog8);
- FdClose(thr, pc, newfd);
- init(thr, pc, newfd, ref(od->sync));
+ FdClose(thr, pc, newfd, write);
+ init(thr, pc, newfd, ref(od->sync), write);
}
void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) {
diff --git a/lib/tsan/rtl/tsan_fd.h b/lib/tsan/rtl/tsan_fd.h
index 75c616dae0fd..64dc84f6edd6 100644
--- a/lib/tsan/rtl/tsan_fd.h
+++ b/lib/tsan/rtl/tsan_fd.h
@@ -42,9 +42,9 @@ void FdInit();
void FdAcquire(ThreadState *thr, uptr pc, int fd);
void FdRelease(ThreadState *thr, uptr pc, int fd);
void FdAccess(ThreadState *thr, uptr pc, int fd);
-void FdClose(ThreadState *thr, uptr pc, int fd);
+void FdClose(ThreadState *thr, uptr pc, int fd, bool write = true);
void FdFileCreate(ThreadState *thr, uptr pc, int fd);
-void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd);
+void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd, bool write);
void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd);
void FdEventCreate(ThreadState *thr, uptr pc, int fd);
void FdSignalCreate(ThreadState *thr, uptr pc, int fd);
diff --git a/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc
index 1e81ef3d0003..5de227a42dee 100644
--- a/lib/tsan/rtl/tsan_flags.cc
+++ b/lib/tsan/rtl/tsan_flags.cc
@@ -17,6 +17,7 @@
#include "tsan_flags.h"
#include "tsan_rtl.h"
#include "tsan_mman.h"
+#include "ubsan/ubsan_flags.h"
namespace __tsan {
@@ -47,15 +48,13 @@ void RegisterTsanFlags(FlagParser *parser, Flags *f) {
RegisterFlag(parser, #Name, Description, &f->Name);
#include "tsan_flags.inc"
#undef TSAN_FLAG
+ // DDFlags
+ RegisterFlag(parser, "second_deadlock_stack",
+ "Report where each mutex is locked in deadlock reports",
+ &f->second_deadlock_stack);
}
void InitializeFlags(Flags *f, const char *env) {
- FlagParser parser;
- RegisterTsanFlags(&parser, f);
- RegisterCommonFlags(&parser);
-
- f->SetDefaults();
-
SetCommonFlagsDefaults();
{
// Override some common flags defaults.
@@ -70,10 +69,32 @@ void InitializeFlags(Flags *f, const char *env) {
OverrideCommonFlags(cf);
}
+ f->SetDefaults();
+
+ FlagParser parser;
+ RegisterTsanFlags(&parser, f);
+ RegisterCommonFlags(&parser);
+
+#if TSAN_CONTAINS_UBSAN
+ __ubsan::Flags *uf = __ubsan::flags();
+ uf->SetDefaults();
+
+ FlagParser ubsan_parser;
+ __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
+ RegisterCommonFlags(&ubsan_parser);
+#endif
+
// Let a frontend override.
parser.ParseString(__tsan_default_options());
+#if TSAN_CONTAINS_UBSAN
+ const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
+ ubsan_parser.ParseString(ubsan_default_options);
+#endif
// Override from command line.
parser.ParseString(env);
+#if TSAN_CONTAINS_UBSAN
+ ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
+#endif
// Sanity check.
if (!f->report_bugs) {
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
index 31ff7d56aac7..b1a7ae6de328 100644
--- a/lib/tsan/rtl/tsan_interceptors.cc
+++ b/lib/tsan/rtl/tsan_interceptors.cc
@@ -20,6 +20,7 @@
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "interception/interception.h"
+#include "tsan_interceptors.h"
#include "tsan_interface.h"
#include "tsan_platform.h"
#include "tsan_suppressions.h"
@@ -31,10 +32,8 @@ using namespace __tsan; // NOLINT
#if SANITIZER_FREEBSD
#define __errno_location __error
-#define __libc_malloc __malloc
#define __libc_realloc __realloc
#define __libc_calloc __calloc
-#define __libc_free __free
#define stdout __stdoutp
#define stderr __stderrp
#endif
@@ -78,10 +77,8 @@ extern "C" void *pthread_self();
extern "C" void _exit(int status);
extern "C" int *__errno_location();
extern "C" int fileno_unlocked(void *stream);
-extern "C" void *__libc_malloc(uptr size);
extern "C" void *__libc_calloc(uptr size, uptr n);
extern "C" void *__libc_realloc(void *ptr, uptr size);
-extern "C" void __libc_free(void *ptr);
extern "C" int dirfd(void *dirp);
#if !SANITIZER_FREEBSD
extern "C" int mallopt(int param, int value);
@@ -145,7 +142,10 @@ struct sigaction_t {
const sighandler_t SIG_DFL = (sighandler_t)0;
const sighandler_t SIG_IGN = (sighandler_t)1;
const sighandler_t SIG_ERR = (sighandler_t)-1;
-#ifdef __mips__
+#if SANITIZER_FREEBSD
+const int SA_SIGINFO = 0x40;
+const int SIG_SETMASK = 3;
+#elif defined(__mips__)
const int SA_SIGINFO = 8;
const int SIG_SETMASK = 3;
#else
@@ -153,9 +153,8 @@ const int SA_SIGINFO = 4;
const int SIG_SETMASK = 2;
#endif
-namespace std {
-struct nothrow_t {};
-} // namespace std
+#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED \
+ (!cur_thread()->is_inited)
static sigaction_t sigactions[kSigCount];
@@ -167,7 +166,7 @@ struct SignalDesc {
ucontext_t ctx;
};
-struct SignalContext {
+struct ThreadSignalContext {
int int_signal_send;
atomic_uintptr_t in_blocking_func;
atomic_uintptr_t have_pending_signals;
@@ -194,10 +193,10 @@ void InitializeLibIgnore() {
} // namespace __tsan
-static SignalContext *SigCtx(ThreadState *thr) {
- SignalContext *ctx = (SignalContext*)thr->signal_ctx;
+static ThreadSignalContext *SigCtx(ThreadState *thr) {
+ ThreadSignalContext *ctx = (ThreadSignalContext*)thr->signal_ctx;
if (ctx == 0 && !thr->is_dead) {
- ctx = (SignalContext*)MmapOrDie(sizeof(*ctx), "SignalContext");
+ ctx = (ThreadSignalContext*)MmapOrDie(sizeof(*ctx), "ThreadSignalContext");
MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx));
thr->signal_ctx = ctx;
}
@@ -206,16 +205,6 @@ static SignalContext *SigCtx(ThreadState *thr) {
static unsigned g_thread_finalize_key;
-class ScopedInterceptor {
- public:
- ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc);
- ~ScopedInterceptor();
- private:
- ThreadState *const thr_;
- const uptr pc_;
- bool in_ignored_lib_;
-};
-
ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname,
uptr pc)
: thr_(thr)
@@ -245,14 +234,6 @@ ScopedInterceptor::~ScopedInterceptor() {
}
}
-#define SCOPED_INTERCEPTOR_RAW(func, ...) \
- ThreadState *thr = cur_thread(); \
- const uptr caller_pc = GET_CALLER_PC(); \
- ScopedInterceptor si(thr, #func, caller_pc); \
- const uptr pc = StackTrace::GetCurrentPc(); \
- (void)pc; \
-/**/
-
#define SCOPED_TSAN_INTERCEPTOR(func, ...) \
SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
if (REAL(func) == 0) { \
@@ -271,6 +252,13 @@ ScopedInterceptor::~ScopedInterceptor() {
# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver)
#endif
+#define READ_STRING_OF_LEN(thr, pc, s, len, n) \
+ MemoryAccessRange((thr), (pc), (uptr)(s), \
+ common_flags()->strict_string_checks ? (len) + 1 : (n), false)
+
+#define READ_STRING(thr, pc, s, n) \
+ READ_STRING_OF_LEN((thr), (pc), (s), internal_strlen(s), (n))
+
#define BLOCK_REAL(name) (BlockingCall(thr), REAL(name))
struct BlockingCall {
@@ -298,7 +286,7 @@ struct BlockingCall {
}
ThreadState *thr;
- SignalContext *ctx;
+ ThreadSignalContext *ctx;
};
TSAN_INTERCEPTOR(unsigned, sleep, unsigned sec) {
@@ -410,7 +398,7 @@ static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) {
}
static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) {
- if (thr->shadow_stack_pos == 0) // called from libc guts during bootstrap
+ if (!thr->is_inited) // called from libc guts during bootstrap
return;
// Cleanup old bufs.
JmpBufGarbageCollect(thr, sp);
@@ -419,7 +407,7 @@ static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) {
buf->sp = sp;
buf->mangled_sp = mangled_sp;
buf->shadow_stack_pos = thr->shadow_stack_pos;
- SignalContext *sctx = SigCtx(thr);
+ ThreadSignalContext *sctx = SigCtx(thr);
buf->int_signal_send = sctx ? sctx->int_signal_send : 0;
buf->in_blocking_func = sctx ?
atomic_load(&sctx->in_blocking_func, memory_order_relaxed) :
@@ -442,7 +430,7 @@ static void LongJmp(ThreadState *thr, uptr *env) {
// Unwind the stack.
while (thr->shadow_stack_pos > buf->shadow_stack_pos)
FuncExit(thr);
- SignalContext *sctx = SigCtx(thr);
+ ThreadSignalContext *sctx = SigCtx(thr);
if (sctx) {
sctx->int_signal_send = buf->int_signal_send;
atomic_store(&sctx->in_blocking_func, buf->in_blocking_func,
@@ -585,73 +573,6 @@ TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) {
return user_alloc_usable_size(p);
}
-#define OPERATOR_NEW_BODY(mangled_name) \
- if (cur_thread()->in_symbolizer) \
- return __libc_malloc(size); \
- void *p = 0; \
- { \
- SCOPED_INTERCEPTOR_RAW(mangled_name, size); \
- p = user_alloc(thr, pc, size); \
- } \
- invoke_malloc_hook(p, size); \
- return p;
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new(__sanitizer::uptr size);
-void *operator new(__sanitizer::uptr size) {
- OPERATOR_NEW_BODY(_Znwm);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new[](__sanitizer::uptr size);
-void *operator new[](__sanitizer::uptr size) {
- OPERATOR_NEW_BODY(_Znam);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new(__sanitizer::uptr size, std::nothrow_t const&);
-void *operator new(__sanitizer::uptr size, std::nothrow_t const&) {
- OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new[](__sanitizer::uptr size, std::nothrow_t const&);
-void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) {
- OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t);
-}
-
-#define OPERATOR_DELETE_BODY(mangled_name) \
- if (ptr == 0) return; \
- if (cur_thread()->in_symbolizer) \
- return __libc_free(ptr); \
- invoke_free_hook(ptr); \
- SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \
- user_free(thr, pc, ptr);
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete(void *ptr) throw();
-void operator delete(void *ptr) throw() {
- OPERATOR_DELETE_BODY(_ZdlPv);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete[](void *ptr) throw();
-void operator delete[](void *ptr) throw() {
- OPERATOR_DELETE_BODY(_ZdaPv);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete(void *ptr, std::nothrow_t const&);
-void operator delete(void *ptr, std::nothrow_t const&) {
- OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete[](void *ptr, std::nothrow_t const&);
-void operator delete[](void *ptr, std::nothrow_t const&) {
- OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t);
-}
-
TSAN_INTERCEPTOR(uptr, strlen, const char *s) {
SCOPED_TSAN_INTERCEPTOR(strlen, s);
uptr len = internal_strlen(s);
@@ -660,15 +581,21 @@ TSAN_INTERCEPTOR(uptr, strlen, const char *s) {
}
TSAN_INTERCEPTOR(void*, memset, void *dst, int v, uptr size) {
- SCOPED_TSAN_INTERCEPTOR(memset, dst, v, size);
- MemoryAccessRange(thr, pc, (uptr)dst, size, true);
+ // On FreeBSD we get here from libthr internals on thread initialization.
+ if (!COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) {
+ SCOPED_TSAN_INTERCEPTOR(memset, dst, v, size);
+ MemoryAccessRange(thr, pc, (uptr)dst, size, true);
+ }
return internal_memset(dst, v, size);
}
TSAN_INTERCEPTOR(void*, memcpy, void *dst, const void *src, uptr size) {
- SCOPED_TSAN_INTERCEPTOR(memcpy, dst, src, size);
- MemoryAccessRange(thr, pc, (uptr)dst, size, true);
- MemoryAccessRange(thr, pc, (uptr)src, size, false);
+ // On FreeBSD we get here from libthr internals on thread initialization.
+ if (!COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) {
+ SCOPED_TSAN_INTERCEPTOR(memcpy, dst, src, size);
+ MemoryAccessRange(thr, pc, (uptr)dst, size, true);
+ MemoryAccessRange(thr, pc, (uptr)src, size, false);
+ }
return internal_memcpy(dst, src, size);
}
@@ -696,8 +623,9 @@ TSAN_INTERCEPTOR(void*, memmove, void *dst, void *src, uptr n) {
TSAN_INTERCEPTOR(char*, strchr, char *s, int c) {
SCOPED_TSAN_INTERCEPTOR(strchr, s, c);
char *res = REAL(strchr)(s, c);
- uptr len = res ? (char*)res - (char*)s + 1 : internal_strlen(s) + 1;
- MemoryAccessRange(thr, pc, (uptr)s, len, false);
+ uptr len = internal_strlen(s);
+ uptr n = res ? (char*)res - (char*)s + 1 : len + 1;
+ READ_STRING_OF_LEN(thr, pc, s, len, n);
return res;
}
@@ -705,7 +633,7 @@ TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) {
SCOPED_TSAN_INTERCEPTOR(strchrnul, s, c);
char *res = REAL(strchrnul)(s, c);
uptr len = (char*)res - (char*)s + 1;
- MemoryAccessRange(thr, pc, (uptr)s, len, false);
+ READ_STRING(thr, pc, s, len);
return res;
}
@@ -731,16 +659,6 @@ TSAN_INTERCEPTOR(char*, strncpy, char *dst, char *src, uptr n) {
return REAL(strncpy)(dst, src, n);
}
-TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) {
- SCOPED_TSAN_INTERCEPTOR(strstr, s1, s2);
- const char *res = REAL(strstr)(s1, s2);
- uptr len1 = internal_strlen(s1);
- uptr len2 = internal_strlen(s2);
- MemoryAccessRange(thr, pc, (uptr)s1, len1 + 1, false);
- MemoryAccessRange(thr, pc, (uptr)s2, len2 + 1, false);
- return res;
-}
-
TSAN_INTERCEPTOR(char*, strdup, const char *str) {
SCOPED_TSAN_INTERCEPTOR(strdup, str);
// strdup will call malloc, so no instrumentation is required here.
@@ -796,7 +714,11 @@ TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot,
TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz);
- DontNeedShadowFor((uptr)addr, sz);
+ if (sz != 0) {
+ // If sz == 0, munmap will return EINVAL and don't unmap any memory.
+ DontNeedShadowFor((uptr)addr, sz);
+ ctx->metamap.ResetRange(thr, pc, (uptr)addr, (uptr)sz);
+ }
int res = REAL(munmap)(addr, sz);
return res;
}
@@ -878,7 +800,7 @@ static void thread_finalize(void *v) {
{
ThreadState *thr = cur_thread();
ThreadFinish(thr);
- SignalContext *sctx = thr->signal_ctx;
+ ThreadSignalContext *sctx = thr->signal_ctx;
if (sctx) {
thr->signal_ctx = 0;
UnmapOrDie(sctx, sizeof(*sctx));
@@ -904,15 +826,15 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
ScopedIgnoreInterceptors ignore;
ThreadIgnoreBegin(thr, 0);
if (pthread_setspecific(g_thread_finalize_key,
- (void *)kPthreadDestructorIterations)) {
+ (void *)GetPthreadDestructorIterations())) {
Printf("ThreadSanitizer: failed to set thread key\n");
Die();
}
ThreadIgnoreEnd(thr, 0);
while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
pthread_yield();
- atomic_store(&p->tid, 0, memory_order_release);
ThreadStart(thr, tid, GetTid());
+ atomic_store(&p->tid, 0, memory_order_release);
}
void *res = callback(param);
// Prevent the callback from being tail called,
@@ -960,6 +882,13 @@ TSAN_INTERCEPTOR(int, pthread_create,
if (res == 0) {
int tid = ThreadCreate(thr, pc, *(uptr*)th, detached);
CHECK_NE(tid, 0);
+ // Synchronization on p.tid serves two purposes:
+ // 1. ThreadCreate must finish before the new thread starts.
+ // Otherwise the new thread can call pthread_detach, but the pthread_t
+ // identifier is not yet registered in ThreadRegistry by ThreadCreate.
+ // 2. ThreadStart must finish before this thread continues.
+ // Otherwise, this thread can call pthread_detach and reset thr->sync
+ // before the new thread got a chance to acquire from it in ThreadStart.
atomic_store(&p.tid, tid, memory_order_release);
while (atomic_load(&p.tid, memory_order_acquire) != 0)
pthread_yield();
@@ -1032,13 +961,25 @@ static void *init_cond(void *c, bool force = false) {
}
struct CondMutexUnlockCtx {
+ ScopedInterceptor *si;
ThreadState *thr;
uptr pc;
void *m;
};
static void cond_mutex_unlock(CondMutexUnlockCtx *arg) {
+ // pthread_cond_wait interceptor has enabled async signal delivery
+ // (see BlockingCall below). Disable async signals since we are running
+ // tsan code. Also ScopedInterceptor and BlockingCall destructors won't run
+ // since the thread is cancelled, so we have to manually execute them
+ // (the thread still can run some user code due to pthread_cleanup_push).
+ ThreadSignalContext *ctx = SigCtx(arg->thr);
+ CHECK_EQ(atomic_load(&ctx->in_blocking_func, memory_order_relaxed), 1);
+ atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
MutexLock(arg->thr, arg->pc, (uptr)arg->m);
+ // Undo BlockingCall ctor effects.
+ arg->thr->ignore_interceptors--;
+ arg->si->~ScopedInterceptor();
}
INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
@@ -1051,14 +992,19 @@ INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) {
void *cond = init_cond(c);
SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, cond, m);
- MutexUnlock(thr, pc, (uptr)m);
MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
- CondMutexUnlockCtx arg = {thr, pc, m};
+ MutexUnlock(thr, pc, (uptr)m);
+ CondMutexUnlockCtx arg = {&si, thr, pc, m};
+ int res = 0;
// This ensures that we handle mutex lock even in case of pthread_cancel.
// See test/tsan/cond_cancel.cc.
- int res = call_pthread_cancel_with_cleanup(
- (int(*)(void *c, void *m, void *abstime))REAL(pthread_cond_wait),
- cond, m, 0, (void(*)(void *arg))cond_mutex_unlock, &arg);
+ {
+ // Enable signal delivery while the thread is blocked.
+ BlockingCall bc(thr);
+ res = call_pthread_cancel_with_cleanup(
+ (int(*)(void *c, void *m, void *abstime))REAL(pthread_cond_wait),
+ cond, m, 0, (void(*)(void *arg))cond_mutex_unlock, &arg);
+ }
if (res == errno_EOWNERDEAD)
MutexRepair(thr, pc, (uptr)m);
MutexLock(thr, pc, (uptr)m);
@@ -1068,14 +1014,18 @@ INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) {
INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) {
void *cond = init_cond(c);
SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, cond, m, abstime);
- MutexUnlock(thr, pc, (uptr)m);
MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
- CondMutexUnlockCtx arg = {thr, pc, m};
+ MutexUnlock(thr, pc, (uptr)m);
+ CondMutexUnlockCtx arg = {&si, thr, pc, m};
+ int res = 0;
// This ensures that we handle mutex lock even in case of pthread_cancel.
// See test/tsan/cond_cancel.cc.
- int res = call_pthread_cancel_with_cleanup(
- REAL(pthread_cond_timedwait), cond, m, abstime,
- (void(*)(void *arg))cond_mutex_unlock, &arg);
+ {
+ BlockingCall bc(thr);
+ res = call_pthread_cancel_with_cleanup(
+ REAL(pthread_cond_timedwait), cond, m, abstime,
+ (void(*)(void *arg))cond_mutex_unlock, &arg);
+ }
if (res == errno_EOWNERDEAD)
MutexRepair(thr, pc, (uptr)m);
MutexLock(thr, pc, (uptr)m);
@@ -1382,6 +1332,7 @@ TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__xstat)(version, path, buf);
}
#define TSAN_MAYBE_INTERCEPT___XSTAT TSAN_INTERCEPT(__xstat)
@@ -1392,9 +1343,11 @@ TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) {
TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) {
#if SANITIZER_FREEBSD
SCOPED_TSAN_INTERCEPTOR(stat, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(stat)(path, buf);
#else
SCOPED_TSAN_INTERCEPTOR(__xstat, 0, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__xstat)(0, path, buf);
#endif
}
@@ -1402,6 +1355,7 @@ TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__xstat64)(version, path, buf);
}
#define TSAN_MAYBE_INTERCEPT___XSTAT64 TSAN_INTERCEPT(__xstat64)
@@ -1412,6 +1366,7 @@ TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__xstat64)(0, path, buf);
}
#define TSAN_MAYBE_INTERCEPT_STAT64 TSAN_INTERCEPT(stat64)
@@ -1422,6 +1377,7 @@ TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__lxstat)(version, path, buf);
}
#define TSAN_MAYBE_INTERCEPT___LXSTAT TSAN_INTERCEPT(__lxstat)
@@ -1432,9 +1388,11 @@ TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) {
TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) {
#if SANITIZER_FREEBSD
SCOPED_TSAN_INTERCEPTOR(lstat, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(lstat)(path, buf);
#else
SCOPED_TSAN_INTERCEPTOR(__lxstat, 0, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__lxstat)(0, path, buf);
#endif
}
@@ -1442,6 +1400,7 @@ TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__lxstat64)(version, path, buf);
}
#define TSAN_MAYBE_INTERCEPT___LXSTAT64 TSAN_INTERCEPT(__lxstat64)
@@ -1452,6 +1411,7 @@ TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf);
+ READ_STRING(thr, pc, path, 0);
return REAL(__lxstat64)(0, path, buf);
}
#define TSAN_MAYBE_INTERCEPT_LSTAT64 TSAN_INTERCEPT(lstat64)
@@ -1511,6 +1471,7 @@ TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) {
TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode);
+ READ_STRING(thr, pc, name, 0);
int fd = REAL(open)(name, flags, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
@@ -1520,6 +1481,7 @@ TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode);
+ READ_STRING(thr, pc, name, 0);
int fd = REAL(open64)(name, flags, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
@@ -1532,6 +1494,7 @@ TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat, name, mode);
+ READ_STRING(thr, pc, name, 0);
int fd = REAL(creat)(name, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
@@ -1541,6 +1504,7 @@ TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat64, name, mode);
+ READ_STRING(thr, pc, name, 0);
int fd = REAL(creat64)(name, mode);
if (fd >= 0)
FdFileCreate(thr, pc, fd);
@@ -1555,7 +1519,7 @@ TSAN_INTERCEPTOR(int, dup, int oldfd) {
SCOPED_TSAN_INTERCEPTOR(dup, oldfd);
int newfd = REAL(dup)(oldfd);
if (oldfd >= 0 && newfd >= 0 && newfd != oldfd)
- FdDup(thr, pc, oldfd, newfd);
+ FdDup(thr, pc, oldfd, newfd, true);
return newfd;
}
@@ -1563,7 +1527,7 @@ TSAN_INTERCEPTOR(int, dup2, int oldfd, int newfd) {
SCOPED_TSAN_INTERCEPTOR(dup2, oldfd, newfd);
int newfd2 = REAL(dup2)(oldfd, newfd);
if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
- FdDup(thr, pc, oldfd, newfd2);
+ FdDup(thr, pc, oldfd, newfd2, false);
return newfd2;
}
@@ -1571,7 +1535,7 @@ TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) {
SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags);
int newfd2 = REAL(dup3)(oldfd, newfd, flags);
if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
- FdDup(thr, pc, oldfd, newfd2);
+ FdDup(thr, pc, oldfd, newfd2, false);
return newfd2;
}
@@ -1940,7 +1904,7 @@ static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire,
}
void ProcessPendingSignals(ThreadState *thr) {
- SignalContext *sctx = SigCtx(thr);
+ ThreadSignalContext *sctx = SigCtx(thr);
if (sctx == 0 ||
atomic_load(&sctx->have_pending_signals, memory_order_relaxed) == 0)
return;
@@ -1948,8 +1912,8 @@ void ProcessPendingSignals(ThreadState *thr) {
atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed);
// These are too big for stack.
static THREADLOCAL __sanitizer_sigset_t emptyset, oldset;
- REAL(sigfillset)(&emptyset);
- pthread_sigmask(SIG_SETMASK, &emptyset, &oldset);
+ CHECK_EQ(0, REAL(sigfillset)(&emptyset));
+ CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &emptyset, &oldset));
for (int sig = 0; sig < kSigCount; sig++) {
SignalDesc *signal = &sctx->pending_signals[sig];
if (signal->armed) {
@@ -1958,13 +1922,13 @@ void ProcessPendingSignals(ThreadState *thr) {
&signal->siginfo, &signal->ctx);
}
}
- pthread_sigmask(SIG_SETMASK, &oldset, 0);
+ CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &oldset, 0));
atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed);
}
} // namespace __tsan
-static bool is_sync_signal(SignalContext *sctx, int sig) {
+static bool is_sync_signal(ThreadSignalContext *sctx, int sig) {
return sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS ||
// If we are sending signal to ourselves, we must process it now.
@@ -1974,7 +1938,7 @@ static bool is_sync_signal(SignalContext *sctx, int sig) {
void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
my_siginfo_t *info, void *ctx) {
ThreadState *thr = cur_thread();
- SignalContext *sctx = SigCtx(thr);
+ ThreadSignalContext *sctx = SigCtx(thr);
if (sig < 0 || sig >= kSigCount) {
VPrintf(1, "ThreadSanitizer: ignoring signal %d\n", sig);
return;
@@ -2083,7 +2047,7 @@ TSAN_INTERCEPTOR(int, sigsuspend, const __sanitizer_sigset_t *mask) {
TSAN_INTERCEPTOR(int, raise, int sig) {
SCOPED_TSAN_INTERCEPTOR(raise, sig);
- SignalContext *sctx = SigCtx(thr);
+ ThreadSignalContext *sctx = SigCtx(thr);
CHECK_NE(sctx, 0);
int prev = sctx->int_signal_send;
sctx->int_signal_send = sig;
@@ -2095,7 +2059,7 @@ TSAN_INTERCEPTOR(int, raise, int sig) {
TSAN_INTERCEPTOR(int, kill, int pid, int sig) {
SCOPED_TSAN_INTERCEPTOR(kill, pid, sig);
- SignalContext *sctx = SigCtx(thr);
+ ThreadSignalContext *sctx = SigCtx(thr);
CHECK_NE(sctx, 0);
int prev = sctx->int_signal_send;
if (pid == (int)internal_getpid()) {
@@ -2111,7 +2075,7 @@ TSAN_INTERCEPTOR(int, kill, int pid, int sig) {
TSAN_INTERCEPTOR(int, pthread_kill, void *tid, int sig) {
SCOPED_TSAN_INTERCEPTOR(pthread_kill, tid, sig);
- SignalContext *sctx = SigCtx(thr);
+ ThreadSignalContext *sctx = SigCtx(thr);
CHECK_NE(sctx, 0);
int prev = sctx->int_signal_send;
if (tid == pthread_self()) {
@@ -2180,6 +2144,50 @@ TSAN_INTERCEPTOR(int, vfork, int fake) {
return WRAP(fork)(fake);
}
+typedef int (*dl_iterate_phdr_cb_t)(__sanitizer_dl_phdr_info *info, SIZE_T size,
+ void *data);
+struct dl_iterate_phdr_data {
+ ThreadState *thr;
+ uptr pc;
+ dl_iterate_phdr_cb_t cb;
+ void *data;
+};
+
+static bool IsAppNotRodata(uptr addr) {
+ return IsAppMem(addr) && *(u64*)MemToShadow(addr) != kShadowRodata;
+}
+
+static int dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size,
+ void *data) {
+ dl_iterate_phdr_data *cbdata = (dl_iterate_phdr_data *)data;
+ // dlopen/dlclose allocate/free dynamic-linker-internal memory, which is later
+ // accessible in dl_iterate_phdr callback. But we don't see synchronization
+ // inside of dynamic linker, so we "unpoison" it here in order to not
+ // produce false reports. Ignoring malloc/free in dlopen/dlclose is not enough
+ // because some libc functions call __libc_dlopen.
+ if (info && IsAppNotRodata((uptr)info->dlpi_name))
+ MemoryResetRange(cbdata->thr, cbdata->pc, (uptr)info->dlpi_name,
+ internal_strlen(info->dlpi_name));
+ int res = cbdata->cb(info, size, cbdata->data);
+ // Perform the check one more time in case info->dlpi_name was overwritten
+ // by user callback.
+ if (info && IsAppNotRodata((uptr)info->dlpi_name))
+ MemoryResetRange(cbdata->thr, cbdata->pc, (uptr)info->dlpi_name,
+ internal_strlen(info->dlpi_name));
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb_t cb, void *data) {
+ SCOPED_TSAN_INTERCEPTOR(dl_iterate_phdr, cb, data);
+ dl_iterate_phdr_data cbdata;
+ cbdata.thr = thr;
+ cbdata.pc = pc;
+ cbdata.cb = cb;
+ cbdata.data = data;
+ int res = REAL(dl_iterate_phdr)(dl_iterate_phdr_cb, &cbdata);
+ return res;
+}
+
static int OnExit(ThreadState *thr) {
int status = Finalize(thr);
FlushStreams();
@@ -2311,6 +2319,14 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc,
HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \
((TsanInterceptorContext *)ctx)->pc, msg)
+#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
+ if (TsanThread *t = GetCurrentThread()) { \
+ *begin = t->tls_begin(); \
+ *end = t->tls_end(); \
+ } else { \
+ *begin = *end = 0; \
+ }
+
#include "sanitizer_common/sanitizer_common_interceptors.inc"
#define TSAN_SYSCALL() \
@@ -2490,7 +2506,6 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(strrchr);
TSAN_INTERCEPT(strcpy); // NOLINT
TSAN_INTERCEPT(strncpy);
- TSAN_INTERCEPT(strstr);
TSAN_INTERCEPT(strdup);
TSAN_INTERCEPT(pthread_create);
@@ -2606,6 +2621,7 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(fork);
TSAN_INTERCEPT(vfork);
+ TSAN_INTERCEPT(dl_iterate_phdr);
TSAN_INTERCEPT(on_exit);
TSAN_INTERCEPT(__cxa_atexit);
TSAN_INTERCEPT(_exit);
diff --git a/lib/tsan/rtl/tsan_interceptors.h b/lib/tsan/rtl/tsan_interceptors.h
new file mode 100644
index 000000000000..49b79a7c5f9e
--- /dev/null
+++ b/lib/tsan/rtl/tsan_interceptors.h
@@ -0,0 +1,37 @@
+#ifndef TSAN_INTERCEPTORS_H
+#define TSAN_INTERCEPTORS_H
+
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+class ScopedInterceptor {
+ public:
+ ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc);
+ ~ScopedInterceptor();
+ private:
+ ThreadState *const thr_;
+ const uptr pc_;
+ bool in_ignored_lib_;
+};
+
+} // namespace __tsan
+
+#define SCOPED_INTERCEPTOR_RAW(func, ...) \
+ ThreadState *thr = cur_thread(); \
+ const uptr caller_pc = GET_CALLER_PC(); \
+ ScopedInterceptor si(thr, #func, caller_pc); \
+ const uptr pc = StackTrace::GetCurrentPc(); \
+ (void)pc; \
+/**/
+
+#if SANITIZER_FREEBSD
+#define __libc_free __free
+#define __libc_malloc __malloc
+#endif
+
+extern "C" void __libc_free(void *ptr);
+extern "C" void *__libc_malloc(uptr size);
+
+#endif // TSAN_INTERCEPTORS_H
diff --git a/lib/tsan/rtl/tsan_interface.cc b/lib/tsan/rtl/tsan_interface.cc
index 9bc9a696363d..809d2abeb0be 100644
--- a/lib/tsan/rtl/tsan_interface.cc
+++ b/lib/tsan/rtl/tsan_interface.cc
@@ -38,6 +38,16 @@ void __tsan_write16(void *addr) {
MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8);
}
+void __tsan_read16_pc(void *addr, void *pc) {
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8);
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr + 8, kSizeLog8);
+}
+
+void __tsan_write16_pc(void *addr, void *pc) {
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8);
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr + 8, kSizeLog8);
+}
+
// __tsan_unaligned_read/write calls are emitted by compiler.
void __tsan_unaligned_read2(const void *addr) {
diff --git a/lib/tsan/rtl/tsan_interface.h b/lib/tsan/rtl/tsan_interface.h
index a05e6f0f6d09..41e084b82ab1 100644
--- a/lib/tsan/rtl/tsan_interface.h
+++ b/lib/tsan/rtl/tsan_interface.h
@@ -51,6 +51,18 @@ SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write4(void *addr);
SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write8(void *addr);
SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write16(void *addr);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read1_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read2_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read4_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read8_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read16_pc(void *addr, void *pc);
+
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write1_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write2_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write4_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write8_pc(void *addr, void *pc);
+SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write16_pc(void *addr, void *pc);
+
SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_read(void **vptr_p);
SANITIZER_INTERFACE_ATTRIBUTE
void __tsan_vptr_update(void **vptr_p, void *new_val);
diff --git a/lib/tsan/rtl/tsan_interface_inl.h b/lib/tsan/rtl/tsan_interface_inl.h
index 0187e49d96e5..8852aa348b8e 100644
--- a/lib/tsan/rtl/tsan_interface_inl.h
+++ b/lib/tsan/rtl/tsan_interface_inl.h
@@ -50,6 +50,38 @@ void __tsan_write8(void *addr) {
MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8);
}
+void __tsan_read1_pc(void *addr, void *pc) {
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog1);
+}
+
+void __tsan_read2_pc(void *addr, void *pc) {
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog2);
+}
+
+void __tsan_read4_pc(void *addr, void *pc) {
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog4);
+}
+
+void __tsan_read8_pc(void *addr, void *pc) {
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8);
+}
+
+void __tsan_write1_pc(void *addr, void *pc) {
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog1);
+}
+
+void __tsan_write2_pc(void *addr, void *pc) {
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog2);
+}
+
+void __tsan_write4_pc(void *addr, void *pc) {
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog4);
+}
+
+void __tsan_write8_pc(void *addr, void *pc) {
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8);
+}
+
void __tsan_vptr_update(void **vptr_p, void *new_val) {
CHECK_EQ(sizeof(vptr_p), 8);
if (*vptr_p != new_val) {
diff --git a/lib/tsan/rtl/tsan_mman.cc b/lib/tsan/rtl/tsan_mman.cc
index ebb3f77fb992..12a616ff5ae8 100644
--- a/lib/tsan/rtl/tsan_mman.cc
+++ b/lib/tsan/rtl/tsan_mman.cc
@@ -36,6 +36,23 @@ struct MapUnmapCallback {
// We are about to unmap a chunk of user memory.
// Mark the corresponding shadow memory as not needed.
DontNeedShadowFor(p, size);
+ // Mark the corresponding meta shadow memory as not needed.
+ // Note the block does not contain any meta info at this point
+ // (this happens after free).
+ const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize;
+ const uptr kPageSize = GetPageSizeCached() * kMetaRatio;
+ // Block came from LargeMmapAllocator, so must be large.
+ // We rely on this in the calculations below.
+ CHECK_GE(size, 2 * kPageSize);
+ uptr diff = RoundUp(p, kPageSize) - p;
+ if (diff != 0) {
+ p += diff;
+ size -= diff;
+ }
+ diff = p + size - RoundDown(p + size, kPageSize);
+ if (diff != 0)
+ size -= diff;
+ FlushUnneededShadowMemory((uptr)MemToMeta(p), size / kMetaRatio);
}
};
diff --git a/lib/tsan/rtl/tsan_new_delete.cc b/lib/tsan/rtl/tsan_new_delete.cc
new file mode 100644
index 000000000000..2d9d044e42fa
--- /dev/null
+++ b/lib/tsan/rtl/tsan_new_delete.cc
@@ -0,0 +1,88 @@
+//===-- tsan_new_delete.cc ----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Interceptors for operators new and delete.
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "tsan_interceptors.h"
+
+using namespace __tsan; // NOLINT
+
+namespace std {
+struct nothrow_t {};
+} // namespace std
+
+#define OPERATOR_NEW_BODY(mangled_name) \
+ if (cur_thread()->in_symbolizer) \
+ return __libc_malloc(size); \
+ void *p = 0; \
+ { \
+ SCOPED_INTERCEPTOR_RAW(mangled_name, size); \
+ p = user_alloc(thr, pc, size); \
+ } \
+ invoke_malloc_hook(p, size); \
+ return p;
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new(__sanitizer::uptr size);
+void *operator new(__sanitizer::uptr size) {
+ OPERATOR_NEW_BODY(_Znwm);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new[](__sanitizer::uptr size);
+void *operator new[](__sanitizer::uptr size) {
+ OPERATOR_NEW_BODY(_Znam);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new(__sanitizer::uptr size, std::nothrow_t const&);
+void *operator new(__sanitizer::uptr size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new[](__sanitizer::uptr size, std::nothrow_t const&);
+void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t);
+}
+
+#define OPERATOR_DELETE_BODY(mangled_name) \
+ if (ptr == 0) return; \
+ if (cur_thread()->in_symbolizer) \
+ return __libc_free(ptr); \
+ invoke_free_hook(ptr); \
+ SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \
+ user_free(thr, pc, ptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete(void *ptr) throw();
+void operator delete(void *ptr) throw() {
+ OPERATOR_DELETE_BODY(_ZdlPv);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete[](void *ptr) throw();
+void operator delete[](void *ptr) throw() {
+ OPERATOR_DELETE_BODY(_ZdaPv);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete(void *ptr, std::nothrow_t const&);
+void operator delete(void *ptr, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete[](void *ptr, std::nothrow_t const&);
+void operator delete[](void *ptr, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t);
+}
diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc
index 659e8d8a8345..1309058210ce 100644
--- a/lib/tsan/rtl/tsan_platform_linux.cc
+++ b/lib/tsan/rtl/tsan_platform_linux.cc
@@ -18,6 +18,7 @@
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_posix.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "sanitizer_common/sanitizer_stoptheworld.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
@@ -135,7 +136,7 @@ static void ProtectRange(uptr beg, uptr end) {
CHECK_LE(beg, end);
if (beg == end)
return;
- if (beg != (uptr)Mprotect(beg, end - beg)) {
+ if (beg != (uptr)MmapNoAccess(beg, end - beg)) {
Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
Printf("FATAL: Make sure you are not using unlimited stack\n");
Die();
@@ -201,8 +202,8 @@ static void MapRodata() {
void InitializeShadowMemory() {
// Map memory shadow.
- uptr shadow = (uptr)MmapFixedNoReserve(kShadowBeg,
- kShadowEnd - kShadowBeg);
+ uptr shadow =
+ (uptr)MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg, "shadow");
if (shadow != kShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "
@@ -223,6 +224,10 @@ void InitializeShadowMemory() {
#endif
NoHugePagesInRegion(MemToShadow(kMadviseRangeBeg),
kMadviseRangeSize * kShadowMultiplier);
+ // Meta shadow is compressing and we don't flush it,
+ // so it makes sense to mark it as NOHUGEPAGE to not over-allocate memory.
+ // On one program it reduces memory consumption from 5GB to 2.5GB.
+ NoHugePagesInRegion(kMetaShadowBeg, kMetaShadowEnd - kMetaShadowBeg);
if (common_flags()->use_madv_dontdump)
DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg);
DPrintf("memory shadow: %zx-%zx (%zuGB)\n",
@@ -231,7 +236,8 @@ void InitializeShadowMemory() {
// Map meta shadow.
uptr meta_size = kMetaShadowEnd - kMetaShadowBeg;
- uptr meta = (uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size);
+ uptr meta =
+ (uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size, "meta shadow");
if (meta != kMetaShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "
@@ -394,6 +400,8 @@ int ExtractRecvmsgFDs(void *msgp, int *fds, int nfd) {
return res;
}
+// Note: this function runs with async signals enabled,
+// so it must not touch any tsan state.
int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
void *abstime), void *c, void *m, void *abstime,
void(*cleanup)(void *arg), void *arg) {
diff --git a/lib/tsan/rtl/tsan_platform_mac.cc b/lib/tsan/rtl/tsan_platform_mac.cc
index 63f1748e13ce..b72d9b07ef35 100644
--- a/lib/tsan/rtl/tsan_platform_mac.cc
+++ b/lib/tsan/rtl/tsan_platform_mac.cc
@@ -76,6 +76,8 @@ void InitializePlatform() {
}
#ifndef SANITIZER_GO
+// Note: this function runs with async signals enabled,
+// so it must not touch any tsan state.
int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
void *abstime), void *c, void *m, void *abstime,
void(*cleanup)(void *arg), void *arg) {
diff --git a/lib/tsan/rtl/tsan_report.cc b/lib/tsan/rtl/tsan_report.cc
index 7e69cb4ecfbc..f4b06878a58e 100644
--- a/lib/tsan/rtl/tsan_report.cc
+++ b/lib/tsan/rtl/tsan_report.cc
@@ -120,6 +120,7 @@ void PrintStack(const ReportStack *ent) {
for (int i = 0; frame && frame->info.address; frame = frame->next, i++) {
InternalScopedString res(2 * GetPageSizeCached());
RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info,
+ common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix, "__interceptor_");
Printf("%s\n", res.data());
}
@@ -335,10 +336,8 @@ void PrintReport(const ReportDesc *rep) {
Printf(" And %d more similar thread leaks.\n\n", rep->count - 1);
if (ReportStack *stack = ChooseSummaryStack(rep)) {
- if (SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames)) {
- const AddressInfo &info = frame->info;
- ReportErrorSummary(rep_typ_str, info.file, info.line, info.function);
- }
+ if (SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames))
+ ReportErrorSummary(rep_typ_str, frame->info);
}
Printf("==================\n");
diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc
index b76f3e05dde6..63c356b228a4 100644
--- a/lib/tsan/rtl/tsan_rtl.cc
+++ b/lib/tsan/rtl/tsan_rtl.cc
@@ -24,6 +24,7 @@
#include "tsan_mman.h"
#include "tsan_suppressions.h"
#include "tsan_symbolize.h"
+#include "ubsan/ubsan_init.h"
#ifdef __SSE3__
// <emmintrin.h> transitively includes <stdlib.h>,
@@ -66,9 +67,12 @@ static char thread_registry_placeholder[sizeof(ThreadRegistry)];
static ThreadContextBase *CreateThreadContext(u32 tid) {
// Map thread trace when context is created.
- MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event));
+ char name[50];
+ internal_snprintf(name, sizeof(name), "trace %u", tid);
+ MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event), name);
const uptr hdr = GetThreadTraceHeader(tid);
- MapThreadTrace(hdr, sizeof(Trace));
+ internal_snprintf(name, sizeof(name), "trace header %u", tid);
+ MapThreadTrace(hdr, sizeof(Trace), name);
new((void*)hdr) Trace();
// We are going to use only a small part of the trace with the default
// value of history_size. However, the constructor writes to the whole trace.
@@ -133,7 +137,7 @@ static void MemoryProfiler(Context *ctx, fd_t fd, int i) {
ctx->thread_registry->GetNumberOfThreads(&n_threads, &n_running_threads);
InternalScopedBuffer<char> buf(4096);
WriteMemoryProfile(buf.data(), buf.size(), n_threads, n_running_threads);
- internal_write(fd, buf.data(), internal_strlen(buf.data()));
+ WriteToFile(fd, buf.data(), internal_strlen(buf.data()));
}
static void BackgroundThread(void *arg) {
@@ -153,12 +157,12 @@ static void BackgroundThread(void *arg) {
} else {
InternalScopedString filename(kMaxPathLength);
filename.append("%s.%d", flags()->profile_memory, (int)internal_getpid());
- uptr openrv = OpenFile(filename.data(), true);
- if (internal_iserror(openrv)) {
+ fd_t fd = OpenFile(filename.data(), WrOnly);
+ if (fd == kInvalidFd) {
Printf("ThreadSanitizer: failed to open memory profile file '%s'\n",
&filename[0]);
} else {
- mprof_fd = openrv;
+ mprof_fd = fd;
}
}
}
@@ -236,7 +240,7 @@ void MapShadow(uptr addr, uptr size) {
// Global data is not 64K aligned, but there are no adjacent mappings,
// so we can get away with unaligned mapping.
// CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
- MmapFixedNoReserve(MemToShadow(addr), size * kShadowMultiplier);
+ MmapFixedNoReserve(MemToShadow(addr), size * kShadowMultiplier, "shadow");
// Meta shadow is 2:1, so tread carefully.
static bool data_mapped = false;
@@ -248,7 +252,7 @@ void MapShadow(uptr addr, uptr size) {
if (!data_mapped) {
// First call maps data+bss.
data_mapped = true;
- MmapFixedNoReserve(meta_begin, meta_end - meta_begin);
+ MmapFixedNoReserve(meta_begin, meta_end - meta_begin, "meta shadow");
} else {
// Mapping continous heap.
// Windows wants 64K alignment.
@@ -258,19 +262,19 @@ void MapShadow(uptr addr, uptr size) {
return;
if (meta_begin < mapped_meta_end)
meta_begin = mapped_meta_end;
- MmapFixedNoReserve(meta_begin, meta_end - meta_begin);
+ MmapFixedNoReserve(meta_begin, meta_end - meta_begin, "meta shadow");
mapped_meta_end = meta_end;
}
VPrintf(2, "mapped meta shadow for (%p-%p) at (%p-%p)\n",
addr, addr+size, meta_begin, meta_end);
}
-void MapThreadTrace(uptr addr, uptr size) {
+void MapThreadTrace(uptr addr, uptr size, const char *name) {
DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size);
CHECK_GE(addr, kTraceMemBeg);
CHECK_LE(addr + size, kTraceMemEnd);
CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
- uptr addr1 = (uptr)MmapFixedNoReserve(addr, size);
+ uptr addr1 = (uptr)MmapFixedNoReserve(addr, size, name);
if (addr1 != addr) {
Printf("FATAL: ThreadSanitizer can not mmap thread trace (%p/%p->%p)\n",
addr, size, addr1);
@@ -315,6 +319,7 @@ void Initialize(ThreadState *thr) {
ctx = new(ctx_placeholder) Context;
const char *options = GetEnv(kTsanOptionsEnv);
InitializeFlags(&ctx->flags, options);
+ CacheBinaryName();
#ifndef SANITIZER_GO
InitializeAllocator();
#endif
@@ -350,6 +355,9 @@ void Initialize(ThreadState *thr) {
int tid = ThreadCreate(thr, 0, 0, true);
CHECK_EQ(tid, 0);
ThreadStart(thr, tid, internal_getpid());
+#if TSAN_CONTAINS_UBSAN
+ __ubsan::InitAsPlugin();
+#endif
ctx->initialized = true;
if (flags()->stop_on_start) {
@@ -461,7 +469,7 @@ void GrowShadowStack(ThreadState *thr) {
#endif
u32 CurrentStackId(ThreadState *thr, uptr pc) {
- if (thr->shadow_stack_pos == 0) // May happen during bootstrap.
+ if (!thr->is_inited) // May happen during bootstrap.
return 0;
if (pc != 0) {
#ifndef SANITIZER_GO
diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h
index 7a60e5c55af4..a13e4b6379f0 100644
--- a/lib/tsan/rtl/tsan_rtl.h
+++ b/lib/tsan/rtl/tsan_rtl.h
@@ -313,7 +313,7 @@ class Shadow : public FastState {
}
};
-struct SignalContext;
+struct ThreadSignalContext;
struct JmpBuf {
uptr sp;
@@ -371,6 +371,7 @@ struct ThreadState {
const int unique_id;
bool in_symbolizer;
bool in_ignored_lib;
+ bool is_inited;
bool is_dead;
bool is_freeing;
bool is_vptr_access;
@@ -387,7 +388,7 @@ struct ThreadState {
DDLogicalThread *dd_lt;
atomic_uintptr_t in_signal_handler;
- SignalContext *signal_ctx;
+ ThreadSignalContext *signal_ctx;
DenseSlabAllocCache block_cache;
DenseSlabAllocCache sync_cache;
@@ -430,13 +431,13 @@ class ThreadContext : public ThreadContextBase {
u64 epoch1;
// Override superclass callbacks.
- void OnDead();
- void OnJoined(void *arg);
- void OnFinished();
- void OnStarted(void *arg);
- void OnCreated(void *arg);
- void OnReset();
- void OnDetached(void *arg);
+ void OnDead() override;
+ void OnJoined(void *arg) override;
+ void OnFinished() override;
+ void OnStarted(void *arg) override;
+ void OnCreated(void *arg) override;
+ void OnReset() override;
+ void OnDetached(void *arg) override;
};
struct RacyStacks {
@@ -573,7 +574,7 @@ void ALWAYS_INLINE StatSet(ThreadState *thr, StatType typ, u64 n) {
}
void MapShadow(uptr addr, uptr size);
-void MapThreadTrace(uptr addr, uptr size);
+void MapThreadTrace(uptr addr, uptr size, const char *name);
void DontNeedShadowFor(uptr addr, uptr size);
void InitializeShadowMemory();
void InitializeInterceptors();
diff --git a/lib/tsan/rtl/tsan_rtl_mutex.cc b/lib/tsan/rtl/tsan_rtl_mutex.cc
index ddf2b69bdbbc..09180d88a6fb 100644
--- a/lib/tsan/rtl/tsan_rtl_mutex.cc
+++ b/lib/tsan/rtl/tsan_rtl_mutex.cc
@@ -36,12 +36,8 @@ struct Callback : DDCallback {
DDCallback::lt = thr->dd_lt;
}
- virtual u32 Unwind() {
- return CurrentStackId(thr, pc);
- }
- virtual int UniqueTid() {
- return thr->unique_id;
- }
+ u32 Unwind() override { return CurrentStackId(thr, pc); }
+ int UniqueTid() override { return thr->unique_id; }
};
void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s) {
diff --git a/lib/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc
index 8ed1fbf2edae..66c78cfdd7c0 100644
--- a/lib/tsan/rtl/tsan_rtl_thread.cc
+++ b/lib/tsan/rtl/tsan_rtl_thread.cc
@@ -120,6 +120,7 @@ void ThreadContext::OnStarted(void *arg) {
AcquireImpl(thr, 0, &sync);
StatInc(thr, StatSyncAcquire);
sync.Reset(&thr->clock_cache);
+ thr->is_inited = true;
DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx "
"tls_addr=%zx tls_size=%zx\n",
tid, (uptr)epoch0, args->stk_addr, args->stk_size,
diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc
index 5413f04af1d2..e382f21f0dff 100644
--- a/lib/tsan/rtl/tsan_suppressions.cc
+++ b/lib/tsan/rtl/tsan_suppressions.cc
@@ -21,6 +21,7 @@
#include "tsan_mman.h"
#include "tsan_platform.h"
+#ifndef SANITIZER_GO
// Suppressions for true/false positives in standard libraries.
static const char *const std_suppressions =
// Libstdc++ 4.4 has data races in std::string.
@@ -33,7 +34,6 @@ static const char *const std_suppressions =
"race:std::_Sp_counted_ptr_inplace<std::thread::_Impl\n";
// Can be overriden in frontend.
-#ifndef SANITIZER_GO
extern "C" const char *WEAK __tsan_default_suppressions() {
return 0;
}
@@ -44,8 +44,9 @@ namespace __tsan {
ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
static SuppressionContext *suppression_ctx = nullptr;
static const char *kSuppressionTypes[] = {
- kSuppressionRace, kSuppressionMutex, kSuppressionThread,
- kSuppressionSignal, kSuppressionLib, kSuppressionDeadlock};
+ kSuppressionRace, kSuppressionRaceTop, kSuppressionMutex,
+ kSuppressionThread, kSuppressionSignal, kSuppressionLib,
+ kSuppressionDeadlock};
void InitializeSuppressions() {
CHECK_EQ(nullptr, suppression_ctx);
@@ -94,6 +95,18 @@ static const char *conv(ReportType typ) {
Die();
}
+static uptr IsSuppressed(const char *stype, const AddressInfo &info,
+ Suppression **sp) {
+ if (suppression_ctx->Match(info.function, stype, sp) ||
+ suppression_ctx->Match(info.file, stype, sp) ||
+ suppression_ctx->Match(info.module, stype, sp)) {
+ DPrintf("ThreadSanitizer: matched suppression '%s'\n", (*sp)->templ);
+ (*sp)->hit_count++;
+ return info.address;
+ }
+ return 0;
+}
+
uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
CHECK(suppression_ctx);
if (!suppression_ctx->SuppressionCount() || stack == 0 ||
@@ -102,19 +115,14 @@ uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
const char *stype = conv(typ);
if (0 == internal_strcmp(stype, kSuppressionNone))
return 0;
- Suppression *s;
for (const SymbolizedStack *frame = stack->frames; frame;
- frame = frame->next) {
- const AddressInfo &info = frame->info;
- if (suppression_ctx->Match(info.function, stype, &s) ||
- suppression_ctx->Match(info.file, stype, &s) ||
- suppression_ctx->Match(info.module, stype, &s)) {
- DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ);
- s->hit_count++;
- *sp = s;
- return info.address;
- }
+ frame = frame->next) {
+ uptr pc = IsSuppressed(stype, frame->info, sp);
+ if (pc != 0)
+ return pc;
}
+ if (0 == internal_strcmp(stype, kSuppressionRace) && stack->frames != nullptr)
+ return IsSuppressed(kSuppressionRaceTop, stack->frames->info, sp);
return 0;
}
diff --git a/lib/tsan/rtl/tsan_suppressions.h b/lib/tsan/rtl/tsan_suppressions.h
index e6d279c33a4e..526952d5bd15 100644
--- a/lib/tsan/rtl/tsan_suppressions.h
+++ b/lib/tsan/rtl/tsan_suppressions.h
@@ -20,6 +20,7 @@ namespace __tsan {
const char kSuppressionNone[] = "none";
const char kSuppressionRace[] = "race";
+const char kSuppressionRaceTop[] = "race_top";
const char kSuppressionMutex[] = "mutex";
const char kSuppressionThread[] = "thread";
const char kSuppressionSignal[] = "signal";
diff --git a/lib/tsan/rtl/tsan_symbolize.cc b/lib/tsan/rtl/tsan_symbolize.cc
index 3beb44f7ad3b..a6b9bca0501d 100644
--- a/lib/tsan/rtl/tsan_symbolize.cc
+++ b/lib/tsan/rtl/tsan_symbolize.cc
@@ -38,16 +38,10 @@ void ExitSymbolizer() {
// May be overriden by JIT/JAVA/etc,
// whatever produces PCs marked with kExternalPCBit.
-extern "C" bool __tsan_symbolize_external(uptr pc,
- char *func_buf, uptr func_siz,
- char *file_buf, uptr file_siz,
- int *line, int *col)
- SANITIZER_WEAK_ATTRIBUTE;
-
-bool __tsan_symbolize_external(uptr pc,
- char *func_buf, uptr func_siz,
- char *file_buf, uptr file_siz,
- int *line, int *col) {
+extern "C" bool WEAK __tsan_symbolize_external(uptr pc,
+ char *func_buf, uptr func_siz,
+ char *file_buf, uptr file_siz,
+ int *line, int *col) {
return false;
}
diff --git a/lib/tsan/rtl/tsan_symbolize.h b/lib/tsan/rtl/tsan_symbolize.h
index b59b6cfb8341..5a9710a3cd30 100644
--- a/lib/tsan/rtl/tsan_symbolize.h
+++ b/lib/tsan/rtl/tsan_symbolize.h
@@ -18,10 +18,6 @@
namespace __tsan {
-// Denotes fake PC values that come from JIT/JAVA/etc.
-// For such PC values __tsan_symbolize_external() will be called.
-const uptr kExternalPCBit = 1ULL << 60;
-
void EnterSymbolizer();
void ExitSymbolizer();
SymbolizedStack *SymbolizeCode(uptr addr);
diff --git a/lib/tsan/rtl/tsan_sync.cc b/lib/tsan/rtl/tsan_sync.cc
index 1041073bed51..4202d30a2e17 100644
--- a/lib/tsan/rtl/tsan_sync.cc
+++ b/lib/tsan/rtl/tsan_sync.cc
@@ -80,17 +80,21 @@ uptr MetaMap::FreeBlock(ThreadState *thr, uptr pc, uptr p) {
return sz;
}
-void MetaMap::FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz) {
+bool MetaMap::FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz) {
+ bool has_something = false;
u32 *meta = MemToMeta(p);
u32 *end = MemToMeta(p + sz);
if (end == meta)
end++;
for (; meta < end; meta++) {
u32 idx = *meta;
+ if (idx == 0) {
+ // Note: don't write to meta in this case -- the block can be huge.
+ continue;
+ }
*meta = 0;
- for (;;) {
- if (idx == 0)
- break;
+ has_something = true;
+ while (idx != 0) {
if (idx & kFlagBlock) {
block_alloc_.Free(&thr->block_cache, idx & ~kFlagMask);
break;
@@ -106,6 +110,64 @@ void MetaMap::FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz) {
}
}
}
+ return has_something;
+}
+
+// ResetRange removes all meta objects from the range.
+// It is called for large mmap-ed regions. The function is best-effort wrt
+// freeing of meta objects, because we don't want to page in the whole range
+// which can be huge. The function probes pages one-by-one until it finds a page
+// without meta objects, at this point it stops freeing meta objects. Because
+// thread stacks grow top-down, we do the same starting from end as well.
+void MetaMap::ResetRange(ThreadState *thr, uptr pc, uptr p, uptr sz) {
+ const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize;
+ const uptr kPageSize = GetPageSizeCached() * kMetaRatio;
+ if (sz <= 4 * kPageSize) {
+ // If the range is small, just do the normal free procedure.
+ FreeRange(thr, pc, p, sz);
+ return;
+ }
+ // First, round both ends of the range to page size.
+ uptr diff = RoundUp(p, kPageSize) - p;
+ if (diff != 0) {
+ FreeRange(thr, pc, p, diff);
+ p += diff;
+ sz -= diff;
+ }
+ diff = p + sz - RoundDown(p + sz, kPageSize);
+ if (diff != 0) {
+ FreeRange(thr, pc, p + sz - diff, diff);
+ sz -= diff;
+ }
+ // Now we must have a non-empty page-aligned range.
+ CHECK_GT(sz, 0);
+ CHECK_EQ(p, RoundUp(p, kPageSize));
+ CHECK_EQ(sz, RoundUp(sz, kPageSize));
+ const uptr p0 = p;
+ const uptr sz0 = sz;
+ // Probe start of the range.
+ while (sz > 0) {
+ bool has_something = FreeRange(thr, pc, p, kPageSize);
+ p += kPageSize;
+ sz -= kPageSize;
+ if (!has_something)
+ break;
+ }
+ // Probe end of the range.
+ while (sz > 0) {
+ bool has_something = FreeRange(thr, pc, p - kPageSize, kPageSize);
+ sz -= kPageSize;
+ if (!has_something)
+ break;
+ }
+ // Finally, page out the whole range (including the parts that we've just
+ // freed). Note: we can't simply madvise, because we need to leave a zeroed
+ // range (otherwise __tsan_java_move can crash if it encounters a left-over
+ // meta objects in java heap).
+ uptr metap = (uptr)MemToMeta(p0);
+ uptr metasz = sz0 / kMetaRatio;
+ UnmapOrDie((void*)metap, metasz);
+ MmapFixedNoReserve(metap, metasz);
}
MBlock* MetaMap::GetBlock(uptr p) {
diff --git a/lib/tsan/rtl/tsan_sync.h b/lib/tsan/rtl/tsan_sync.h
index 574810d8a542..2d12cdff8b2f 100644
--- a/lib/tsan/rtl/tsan_sync.h
+++ b/lib/tsan/rtl/tsan_sync.h
@@ -73,7 +73,8 @@ class MetaMap {
void AllocBlock(ThreadState *thr, uptr pc, uptr p, uptr sz);
uptr FreeBlock(ThreadState *thr, uptr pc, uptr p);
- void FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz);
+ bool FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz);
+ void ResetRange(ThreadState *thr, uptr pc, uptr p, uptr sz);
MBlock* GetBlock(uptr p);
SyncVar* GetOrCreateAndLock(ThreadState *thr, uptr pc,
diff --git a/lib/tsan/rtl/tsan_update_shadow_word_inl.h b/lib/tsan/rtl/tsan_update_shadow_word_inl.h
index c80e0a88d0eb..6e3ac2fa16f6 100644
--- a/lib/tsan/rtl/tsan_update_shadow_word_inl.h
+++ b/lib/tsan/rtl/tsan_update_shadow_word_inl.h
@@ -38,7 +38,8 @@ do {
}
StatInc(thr, StatShadowAnotherThread);
if (HappensBefore(old, thr)) {
- StoreIfNotYetStored(sp, &store_word);
+ if (old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic))
+ StoreIfNotYetStored(sp, &store_word);
break;
}
if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic))
diff --git a/lib/tsan/tests/CMakeLists.txt b/lib/tsan/tests/CMakeLists.txt
index e0c3f8a1a6d8..1c3f98f3f0bc 100644
--- a/lib/tsan/tests/CMakeLists.txt
+++ b/lib/tsan/tests/CMakeLists.txt
@@ -36,7 +36,7 @@ macro(add_tsan_unittest testname)
# Build unit tests only for 64-bit Linux.
if(UNIX AND NOT APPLE)
foreach(arch ${TSAN_SUPPORTED_ARCH})
- parse_arguments(TEST "SOURCES;HEADERS" "" ${ARGN})
+ cmake_parse_arguments(TEST "" "" "SOURCES;HEADERS" ${ARGN})
set(TEST_OBJECTS)
foreach(SOURCE ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE})
tsan_compile(TEST_OBJECTS ${SOURCE} ${arch} ${TEST_HEADERS})
diff --git a/lib/ubsan/CMakeLists.txt b/lib/ubsan/CMakeLists.txt
index 09c7a851e075..246236b78ab5 100644
--- a/lib/ubsan/CMakeLists.txt
+++ b/lib/ubsan/CMakeLists.txt
@@ -8,50 +8,98 @@ set(UBSAN_SOURCES
ubsan_value.cc
)
+set(UBSAN_STANDALONE_SOURCES
+ ubsan_init_standalone.cc
+ )
+
set(UBSAN_CXX_SOURCES
ubsan_handlers_cxx.cc
ubsan_type_hash.cc
+ ubsan_type_hash_itanium.cc
+ ubsan_type_hash_win.cc
)
include_directories(..)
set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_no_rtti_flag(UBSAN_CFLAGS)
+set(UBSAN_STANDALONE_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+append_no_rtti_flag(UBSAN_STANDALONE_CFLAGS)
set(UBSAN_CXXFLAGS ${SANITIZER_COMMON_CFLAGS})
add_custom_target(ubsan)
if(APPLE)
- # Build universal binary on APPLE.
- add_compiler_rt_osx_static_runtime(clang_rt.ubsan_osx
- ARCH ${UBSAN_SUPPORTED_ARCH}
- SOURCES ${UBSAN_SOURCES} ${UBSAN_CXX_SOURCES}
- $<TARGET_OBJECTS:RTSanitizerCommon.osx>
+ set(UBSAN_COMMON_SOURCES ${UBSAN_SOURCES})
+ if(SANITIZER_CAN_USE_CXXABI)
+ list(APPEND UBSAN_COMMON_SOURCES ${UBSAN_CXX_SOURCES})
+ endif()
+
+ # Common parts of UBSan runtime.
+ add_compiler_rt_object_libraries(RTUbsan
+ OS ${SANITIZER_COMMON_SUPPORTED_OS}
+ ARCHS ${UBSAN_COMMON_SUPPORTED_ARCH}
+ SOURCES ${UBSAN_COMMON_SOURCES}
CFLAGS ${UBSAN_CXXFLAGS})
- add_dependencies(ubsan clang_rt.ubsan_osx)
+
+ if(COMPILER_RT_HAS_UBSAN)
+ # Initializer of standalone UBSan runtime.
+ add_compiler_rt_object_libraries(RTUbsan_standalone
+ OS ${SANITIZER_COMMON_SUPPORTED_OS}
+ ARCHS ${UBSAN_SUPPORTED_ARCH}
+ SOURCES ${UBSAN_STANDALONE_SOURCES}
+ CFLAGS ${UBSAN_STANDALONE_CFLAGS})
+ foreach(os ${SANITIZER_COMMON_SUPPORTED_OS})
+ add_compiler_rt_darwin_dynamic_runtime(clang_rt.ubsan_${os}_dynamic ${os}
+ ARCHS ${UBSAN_SUPPORTED_ARCH}
+ SOURCES $<TARGET_OBJECTS:RTUbsan.${os}>
+ $<TARGET_OBJECTS:RTUbsan_standalone.${os}>
+ $<TARGET_OBJECTS:RTSanitizerCommon.${os}>)
+
+ add_dependencies(ubsan clang_rt.ubsan_${os}_dynamic)
+ endforeach()
+ endif()
+
else()
- # Build separate libraries for each target.
- foreach(arch ${UBSAN_SUPPORTED_ARCH})
- # Main UBSan runtime.
- add_compiler_rt_runtime(clang_rt.ubsan-${arch} ${arch} STATIC
- SOURCES ${UBSAN_SOURCES}
- CFLAGS ${UBSAN_CFLAGS})
- # C++-specific parts of UBSan runtime. Requires a C++ ABI library.
- add_compiler_rt_runtime(clang_rt.ubsan_cxx-${arch} ${arch} STATIC
- SOURCES ${UBSAN_CXX_SOURCES}
- CFLAGS ${UBSAN_CXXFLAGS})
- add_dependencies(ubsan
- clang_rt.san-${arch}
- clang_rt.ubsan-${arch}
- clang_rt.ubsan_cxx-${arch})
- if (UNIX AND NOT ${arch} STREQUAL "i386" AND NOT ${arch} STREQUAL "i686")
- add_sanitizer_rt_symbols(clang_rt.ubsan-${arch} ubsan.syms.extra)
- add_sanitizer_rt_symbols(clang_rt.ubsan_cxx-${arch} ubsan.syms.extra)
+ # Common parts of UBSan runtime.
+ add_compiler_rt_object_libraries(RTUbsan
+ ARCHS ${UBSAN_COMMON_SUPPORTED_ARCH}
+ SOURCES ${UBSAN_SOURCES} CFLAGS ${UBSAN_CFLAGS})
+ # C++-specific parts of UBSan runtime. Requires a C++ ABI library.
+ add_compiler_rt_object_libraries(RTUbsan_cxx
+ ARCHS ${UBSAN_COMMON_SUPPORTED_ARCH}
+ SOURCES ${UBSAN_CXX_SOURCES} CFLAGS ${UBSAN_CXXFLAGS})
+
+ if(COMPILER_RT_HAS_UBSAN)
+ # Initializer of standalone UBSan runtime.
+ add_compiler_rt_object_libraries(RTUbsan_standalone
+ ARCHS ${UBSAN_SUPPORTED_ARCH}
+ SOURCES ${UBSAN_STANDALONE_SOURCES} CFLAGS ${UBSAN_STANDALONE_CFLAGS})
+
+ foreach(arch ${UBSAN_SUPPORTED_ARCH})
+ # Standalone UBSan runtimes.
+ add_compiler_rt_runtime(clang_rt.ubsan_standalone-${arch} ${arch} STATIC
+ SOURCES $<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
+ $<TARGET_OBJECTS:RTUbsan.${arch}>
+ $<TARGET_OBJECTS:RTUbsan_standalone.${arch}>
+ CFLAGS ${UBSAN_CFLAGS})
+ add_compiler_rt_runtime(clang_rt.ubsan_standalone_cxx-${arch} ${arch} STATIC
+ SOURCES $<TARGET_OBJECTS:RTUbsan_cxx.${arch}>
+ CFLAGS ${UBSAN_CXXFLAGS})
+
add_dependencies(ubsan
- clang_rt.ubsan-${arch}-symbols
- clang_rt.ubsan_cxx-${arch}-symbols)
- endif()
- endforeach()
+ clang_rt.ubsan_standalone-${arch}
+ clang_rt.ubsan_standalone_cxx-${arch})
+ if (UNIX AND NOT ${arch} MATCHES "i386|i686")
+ add_sanitizer_rt_symbols(clang_rt.ubsan_standalone-${arch} ubsan.syms.extra)
+ add_sanitizer_rt_symbols(clang_rt.ubsan_standalone_cxx-${arch} ubsan.syms.extra)
+ add_dependencies(ubsan
+ clang_rt.ubsan_standalone-${arch}-symbols
+ clang_rt.ubsan_standalone_cxx-${arch}-symbols)
+ endif()
+ endforeach()
+ endif()
endif()
add_dependencies(compiler-rt ubsan)
diff --git a/lib/ubsan/Makefile.mk b/lib/ubsan/Makefile.mk
index d5561f41b152..ec3f5c5da273 100644
--- a/lib/ubsan/Makefile.mk
+++ b/lib/ubsan/Makefile.mk
@@ -11,8 +11,9 @@ ModuleName := ubsan
SubDirs :=
Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
+StandaloneSources := ubsan_init_standalone.cc
CXXSources := ubsan_type_hash.cc ubsan_handlers_cxx.cc
-CSources := $(filter-out $(CXXSources),$(Sources))
+CSources := $(filter-out $(StandaloneSources),$(filter-out $(CXXSources),$(Sources)))
ObjNames := $(Sources:%.cc=%.o)
Implementation := Generic
@@ -24,3 +25,4 @@ Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h)
# Define a convenience variable for all the ubsan functions.
UbsanFunctions := $(CSources:%.cc=%)
UbsanCXXFunctions := $(CXXSources:%.cc=%)
+UbsanStandaloneFunctions := $(StandaloneSources:%.cc=%)
diff --git a/lib/ubsan/ubsan_diag.cc b/lib/ubsan/ubsan_diag.cc
index 4f2a2a9f3562..3d5042b51d2e 100644
--- a/lib/ubsan/ubsan_diag.cc
+++ b/lib/ubsan/ubsan_diag.cc
@@ -11,6 +11,8 @@
//
//===----------------------------------------------------------------------===//
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
#include "ubsan_diag.h"
#include "ubsan_init.h"
#include "ubsan_flags.h"
@@ -25,7 +27,7 @@
using namespace __ubsan;
static void MaybePrintStackTrace(uptr pc, uptr bp) {
- // We assume that flags are already parsed: InitIfNecessary
+ // We assume that flags are already parsed, as UBSan runtime
// will definitely be called when we print the first diagnostics message.
if (!flags()->print_stacktrace)
return;
@@ -44,17 +46,25 @@ static void MaybePrintStackTrace(uptr pc, uptr bp) {
static void MaybeReportErrorSummary(Location Loc) {
if (!common_flags()->print_summary)
return;
- // Don't try to unwind the stack trace in UBSan summaries: just use the
- // provided location.
+ const char *ErrorType = "undefined-behavior";
if (Loc.isSourceLocation()) {
SourceLocation SLoc = Loc.getSourceLocation();
if (!SLoc.isInvalid()) {
- ReportErrorSummary("undefined-behavior", SLoc.getFilename(),
- SLoc.getLine(), "");
+ AddressInfo AI;
+ AI.file = internal_strdup(SLoc.getFilename());
+ AI.line = SLoc.getLine();
+ AI.column = SLoc.getColumn();
+ AI.function = internal_strdup(""); // Avoid printing ?? as function name.
+ ReportErrorSummary(ErrorType, AI);
+ AI.Clear();
return;
}
+ } else if (Loc.isSymbolizedStack()) {
+ const AddressInfo &AI = Loc.getSymbolizedStack()->info;
+ ReportErrorSummary(ErrorType, AI);
+ return;
}
- ReportErrorSummary("undefined-behavior");
+ ReportErrorSummary(ErrorType);
}
namespace {
@@ -69,7 +79,7 @@ class Decorator : public SanitizerCommonDecorator {
}
SymbolizedStack *__ubsan::getSymbolizedLocation(uptr PC) {
- InitIfNecessary();
+ InitAsStandaloneIfNecessary();
return Symbolizer::GetOrInit()->SymbolizePC(PC);
}
@@ -111,7 +121,8 @@ static void renderLocation(Location Loc) {
LocBuffer.append("<unknown>");
else
RenderSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(),
- SLoc.getColumn(), common_flags()->strip_path_prefix);
+ SLoc.getColumn(), common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
break;
}
case Location::LK_Memory:
@@ -121,6 +132,7 @@ static void renderLocation(Location Loc) {
const AddressInfo &Info = Loc.getSymbolizedStack()->info;
if (Info.file) {
RenderSourceLocation(&LocBuffer, Info.file, Info.line, Info.column,
+ common_flags()->symbolize_vs_style,
common_flags()->strip_path_prefix);
} else if (Info.module) {
RenderModuleLocation(&LocBuffer, Info.module, Info.module_offset,
@@ -153,8 +165,12 @@ static void renderText(const char *Message, const Diag::Arg *Args) {
case Diag::AK_String:
Printf("%s", A.String);
break;
- case Diag::AK_Mangled: {
- Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
+ case Diag::AK_TypeName: {
+ if (SANITIZER_WINDOWS)
+ // The Windows implementation demangles names early.
+ Printf("'%s'", A.String);
+ else
+ Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
break;
}
case Diag::AK_SInt:
@@ -174,7 +190,11 @@ static void renderText(const char *Message, const Diag::Arg *Args) {
// FIXME: Support floating-point formatting in sanitizer_common's
// printf, and stop using snprintf here.
char Buffer[32];
+#if SANITIZER_WINDOWS
+ sprintf_s(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float);
+#else
snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float);
+#endif
Printf("%s", Buffer);
break;
}
@@ -323,7 +343,7 @@ Diag::~Diag() {
ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc)
: Opts(Opts), SummaryLoc(SummaryLoc) {
- InitIfNecessary();
+ InitAsStandaloneIfNecessary();
CommonSanitizerReportMutex.Lock();
}
@@ -348,11 +368,10 @@ void __ubsan::InitializeSuppressions() {
}
bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) {
- // If .preinit_array is not used, it is possible that the UBSan runtime is not
- // initialized.
- if (!SANITIZER_CAN_USE_PREINIT_ARRAY)
- InitIfNecessary();
+ InitAsStandaloneIfNecessary();
CHECK(suppression_ctx);
Suppression *s;
return suppression_ctx->Match(TypeName, kVptrCheck, &s);
}
+
+#endif // CAN_SANITIZE_UB
diff --git a/lib/ubsan/ubsan_diag.h b/lib/ubsan/ubsan_diag.h
index 44dca90b7a53..615a646d9419 100644
--- a/lib/ubsan/ubsan_diag.h
+++ b/lib/ubsan/ubsan_diag.h
@@ -113,11 +113,11 @@ public:
const char *getText() const { return Text; }
};
-/// \brief A mangled C++ name. Really just a strong typedef for 'const char*'.
-class MangledName {
+/// \brief A C++ type name. Really just a strong typedef for 'const char*'.
+class TypeName {
const char *Name;
public:
- MangledName(const char *Name) : Name(Name) {}
+ TypeName(const char *Name) : Name(Name) {}
const char *getName() const { return Name; }
};
@@ -141,7 +141,7 @@ public:
/// Kinds of arguments, corresponding to members of \c Arg's union.
enum ArgKind {
AK_String, ///< A string argument, displayed as-is.
- AK_Mangled,///< A C++ mangled name, demangled before display.
+ AK_TypeName,///< A C++ type name, possibly demangled before display.
AK_UInt, ///< An unsigned integer argument.
AK_SInt, ///< A signed integer argument.
AK_Float, ///< A floating-point argument.
@@ -152,7 +152,7 @@ public:
struct Arg {
Arg() {}
Arg(const char *String) : Kind(AK_String), String(String) {}
- Arg(MangledName MN) : Kind(AK_Mangled), String(MN.getName()) {}
+ Arg(TypeName TN) : Kind(AK_TypeName), String(TN.getName()) {}
Arg(UIntMax UInt) : Kind(AK_UInt), UInt(UInt) {}
Arg(SIntMax SInt) : Kind(AK_SInt), SInt(SInt) {}
Arg(FloatMax Float) : Kind(AK_Float), Float(Float) {}
@@ -202,7 +202,7 @@ public:
~Diag();
Diag &operator<<(const char *Str) { return AddArg(Str); }
- Diag &operator<<(MangledName MN) { return AddArg(MN); }
+ Diag &operator<<(TypeName TN) { return AddArg(TN); }
Diag &operator<<(unsigned long long V) { return AddArg(UIntMax(V)); }
Diag &operator<<(const void *V) { return AddArg(V); }
Diag &operator<<(const TypeDescriptor &V);
diff --git a/lib/ubsan/ubsan_flags.cc b/lib/ubsan/ubsan_flags.cc
index 0dbffc9b102a..6e5ad4c7967c 100644
--- a/lib/ubsan/ubsan_flags.cc
+++ b/lib/ubsan/ubsan_flags.cc
@@ -11,6 +11,8 @@
//
//===----------------------------------------------------------------------===//
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
#include "ubsan_flags.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flags.h"
@@ -18,7 +20,7 @@
namespace __ubsan {
-static const char *MaybeCallUbsanDefaultOptions() {
+const char *MaybeCallUbsanDefaultOptions() {
return (&__ubsan_default_options) ? __ubsan_default_options() : "";
}
@@ -37,31 +39,22 @@ void RegisterUbsanFlags(FlagParser *parser, Flags *f) {
#undef UBSAN_FLAG
}
-void InitializeFlags(bool standalone) {
- Flags *f = flags();
- FlagParser parser;
- RegisterUbsanFlags(&parser, f);
-
- if (standalone) {
- RegisterCommonFlags(&parser);
-
- SetCommonFlagsDefaults();
+void InitializeFlags() {
+ SetCommonFlagsDefaults();
+ {
CommonFlags cf;
cf.CopyFrom(*common_flags());
cf.print_summary = false;
OverrideCommonFlags(cf);
- } else {
- // Ignore common flags if not standalone.
- // This is inconsistent with LSan, which allows common flags in LSAN_FLAGS.
- // This is caused by undefined initialization order between ASan and UBsan,
- // which makes it impossible to make sure that common flags from ASAN_OPTIONS
- // have not been used (in __asan_init) before they are overwritten with flags
- // from UBSAN_OPTIONS.
- CommonFlags cf_ignored;
- RegisterCommonFlags(&parser, &cf_ignored);
}
+ Flags *f = flags();
f->SetDefaults();
+
+ FlagParser parser;
+ RegisterCommonFlags(&parser);
+ RegisterUbsanFlags(&parser, f);
+
// Override from user-specified string.
parser.ParseString(MaybeCallUbsanDefaultOptions());
// Override from environment variable.
@@ -71,9 +64,22 @@ void InitializeFlags(bool standalone) {
} // namespace __ubsan
-#if !SANITIZER_SUPPORTS_WEAK_HOOKS
extern "C" {
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
const char *__ubsan_default_options() { return ""; }
-} // extern "C"
#endif
+
+#if SANITIZER_WINDOWS
+const char *__ubsan_default_default_options() { return ""; }
+# ifdef _WIN64
+# pragma comment(linker, "/alternatename:__ubsan_default_options=__ubsan_default_default_options")
+# else
+# pragma comment(linker, "/alternatename:___ubsan_default_options=___ubsan_default_default_options")
+# endif
+#endif
+
+} // extern "C"
+
+#endif // CAN_SANITIZE_UB
diff --git a/lib/ubsan/ubsan_flags.h b/lib/ubsan/ubsan_flags.h
index b47f14e1e2fd..18aed9b052ac 100644
--- a/lib/ubsan/ubsan_flags.h
+++ b/lib/ubsan/ubsan_flags.h
@@ -15,6 +15,10 @@
#include "sanitizer_common/sanitizer_internal_defs.h"
+namespace __sanitizer {
+class FlagParser;
+}
+
namespace __ubsan {
struct Flags {
@@ -28,7 +32,10 @@ struct Flags {
extern Flags ubsan_flags;
inline Flags *flags() { return &ubsan_flags; }
-void InitializeFlags(bool standalone);
+void InitializeFlags();
+void RegisterUbsanFlags(FlagParser *parser, Flags *f);
+
+const char *MaybeCallUbsanDefaultOptions();
} // namespace __ubsan
diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc
index 78e7508f7f93..a65b2f5e3fc7 100644
--- a/lib/ubsan/ubsan_handlers.cc
+++ b/lib/ubsan/ubsan_handlers.cc
@@ -11,6 +11,8 @@
//
//===----------------------------------------------------------------------===//
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
#include "ubsan_handlers.h"
#include "ubsan_diag.h"
@@ -419,3 +421,5 @@ void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) {
handleNonNullArg(Data, Opts);
Die();
}
+
+#endif // CAN_SANITIZE_UB
diff --git a/lib/ubsan/ubsan_handlers_cxx.cc b/lib/ubsan/ubsan_handlers_cxx.cc
index 4718e6eacee2..6984a963deb6 100644
--- a/lib/ubsan/ubsan_handlers_cxx.cc
+++ b/lib/ubsan/ubsan_handlers_cxx.cc
@@ -13,6 +13,8 @@
//
//===----------------------------------------------------------------------===//
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
#include "ubsan_handlers_cxx.h"
#include "ubsan_diag.h"
#include "ubsan_type_hash.h"
@@ -35,7 +37,7 @@ static void HandleDynamicTypeCacheMiss(
return;
// Check if error report should be suppressed.
- DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer);
+ DynamicTypeInfo DTI = getDynamicTypeInfoFromObject((void*)Pointer);
if (DTI.isValid() && IsVptrCheckSuppressed(DTI.getMostDerivedTypeName()))
return;
@@ -52,19 +54,19 @@ static void HandleDynamicTypeCacheMiss(
// If possible, say what type it actually points to.
if (!DTI.isValid())
Diag(Pointer, DL_Note, "object has invalid vptr")
- << MangledName(DTI.getMostDerivedTypeName())
+ << TypeName(DTI.getMostDerivedTypeName())
<< Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
else if (!DTI.getOffset())
Diag(Pointer, DL_Note, "object is of type %0")
- << MangledName(DTI.getMostDerivedTypeName())
+ << TypeName(DTI.getMostDerivedTypeName())
<< Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
else
// FIXME: Find the type at the specified offset, and include that
// in the note.
Diag(Pointer - DTI.getOffset(), DL_Note,
"object is base class subobject at offset %0 within object of type %1")
- << DTI.getOffset() << MangledName(DTI.getMostDerivedTypeName())
- << MangledName(DTI.getSubobjectTypeName())
+ << DTI.getOffset() << TypeName(DTI.getMostDerivedTypeName())
+ << TypeName(DTI.getSubobjectTypeName())
<< Range(Pointer, Pointer + sizeof(uptr),
"vptr for %2 base class of %1");
}
@@ -79,3 +81,42 @@ void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort(
GET_REPORT_OPTIONS(true);
HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts);
}
+
+static void HandleCFIBadType(CFIBadTypeData *Data, ValueHandle Vtable,
+ ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ ScopedReport R(Opts, Loc);
+ DynamicTypeInfo DTI = getDynamicTypeInfoFromVtable((void*)Vtable);
+
+ static const char *TypeCheckKinds[] = {
+ "virtual call",
+ "non-virtual call",
+ "base-to-derived cast",
+ "cast to unrelated type",
+ };
+
+ Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during "
+ "%1 (vtable address %2)")
+ << Data->Type << TypeCheckKinds[Data->TypeCheckKind] << (void *)Vtable;
+
+ // If possible, say what type it actually points to.
+ if (!DTI.isValid())
+ Diag(Vtable, DL_Note, "invalid vtable");
+ else
+ Diag(Vtable, DL_Note, "vtable is of type %0")
+ << TypeName(DTI.getMostDerivedTypeName());
+}
+
+void __ubsan::__ubsan_handle_cfi_bad_type(CFIBadTypeData *Data,
+ ValueHandle Vtable) {
+ GET_REPORT_OPTIONS(false);
+ HandleCFIBadType(Data, Vtable, Opts);
+}
+
+void __ubsan::__ubsan_handle_cfi_bad_type_abort(CFIBadTypeData *Data,
+ ValueHandle Vtable) {
+ GET_REPORT_OPTIONS(true);
+ HandleCFIBadType(Data, Vtable, Opts);
+}
+
+#endif // CAN_SANITIZE_UB
diff --git a/lib/ubsan/ubsan_handlers_cxx.h b/lib/ubsan/ubsan_handlers_cxx.h
index cb1bca78b833..92050d99e48e 100644
--- a/lib/ubsan/ubsan_handlers_cxx.h
+++ b/lib/ubsan/ubsan_handlers_cxx.h
@@ -25,6 +25,12 @@ struct DynamicTypeCacheMissData {
unsigned char TypeCheckKind;
};
+struct CFIBadTypeData {
+ SourceLocation Loc;
+ const TypeDescriptor &Type;
+ unsigned char TypeCheckKind;
+};
+
/// \brief Handle a runtime type check failure, caused by an incorrect vptr.
/// When this handler is called, all we know is that the type was not in the
/// cache; this does not necessarily imply the existence of a bug.
@@ -35,6 +41,13 @@ extern "C" SANITIZER_INTERFACE_ATTRIBUTE
void __ubsan_handle_dynamic_type_cache_miss_abort(
DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash);
+/// \brief Handle a control flow integrity check failure by printing a
+/// diagnostic.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
+__ubsan_handle_cfi_bad_type(CFIBadTypeData *Data, ValueHandle Vtable);
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
+__ubsan_handle_cfi_bad_type_abort(CFIBadTypeData *Data, ValueHandle Vtable);
+
}
#endif // UBSAN_HANDLERS_H
diff --git a/lib/ubsan/ubsan_init.cc b/lib/ubsan/ubsan_init.cc
index 219273d51921..73398ceafc34 100644
--- a/lib/ubsan/ubsan_init.cc
+++ b/lib/ubsan/ubsan_init.cc
@@ -11,6 +11,8 @@
//
//===----------------------------------------------------------------------===//
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
#include "ubsan_diag.h"
#include "ubsan_init.h"
#include "ubsan_flags.h"
@@ -21,43 +23,56 @@
using namespace __ubsan;
-static bool ubsan_inited;
+static enum {
+ UBSAN_MODE_UNKNOWN = 0,
+ UBSAN_MODE_STANDALONE,
+ UBSAN_MODE_PLUGIN
+} ubsan_mode;
+static StaticSpinMutex ubsan_init_mu;
-void __ubsan::InitIfNecessary() {
-#if !SANITIZER_CAN_USE_PREINIT_ARRAY
- // No need to lock mutex if we're initializing from preinit array.
- static StaticSpinMutex init_mu;
- SpinMutexLock l(&init_mu);
-#endif
- if (LIKELY(ubsan_inited))
- return;
- bool standalone = false;
- if (0 == internal_strcmp(SanitizerToolName, "SanitizerTool")) {
- // WARNING: If this condition holds, then either UBSan runs in a standalone
- // mode, or initializer for another sanitizer hasn't run yet. In a latter
- // case, another sanitizer will overwrite "SanitizerToolName" and reparse
- // common flags. It means, that we are not allowed to *use* common flags
- // in this function.
- SanitizerToolName = "UndefinedBehaviorSanitizer";
- standalone = true;
- }
- // Initialize UBSan-specific flags.
- InitializeFlags(standalone);
+static void CommonInit() {
InitializeSuppressions();
+}
+
+static void CommonStandaloneInit() {
+ SanitizerToolName = "UndefinedBehaviorSanitizer";
+ InitializeFlags();
+ CacheBinaryName();
+ __sanitizer_set_report_path(common_flags()->log_path);
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
- ubsan_inited = true;
+ CommonInit();
+ ubsan_mode = UBSAN_MODE_STANDALONE;
+}
+
+void __ubsan::InitAsStandalone() {
+ if (SANITIZER_CAN_USE_PREINIT_ARRAY) {
+ CHECK_EQ(UBSAN_MODE_UNKNOWN, ubsan_mode);
+ CommonStandaloneInit();
+ return;
+ }
+ SpinMutexLock l(&ubsan_init_mu);
+ CHECK_NE(UBSAN_MODE_PLUGIN, ubsan_mode);
+ if (ubsan_mode == UBSAN_MODE_UNKNOWN)
+ CommonStandaloneInit();
}
-#if SANITIZER_CAN_USE_PREINIT_ARRAY
-__attribute__((section(".preinit_array"), used))
-void (*__local_ubsan_preinit)(void) = __ubsan::InitIfNecessary;
-#else
-// Use a dynamic initializer.
-class UbsanInitializer {
- public:
- UbsanInitializer() {
- InitIfNecessary();
+void __ubsan::InitAsStandaloneIfNecessary() {
+ if (SANITIZER_CAN_USE_PREINIT_ARRAY) {
+ CHECK_NE(UBSAN_MODE_UNKNOWN, ubsan_mode);
+ return;
}
-};
-static UbsanInitializer ubsan_initializer;
-#endif // SANITIZER_CAN_USE_PREINIT_ARRAY
+ SpinMutexLock l(&ubsan_init_mu);
+ if (ubsan_mode == UBSAN_MODE_UNKNOWN)
+ CommonStandaloneInit();
+}
+
+void __ubsan::InitAsPlugin() {
+#if !SANITIZER_CAN_USE_PREINIT_ARRAY
+ SpinMutexLock l(&ubsan_init_mu);
+#endif
+ CHECK_EQ(UBSAN_MODE_UNKNOWN, ubsan_mode);
+ CommonInit();
+ ubsan_mode = UBSAN_MODE_PLUGIN;
+}
+
+#endif // CAN_SANITIZE_UB
diff --git a/lib/ubsan/ubsan_init.h b/lib/ubsan/ubsan_init.h
index 18356cfca617..103ae24d15b3 100644
--- a/lib/ubsan/ubsan_init.h
+++ b/lib/ubsan/ubsan_init.h
@@ -15,9 +15,16 @@
namespace __ubsan {
-// NOTE: This function might take a lock (if .preinit_array initialization is
-// not used). It's generally a bad idea to call it on a fast path.
-void InitIfNecessary();
+// Initialize UBSan as a standalone tool. Typically should be called early
+// during initialization.
+void InitAsStandalone();
+
+// Initialize UBSan as a standalone tool, if it hasn't been initialized before.
+void InitAsStandaloneIfNecessary();
+
+// Initializes UBSan as a plugin tool. This function should be called once
+// from "parent tool" (e.g. ASan) initialization.
+void InitAsPlugin();
} // namespace __ubsan
diff --git a/lib/ubsan/ubsan_init_standalone.cc b/lib/ubsan/ubsan_init_standalone.cc
new file mode 100644
index 000000000000..ff1a20efea3d
--- /dev/null
+++ b/lib/ubsan/ubsan_init_standalone.cc
@@ -0,0 +1,35 @@
+//===-- ubsan_init_standalone.cc ------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Initialization of standalone UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if !CAN_SANITIZE_UB
+# error "UBSan is not supported on this platform!"
+#endif
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "ubsan_init.h"
+
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
+__attribute__((section(".preinit_array"), used))
+void (*__local_ubsan_preinit)(void) = __ubsan::InitAsStandalone;
+#else
+// Use a dynamic initializer.
+class UbsanStandaloneInitializer {
+ public:
+ UbsanStandaloneInitializer() {
+ __ubsan::InitAsStandalone();
+ }
+};
+static UbsanStandaloneInitializer ubsan_standalone_initializer;
+#endif // SANITIZER_CAN_USE_PREINIT_ARRAY
+
diff --git a/lib/ubsan/ubsan_platform.h b/lib/ubsan/ubsan_platform.h
new file mode 100644
index 000000000000..002ecf32a595
--- /dev/null
+++ b/lib/ubsan/ubsan_platform.h
@@ -0,0 +1,27 @@
+//===-- ubsan_platform.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the platforms which UBSan is supported at.
+//
+//===----------------------------------------------------------------------===//
+#ifndef UBSAN_PLATFORM_H
+#define UBSAN_PLATFORM_H
+
+// Other platforms should be easy to add, and probably work as-is.
+#if (defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)) && \
+ (defined(__x86_64__) || defined(__i386__) || defined(__arm__) || \
+ defined(__aarch64__) || defined(__mips__) || defined(__powerpc64__))
+# define CAN_SANITIZE_UB 1
+#elif defined(_WIN32)
+# define CAN_SANITIZE_UB 1
+#else
+# define CAN_SANITIZE_UB 0
+#endif
+
+#endif
diff --git a/lib/ubsan/ubsan_type_hash.cc b/lib/ubsan/ubsan_type_hash.cc
index a388bcc6d72e..a217c862c62a 100644
--- a/lib/ubsan/ubsan_type_hash.cc
+++ b/lib/ubsan/ubsan_type_hash.cc
@@ -11,240 +11,24 @@
// relationships. This file is only linked into C++ compilations, and is
// permitted to use language features which require a C++ ABI library.
//
+// Most of the implementation lives in an ABI-specific source file
+// (ubsan_type_hash_{itanium,win}.cc).
+//
//===----------------------------------------------------------------------===//
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
#include "ubsan_type_hash.h"
#include "sanitizer_common/sanitizer_common.h"
-// The following are intended to be binary compatible with the definitions
-// given in the Itanium ABI. We make no attempt to be ODR-compatible with
-// those definitions, since existing ABI implementations aren't.
-
-namespace std {
- class type_info {
- public:
- virtual ~type_info();
-
- const char *__type_name;
- };
-}
-
-namespace __cxxabiv1 {
-
-/// Type info for classes with no bases, and base class for type info for
-/// classes with bases.
-class __class_type_info : public std::type_info {
- virtual ~__class_type_info();
-};
-
-/// Type info for classes with simple single public inheritance.
-class __si_class_type_info : public __class_type_info {
-public:
- virtual ~__si_class_type_info();
-
- const __class_type_info *__base_type;
-};
-
-class __base_class_type_info {
-public:
- const __class_type_info *__base_type;
- long __offset_flags;
-
- enum __offset_flags_masks {
- __virtual_mask = 0x1,
- __public_mask = 0x2,
- __offset_shift = 8
- };
-};
-
-/// Type info for classes with multiple, virtual, or non-public inheritance.
-class __vmi_class_type_info : public __class_type_info {
-public:
- virtual ~__vmi_class_type_info();
-
- unsigned int flags;
- unsigned int base_count;
- __base_class_type_info base_info[1];
-};
-
-}
-
-namespace abi = __cxxabiv1;
-
-// We implement a simple two-level cache for type-checking results. For each
-// (vptr,type) pair, a hash is computed. This hash is assumed to be globally
-// unique; if it collides, we will get false negatives, but:
-// * such a collision would have to occur on the *first* bad access,
-// * the probability of such a collision is low (and for a 64-bit target, is
-// negligible), and
-// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs
-// give better coverage.
-//
-// The first caching layer is a small hash table with no chaining; buckets are
-// reused as needed. The second caching layer is a large hash table with open
-// chaining. We can freely evict from either layer since this is just a cache.
-//
-// FIXME: Make these hash table accesses thread-safe. The races here are benign:
-// assuming the unsequenced loads and stores don't misbehave too badly,
-// the worst case is false negatives or poor cache behavior, not false
-// positives or crashes.
-
-/// Find a bucket to store the given hash value in.
-static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) {
- static const unsigned HashTableSize = 65537;
- static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize];
-
- unsigned First = (V & 65535) ^ 1;
- unsigned Probe = First;
- for (int Tries = 5; Tries; --Tries) {
- if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V)
- return &__ubsan_vptr_hash_set[Probe];
- Probe += ((V >> 16) & 65535) + 1;
- if (Probe >= HashTableSize)
- Probe -= HashTableSize;
- }
- // FIXME: Pick a random entry from the probe sequence to evict rather than
- // just taking the first.
- return &__ubsan_vptr_hash_set[First];
-}
-
/// A cache of recently-checked hashes. Mini hash table with "random" evictions.
__ubsan::HashValue
__ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize];
-/// \brief Determine whether \p Derived has a \p Base base class subobject at
-/// offset \p Offset.
-static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
- const abi::__class_type_info *Base,
- sptr Offset) {
- if (Derived->__type_name == Base->__type_name)
- return Offset == 0;
-
- if (const abi::__si_class_type_info *SI =
- dynamic_cast<const abi::__si_class_type_info*>(Derived))
- return isDerivedFromAtOffset(SI->__base_type, Base, Offset);
-
- const abi::__vmi_class_type_info *VTI =
- dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
- if (!VTI)
- // No base class subobjects.
- return false;
-
- // Look for a base class which is derived from \p Base at the right offset.
- for (unsigned int base = 0; base != VTI->base_count; ++base) {
- // FIXME: Curtail the recursion if this base can't possibly contain the
- // given offset.
- sptr OffsetHere = VTI->base_info[base].__offset_flags >>
- abi::__base_class_type_info::__offset_shift;
- if (VTI->base_info[base].__offset_flags &
- abi::__base_class_type_info::__virtual_mask)
- // For now, just punt on virtual bases and say 'yes'.
- // FIXME: OffsetHere is the offset in the vtable of the virtual base
- // offset. Read the vbase offset out of the vtable and use it.
- return true;
- if (isDerivedFromAtOffset(VTI->base_info[base].__base_type,
- Base, Offset - OffsetHere))
- return true;
- }
-
- return false;
-}
-
-/// \brief Find the derived-most dynamic base class of \p Derived at offset
-/// \p Offset.
-static const abi::__class_type_info *findBaseAtOffset(
- const abi::__class_type_info *Derived, sptr Offset) {
- if (!Offset)
- return Derived;
-
- if (const abi::__si_class_type_info *SI =
- dynamic_cast<const abi::__si_class_type_info*>(Derived))
- return findBaseAtOffset(SI->__base_type, Offset);
-
- const abi::__vmi_class_type_info *VTI =
- dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
- if (!VTI)
- // No base class subobjects.
- return 0;
-
- for (unsigned int base = 0; base != VTI->base_count; ++base) {
- sptr OffsetHere = VTI->base_info[base].__offset_flags >>
- abi::__base_class_type_info::__offset_shift;
- if (VTI->base_info[base].__offset_flags &
- abi::__base_class_type_info::__virtual_mask)
- // FIXME: Can't handle virtual bases yet.
- continue;
- if (const abi::__class_type_info *Base =
- findBaseAtOffset(VTI->base_info[base].__base_type,
- Offset - OffsetHere))
- return Base;
- }
-
- return 0;
-}
-
-namespace {
-
-struct VtablePrefix {
- /// The offset from the vptr to the start of the most-derived object.
- /// This should never be greater than zero, and will usually be exactly
- /// zero.
- sptr Offset;
- /// The type_info object describing the most-derived class type.
- std::type_info *TypeInfo;
-};
-VtablePrefix *getVtablePrefix(void *Object) {
- VtablePrefix **VptrPtr = reinterpret_cast<VtablePrefix**>(Object);
- if (!*VptrPtr)
- return 0;
- VtablePrefix *Prefix = *VptrPtr - 1;
- if (Prefix->Offset > 0 || !Prefix->TypeInfo)
- // This can't possibly be a valid vtable.
- return 0;
- return Prefix;
-}
-
+__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfoFromObject(void *Object) {
+ void *VtablePtr = *reinterpret_cast<void **>(Object);
+ return getDynamicTypeInfoFromVtable(VtablePtr);
}
-bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
- // A crash anywhere within this function probably means the vptr is corrupted.
- // FIXME: Perform these checks more cautiously.
-
- // Check whether this is something we've evicted from the cache.
- HashValue *Bucket = getTypeCacheHashTableBucket(Hash);
- if (*Bucket == Hash) {
- __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
- return true;
- }
-
- VtablePrefix *Vtable = getVtablePrefix(Object);
- if (!Vtable)
- return false;
-
- // Check that this is actually a type_info object for a class type.
- abi::__class_type_info *Derived =
- dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo);
- if (!Derived)
- return false;
-
- abi::__class_type_info *Base = (abi::__class_type_info*)Type;
- if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset))
- return false;
-
- // Success. Cache this result.
- __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
- *Bucket = Hash;
- return true;
-}
-
-__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfo(void *Object) {
- VtablePrefix *Vtable = getVtablePrefix(Object);
- if (!Vtable)
- return DynamicTypeInfo(0, 0, 0);
- const abi::__class_type_info *ObjectType = findBaseAtOffset(
- static_cast<const abi::__class_type_info*>(Vtable->TypeInfo),
- -Vtable->Offset);
- return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset,
- ObjectType ? ObjectType->__type_name : "<unknown>");
-}
+#endif // CAN_SANITIZE_UB
diff --git a/lib/ubsan/ubsan_type_hash.h b/lib/ubsan/ubsan_type_hash.h
index 58ecd3de9864..695fed905a73 100644
--- a/lib/ubsan/ubsan_type_hash.h
+++ b/lib/ubsan/ubsan_type_hash.h
@@ -41,7 +41,10 @@ public:
};
/// \brief Get information about the dynamic type of an object.
-DynamicTypeInfo getDynamicTypeInfo(void *Object);
+DynamicTypeInfo getDynamicTypeInfoFromObject(void *Object);
+
+/// \brief Get information about the dynamic type of an object from its vtable.
+DynamicTypeInfo getDynamicTypeInfoFromVtable(void *Vtable);
/// \brief Check whether the dynamic type of \p Object has a \p Type subobject
/// at offset 0.
diff --git a/lib/ubsan/ubsan_type_hash_itanium.cc b/lib/ubsan/ubsan_type_hash_itanium.cc
new file mode 100644
index 000000000000..b84e88d4c71d
--- /dev/null
+++ b/lib/ubsan/ubsan_type_hash_itanium.cc
@@ -0,0 +1,251 @@
+//===-- ubsan_type_hash_itanium.cc ----------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of type hashing/lookup for Itanium C++ ABI.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB && !SANITIZER_WINDOWS
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+// The following are intended to be binary compatible with the definitions
+// given in the Itanium ABI. We make no attempt to be ODR-compatible with
+// those definitions, since existing ABI implementations aren't.
+
+namespace std {
+ class type_info {
+ public:
+ virtual ~type_info();
+
+ const char *__type_name;
+ };
+}
+
+namespace __cxxabiv1 {
+
+/// Type info for classes with no bases, and base class for type info for
+/// classes with bases.
+class __class_type_info : public std::type_info {
+ ~__class_type_info() override;
+};
+
+/// Type info for classes with simple single public inheritance.
+class __si_class_type_info : public __class_type_info {
+public:
+ ~__si_class_type_info() override;
+
+ const __class_type_info *__base_type;
+};
+
+class __base_class_type_info {
+public:
+ const __class_type_info *__base_type;
+ long __offset_flags;
+
+ enum __offset_flags_masks {
+ __virtual_mask = 0x1,
+ __public_mask = 0x2,
+ __offset_shift = 8
+ };
+};
+
+/// Type info for classes with multiple, virtual, or non-public inheritance.
+class __vmi_class_type_info : public __class_type_info {
+public:
+ ~__vmi_class_type_info() override;
+
+ unsigned int flags;
+ unsigned int base_count;
+ __base_class_type_info base_info[1];
+};
+
+}
+
+namespace abi = __cxxabiv1;
+
+// We implement a simple two-level cache for type-checking results. For each
+// (vptr,type) pair, a hash is computed. This hash is assumed to be globally
+// unique; if it collides, we will get false negatives, but:
+// * such a collision would have to occur on the *first* bad access,
+// * the probability of such a collision is low (and for a 64-bit target, is
+// negligible), and
+// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs
+// give better coverage.
+//
+// The first caching layer is a small hash table with no chaining; buckets are
+// reused as needed. The second caching layer is a large hash table with open
+// chaining. We can freely evict from either layer since this is just a cache.
+//
+// FIXME: Make these hash table accesses thread-safe. The races here are benign:
+// assuming the unsequenced loads and stores don't misbehave too badly,
+// the worst case is false negatives or poor cache behavior, not false
+// positives or crashes.
+
+/// Find a bucket to store the given hash value in.
+static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) {
+ static const unsigned HashTableSize = 65537;
+ static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize];
+
+ unsigned First = (V & 65535) ^ 1;
+ unsigned Probe = First;
+ for (int Tries = 5; Tries; --Tries) {
+ if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V)
+ return &__ubsan_vptr_hash_set[Probe];
+ Probe += ((V >> 16) & 65535) + 1;
+ if (Probe >= HashTableSize)
+ Probe -= HashTableSize;
+ }
+ // FIXME: Pick a random entry from the probe sequence to evict rather than
+ // just taking the first.
+ return &__ubsan_vptr_hash_set[First];
+}
+
+/// \brief Determine whether \p Derived has a \p Base base class subobject at
+/// offset \p Offset.
+static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
+ const abi::__class_type_info *Base,
+ sptr Offset) {
+ if (Derived->__type_name == Base->__type_name)
+ return Offset == 0;
+
+ if (const abi::__si_class_type_info *SI =
+ dynamic_cast<const abi::__si_class_type_info*>(Derived))
+ return isDerivedFromAtOffset(SI->__base_type, Base, Offset);
+
+ const abi::__vmi_class_type_info *VTI =
+ dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
+ if (!VTI)
+ // No base class subobjects.
+ return false;
+
+ // Look for a base class which is derived from \p Base at the right offset.
+ for (unsigned int base = 0; base != VTI->base_count; ++base) {
+ // FIXME: Curtail the recursion if this base can't possibly contain the
+ // given offset.
+ sptr OffsetHere = VTI->base_info[base].__offset_flags >>
+ abi::__base_class_type_info::__offset_shift;
+ if (VTI->base_info[base].__offset_flags &
+ abi::__base_class_type_info::__virtual_mask)
+ // For now, just punt on virtual bases and say 'yes'.
+ // FIXME: OffsetHere is the offset in the vtable of the virtual base
+ // offset. Read the vbase offset out of the vtable and use it.
+ return true;
+ if (isDerivedFromAtOffset(VTI->base_info[base].__base_type,
+ Base, Offset - OffsetHere))
+ return true;
+ }
+
+ return false;
+}
+
+/// \brief Find the derived-most dynamic base class of \p Derived at offset
+/// \p Offset.
+static const abi::__class_type_info *findBaseAtOffset(
+ const abi::__class_type_info *Derived, sptr Offset) {
+ if (!Offset)
+ return Derived;
+
+ if (const abi::__si_class_type_info *SI =
+ dynamic_cast<const abi::__si_class_type_info*>(Derived))
+ return findBaseAtOffset(SI->__base_type, Offset);
+
+ const abi::__vmi_class_type_info *VTI =
+ dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
+ if (!VTI)
+ // No base class subobjects.
+ return 0;
+
+ for (unsigned int base = 0; base != VTI->base_count; ++base) {
+ sptr OffsetHere = VTI->base_info[base].__offset_flags >>
+ abi::__base_class_type_info::__offset_shift;
+ if (VTI->base_info[base].__offset_flags &
+ abi::__base_class_type_info::__virtual_mask)
+ // FIXME: Can't handle virtual bases yet.
+ continue;
+ if (const abi::__class_type_info *Base =
+ findBaseAtOffset(VTI->base_info[base].__base_type,
+ Offset - OffsetHere))
+ return Base;
+ }
+
+ return 0;
+}
+
+namespace {
+
+struct VtablePrefix {
+ /// The offset from the vptr to the start of the most-derived object.
+ /// This will only be greater than zero in some virtual base class vtables
+ /// used during object con-/destruction, and will usually be exactly zero.
+ sptr Offset;
+ /// The type_info object describing the most-derived class type.
+ std::type_info *TypeInfo;
+};
+VtablePrefix *getVtablePrefix(void *Vtable) {
+ VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable);
+ if (!Vptr)
+ return 0;
+ VtablePrefix *Prefix = Vptr - 1;
+ if (!Prefix->TypeInfo)
+ // This can't possibly be a valid vtable.
+ return 0;
+ return Prefix;
+}
+
+}
+
+bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
+ // A crash anywhere within this function probably means the vptr is corrupted.
+ // FIXME: Perform these checks more cautiously.
+
+ // Check whether this is something we've evicted from the cache.
+ HashValue *Bucket = getTypeCacheHashTableBucket(Hash);
+ if (*Bucket == Hash) {
+ __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
+ return true;
+ }
+
+ void *VtablePtr = *reinterpret_cast<void **>(Object);
+ VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
+ if (!Vtable)
+ return false;
+
+ // Check that this is actually a type_info object for a class type.
+ abi::__class_type_info *Derived =
+ dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo);
+ if (!Derived)
+ return false;
+
+ abi::__class_type_info *Base = (abi::__class_type_info*)Type;
+ if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset))
+ return false;
+
+ // Success. Cache this result.
+ __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
+ *Bucket = Hash;
+ return true;
+}
+
+__ubsan::DynamicTypeInfo
+__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) {
+ VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
+ if (!Vtable)
+ return DynamicTypeInfo(0, 0, 0);
+ const abi::__class_type_info *ObjectType = findBaseAtOffset(
+ static_cast<const abi::__class_type_info*>(Vtable->TypeInfo),
+ -Vtable->Offset);
+ return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset,
+ ObjectType ? ObjectType->__type_name : "<unknown>");
+}
+
+#endif // CAN_SANITIZE_UB && !SANITIZER_WINDOWS
diff --git a/lib/ubsan/ubsan_type_hash_win.cc b/lib/ubsan/ubsan_type_hash_win.cc
new file mode 100644
index 000000000000..271c4aaf6fa8
--- /dev/null
+++ b/lib/ubsan/ubsan_type_hash_win.cc
@@ -0,0 +1,81 @@
+//===-- ubsan_type_hash_win.cc --------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of type hashing/lookup for Microsoft C++ ABI.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB && SANITIZER_WINDOWS
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+#include <typeinfo>
+
+struct CompleteObjectLocator {
+ int is_image_relative;
+ int offset_to_top;
+ int vfptr_offset;
+ int rtti_addr;
+ int chd_addr;
+ int obj_locator_addr;
+};
+
+struct CompleteObjectLocatorAbs {
+ int is_image_relative;
+ int offset_to_top;
+ int vfptr_offset;
+ std::type_info *rtti_addr;
+ void *chd_addr;
+ CompleteObjectLocator *obj_locator_addr;
+};
+
+bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
+ // FIXME: Implement.
+ return false;
+}
+
+__ubsan::DynamicTypeInfo
+__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) {
+ // The virtual table may not have a complete object locator if the object
+ // was compiled without RTTI (i.e. we might be reading from some other global
+ // laid out before the virtual table), so we need to carefully validate each
+ // pointer dereference and perform sanity checks.
+ CompleteObjectLocator **obj_locator_ptr =
+ ((CompleteObjectLocator**)VtablePtr)-1;
+ if (!IsAccessibleMemoryRange((uptr)obj_locator_ptr, sizeof(void*)))
+ return DynamicTypeInfo(0, 0, 0);
+
+ CompleteObjectLocator *obj_locator = *obj_locator_ptr;
+ if (!IsAccessibleMemoryRange((uptr)obj_locator,
+ sizeof(CompleteObjectLocator)))
+ return DynamicTypeInfo(0, 0, 0);
+
+ std::type_info *tinfo;
+ if (obj_locator->is_image_relative == 1) {
+ char *image_base = ((char *)obj_locator) - obj_locator->obj_locator_addr;
+ tinfo = (std::type_info *)(image_base + obj_locator->rtti_addr);
+ } else if (obj_locator->is_image_relative == 0)
+ tinfo = ((CompleteObjectLocatorAbs *)obj_locator)->rtti_addr;
+ else
+ // Probably not a complete object locator.
+ return DynamicTypeInfo(0, 0, 0);
+
+ if (!IsAccessibleMemoryRange((uptr)tinfo, sizeof(std::type_info)))
+ return DynamicTypeInfo(0, 0, 0);
+
+ // Okay, this is probably a std::type_info. Request its name.
+ // FIXME: Implement a base class search like we do for Itanium.
+ return DynamicTypeInfo(tinfo->name(), obj_locator->offset_to_top,
+ "<unknown>");
+}
+
+#endif // CAN_SANITIZE_UB && SANITIZER_WINDOWS
diff --git a/lib/ubsan/ubsan_value.cc b/lib/ubsan/ubsan_value.cc
index ab74720f60cf..79dc4c8e8271 100644
--- a/lib/ubsan/ubsan_value.cc
+++ b/lib/ubsan/ubsan_value.cc
@@ -12,6 +12,8 @@
//
//===----------------------------------------------------------------------===//
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
#include "ubsan_value.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
@@ -81,7 +83,14 @@ FloatMax Value::getFloatValue() const {
#endif
case 32: {
float Value;
- internal_memcpy(&Value, &Val, 4);
+#if defined(__BIG_ENDIAN__)
+ // For big endian the float value is in the last 4 bytes.
+ // On some targets we may only have 4 bytes so we count backwards from
+ // the end of Val to account for both the 32-bit and 64-bit cases.
+ internal_memcpy(&Value, ((const char*)(&Val + 1)) - 4, 4);
+#else
+ internal_memcpy(&Value, &Val, 4);
+#endif
return Value;
}
case 64: {
@@ -100,3 +109,5 @@ FloatMax Value::getFloatValue() const {
}
UNREACHABLE("unexpected floating point bit width");
}
+
+#endif // CAN_SANITIZE_UB
diff --git a/lib/ubsan/ubsan_value.h b/lib/ubsan/ubsan_value.h
index a6810844049f..72eee15551ed 100644
--- a/lib/ubsan/ubsan_value.h
+++ b/lib/ubsan/ubsan_value.h
@@ -14,12 +14,6 @@
#ifndef UBSAN_VALUE_H
#define UBSAN_VALUE_H
-// For now, only support Linux, FreeBSD and Darwin. Other platforms should
-// be easy to add, and probably work as-is.
-#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__APPLE__)
-#error "UBSan not supported for this platform!"
-#endif
-
#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
@@ -32,7 +26,6 @@ __extension__ typedef unsigned __int128 u128;
#define HAVE_INT128_T 0
#endif
-
namespace __ubsan {
/// \brief Largest integer types we support.