aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2015-02-22 22:43:40 +0000
committerDimitry Andric <dim@FreeBSD.org>2015-02-22 22:43:40 +0000
commitcd2dd3df15523e2be8d2bbace27641d6ac9fa40d (patch)
treefbdacaec253cc5ceee88cb44de5545fa32c8bd67
parent476c4db3dc56bee43df384704c75ccc71cfa7a1d (diff)
downloadsrc-cd2dd3df15523e2be8d2bbace27641d6ac9fa40d.tar.gz
src-cd2dd3df15523e2be8d2bbace27641d6ac9fa40d.zip
Import compiler-rt trunk r230183.vendor/compiler-rt/compiler-rt-r230183
Notes
Notes: svn path=/vendor/compiler-rt/dist/; revision=279192 svn path=/vendor/compiler-rt/compiler-rt-r230183/; revision=279193; tag=vendor/compiler-rt/compiler-rt-r230183
-rw-r--r--cmake/config-ix.cmake4
-rw-r--r--lib/Makefile.mk3
-rw-r--r--lib/asan/README.txt14
-rw-r--r--lib/asan/asan_flags.cc55
-rw-r--r--lib/asan/asan_flags.h4
-rw-r--r--lib/asan/asan_flags.inc1
-rw-r--r--lib/asan/asan_globals.cc16
-rw-r--r--lib/asan/asan_interface_internal.h7
-rw-r--r--lib/asan/asan_internal.h1
-rw-r--r--lib/asan/asan_linux.cc4
-rw-r--r--lib/asan/asan_mac.cc8
-rw-r--r--lib/asan/asan_rtl.cc4
-rw-r--r--lib/asan/asan_suppressions.cc40
-rw-r--r--lib/asan/asan_win.cc4
-rw-r--r--lib/asan/asan_win_dll_thunk.cc36
-rw-r--r--lib/asan/asan_win_dynamic_runtime_thunk.cc85
-rwxr-xr-xlib/asan/scripts/asan_device_setup158
-rw-r--r--lib/asan/tests/asan_noinst_test.cc5
-rw-r--r--lib/builtins/clear_cache.c8
-rw-r--r--lib/dfsan/Makefile.mk23
-rw-r--r--lib/dfsan/dfsan.cc13
-rw-r--r--lib/lsan/Makefile.mk3
-rw-r--r--lib/lsan/lsan.cc32
-rw-r--r--lib/lsan/lsan_allocator.cc18
-rw-r--r--lib/lsan/lsan_common.cc71
-rw-r--r--lib/lsan/lsan_common.h10
-rw-r--r--lib/lsan/lsan_flags.inc1
-rw-r--r--lib/msan/Makefile.mk24
-rw-r--r--lib/msan/msan.cc16
-rw-r--r--lib/msan/tests/msan_test.cc110
-rw-r--r--lib/sanitizer_common/CMakeLists.txt1
-rw-r--r--lib/sanitizer_common/sanitizer_common.cc42
-rw-r--r--lib/sanitizer_common/sanitizer_common.h6
-rw-r--r--lib/sanitizer_common/sanitizer_common_libcdep.cc2
-rw-r--r--lib/sanitizer_common/sanitizer_common_syscalls.inc6
-rw-r--r--lib/sanitizer_common/sanitizer_coverage_libcdep.cc23
-rw-r--r--lib/sanitizer_common/sanitizer_flag_parser.h2
-rw-r--r--lib/sanitizer_common/sanitizer_flags.cc2
-rw-r--r--lib/sanitizer_common/sanitizer_flags.inc1
-rw-r--r--lib/sanitizer_common/sanitizer_interface_internal.h58
-rw-r--r--lib/sanitizer_common/sanitizer_internal_defs.h35
-rw-r--r--lib/sanitizer_common/sanitizer_libignore.cc26
-rw-r--r--lib/sanitizer_common/sanitizer_libignore.h9
-rw-r--r--lib/sanitizer_common/sanitizer_linux.cc7
-rw-r--r--lib/sanitizer_common/sanitizer_linux.h2
-rw-r--r--lib/sanitizer_common/sanitizer_linux_libcdep.cc44
-rw-r--r--lib/sanitizer_common/sanitizer_platform_interceptors.h2
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_posix.cc18
-rw-r--r--lib/sanitizer_common/sanitizer_platform_limits_posix.h6
-rw-r--r--lib/sanitizer_common/sanitizer_posix.cc7
-rw-r--r--lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc146
-rw-r--r--lib/sanitizer_common/sanitizer_suppressions.cc129
-rw-r--r--lib/sanitizer_common/sanitizer_suppressions.h50
-rw-r--r--lib/sanitizer_common/sanitizer_symbolizer_win.cc72
-rw-r--r--lib/sanitizer_common/sanitizer_syscall_generic.inc4
-rw-r--r--lib/sanitizer_common/sanitizer_win.cc6
-rw-r--r--lib/sanitizer_common/tests/sanitizer_linux_test.cc8
-rw-r--r--lib/sanitizer_common/tests/sanitizer_suppressions_test.cc106
-rw-r--r--lib/tsan/CMakeLists.txt55
-rw-r--r--lib/tsan/Makefile.mk18
-rw-r--r--lib/tsan/dd/dd_rtl.cc8
-rw-r--r--lib/tsan/go/build.bat4
-rwxr-xr-xlib/tsan/go/buildgo.sh2
-rw-r--r--lib/tsan/rtl/Makefile.mk25
-rw-r--r--lib/tsan/rtl/tsan_defs.h21
-rw-r--r--lib/tsan/rtl/tsan_flags.cc2
-rw-r--r--lib/tsan/rtl/tsan_flags.inc3
-rw-r--r--lib/tsan/rtl/tsan_interceptors.cc39
-rw-r--r--lib/tsan/rtl/tsan_interface_atomic.cc7
-rw-r--r--lib/tsan/rtl/tsan_platform.h48
-rw-r--r--lib/tsan/rtl/tsan_platform_linux.cc21
-rw-r--r--lib/tsan/rtl/tsan_report.cc5
-rw-r--r--lib/tsan/rtl/tsan_rtl.cc39
-rw-r--r--lib/tsan/rtl/tsan_rtl.h40
-rw-r--r--lib/tsan/rtl/tsan_rtl_report.cc6
-rw-r--r--lib/tsan/rtl/tsan_rtl_thread.cc4
-rw-r--r--lib/tsan/rtl/tsan_stat.cc9
-rw-r--r--lib/tsan/rtl/tsan_suppressions.cc81
-rw-r--r--lib/tsan/rtl/tsan_suppressions.h9
-rw-r--r--lib/tsan/rtl/tsan_trace.h8
-rw-r--r--lib/tsan/tests/CMakeLists.txt40
-rw-r--r--lib/ubsan/CMakeLists.txt5
-rw-r--r--lib/ubsan/ubsan_diag.cc74
-rw-r--r--lib/ubsan/ubsan_diag.h70
-rw-r--r--lib/ubsan/ubsan_flags.inc1
-rw-r--r--lib/ubsan/ubsan_handlers.cc31
-rw-r--r--lib/ubsan/ubsan_handlers_cxx.cc3
-rw-r--r--lib/ubsan/ubsan_init.cc4
-rw-r--r--make/platform/clang_linux.mk75
-rw-r--r--test/CMakeLists.txt7
-rw-r--r--test/asan/CMakeLists.txt6
-rw-r--r--test/asan/TestCases/Windows/dll_host.cc7
-rw-r--r--test/asan/TestCases/Windows/globals_multiple_dlls.cc51
-rw-r--r--test/asan/TestCases/Windows/oom.cc12
-rw-r--r--test/asan/TestCases/Windows/symbols_path.cc22
-rw-r--r--test/asan/TestCases/dlclose-test.cc7
-rw-r--r--test/asan/TestCases/gc-test.cc6
-rw-r--r--test/asan/lit.cfg8
-rw-r--r--test/cfi/CMakeLists.txt23
-rw-r--r--test/cfi/anon-namespace.cpp93
-rw-r--r--test/cfi/lit.cfg35
-rw-r--r--test/cfi/lit.site.cfg.in2
-rw-r--r--test/cfi/multiple-inheritance.cpp82
-rw-r--r--test/cfi/overwrite.cpp67
-rw-r--r--test/cfi/simple-fail.cpp99
-rw-r--r--test/cfi/simple-pass.cpp97
-rw-r--r--test/cfi/utils.h53
-rw-r--r--test/cfi/vdtor.cpp62
-rw-r--r--test/lit.common.cfg3
-rw-r--r--test/lit.common.configured.in2
-rw-r--r--test/lsan/lit.common.cfg4
-rw-r--r--test/msan/mmap_below_shadow.cc5
-rw-r--r--test/msan/strlen_of_shadow.cc4
-rw-r--r--test/msan/vector_select.cc8
-rw-r--r--test/sanitizer_common/TestCases/Linux/ptrace.cc14
-rw-r--r--test/tsan/global_race.cc6
-rw-r--r--test/tsan/global_race2.cc6
-rw-r--r--test/tsan/global_race3.cc6
-rw-r--r--test/tsan/map32bit.cc3
-rw-r--r--test/tsan/mmap_large.cc4
-rw-r--r--test/tsan/signal_segv_handler.cc39
-rw-r--r--test/tsan/test.h9
-rw-r--r--test/ubsan/TestCases/TypeCheck/Function/function.cpp21
-rw-r--r--test/ubsan/TestCases/TypeCheck/null.cpp10
-rw-r--r--test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp2
-rw-r--r--test/ubsan/TestCases/TypeCheck/vptr.cpp18
126 files changed, 2223 insertions, 1035 deletions
diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake
index c8c01e96b3a1..97a22cbdc7b9 100644
--- a/cmake/config-ix.cmake
+++ b/cmake/config-ix.cmake
@@ -199,7 +199,7 @@ filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH
filter_available_targets(ASAN_SUPPORTED_ARCH
x86_64 i386 i686 powerpc64 powerpc64le arm mips mipsel mips64 mips64el)
filter_available_targets(DFSAN_SUPPORTED_ARCH x86_64 mips64 mips64el)
-filter_available_targets(LSAN_SUPPORTED_ARCH x86_64)
+filter_available_targets(LSAN_SUPPORTED_ARCH x86_64 mips64 mips64el)
# LSan common files should be available on all architectures supported
# by other sanitizers (even if they build into dummy object files).
filter_available_targets(LSAN_COMMON_SUPPORTED_ARCH
@@ -207,7 +207,7 @@ filter_available_targets(LSAN_COMMON_SUPPORTED_ARCH
filter_available_targets(MSAN_SUPPORTED_ARCH x86_64 mips64 mips64el)
filter_available_targets(PROFILE_SUPPORTED_ARCH x86_64 i386 i686 arm mips mips64
mipsel mips64el aarch64 powerpc64 powerpc64le)
-filter_available_targets(TSAN_SUPPORTED_ARCH x86_64)
+filter_available_targets(TSAN_SUPPORTED_ARCH x86_64 mips64 mips64el)
filter_available_targets(UBSAN_SUPPORTED_ARCH x86_64 i386 i686 arm aarch64 mips mipsel mips64 mips64el)
if(ANDROID)
diff --git a/lib/Makefile.mk b/lib/Makefile.mk
index ed9690d467aa..7eb6489fec8c 100644
--- a/lib/Makefile.mk
+++ b/lib/Makefile.mk
@@ -12,11 +12,8 @@ SubDirs :=
# Add submodules.
SubDirs += asan
SubDirs += builtins
-SubDirs += dfsan
SubDirs += interception
SubDirs += lsan
-SubDirs += msan
SubDirs += profile
SubDirs += sanitizer_common
-SubDirs += tsan
SubDirs += ubsan
diff --git a/lib/asan/README.txt b/lib/asan/README.txt
index b9c43acd5fe4..8cc9bb17b59d 100644
--- a/lib/asan/README.txt
+++ b/lib/asan/README.txt
@@ -1,7 +1,6 @@
AddressSanitizer RT
================================
-This directory contains sources of the AddressSanitizer (asan) runtime library.
-We are in the process of integrating AddressSanitizer with LLVM, stay tuned.
+This directory contains sources of the AddressSanitizer (ASan) runtime library.
Directory structure:
README.txt : This file.
@@ -13,14 +12,13 @@ tests/* : ASan unit tests.
Also ASan runtime needs the following libraries:
lib/interception/ : Machinery used to intercept function calls.
-lib/sanitizer_common/ : Code shared between ASan and TSan.
+lib/sanitizer_common/ : Code shared between various sanitizers.
-Currently ASan runtime can be built by both make and cmake build systems.
-(see compiler-rt/make and files Makefile.mk for make-based build and
-files CMakeLists.txt for cmake-based build).
+ASan runtime currently also embeds part of LeakSanitizer runtime for
+leak detection (lib/lsan/lsan_common.{cc,h}).
-ASan unit and output tests work only with cmake. You may run this
-command from the root of your cmake build tree:
+ASan runtime can only be built by CMake. You can run ASan tests
+from the root of your CMake build tree:
make check-asan
diff --git a/lib/asan/asan_flags.cc b/lib/asan/asan_flags.cc
index 1d82ab0e725f..efb7767d5d91 100644
--- a/lib/asan/asan_flags.cc
+++ b/lib/asan/asan_flags.cc
@@ -46,18 +46,15 @@ void Flags::SetDefaults() {
#undef ASAN_FLAG
}
-void RegisterAsanFlags(FlagParser *parser, Flags *f) {
+static void RegisterAsanFlags(FlagParser *parser, Flags *f) {
#define ASAN_FLAG(Type, Name, DefaultValue, Description) \
RegisterFlag(parser, #Name, Description, &f->Name);
#include "asan_flags.inc"
#undef ASAN_FLAG
}
-void InitializeFlags(Flags *f) {
- FlagParser parser;
- RegisterAsanFlags(&parser, f);
- RegisterCommonFlags(&parser);
-
+void InitializeFlags() {
+ // Set the default values and prepare for parsing ASan and common flags.
SetCommonFlagsDefaults();
{
CommonFlags cf;
@@ -68,28 +65,44 @@ void InitializeFlags(Flags *f) {
cf.intercept_tls_get_addr = true;
OverrideCommonFlags(cf);
}
-
- const int kDefaultQuarantineSizeMb = (ASAN_LOW_MEMORY) ? 1UL << 6 : 1UL << 8;
+ Flags *f = flags();
f->SetDefaults();
- // Override from compile definition.
- const char *compile_def = MaybeUseAsanDefaultOptionsCompileDefinition();
- parser.ParseString(compile_def);
+ FlagParser asan_parser;
+ RegisterAsanFlags(&asan_parser, f);
+ RegisterCommonFlags(&asan_parser);
+
+ // Set the default values and prepare for parsing LSan flags (which can also
+ // overwrite common flags).
+#if CAN_SANITIZE_LEAKS
+ __lsan::Flags *lf = __lsan::flags();
+ lf->SetDefaults();
+
+ FlagParser lsan_parser;
+ __lsan::RegisterLsanFlags(&lsan_parser, lf);
+ RegisterCommonFlags(&lsan_parser);
+#endif
+
+ // Override from ASan compile definition.
+ const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition();
+ asan_parser.ParseString(asan_compile_def);
// Override from user-specified string.
- const char *default_options = MaybeCallAsanDefaultOptions();
- parser.ParseString(default_options);
+ const char *asan_default_options = MaybeCallAsanDefaultOptions();
+ asan_parser.ParseString(asan_default_options);
// Override from command line.
- const char *env = GetEnv("ASAN_OPTIONS");
- if (env) parser.ParseString(env);
+ asan_parser.ParseString(GetEnv("ASAN_OPTIONS"));
+#if CAN_SANITIZE_LEAKS
+ lsan_parser.ParseString(GetEnv("LSAN_OPTIONS"));
+#endif
// Let activation flags override current settings. On Android they come
// from a system property. On other platforms this is no-op.
if (!flags()->start_deactivated) {
char buf[100];
GetExtraActivationFlags(buf, sizeof(buf));
- parser.ParseString(buf);
+ asan_parser.ParseString(buf);
}
SetVerbosity(common_flags()->verbosity);
@@ -97,7 +110,10 @@ void InitializeFlags(Flags *f) {
// TODO(eugenis): dump all flags at verbosity>=2?
if (Verbosity()) ReportUnrecognizedFlags();
- if (common_flags()->help) parser.PrintFlagDescriptions();
+ if (common_flags()->help) {
+ // TODO(samsonov): print all of the flags (ASan, LSan, common).
+ asan_parser.PrintFlagDescriptions();
+ }
// Flag validation:
if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) {
@@ -127,8 +143,11 @@ void InitializeFlags(Flags *f) {
}
if (f->quarantine_size >= 0)
f->quarantine_size_mb = f->quarantine_size >> 20;
- if (f->quarantine_size_mb < 0)
+ if (f->quarantine_size_mb < 0) {
+ const int kDefaultQuarantineSizeMb =
+ (ASAN_LOW_MEMORY) ? 1UL << 6 : 1UL << 8;
f->quarantine_size_mb = kDefaultQuarantineSizeMb;
+ }
}
} // namespace __asan
diff --git a/lib/asan/asan_flags.h b/lib/asan/asan_flags.h
index 7653543f6d88..4935161c30f1 100644
--- a/lib/asan/asan_flags.h
+++ b/lib/asan/asan_flags.h
@@ -41,8 +41,8 @@ extern Flags asan_flags_dont_use_directly;
inline Flags *flags() {
return &asan_flags_dont_use_directly;
}
-void RegisterAsanFlags(FlagParser *parser, Flags *f);
-void InitializeFlags(Flags *f);
+
+void InitializeFlags();
} // namespace __asan
diff --git a/lib/asan/asan_flags.inc b/lib/asan/asan_flags.inc
index ec9d41980d9c..53a8a4039e7e 100644
--- a/lib/asan/asan_flags.inc
+++ b/lib/asan/asan_flags.inc
@@ -142,3 +142,4 @@ ASAN_FLAG(int, detect_odr_violation, 2,
"have different sizes")
ASAN_FLAG(bool, dump_instruction_bytes, false,
"If true, dump 16 bytes starting at the instruction that caused SEGV")
+ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc
index c4571953c408..06140bbb360a 100644
--- a/lib/asan/asan_globals.cc
+++ b/lib/asan/asan_globals.cc
@@ -182,6 +182,8 @@ static void RegisterGlobal(const Global *g) {
static void UnregisterGlobal(const Global *g) {
CHECK(asan_inited);
+ if (flags()->report_globals >= 2)
+ ReportGlobal(*g, "Removed");
CHECK(flags()->report_globals);
CHECK(AddrIsInMem(g->beg));
CHECK(AddrIsAlignedByGranularity(g->beg));
@@ -208,6 +210,20 @@ 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
diff --git a/lib/asan/asan_interface_internal.h b/lib/asan/asan_interface_internal.h
index edaf44d7893a..ea7540f6bb56 100644
--- a/lib/asan/asan_interface_internal.h
+++ b/lib/asan/asan_interface_internal.h
@@ -9,8 +9,11 @@
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
-// This header can be included by the instrumented program to fetch
-// data (mostly allocator statistics) from ASan runtime library.
+// This header declares the AddressSanitizer runtime interface functions.
+// The runtime library has to define these functions so the instrumented program
+// could call them.
+//
+// See also include/sanitizer/asan_interface.h
//===----------------------------------------------------------------------===//
#ifndef ASAN_INTERFACE_INTERNAL_H
#define ASAN_INTERFACE_INTERNAL_H
diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h
index a8e23ce772ff..ffd3ff82d71e 100644
--- a/lib/asan/asan_internal.h
+++ b/lib/asan/asan_internal.h
@@ -93,6 +93,7 @@ void AsanCheckIncompatibleRT();
void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
void AsanOnSIGSEGV(int, void *siginfo, void *context);
+void DisableReexec();
void MaybeReexec();
void ReadContextStack(void *context, uptr *stack, uptr *ssize);
void AsanPlatformThreadInit();
diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc
index 65605009005f..8e8bafd47af6 100644
--- a/lib/asan/asan_linux.cc
+++ b/lib/asan/asan_linux.cc
@@ -68,6 +68,10 @@ asan_rt_version_t __asan_rt_version;
namespace __asan {
+void DisableReexec() {
+ // No need to re-exec on Linux.
+}
+
void MaybeReexec() {
// No need to re-exec on Linux.
}
diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc
index 5c2caeae4934..b35368617dca 100644
--- a/lib/asan/asan_mac.cc
+++ b/lib/asan/asan_mac.cc
@@ -101,7 +101,15 @@ void LeakyResetEnv(const char *name, const char *name_value) {
}
}
+static bool reexec_disabled = false;
+
+void DisableReexec() {
+ reexec_disabled = true;
+}
+
void MaybeReexec() {
+ if (reexec_disabled) return;
+
// Make sure the dynamic ASan runtime library is preloaded so that the
// wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
// ourselves.
diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc
index 0b2a23d40379..9126e71a6437 100644
--- a/lib/asan/asan_rtl.cc
+++ b/lib/asan/asan_rtl.cc
@@ -314,7 +314,7 @@ static void AsanInitInternal() {
// Initialize flags. This must be done early, because most of the
// initialization steps look at flags().
- InitializeFlags(flags());
+ InitializeFlags();
SetCanPoisonMemory(flags()->poison_heap);
SetMallocContextSize(common_flags()->malloc_context_size);
@@ -440,7 +440,7 @@ static void AsanInitInternal() {
SanitizerInitializeUnwinder();
#if CAN_SANITIZE_LEAKS
- __lsan::InitCommonLsan(false);
+ __lsan::InitCommonLsan();
if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
Atexit(__lsan::DoLeakCheck);
}
diff --git a/lib/asan/asan_suppressions.cc b/lib/asan/asan_suppressions.cc
index ef554716faa0..62198aec64e7 100644
--- a/lib/asan/asan_suppressions.cc
+++ b/lib/asan/asan_suppressions.cc
@@ -15,57 +15,62 @@
#include "asan_suppressions.h"
#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_suppressions.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
namespace __asan {
-static bool suppressions_inited = false;
+ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+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 *kSuppressionTypes[] = {
+ kInterceptorName, kInterceptorViaFunction, kInterceptorViaLibrary};
void InitializeSuppressions() {
- CHECK(!suppressions_inited);
- SuppressionContext::InitIfNecessary();
- suppressions_inited = true;
+ CHECK_EQ(nullptr, suppression_ctx);
+ suppression_ctx = new (suppression_placeholder) // NOLINT
+ SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+ suppression_ctx->ParseFromFile(flags()->suppressions);
}
bool IsInterceptorSuppressed(const char *interceptor_name) {
- CHECK(suppressions_inited);
- SuppressionContext *ctx = SuppressionContext::Get();
+ CHECK(suppression_ctx);
Suppression *s;
// Match "interceptor_name" suppressions.
- return ctx->Match(interceptor_name, SuppressionInterceptorName, &s);
+ return suppression_ctx->Match(interceptor_name, kInterceptorName, &s);
}
bool HaveStackTraceBasedSuppressions() {
- CHECK(suppressions_inited);
- SuppressionContext *ctx = SuppressionContext::Get();
- return ctx->HasSuppressionType(SuppressionInterceptorViaFunction) ||
- ctx->HasSuppressionType(SuppressionInterceptorViaLibrary);
+ CHECK(suppression_ctx);
+ return suppression_ctx->HasSuppressionType(kInterceptorViaFunction) ||
+ suppression_ctx->HasSuppressionType(kInterceptorViaLibrary);
}
bool IsStackTraceSuppressed(const StackTrace *stack) {
- CHECK(suppressions_inited);
if (!HaveStackTraceBasedSuppressions())
return false;
- SuppressionContext *ctx = SuppressionContext::Get();
+ CHECK(suppression_ctx);
Symbolizer *symbolizer = Symbolizer::GetOrInit();
Suppression *s;
for (uptr i = 0; i < stack->size && stack->trace[i]; i++) {
uptr addr = stack->trace[i];
- if (ctx->HasSuppressionType(SuppressionInterceptorViaLibrary)) {
+ 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) &&
- ctx->Match(module_name, SuppressionInterceptorViaLibrary, &s)) {
+ suppression_ctx->Match(module_name, kInterceptorViaLibrary, &s)) {
return true;
}
}
- if (ctx->HasSuppressionType(SuppressionInterceptorViaFunction)) {
+ if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) {
SymbolizedStack *frames = symbolizer->SymbolizePC(addr);
for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
const char *function_name = cur->info.function;
@@ -73,7 +78,8 @@ bool IsStackTraceSuppressed(const StackTrace *stack) {
continue;
}
// Match "interceptor_via_fun" suppressions.
- if (ctx->Match(function_name, SuppressionInterceptorViaFunction, &s)) {
+ if (suppression_ctx->Match(function_name, kInterceptorViaFunction,
+ &s)) {
frames->ClearAll();
return true;
}
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index 4f02b022fe79..693f0bcdee8e 100644
--- a/lib/asan/asan_win.cc
+++ b/lib/asan/asan_win.cc
@@ -60,6 +60,10 @@ void PlatformTSDDtor(void *tsd) {
AsanThread::TSDDtor(tsd);
}
// ---------------------- Various stuff ---------------- {{{1
+void DisableReexec() {
+ // No need to re-exec on Windows.
+}
+
void MaybeReexec() {
// No need to re-exec on Windows.
}
diff --git a/lib/asan/asan_win_dll_thunk.cc b/lib/asan/asan_win_dll_thunk.cc
index b38a2d16087f..5d39e33096a8 100644
--- a/lib/asan/asan_win_dll_thunk.cc
+++ b/lib/asan/asan_win_dll_thunk.cc
@@ -294,7 +294,43 @@ INTERFACE_FUNCTION(__asan_stack_free_8)
INTERFACE_FUNCTION(__asan_stack_free_9)
INTERFACE_FUNCTION(__asan_stack_free_10)
+// FIXME: we might want to have a sanitizer_win_dll_thunk?
+INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
+INTERFACE_FUNCTION(__sanitizer_cov)
+INTERFACE_FUNCTION(__sanitizer_cov_dump)
+INTERFACE_FUNCTION(__sanitizer_cov_indir_call16)
+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_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)
+INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size)
+INTERFACE_FUNCTION(__sanitizer_get_free_bytes)
+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_sandbox_on_notify)
+INTERFACE_FUNCTION(__sanitizer_set_death_callback)
+INTERFACE_FUNCTION(__sanitizer_set_report_path)
+INTERFACE_FUNCTION(__sanitizer_unaligned_load16)
+INTERFACE_FUNCTION(__sanitizer_unaligned_load32)
+INTERFACE_FUNCTION(__sanitizer_unaligned_load64)
+INTERFACE_FUNCTION(__sanitizer_unaligned_store16)
+INTERFACE_FUNCTION(__sanitizer_unaligned_store32)
+INTERFACE_FUNCTION(__sanitizer_unaligned_store64)
+INTERFACE_FUNCTION(__sanitizer_verify_contiguous_container)
// TODO(timurrrr): Add more interface functions on the as-needed basis.
diff --git a/lib/asan/asan_win_dynamic_runtime_thunk.cc b/lib/asan/asan_win_dynamic_runtime_thunk.cc
index 3a4de7dbf1fb..19456141c1ec 100644
--- a/lib/asan/asan_win_dynamic_runtime_thunk.cc
+++ b/lib/asan/asan_win_dynamic_runtime_thunk.cc
@@ -23,10 +23,11 @@
// Using #ifdef rather than relying on Makefiles etc.
// simplifies the build procedure.
#ifdef ASAN_DYNAMIC_RUNTIME_THUNK
-extern "C" {
-__declspec(dllimport) int __asan_set_seh_filter();
-__declspec(dllimport) int __asan_should_detect_stack_use_after_return();
+#include <windows.h>
+#include <psapi.h>
+extern "C" {
+////////////////////////////////////////////////////////////////////////////////
// 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.
//
@@ -37,16 +38,82 @@ __declspec(dllimport) int __asan_should_detect_stack_use_after_return();
// 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.
+__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);
+}
+
+namespace {
+void *this_module_base, *this_module_end;
+
+void UnregisterGlobals() {
+ __asan::UnregisterGlobalsInRange(this_module_base, this_module_end);
+}
+
+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;
-// Set the ASan-specific SEH handler at the end of CRT initialization of each
-// module (see asan_win.cc for the details).
+ 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).
+__declspec(allocate(".CRT$XID"))
+static int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals;
+
+// We need to set the ASan-specific SEH handler at the end of CRT initialization
+// of each module (see also asan_win.cc).
//
// Unfortunately, putting a pointer to __asan_set_seh_filter into
// __asan_intercept_seh gets optimized out, so we have to use an extra function.
-static int SetSEHFilter() { return __asan_set_seh_filter(); }
-#pragma section(".CRT$XIZ", long, read) // NOLINT
-__declspec(allocate(".CRT$XIZ")) int (*__asan_seh_interceptor)() = SetSEHFilter;
-}
+extern "C" __declspec(allocate(".CRT$XIZ"))
+int (*__asan_seh_interceptor)() = SetSEHFilter;
+
#endif // ASAN_DYNAMIC_RUNTIME_THUNK
diff --git a/lib/asan/scripts/asan_device_setup b/lib/asan/scripts/asan_device_setup
index c143b9fab13c..104e07b722ca 100755
--- a/lib/asan/scripts/asan_device_setup
+++ b/lib/asan/scripts/asan_device_setup
@@ -18,6 +18,7 @@ revert=no
extra_options=
device=
lib=
+use_su=0
function usage {
echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra-options options]"
@@ -26,13 +27,70 @@ function usage {
echo " --extra-options: Extra ASAN_OPTIONS."
echo " --device: Install to the given device. Use 'adb devices' to find"
echo " device-id."
+ echo " --use-su: Use 'su -c' prefix for every adb command instead of using"
+ echo " 'adb root' once."
echo
exit 1
}
+function adb_push {
+ if [ $use_su -eq 0 ]; then
+ $ADB push "$1" "$2"
+ else
+ local FILENAME=$(basename $1)
+ $ADB push "$1" "/data/local/tmp/$FILENAME"
+ $ADB shell su -c "rm \\\"$2/$FILENAME\\\"" >&/dev/null
+ $ADB shell su -c "cat \\\"/data/local/tmp/$FILENAME\\\" > \\\"$2/$FILENAME\\\""
+ $ADB shell su -c "rm \\\"/data/local/tmp/$FILENAME\\\""
+ fi
+}
+
+function adb_remount {
+ if [ $use_su -eq 0 ]; then
+ $ADB remount
+ else
+ local STORAGE=`$ADB shell mount | grep /system | cut -d ' ' -f1`
+ if [ "$STORAGE" != "" ]; then
+ echo Remounting $STORAGE at /system
+ $ADB shell su -c "mount -o remount,rw $STORAGE /system"
+ else
+ echo Failed to get storage device name for "/system" mount point
+ fi
+ fi
+}
+
+function adb_shell {
+ if [ $use_su -eq 0 ]; then
+ $ADB shell $@
+ else
+ $ADB shell su -c "$*"
+ fi
+}
+
+function adb_root {
+ if [ $use_su -eq 0 ]; then
+ $ADB root
+ fi
+}
+
+function adb_wait_for_device {
+ $ADB wait-for-device
+}
+
+function adb_pull {
+ if [ $use_su -eq 0 ]; then
+ $ADB pull "$1" "$2"
+ else
+ local FILENAME=$(basename $1)
+ $ADB shell rm "/data/local/tmp/$FILENAME" >&/dev/null
+ $ADB shell su -c "[ -f \\\"$1\\\" ] && cat \\\"$1\\\" > \\\"/data/local/tmp/$FILENAME\\\" && chown root.shell \\\"/data/local/tmp/$FILENAME\\\" && chmod 755 \\\"/data/local/tmp/$FILENAME\\\"" &&
+ $ADB pull "/data/local/tmp/$FILENAME" "$2" >&/dev/null && $ADB shell "rm \"/data/local/tmp/$FILENAME\""
+ fi
+}
+
function get_device_arch { # OUTVAR
local _outvar=$1
- local _ABI=$($ADB shell getprop ro.product.cpu.abi)
+ local _ABI=$(adb_shell getprop ro.product.cpu.abi)
local _ARCH=
if [[ $_ABI == x86* ]]; then
_ARCH=i686
@@ -74,6 +132,9 @@ while [[ $# > 0 ]]; do
fi
device="$1"
;;
+ --use-su)
+ use_su=1
+ ;;
*)
usage
;;
@@ -86,12 +147,25 @@ if [[ x$device != x ]]; then
ADB="$ADB -s $device"
fi
+if [ $use_su -eq 1 ]; then
+ # Test if 'su' is present on the device
+ SU_TEST_OUT=`$ADB shell su -c "echo foo" 2>&1 | sed 's/\r$//'`
+ if [ $? != 0 -o "$SU_TEST_OUT" != "foo" ]; then
+ echo "ERROR: Cannot use 'su -c':"
+ echo "$ adb shell su -c \"echo foo\""
+ echo $SU_TEST_OUT
+ echo "Check that 'su' binary is correctly installed on the device or omit"
+ echo " --use-su flag"
+ exit 1
+ fi
+fi
+
echo '>> Remounting /system rw'
-$ADB wait-for-device
-$ADB root
-$ADB wait-for-device
-$ADB remount
-$ADB wait-for-device
+adb_wait_for_device
+adb_root
+adb_wait_for_device
+adb_remount
+adb_wait_for_device
get_device_arch ARCH
echo "Target architecture: $ARCH"
@@ -100,22 +174,24 @@ ASAN_RT="libclang_rt.asan-$ARCH-android.so"
if [[ x$revert == xyes ]]; then
echo '>> Uninstalling ASan'
- if ! $ADB shell readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then
+ if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
echo '>> Pre-L device detected.'
- $ADB shell mv /system/bin/app_process.real /system/bin/app_process
- $ADB shell rm /system/bin/asanwrapper
- $ADB shell rm /system/lib/$ASAN_RT
+ adb_shell mv /system/bin/app_process.real /system/bin/app_process
+ adb_shell rm /system/bin/asanwrapper
else
- $ADB shell rm /system/bin/app_process.wrap
- $ADB shell rm /system/bin/asanwrapper
- $ADB shell rm /system/lib/$ASAN_RT
- $ADB shell rm /system/bin/app_process
- $ADB shell ln -s /system/bin/app_process32 /system/bin/app_process
+ adb_shell rm /system/bin/app_process.wrap
+ adb_shell rm /system/bin/asanwrapper
+ adb_shell rm /system/bin/app_process
+ adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
fi
echo '>> Restarting shell'
- $ADB shell stop
- $ADB shell start
+ adb_shell stop
+ adb_shell start
+
+ # Remove the library on the last step to give a chance to the 'su' binary to
+ # be executed without problem.
+ adb_shell rm /system/lib/$ASAN_RT
echo '>> Done'
exit 0
@@ -146,28 +222,28 @@ TMPDIROLD="$TMPDIRBASE/old"
TMPDIR="$TMPDIRBASE/new"
mkdir "$TMPDIROLD"
-RELEASE=$($ADB shell getprop ro.build.version.release)
+RELEASE=$(adb_shell getprop ro.build.version.release)
PRE_L=0
if echo "$RELEASE" | grep '^4\.' >&/dev/null; then
PRE_L=1
fi
-if ! $ADB shell readlink /system/bin/app_process | grep 'app_process' >&/dev/null; then
+if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
- if $ADB pull /system/bin/app_process.real /dev/null >&/dev/null; then
+ if adb_pull /system/bin/app_process.real /dev/null >&/dev/null; then
echo '>> Old-style ASan installation detected. Reverting.'
- $ADB shell mv /system/bin/app_process.real /system/bin/app_process
+ adb_shell mv /system/bin/app_process.real /system/bin/app_process
fi
echo '>> Pre-L device detected. Setting up app_process symlink.'
- $ADB shell mv /system/bin/app_process /system/bin/app_process32
- $ADB shell ln -s /system/bin/app_process32 /system/bin/app_process
+ adb_shell mv /system/bin/app_process /system/bin/app_process32
+ adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
fi
echo '>> Copying files from the device'
-$ADB pull /system/bin/app_process.wrap "$TMPDIROLD" || true
-$ADB pull /system/bin/asanwrapper "$TMPDIROLD" || true
-$ADB pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
+adb_pull /system/bin/app_process.wrap "$TMPDIROLD" || true
+adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true
+adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
cp -r "$TMPDIROLD" "$TMPDIR"
if [[ -f "$TMPDIR/app_process.wrap" ]]; then
@@ -213,52 +289,52 @@ EOF
if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then
echo '>> Pushing files to the device'
- $ADB push "$TMPDIR/$ASAN_RT" /system/lib/
- $ADB push "$TMPDIR/app_process.wrap" /system/bin/app_process.wrap
- $ADB push "$TMPDIR/asanwrapper" /system/bin/asanwrapper
+ adb_push "$TMPDIR/$ASAN_RT" /system/lib/
+ adb_push "$TMPDIR/app_process.wrap" /system/bin
+ adb_push "$TMPDIR/asanwrapper" /system/bin
- $ADB shell rm /system/bin/app_process
- $ADB shell ln -s /system/bin/app_process.wrap /system/bin/app_process
+ adb_shell rm /system/bin/app_process
+ adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process
- $ADB shell chown root.shell \
+ adb_shell chown root.shell \
/system/lib/"$ASAN_RT" \
/system/bin/app_process.wrap \
/system/bin/asanwrapper
- $ADB shell chmod 644 \
+ adb_shell chmod 644 \
/system/lib/"$ASAN_RT"
- $ADB shell chmod 755 \
+ adb_shell chmod 755 \
/system/bin/app_process.wrap \
/system/bin/asanwrapper
# Make SELinux happy by keeping app_process wrapper and the shell
# it runs on in zygote domain.
ENFORCING=0
- if $ADB shell getenforce | grep Enforcing >/dev/null; then
+ if adb_shell getenforce | grep Enforcing >/dev/null; then
# Sometimes shell is not allowed to change file contexts.
# Temporarily switch to permissive.
ENFORCING=1
- $ADB shell setenforce 0
+ adb_shell setenforce 0
fi
- $ADB shell cp /system/bin/sh /system/bin/sh-from-zygote
+ adb_shell cp /system/bin/sh /system/bin/sh-from-zygote
if [[ PRE_L -eq 1 ]]; then
CTX=u:object_r:system_file:s0
else
CTX=u:object_r:zygote_exec:s0
fi
- $ADB shell chcon $CTX \
+ adb_shell chcon $CTX \
/system/bin/sh-from-zygote \
/system/bin/app_process.wrap \
/system/bin/app_process32
if [ $ENFORCING == 1 ]; then
- $ADB shell setenforce 1
+ adb_shell setenforce 1
fi
echo '>> Restarting shell (asynchronous)'
- $ADB shell stop
- $ADB shell start
+ adb_shell stop
+ adb_shell start
echo '>> Please wait until the device restarts'
else
diff --git a/lib/asan/tests/asan_noinst_test.cc b/lib/asan/tests/asan_noinst_test.cc
index c469d62a6766..6a428fbbc2b9 100644
--- a/lib/asan/tests/asan_noinst_test.cc
+++ b/lib/asan/tests/asan_noinst_test.cc
@@ -33,7 +33,10 @@
// Make sure __asan_init is called before any test case is run.
struct AsanInitCaller {
- AsanInitCaller() { __asan_init(); }
+ AsanInitCaller() {
+ __asan::DisableReexec();
+ __asan_init();
+ }
};
static AsanInitCaller asan_init_caller;
diff --git a/lib/builtins/clear_cache.c b/lib/builtins/clear_cache.c
index 61b1e9bbb5c0..8dc0fb1c5907 100644
--- a/lib/builtins/clear_cache.c
+++ b/lib/builtins/clear_cache.c
@@ -22,10 +22,10 @@
#include <machine/sysarch.h>
#endif
-#if defined(__ANDROID__) && defined(__mips__)
+#if defined(__mips__)
#include <sys/cachectl.h>
#include <sys/syscall.h>
- #ifdef __LP64__
+ #if defined(__ANDROID__) && defined(__LP64__)
/*
* clear_mips_cache - Invalidates instruction cache for Mips.
*/
@@ -109,10 +109,10 @@ void __clear_cache(void *start, void *end) {
#else
compilerrt_abort();
#endif
-#elif defined(__ANDROID__) && defined(__mips__)
+#elif defined(__mips__)
const uintptr_t start_int = (uintptr_t) start;
const uintptr_t end_int = (uintptr_t) end;
- #ifdef __LP64__
+ #if defined(__ANDROID__) && defined(__LP64__)
// Call synci implementation for short address range.
const uintptr_t address_range_limit = 256;
if ((end_int - start_int) <= address_range_limit) {
diff --git a/lib/dfsan/Makefile.mk b/lib/dfsan/Makefile.mk
deleted file mode 100644
index 4aeaac42dea2..000000000000
--- a/lib/dfsan/Makefile.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-#===- lib/dfsan/Makefile.mk --------------------------------*- Makefile -*--===#
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-#===------------------------------------------------------------------------===#
-
-ModuleName := dfsan
-SubDirs :=
-
-Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
-ObjNames := $(Sources:%.cc=%.o)
-
-Implementation := Generic
-
-# FIXME: use automatic dependencies?
-Dependencies := $(wildcard $(Dir)/*.h)
-Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h)
-
-# Define a convenience variable for all the dfsan functions.
-DfsanFunctions := $(Sources:%.cc=%)
diff --git a/lib/dfsan/dfsan.cc b/lib/dfsan/dfsan.cc
index dd0ea61142b8..de5b2ce107b4 100644
--- a/lib/dfsan/dfsan.cc
+++ b/lib/dfsan/dfsan.cc
@@ -317,18 +317,18 @@ void Flags::SetDefaults() {
#undef DFSAN_FLAG
}
-void RegisterDfsanFlags(FlagParser *parser, Flags *f) {
+static void RegisterDfsanFlags(FlagParser *parser, Flags *f) {
#define DFSAN_FLAG(Type, Name, DefaultValue, Description) \
RegisterFlag(parser, #Name, Description, &f->Name);
#include "dfsan_flags.inc"
#undef DFSAN_FLAG
}
-static void InitializeFlags(Flags &f, const char *env) {
+static void InitializeFlags() {
FlagParser parser;
- RegisterDfsanFlags(&parser, &f);
- f.SetDefaults();
- parser.ParseString(env);
+ RegisterDfsanFlags(&parser, &flags());
+ flags().SetDefaults();
+ parser.ParseString(GetEnv("DFSAN_OPTIONS"));
}
static void dfsan_fini() {
@@ -363,8 +363,7 @@ static void dfsan_init(int argc, char **argv, char **envp) {
if (!(init_addr >= kUnusedAddr && init_addr < kAppAddr))
Mprotect(kUnusedAddr, kAppAddr - kUnusedAddr);
- InitializeFlags(flags(), GetEnv("DFSAN_OPTIONS"));
-
+ InitializeFlags();
InitializeInterceptors();
// Register the fini callback to run when the program terminates successfully
diff --git a/lib/lsan/Makefile.mk b/lib/lsan/Makefile.mk
index 2a6b41c98e2c..5e70634e792e 100644
--- a/lib/lsan/Makefile.mk
+++ b/lib/lsan/Makefile.mk
@@ -20,9 +20,6 @@ Dependencies := $(wildcard $(Dir)/*.h)
Dependencies += $(wildcard $(Dir)/../interception/*.h)
Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h)
-# Define a convenience variable for all the lsan functions.
-LsanFunctions := $(Sources:%.cc=%)
-
# lsan functions used in another sanitizers.
LsanCommonSources := $(foreach file,$(wildcard $(Dir)/lsan_common*.cc),$(notdir $(file)))
LsanCommonFunctions := $(LsanCommonSources:%.cc=%)
diff --git a/lib/lsan/lsan.cc b/lib/lsan/lsan.cc
index 1509b2a7d342..6018f7bf6f49 100644
--- a/lib/lsan/lsan.cc
+++ b/lib/lsan/lsan.cc
@@ -15,6 +15,7 @@
#include "lsan.h"
#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "lsan_allocator.h"
#include "lsan_common.h"
@@ -34,13 +35,42 @@ bool WordIsPoisoned(uptr addr) {
using namespace __lsan; // NOLINT
+static void InitializeFlags() {
+ // Set all the default values.
+ SetCommonFlagsDefaults();
+ {
+ CommonFlags cf;
+ cf.CopyFrom(*common_flags());
+ cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
+ cf.malloc_context_size = 30;
+ cf.detect_leaks = true;
+ OverrideCommonFlags(cf);
+ }
+
+ Flags *f = flags();
+ f->SetDefaults();
+
+ FlagParser parser;
+ RegisterLsanFlags(&parser, f);
+ RegisterCommonFlags(&parser);
+
+ parser.ParseString(GetEnv("LSAN_OPTIONS"));
+
+ SetVerbosity(common_flags()->verbosity);
+
+ if (Verbosity()) ReportUnrecognizedFlags();
+
+ if (common_flags()->help) parser.PrintFlagDescriptions();
+}
+
extern "C" void __lsan_init() {
CHECK(!lsan_init_is_running);
if (lsan_inited)
return;
lsan_init_is_running = true;
SanitizerToolName = "LeakSanitizer";
- InitCommonLsan(true);
+ InitializeFlags();
+ InitCommonLsan();
InitializeAllocator();
InitTlsSize();
InitializeInterceptors();
diff --git a/lib/lsan/lsan_allocator.cc b/lib/lsan/lsan_allocator.cc
index 96a2b0428a61..67125dbb3e45 100644
--- a/lib/lsan/lsan_allocator.cc
+++ b/lib/lsan/lsan_allocator.cc
@@ -25,10 +25,6 @@ extern "C" void *memset(void *ptr, int value, uptr num);
namespace __lsan {
-static const uptr kMaxAllowedMallocSize = 8UL << 30;
-static const uptr kAllocatorSpace = 0x600000000000ULL;
-static const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
-
struct ChunkMetadata {
bool allocated : 8; // Must be first.
ChunkTag tag : 2;
@@ -36,8 +32,22 @@ struct ChunkMetadata {
u32 stack_trace_id;
};
+#if defined(__mips64)
+static const uptr kMaxAllowedMallocSize = 4UL << 30;
+static const uptr kRegionSizeLog = 20;
+static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
+typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
+typedef CompactSizeClassMap SizeClassMap;
+typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE,
+ sizeof(ChunkMetadata), SizeClassMap, kRegionSizeLog, ByteMap>
+ PrimaryAllocator;
+#else
+static const uptr kMaxAllowedMallocSize = 8UL << 30;
+static const uptr kAllocatorSpace = 0x600000000000ULL;
+static const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize,
sizeof(ChunkMetadata), DefaultSizeClassMap> PrimaryAllocator;
+#endif
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
typedef LargeMmapAllocator<> SecondaryAllocator;
typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
diff --git a/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc
index 7b3cd1639761..b9e2a1104326 100644
--- a/lib/lsan/lsan_common.cc
+++ b/lib/lsan/lsan_common.cc
@@ -43,46 +43,13 @@ void Flags::SetDefaults() {
#undef LSAN_FLAG
}
-static void RegisterLsanFlags(FlagParser *parser, Flags *f) {
+void RegisterLsanFlags(FlagParser *parser, Flags *f) {
#define LSAN_FLAG(Type, Name, DefaultValue, Description) \
RegisterFlag(parser, #Name, Description, &f->Name);
#include "lsan_flags.inc"
#undef LSAN_FLAG
}
-static void InitializeFlags(bool standalone) {
- Flags *f = flags();
- FlagParser parser;
- RegisterLsanFlags(&parser, f);
- RegisterCommonFlags(&parser);
-
- f->SetDefaults();
-
- // Set defaults for common flags (only in standalone mode) and parse
- // them from LSAN_OPTIONS.
- if (standalone) {
- SetCommonFlagsDefaults();
- CommonFlags cf;
- cf.CopyFrom(*common_flags());
- cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
- cf.malloc_context_size = 30;
- cf.detect_leaks = true;
- OverrideCommonFlags(cf);
- }
-
- bool help_before = common_flags()->help;
-
- const char *options = GetEnv("LSAN_OPTIONS");
- parser.ParseString(options);
-
- SetVerbosity(common_flags()->verbosity);
-
- if (Verbosity()) ReportUnrecognizedFlags();
-
- if (!help_before && common_flags()->help)
- parser.PrintFlagDescriptions();
-}
-
#define LOG_POINTERS(...) \
do { \
if (flags()->log_pointers) Report(__VA_ARGS__); \
@@ -93,14 +60,23 @@ static void InitializeFlags(bool standalone) {
if (flags()->log_threads) Report(__VA_ARGS__); \
} while (0);
-static bool suppressions_inited = false;
+ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = nullptr;
+static const char kSuppressionLeak[] = "leak";
+static const char *kSuppressionTypes[] = { kSuppressionLeak };
void InitializeSuppressions() {
- CHECK(!suppressions_inited);
- SuppressionContext::InitIfNecessary();
+ CHECK_EQ(nullptr, suppression_ctx);
+ suppression_ctx = new (suppression_placeholder) // NOLINT
+ SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+ suppression_ctx->ParseFromFile(flags()->suppressions);
if (&__lsan_default_suppressions)
- SuppressionContext::Get()->Parse(__lsan_default_suppressions());
- suppressions_inited = true;
+ suppression_ctx->Parse(__lsan_default_suppressions());
+}
+
+static SuppressionContext *GetSuppressionContext() {
+ CHECK(suppression_ctx);
+ return suppression_ctx;
}
struct RootRegion {
@@ -116,8 +92,7 @@ void InitializeRootRegions() {
root_regions = new(placeholder) InternalMmapVector<RootRegion>(1);
}
-void InitCommonLsan(bool standalone) {
- InitializeFlags(standalone);
+void InitCommonLsan() {
InitializeRootRegions();
if (common_flags()->detect_leaks) {
// Initialization which can fail or print warnings should only be done if
@@ -140,9 +115,11 @@ static inline bool CanBeAHeapPointer(uptr p) {
// bound on heap addresses.
const uptr kMinAddress = 4 * 4096;
if (p < kMinAddress) return false;
-#ifdef __x86_64__
+#if defined(__x86_64__)
// Accept only canonical form user-space addresses.
return ((p >> 47) == 0);
+#elif defined(__mips64)
+ return ((p >> 40) == 0);
#else
return true;
#endif
@@ -382,7 +359,7 @@ static void CollectLeaksCb(uptr chunk, void *arg) {
static void PrintMatchedSuppressions() {
InternalMmapVector<Suppression *> matched(1);
- SuppressionContext::Get()->GetMatched(&matched);
+ GetSuppressionContext()->GetMatched(&matched);
if (!matched.size())
return;
const char *line = "-----------------------------------------------------";
@@ -461,17 +438,17 @@ static Suppression *GetSuppressionForAddr(uptr addr) {
// Suppress by module name.
const char *module_name;
uptr module_offset;
+ SuppressionContext *suppressions = GetSuppressionContext();
if (Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(addr, &module_name,
&module_offset) &&
- SuppressionContext::Get()->Match(module_name, SuppressionLeak, &s))
+ suppressions->Match(module_name, kSuppressionLeak, &s))
return s;
// Suppress by file or function name.
SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr);
for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
- if (SuppressionContext::Get()->Match(cur->info.function, SuppressionLeak,
- &s) ||
- SuppressionContext::Get()->Match(cur->info.file, SuppressionLeak, &s)) {
+ if (suppressions->Match(cur->info.function, kSuppressionLeak, &s) ||
+ suppressions->Match(cur->info.file, kSuppressionLeak, &s)) {
break;
}
}
diff --git a/lib/lsan/lsan_common.h b/lib/lsan/lsan_common.h
index 64cbef38c7a6..2c3a12ab6bd8 100644
--- a/lib/lsan/lsan_common.h
+++ b/lib/lsan/lsan_common.h
@@ -21,12 +21,17 @@
#include "sanitizer_common/sanitizer_platform.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
-#if SANITIZER_LINUX && defined(__x86_64__) && (SANITIZER_WORDSIZE == 64)
+#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips64)) \
+ && (SANITIZER_WORDSIZE == 64)
#define CAN_SANITIZE_LEAKS 1
#else
#define CAN_SANITIZE_LEAKS 0
#endif
+namespace __sanitizer {
+class FlagParser;
+}
+
namespace __lsan {
// Chunk tags.
@@ -50,6 +55,7 @@ struct Flags {
extern Flags lsan_flags;
inline Flags *flags() { return &lsan_flags; }
+void RegisterLsanFlags(FlagParser *parser, Flags *f);
struct Leak {
u32 id;
@@ -105,7 +111,7 @@ enum IgnoreObjectResult {
};
// Functions called from the parent tool.
-void InitCommonLsan(bool standalone);
+void InitCommonLsan();
void DoLeakCheck();
bool DisabledInThisThread();
diff --git a/lib/lsan/lsan_flags.inc b/lib/lsan/lsan_flags.inc
index 7f00acbf9119..b19b3452b2fc 100644
--- a/lib/lsan/lsan_flags.inc
+++ b/lib/lsan/lsan_flags.inc
@@ -42,3 +42,4 @@ LSAN_FLAG(bool, use_poisoned, false,
"Consider pointers found in poisoned memory to be valid.")
LSAN_FLAG(bool, log_pointers, false, "Debug logging")
LSAN_FLAG(bool, log_threads, false, "Debug logging")
+LSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
diff --git a/lib/msan/Makefile.mk b/lib/msan/Makefile.mk
deleted file mode 100644
index 99e3b036ea11..000000000000
--- a/lib/msan/Makefile.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-#===- lib/msan/Makefile.mk ---------------------------------*- Makefile -*--===#
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-#===------------------------------------------------------------------------===#
-
-ModuleName := msan
-SubDirs :=
-
-Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
-ObjNames := $(Sources:%.cc=%.o)
-
-Implementation := Generic
-
-# FIXME: use automatic dependencies?
-Dependencies := $(wildcard $(Dir)/*.h)
-Dependencies += $(wildcard $(Dir)/../interception/*.h)
-Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h)
-
-# Define a convenience variable for all the msan functions.
-MsanFunctions := $(Sources:%.cc=%)
diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc
index 4adcc9eb8e6a..ed6efbdd682f 100644
--- a/lib/msan/msan.cc
+++ b/lib/msan/msan.cc
@@ -111,7 +111,7 @@ class FlagHandlerKeepGoing : public FlagHandlerBase {
public:
explicit FlagHandlerKeepGoing(bool *halt_on_error)
: halt_on_error_(halt_on_error) {}
- bool Parse(const char *value) {
+ bool Parse(const char *value) final {
bool tmp;
FlagHandler<bool> h(&tmp);
if (!h.Parse(value)) return false;
@@ -120,7 +120,7 @@ class FlagHandlerKeepGoing : public FlagHandlerBase {
}
};
-void RegisterMsanFlags(FlagParser *parser, Flags *f) {
+static void RegisterMsanFlags(FlagParser *parser, Flags *f) {
#define MSAN_FLAG(Type, Name, DefaultValue, Description) \
RegisterFlag(parser, #Name, Description, &f->Name);
#include "msan_flags.inc"
@@ -132,7 +132,8 @@ void RegisterMsanFlags(FlagParser *parser, Flags *f) {
"deprecated, use halt_on_error");
}
-static void InitializeFlags(Flags *f, const char *options) {
+static void InitializeFlags() {
+ Flags *f = flags();
FlagParser parser;
RegisterMsanFlags(&parser, f);
RegisterCommonFlags(&parser);
@@ -156,7 +157,9 @@ static void InitializeFlags(Flags *f, const char *options) {
if (__msan_default_options)
parser.ParseString(__msan_default_options());
- parser.ParseString(options);
+ const char *msan_options = GetEnv("MSAN_OPTIONS");
+ parser.ParseString(msan_options);
+ VPrintf(1, "MSAN_OPTIONS: %s\n", msan_options ? msan_options : "<empty>");
SetVerbosity(common_flags()->verbosity);
@@ -351,8 +354,7 @@ void __msan_init() {
SetDieCallback(MsanDie);
InitTlsSize();
- const char *msan_options = GetEnv("MSAN_OPTIONS");
- InitializeFlags(&msan_flags, msan_options);
+ InitializeFlags();
__sanitizer_set_report_path(common_flags()->log_path);
InitializeInterceptors();
@@ -369,8 +371,6 @@ void __msan_init() {
ReExec();
}
- VPrintf(1, "MSAN_OPTIONS: %s\n", msan_options ? msan_options : "<empty>");
-
__msan_clear_on_return();
if (__msan_get_track_origins())
VPrintf(1, "msan_track_origins\n");
diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc
index 1c5fc5f7f1e3..317f70cbc32e 100644
--- a/lib/msan/tests/msan_test.cc
+++ b/lib/msan/tests/msan_test.cc
@@ -72,12 +72,21 @@ int shmdt(const void *);
# include <mntent.h>
# include <netinet/ether.h>
#else
+# include <signal.h>
# include <netinet/in.h>
# include <pthread_np.h>
# include <sys/uio.h>
# include <sys/mount.h>
+# include <sys/sysctl.h>
+# include <net/ethernet.h>
# define f_namelen f_namemax // FreeBSD names this statfs field so.
# define cpu_set_t cpuset_t
+extern "C" {
+// FreeBSD's <ssp/string.h> defines mempcpy() to be a macro expanding into
+// a __builtin___mempcpy_chk() call, but since Msan RTL defines it as an
+// ordinary function, we can declare it here to complete the tests.
+void *mempcpy(void *dest, const void *src, size_t n);
+}
#endif
#if defined(__i386__) || defined(__x86_64__)
@@ -97,14 +106,17 @@ int shmdt(const void *);
# define DIR_TO_READ "/bin"
# define SUBFILE_TO_READ "cat"
# define SYMLINK_TO_READ "/usr/bin/tar"
+# define SUPERUSER_GROUP "wheel"
#else
# define FILE_TO_READ "/proc/self/stat"
# define DIR_TO_READ "/proc/self"
# define SUBFILE_TO_READ "stat"
# define SYMLINK_TO_READ "/proc/self/exe"
+# define SUPERUSER_GROUP "root"
#endif
-static const size_t kPageSize = 4096;
+const size_t kPageSize = 4096;
+const size_t kMaxPathLength = 4096;
typedef unsigned char U1;
typedef unsigned short U2; // NOLINT
@@ -158,11 +170,11 @@ void ExpectPoisonedWithOrigin(const T& t, unsigned origin) {
EXPECT_EQ(origin, __msan_get_origin((void*)&t));
}
-#define EXPECT_NOT_POISONED(x) ExpectNotPoisoned(x)
+#define EXPECT_NOT_POISONED(x) EXPECT_EQ(true, TestForNotPoisoned((x)))
template<typename T>
-void ExpectNotPoisoned(const T& t) {
- EXPECT_EQ(-1, __msan_test_shadow((void*)&t, sizeof(t)));
+bool TestForNotPoisoned(const T& t) {
+ return __msan_test_shadow((void*)&t, sizeof(t)) == -1;
}
static U8 poisoned_array[100];
@@ -2166,6 +2178,8 @@ TEST(MemorySanitizer, mmap) {
}
}
+// There's no fcvt() on FreeBSD.
+#if !defined(__FreeBSD__)
// FIXME: enable and add ecvt.
// FIXME: check why msandr does nt handle fcvt.
TEST(MemorySanitizer, fcvt) {
@@ -2181,7 +2195,10 @@ TEST(MemorySanitizer, fcvt) {
EXPECT_NOT_POISONED(str[0]);
ASSERT_NE(0U, strlen(str));
}
+#endif
+// There's no fcvt_long() on FreeBSD.
+#if !defined(__FreeBSD__)
TEST(MemorySanitizer, fcvt_long) {
int a, b;
break_optimization(&a);
@@ -2195,7 +2212,7 @@ TEST(MemorySanitizer, fcvt_long) {
EXPECT_NOT_POISONED(str[0]);
ASSERT_NE(0U, strlen(str));
}
-
+#endif
TEST(MemorySanitizer, memchr) {
char x[10];
@@ -2797,9 +2814,20 @@ TEST(MemorySanitizer, getrusage) {
EXPECT_NOT_POISONED(usage.ru_nivcsw);
}
-#ifdef __GLIBC__
-extern char *program_invocation_name;
-#else // __GLIBC__
+#if defined(__FreeBSD__)
+static void GetProgramPath(char *buf, size_t sz) {
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
+ int res = sysctl(mib, 4, buf, &sz, NULL, 0);
+ ASSERT_EQ(0, res);
+}
+#elif defined(__GLIBC__)
+static void GetProgramPath(char *buf, size_t sz) {
+ extern char *program_invocation_name;
+ int res = snprintf(buf, sz, "%s", program_invocation_name);
+ ASSERT_GE(res, 0);
+ ASSERT_LT((size_t)res, sz);
+}
+#else
# error "TODO: port this"
#endif
@@ -2834,21 +2862,29 @@ static int dl_phdr_callback(struct dl_phdr_info *info, size_t size, void *data)
// Compute the path to our loadable DSO. We assume it's in the same
// directory. Only use string routines that we intercept so far to do this.
-static int PathToLoadable(char *buf, size_t sz) {
- const char *basename = "libmsan_loadable.x86_64.so";
- char *argv0 = program_invocation_name;
- char *last_slash = strrchr(argv0, '/');
- assert(last_slash);
- int res =
- snprintf(buf, sz, "%.*s/%s", int(last_slash - argv0), argv0, basename);
- assert(res >= 0);
- return (size_t)res < sz ? 0 : res;
+static void GetPathToLoadable(char *buf, size_t sz) {
+ char program_path[kMaxPathLength];
+ GetProgramPath(program_path, sizeof(program_path));
+
+ const char *last_slash = strrchr(program_path, '/');
+ ASSERT_NE(nullptr, last_slash);
+ size_t dir_len = (size_t)(last_slash - program_path);
+#if defined(__x86_64__)
+ static const char basename[] = "libmsan_loadable.x86_64.so";
+#elif defined(__MIPSEB__) || defined(MIPSEB)
+ static const char basename[] = "libmsan_loadable.mips64.so";
+#elif defined(__mips64)
+ static const char basename[] = "libmsan_loadable.mips64el.so";
+#endif
+ int res = snprintf(buf, sz, "%.*s/%s",
+ (int)dir_len, program_path, basename);
+ ASSERT_GE(res, 0);
+ ASSERT_LT((size_t)res, sz);
}
TEST(MemorySanitizer, dl_iterate_phdr) {
- char path[4096];
- int res = PathToLoadable(path, sizeof(path));
- ASSERT_EQ(0, res);
+ char path[kMaxPathLength];
+ GetPathToLoadable(path, sizeof(path));
// Having at least one dlopen'ed library in the process makes this more
// entertaining.
@@ -2858,15 +2894,13 @@ TEST(MemorySanitizer, dl_iterate_phdr) {
int count = 0;
int result = dl_iterate_phdr(dl_phdr_callback, &count);
ASSERT_GT(count, 0);
-
+
dlclose(lib);
}
-
TEST(MemorySanitizer, dlopen) {
- char path[4096];
- int res = PathToLoadable(path, sizeof(path));
- ASSERT_EQ(0, res);
+ char path[kMaxPathLength];
+ GetPathToLoadable(path, sizeof(path));
// We need to clear shadow for globals when doing dlopen. In order to test
// this, we have to poison the shadow for the DSO before we load it. In
@@ -2891,7 +2925,7 @@ TEST(MemorySanitizer, dlopen) {
// Regression test for a crash in dlopen() interceptor.
TEST(MemorySanitizer, dlopenFailed) {
- const char *path = "/libmsan_loadable_does_not_exist.x86_64.so";
+ const char *path = "/libmsan_loadable_does_not_exist.so";
void *lib = dlopen(path, RTLD_LAZY);
ASSERT_TRUE(lib == NULL);
}
@@ -3271,8 +3305,10 @@ TEST(MemorySanitizer, getgrnam_r) {
struct group grp;
struct group *grpres;
char buf[10000];
- int res = getgrnam_r("root", &grp, buf, sizeof(buf), &grpres);
+ int res = getgrnam_r(SUPERUSER_GROUP, &grp, buf, sizeof(buf), &grpres);
ASSERT_EQ(0, res);
+ // Note that getgrnam_r() returns 0 if the matching group is not found.
+ ASSERT_NE(nullptr, grpres);
EXPECT_NOT_POISONED(grp.gr_name);
ASSERT_TRUE(grp.gr_name != NULL);
EXPECT_NOT_POISONED(grp.gr_name[0]);
@@ -3360,6 +3396,8 @@ TEST(MemorySanitizer, getgrent_r) {
EXPECT_NOT_POISONED(grpres);
}
+// There's no fgetgrent_r() on FreeBSD.
+#if !defined(__FreeBSD__)
TEST(MemorySanitizer, fgetgrent_r) {
FILE *fp = fopen("/etc/group", "r");
struct group grp;
@@ -3375,6 +3413,7 @@ TEST(MemorySanitizer, fgetgrent_r) {
EXPECT_NOT_POISONED(grpres);
fclose(fp);
}
+#endif
TEST(MemorySanitizer, getgroups) {
int n = getgroups(0, 0);
@@ -3502,7 +3541,7 @@ TEST(MemorySanitizer, VolatileBitfield) {
}
TEST(MemorySanitizer, UnalignedLoad) {
- char x[32];
+ char x[32] __attribute__((aligned(8)));
U4 origin = __LINE__;
for (unsigned i = 0; i < sizeof(x) / 4; ++i)
__msan_set_origin(x + 4 * i, 4, origin + i);
@@ -3536,7 +3575,7 @@ TEST(MemorySanitizer, UnalignedLoad) {
}
TEST(MemorySanitizer, UnalignedStore16) {
- char x[5];
+ char x[5] __attribute__((aligned(4)));
U2 y2 = 0;
U4 origin = __LINE__;
__msan_poison(&y2, 1);
@@ -3547,11 +3586,10 @@ TEST(MemorySanitizer, UnalignedStore16) {
EXPECT_POISONED_O(x[1], origin);
EXPECT_NOT_POISONED(x[2]);
EXPECT_POISONED_O(x[3], origin);
- EXPECT_POISONED_O(x[4], origin);
}
TEST(MemorySanitizer, UnalignedStore32) {
- char x[8];
+ char x[8] __attribute__((aligned(4)));
U4 y4 = 0;
U4 origin = __LINE__;
__msan_poison(&y4, 2);
@@ -3569,7 +3607,7 @@ TEST(MemorySanitizer, UnalignedStore32) {
}
TEST(MemorySanitizer, UnalignedStore64) {
- char x[16];
+ char x[16] __attribute__((aligned(8)));
U8 y8 = 0;
U4 origin = __LINE__;
__msan_poison(&y8, 3);
@@ -3592,7 +3630,7 @@ TEST(MemorySanitizer, UnalignedStore64) {
}
TEST(MemorySanitizer, UnalignedStore16_precise) {
- char x[8];
+ char x[8] __attribute__((aligned(4)));
U2 y = 0;
U4 originx1 = __LINE__;
U4 originx2 = __LINE__;
@@ -3615,7 +3653,7 @@ TEST(MemorySanitizer, UnalignedStore16_precise) {
}
TEST(MemorySanitizer, UnalignedStore16_precise2) {
- char x[8];
+ char x[8] __attribute__((aligned(4)));
U2 y = 0;
U4 originx1 = __LINE__;
U4 originx2 = __LINE__;
@@ -3638,7 +3676,7 @@ TEST(MemorySanitizer, UnalignedStore16_precise2) {
}
TEST(MemorySanitizer, UnalignedStore64_precise) {
- char x[12];
+ char x[12] __attribute__((aligned(8)));
U8 y = 0;
U4 originx1 = __LINE__;
U4 originx2 = __LINE__;
@@ -3670,7 +3708,7 @@ TEST(MemorySanitizer, UnalignedStore64_precise) {
}
TEST(MemorySanitizer, UnalignedStore64_precise2) {
- char x[12];
+ char x[12] __attribute__((aligned(8)));
U8 y = 0;
U4 originx1 = __LINE__;
U4 originx2 = __LINE__;
diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt
index 86697e7f7c40..6eb6ca8fc900 100644
--- a/lib/sanitizer_common/CMakeLists.txt
+++ b/lib/sanitizer_common/CMakeLists.txt
@@ -67,6 +67,7 @@ set(SANITIZER_HEADERS
sanitizer_flag_parser.h
sanitizer_flags.h
sanitizer_flags.inc
+ sanitizer_interface_internal.h
sanitizer_internal_defs.h
sanitizer_lfstack.h
sanitizer_libc.h
diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc
index 489081e0760b..4be3c7abf756 100644
--- a/lib/sanitizer_common/sanitizer_common.cc
+++ b/lib/sanitizer_common/sanitizer_common.cc
@@ -288,6 +288,48 @@ void DecreaseTotalMmap(uptr size) {
atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
}
+bool TemplateMatch(const char *templ, const char *str) {
+ if (str == 0 || str[0] == 0)
+ return false;
+ bool start = false;
+ if (templ && templ[0] == '^') {
+ start = true;
+ templ++;
+ }
+ bool asterisk = false;
+ while (templ && templ[0]) {
+ if (templ[0] == '*') {
+ templ++;
+ start = false;
+ asterisk = true;
+ continue;
+ }
+ if (templ[0] == '$')
+ return str[0] == 0 || asterisk;
+ if (str[0] == 0)
+ return false;
+ char *tpos = (char*)internal_strchr(templ, '*');
+ char *tpos1 = (char*)internal_strchr(templ, '$');
+ if (tpos == 0 || (tpos1 && tpos1 < tpos))
+ tpos = tpos1;
+ if (tpos != 0)
+ tpos[0] = 0;
+ const char *str0 = str;
+ const char *spos = internal_strstr(str, templ);
+ str = spos + internal_strlen(templ);
+ templ = tpos;
+ if (tpos)
+ tpos[0] = tpos == tpos1 ? '$' : '*';
+ if (spos == 0)
+ return false;
+ if (start && spos != str0)
+ return false;
+ start = false;
+ asterisk = false;
+ }
+ return true;
+}
+
} // namespace __sanitizer
using namespace __sanitizer; // NOLINT
diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h
index 720cd73a43c5..ff13ef164045 100644
--- a/lib/sanitizer_common/sanitizer_common.h
+++ b/lib/sanitizer_common/sanitizer_common.h
@@ -7,8 +7,8 @@
//
//===----------------------------------------------------------------------===//
//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
+// This file is shared between run-time libraries of sanitizers.
+//
// It declares common functions and classes that are used in both runtimes.
// Implementation of some functions are provided in sanitizer_common, while
// others must be defined by run-time library itself.
@@ -17,6 +17,7 @@
#define SANITIZER_COMMON_H
#include "sanitizer_flags.h"
+#include "sanitizer_interface_internal.h"
#include "sanitizer_internal_defs.h"
#include "sanitizer_libc.h"
#include "sanitizer_list.h"
@@ -241,6 +242,7 @@ void SleepForMillis(int millis);
u64 NanoTime();
int Atexit(void (*function)(void));
void SortArray(uptr *array, uptr size);
+bool TemplateMatch(const char *templ, const char *str);
// Exit
void NORETURN Abort();
diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc
index e357e1cbbfc9..17ef6897ba26 100644
--- a/lib/sanitizer_common/sanitizer_common_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc
@@ -121,7 +121,7 @@ void MaybeStartBackgroudThread() {
// 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.
+ if (!&real_pthread_create) return; // Can't spawn the thread anyway.
internal_start_thread(BackgroundThread, nullptr);
}
diff --git a/lib/sanitizer_common/sanitizer_common_syscalls.inc b/lib/sanitizer_common/sanitizer_common_syscalls.inc
index a52338b62f5e..7e15d51ff35b 100644
--- a/lib/sanitizer_common/sanitizer_common_syscalls.inc
+++ b/lib/sanitizer_common/sanitizer_common_syscalls.inc
@@ -2297,7 +2297,8 @@ PRE_SYSCALL(ni_syscall)() {}
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))
+#if !SANITIZER_ANDROID && \
+ (defined(__i386) || defined(__x86_64) || defined(__mips64))
if (data) {
if (request == ptrace_setregs) {
PRE_READ((void *)data, struct_user_regs_struct_sz);
@@ -2316,7 +2317,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))
+#if !SANITIZER_ANDROID && \
+ (defined(__i386) || defined(__x86_64) || defined(__mips64))
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 e8f42f68a42f..49887b1e91a9 100644
--- a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc
@@ -82,7 +82,7 @@ class CoverageData {
void TraceBasicBlock(s32 *id);
void InitializeGuardArray(s32 *guards);
- void InitializeGuards(s32 *guards, uptr n);
+ void InitializeGuards(s32 *guards, uptr n, const char *module_name);
void ReinitializeGuards();
uptr *data();
@@ -110,6 +110,9 @@ class CoverageData {
// 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;
+
// Caller-Callee (cc) array, size and current index.
static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24);
uptr **cc_array;
@@ -286,13 +289,15 @@ void CoverageData::Extend(uptr npcs) {
atomic_store(&pc_array_size, size, memory_order_release);
}
-void CoverageData::InitializeGuards(s32 *guards, uptr n) {
+void CoverageData::InitializeGuards(s32 *guards, uptr n,
+ const char *module_name) {
// 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);
guard_array_vec.push_back(guards);
}
@@ -450,6 +455,14 @@ void CoverageData::DumpTrace() {
internal_write(fd, out.data(), out.length());
internal_close(fd);
+ fd = CovOpenFile(false, "trace-compunits");
+ if (fd < 0) 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);
+
fd = CovOpenFile(false, "trace-events");
if (fd < 0) return;
uptr bytes_to_write = max_idx * sizeof(tr_event_array[0]);
@@ -675,9 +688,9 @@ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() {
coverage_data.Init();
}
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); }
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_module_init(s32 *guards,
- uptr npcs) {
- coverage_data.InitializeGuards(guards, npcs);
+SANITIZER_INTERFACE_ATTRIBUTE void
+__sanitizer_cov_module_init(s32 *guards, uptr npcs, const char *module_name) {
+ coverage_data.InitializeGuards(guards, npcs, module_name);
if (!common_flags()->coverage_direct) return;
if (SANITIZER_ANDROID && coverage_enabled) {
// dlopen/dlclose interceptors do not work on Android, so we rely on
diff --git a/lib/sanitizer_common/sanitizer_flag_parser.h b/lib/sanitizer_common/sanitizer_flag_parser.h
index 87afb8238c01..0ac7634cb876 100644
--- a/lib/sanitizer_common/sanitizer_flag_parser.h
+++ b/lib/sanitizer_common/sanitizer_flag_parser.h
@@ -31,7 +31,7 @@ class FlagHandler : public FlagHandlerBase {
public:
explicit FlagHandler(T *t) : t_(t) {}
- bool Parse(const char *value);
+ bool Parse(const char *value) final;
};
template <>
diff --git a/lib/sanitizer_common/sanitizer_flags.cc b/lib/sanitizer_common/sanitizer_flags.cc
index a2965351cf2a..e835b46a24fc 100644
--- a/lib/sanitizer_common/sanitizer_flags.cc
+++ b/lib/sanitizer_common/sanitizer_flags.cc
@@ -51,7 +51,7 @@ class FlagHandlerInclude : public FlagHandlerBase {
public:
explicit FlagHandlerInclude(FlagParser *parser) : parser_(parser) {}
- bool Parse(const char *value) {
+ bool Parse(const char *value) final {
char *data;
uptr data_mapped_size;
int err;
diff --git a/lib/sanitizer_common/sanitizer_flags.inc b/lib/sanitizer_common/sanitizer_flags.inc
index e8c8a87537d7..58f7f372228f 100644
--- a/lib/sanitizer_common/sanitizer_flags.inc
+++ b/lib/sanitizer_common/sanitizer_flags.inc
@@ -128,7 +128,6 @@ COMMON_FLAG(const char *, coverage_dir, ".",
COMMON_FLAG(bool, full_address_space, false,
"Sanitize complete address space; "
"by default kernel area on 32-bit platforms will not be sanitized")
-COMMON_FLAG(const char *, suppressions, "", "Suppressions file name.")
COMMON_FLAG(bool, print_suppressions, true,
"Print matched suppressions at exit.")
COMMON_FLAG(
diff --git a/lib/sanitizer_common/sanitizer_interface_internal.h b/lib/sanitizer_common/sanitizer_interface_internal.h
new file mode 100644
index 000000000000..94d9f4e9524a
--- /dev/null
+++ b/lib/sanitizer_common/sanitizer_interface_internal.h
@@ -0,0 +1,58 @@
+//===-- sanitizer_interface_internal.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 run-time libraries of sanitizers.
+//
+// This header declares the sanitizer runtime interface functions.
+// The runtime library has to define these functions so the instrumented program
+// could call them.
+//
+// See also include/sanitizer/common_interface_defs.h
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_INTERFACE_INTERNAL_H
+#define SANITIZER_INTERFACE_INTERNAL_H
+
+#include "sanitizer_internal_defs.h"
+
+extern "C" {
+ // Tell the tools to write their reports to "path.<pid>" instead of stderr.
+ // The special values are "stdout" and "stderr".
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __sanitizer_set_report_path(const char *path);
+
+ typedef struct {
+ int coverage_sandboxed;
+ __sanitizer::sptr coverage_fd;
+ unsigned int coverage_max_block_size;
+ } __sanitizer_sandbox_arguments;
+
+ // Notify the tools that the sandbox is going to be turned on.
+ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
+ __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
+
+ // This function is called by the tool when it has just finished reporting
+ // an error. 'error_summary' is a one-line string that summarizes
+ // the error message. This function can be overridden by the client.
+ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+ void __sanitizer_report_error_summary(const char *error_summary);
+
+ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump();
+ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init();
+ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ void __sanitizer_annotate_contiguous_container(const void *beg,
+ const void *end,
+ const void *old_mid,
+ const void *new_mid);
+ SANITIZER_INTERFACE_ATTRIBUTE
+ int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
+ const void *end);
+} // extern "C"
+
+#endif // SANITIZER_INTERFACE_INTERNAL_H
diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h
index 2a0b41f0bb99..a969f305cd1a 100644
--- a/lib/sanitizer_common/sanitizer_internal_defs.h
+++ b/lib/sanitizer_common/sanitizer_internal_defs.h
@@ -101,41 +101,6 @@ typedef u32 operator_new_size_type;
#endif
} // namespace __sanitizer
-extern "C" {
- // Tell the tools to write their reports to "path.<pid>" instead of stderr.
- // The special values are "stdout" and "stderr".
- SANITIZER_INTERFACE_ATTRIBUTE
- void __sanitizer_set_report_path(const char *path);
-
- typedef struct {
- int coverage_sandboxed;
- __sanitizer::sptr coverage_fd;
- unsigned int coverage_max_block_size;
- } __sanitizer_sandbox_arguments;
-
- // Notify the tools that the sandbox is going to be turned on.
- SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void
- __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
-
- // This function is called by the tool when it has just finished reporting
- // an error. 'error_summary' is a one-line string that summarizes
- // the error message. This function can be overridden by the client.
- SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
- void __sanitizer_report_error_summary(const char *error_summary);
-
- SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump();
- SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init();
- SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(__sanitizer::u32 *guard);
- SANITIZER_INTERFACE_ATTRIBUTE
- void __sanitizer_annotate_contiguous_container(const void *beg,
- const void *end,
- const void *old_mid,
- const void *new_mid);
- SANITIZER_INTERFACE_ATTRIBUTE
- int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
- const void *end);
-} // extern "C"
-
using namespace __sanitizer; // NOLINT
// ----------- ATTENTION -------------
diff --git a/lib/sanitizer_common/sanitizer_libignore.cc b/lib/sanitizer_common/sanitizer_libignore.cc
index 8df0467b1e9b..cefb1dc97a17 100644
--- a/lib/sanitizer_common/sanitizer_libignore.cc
+++ b/lib/sanitizer_common/sanitizer_libignore.cc
@@ -19,24 +19,18 @@ namespace __sanitizer {
LibIgnore::LibIgnore(LinkerInitialized) {
}
-void LibIgnore::Init(const SuppressionContext &supp) {
+void LibIgnore::AddIgnoredLibrary(const char *name_templ) {
BlockingMutexLock lock(&mutex_);
- CHECK_EQ(count_, 0);
- const uptr n = supp.SuppressionCount();
- for (uptr i = 0; i < n; i++) {
- const Suppression *s = supp.SuppressionAt(i);
- if (s->type != SuppressionLib)
- continue;
- if (count_ >= kMaxLibs) {
- Report("%s: too many called_from_lib suppressions (max: %d)\n",
- SanitizerToolName, kMaxLibs);
- Die();
- }
- Lib *lib = &libs_[count_++];
- lib->templ = internal_strdup(s->templ);
- lib->name = 0;
- lib->loaded = false;
+ if (count_ >= kMaxLibs) {
+ Report("%s: too many ignored libraries (max: %d)\n", SanitizerToolName,
+ kMaxLibs);
+ Die();
}
+ Lib *lib = &libs_[count_++];
+ lib->templ = internal_strdup(name_templ);
+ lib->name = nullptr;
+ lib->real_name = nullptr;
+ lib->loaded = false;
}
void LibIgnore::OnLibraryLoaded(const char *name) {
diff --git a/lib/sanitizer_common/sanitizer_libignore.h b/lib/sanitizer_common/sanitizer_libignore.h
index 8e1d584d8e3c..cd56c36c1c0e 100644
--- a/lib/sanitizer_common/sanitizer_libignore.h
+++ b/lib/sanitizer_common/sanitizer_libignore.h
@@ -8,8 +8,8 @@
//===----------------------------------------------------------------------===//
//
// LibIgnore allows to ignore all interceptors called from a particular set
-// of dynamic libraries. LibIgnore remembers all "called_from_lib" suppressions
-// from the provided SuppressionContext; finds code ranges for the libraries;
+// of dynamic libraries. LibIgnore can be initialized with several templates
+// of names of libraries to be ignored. It finds code ranges for the libraries;
// and checks whether the provided PC value belongs to the code ranges.
//
//===----------------------------------------------------------------------===//
@@ -19,7 +19,6 @@
#include "sanitizer_internal_defs.h"
#include "sanitizer_common.h"
-#include "sanitizer_suppressions.h"
#include "sanitizer_atomic.h"
#include "sanitizer_mutex.h"
@@ -29,8 +28,8 @@ class LibIgnore {
public:
explicit LibIgnore(LinkerInitialized);
- // Fetches all "called_from_lib" suppressions from the SuppressionContext.
- void Init(const SuppressionContext &supp);
+ // Must be called during initialization.
+ void AddIgnoredLibrary(const char *name_templ);
// Must be called after a new dynamic library is loaded.
void OnLibraryLoaded(const char *name);
diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc
index 26138ba29d35..8029181a5173 100644
--- a/lib/sanitizer_common/sanitizer_linux.cc
+++ b/lib/sanitizer_common/sanitizer_linux.cc
@@ -860,6 +860,13 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
: "rsp", "memory", "r11", "rcx");
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);
+}
#endif // defined(__x86_64__) && SANITIZER_LINUX
#if SANITIZER_ANDROID
diff --git a/lib/sanitizer_common/sanitizer_linux.h b/lib/sanitizer_common/sanitizer_linux.h
index 3013c25f7c38..b2e603d3a23e 100644
--- a/lib/sanitizer_common/sanitizer_linux.h
+++ b/lib/sanitizer_common/sanitizer_linux.h
@@ -43,7 +43,7 @@ uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5);
// internal_sigaction instead.
int internal_sigaction_norestorer(int signum, const void *act, void *oldact);
void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
-#if defined(__x86_64__)
+#if defined(__x86_64__) || defined(__mips__)
uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
int *parent_tidptr, void *newtls, int *child_tidptr);
#endif
diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
index df42c3604ae9..c71b6257ebc3 100644
--- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc
@@ -168,6 +168,20 @@ static uptr g_tls_size;
# define DL_INTERNAL_FUNCTION
#endif
+#if defined(__mips__)
+// TlsPreTcbSize includes size of struct pthread_descr and size of tcb
+// head structure. It lies before the static tls blocks.
+static uptr TlsPreTcbSize() {
+ const uptr kTcbHead = 16;
+ const uptr kTlsAlign = 16;
+ const uptr kTlsPreTcbSize =
+ (ThreadDescriptorSize() + kTcbHead + kTlsAlign - 1) & ~(kTlsAlign - 1);
+ InitTlsSize();
+ g_tls_size = (g_tls_size + kTlsPreTcbSize + kTlsAlign -1) & ~(kTlsAlign - 1);
+ return kTlsPreTcbSize;
+}
+#endif
+
void InitTlsSize() {
#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO
typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION;
@@ -184,7 +198,8 @@ void InitTlsSize() {
#endif // !SANITIZER_FREEBSD && !SANITIZER_ANDROID
}
-#if (defined(__x86_64__) || defined(__i386__)) && SANITIZER_LINUX
+#if (defined(__x86_64__) || defined(__i386__) || defined(__mips__)) \
+ && SANITIZER_LINUX
// sizeof(struct thread) from glibc.
static atomic_uintptr_t kThreadDescriptorSize;
@@ -192,6 +207,7 @@ uptr ThreadDescriptorSize() {
uptr val = atomic_load(&kThreadDescriptorSize, memory_order_relaxed);
if (val)
return val;
+#if defined(__x86_64__) || defined(__i386__)
#ifdef _CS_GNU_LIBC_VERSION
char buf[64];
uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
@@ -224,6 +240,13 @@ uptr ThreadDescriptorSize() {
return val;
}
#endif
+#elif defined(__mips__)
+ // TODO(sagarthakur): add more values as per different glibc versions.
+ val = FIRST_32_SECOND_64(1152, 1776);
+ if (val)
+ atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
+ return val;
+#endif
return 0;
}
@@ -240,12 +263,24 @@ uptr ThreadSelf() {
asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
# elif defined(__x86_64__)
asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
+# elif defined(__mips__)
+ // MIPS uses TLS variant I. The thread pointer (in hardware register $29)
+ // points to the end of the TCB + 0x7000. The pthread_descr structure is
+ // immediately in front of the TCB. TlsPreTcbSize() includes the size of the
+ // TCB and the size of pthread_descr.
+ const uptr kTlsTcbOffset = 0x7000;
+ uptr thread_pointer;
+ asm volatile(".set push;\
+ .set mips64r2;\
+ rdhwr %0,$29;\
+ .set pop" : "=r" (thread_pointer));
+ descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize();
# else
# error "unsupported CPU arch"
# endif
return descr_addr;
}
-#endif // (defined(__x86_64__) || defined(__i386__)) && SANITIZER_LINUX
+#endif // (x86_64 || i386 || MIPS) && SANITIZER_LINUX
#if SANITIZER_FREEBSD
static void **ThreadSelfSegbase() {
@@ -275,6 +310,9 @@ static void GetTls(uptr *addr, uptr *size) {
*size = GetTlsSize();
*addr -= *size;
*addr += ThreadDescriptorSize();
+# elif defined(__mips__)
+ *addr = ThreadSelf();
+ *size = GetTlsSize();
# else
*addr = 0;
*size = 0;
@@ -298,6 +336,7 @@ static void GetTls(uptr *addr, uptr *size) {
}
#endif
+#if !SANITIZER_GO
uptr GetTlsSize() {
#if SANITIZER_FREEBSD
uptr addr, size;
@@ -307,6 +346,7 @@ uptr GetTlsSize() {
return g_tls_size;
#endif
}
+#endif
void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
uptr *tls_addr, uptr *tls_size) {
diff --git a/lib/sanitizer_common/sanitizer_platform_interceptors.h b/lib/sanitizer_common/sanitizer_platform_interceptors.h
index 54a1cfd8582b..438ecbaa2ec8 100644
--- a/lib/sanitizer_common/sanitizer_platform_interceptors.h
+++ b/lib/sanitizer_common/sanitizer_platform_interceptors.h
@@ -127,7 +127,7 @@
#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)) // NOLINT
+ (defined(__i386) || defined (__x86_64) || defined (__mips64)) // NOLINT
#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
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
index 4eabcd7a3d22..8824c8088f2d 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc
@@ -97,7 +97,6 @@
# include <sys/link_elf.h>
# include <netinet/ip_mroute.h>
# include <netinet/in.h>
-# include <netinet/ip_compat.h>
# include <net/ethernet.h>
# include <net/ppp_defs.h>
# include <glob.h>
@@ -117,6 +116,9 @@
#if SANITIZER_LINUX || SANITIZER_FREEBSD
# include <utime.h>
# include <sys/ptrace.h>
+# if defined(__mips64)
+# include <asm/ptrace.h>
+# endif
#endif
#if !SANITIZER_ANDROID
@@ -140,6 +142,9 @@
#include <sys/shm.h>
#include <sys/statvfs.h>
#include <sys/timex.h>
+#if defined(__mips64)
+# include <sys/procfs.h>
+#endif
#include <sys/user.h>
#include <sys/ustat.h>
#include <linux/cyclades.h>
@@ -284,14 +289,19 @@ namespace __sanitizer {
#endif
#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
- (defined(__i386) || defined(__x86_64))
+ (defined(__i386) || defined(__x86_64) || defined(__mips64))
+#if defined(__mips64)
+ 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);
-#ifdef __x86_64
+#endif // __mips64
+#if (defined(__x86_64) || defined(__mips64))
unsigned struct_user_fpxregs_struct_sz = 0;
#else
unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct);
-#endif
+#endif // __x86_64 || __mips64
int ptrace_peektext = PTRACE_PEEKTEXT;
int ptrace_peekdata = PTRACE_PEEKDATA;
diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
index d375e01a20e4..bd20bea94e93 100644
--- a/lib/sanitizer_common/sanitizer_platform_limits_posix.h
+++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.h
@@ -547,6 +547,10 @@ namespace __sanitizer {
#if SANITIZER_FREEBSD
typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;
+#elif defined(__mips__)
+ struct __sanitizer_kernel_sigset_t {
+ u8 sig[16];
+ };
#else
struct __sanitizer_kernel_sigset_t {
u8 sig[8];
@@ -695,7 +699,7 @@ namespace __sanitizer {
#endif
#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
- (defined(__i386) || defined(__x86_64))
+ (defined(__i386) || defined(__x86_64) || defined(__mips64))
extern unsigned struct_user_regs_struct_sz;
extern unsigned struct_user_fpregs_struct_sz;
extern unsigned struct_user_fpxregs_struct_sz;
diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc
index 6cb51efce2d1..5bc41c2580fb 100644
--- a/lib/sanitizer_common/sanitizer_posix.cc
+++ b/lib/sanitizer_common/sanitizer_posix.cc
@@ -30,6 +30,13 @@
#include <sys/personality.h>
#endif
+#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 {
// ------------- sanitizer_common.h
diff --git a/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
index d20b52483a91..ad20e39556d8 100644
--- a/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
@@ -14,7 +14,7 @@
#include "sanitizer_platform.h"
-#if SANITIZER_LINUX && defined(__x86_64__)
+#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__))
#include "sanitizer_stoptheworld.h"
@@ -89,36 +89,50 @@ class ThreadSuspender {
bool SuspendThread(SuspendedThreadID thread_id);
};
-bool ThreadSuspender::SuspendThread(SuspendedThreadID thread_id) {
+bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) {
// Are we already attached to this thread?
// Currently this check takes linear time, however the number of threads is
// usually small.
- if (suspended_threads_list_.Contains(thread_id))
+ if (suspended_threads_list_.Contains(tid))
return false;
int pterrno;
- if (internal_iserror(internal_ptrace(PTRACE_ATTACH, thread_id, NULL, NULL),
+ if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, NULL, NULL),
&pterrno)) {
// Either the thread is dead, or something prevented us from attaching.
// Log this event and move on.
- VReport(1, "Could not attach to thread %d (errno %d).\n", thread_id,
- pterrno);
+ VReport(1, "Could not attach to thread %d (errno %d).\n", tid, pterrno);
return false;
} else {
- VReport(1, "Attached to thread %d.\n", thread_id);
+ VReport(1, "Attached to thread %d.\n", tid);
// The thread is not guaranteed to stop before ptrace returns, so we must
- // wait on it.
- uptr waitpid_status;
- HANDLE_EINTR(waitpid_status, internal_waitpid(thread_id, NULL, __WALL));
- int wperrno;
- if (internal_iserror(waitpid_status, &wperrno)) {
- // Got a ECHILD error. I don't think this situation is possible, but it
- // doesn't hurt to report it.
- VReport(1, "Waiting on thread %d failed, detaching (errno %d).\n",
- thread_id, wperrno);
- internal_ptrace(PTRACE_DETACH, thread_id, NULL, NULL);
- return false;
+ // wait on it. Note: if the thread receives a signal concurrently,
+ // we can get notification about the signal before notification about stop.
+ // In such case we need to forward the signal to the thread, otherwise
+ // the signal will be missed (as we do PTRACE_DETACH with arg=0) and
+ // any logic relying on signals will break. After forwarding we need to
+ // continue to wait for stopping, because the thread is not stopped yet.
+ // We do ignore delivery of SIGSTOP, because we want to make stop-the-world
+ // as invisible as possible.
+ for (;;) {
+ int status;
+ uptr waitpid_status;
+ HANDLE_EINTR(waitpid_status, internal_waitpid(tid, &status, __WALL));
+ int wperrno;
+ if (internal_iserror(waitpid_status, &wperrno)) {
+ // Got a ECHILD error. I don't think this situation is possible, but it
+ // doesn't hurt to report it.
+ VReport(1, "Waiting on thread %d failed, detaching (errno %d).\n",
+ tid, wperrno);
+ internal_ptrace(PTRACE_DETACH, tid, NULL, NULL);
+ return false;
+ }
+ if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGSTOP) {
+ internal_ptrace(PTRACE_CONT, tid, 0, (void*)(uptr)WSTOPSIG(status));
+ continue;
+ }
+ break;
}
- suspended_threads_list_.Append(thread_id);
+ suspended_threads_list_.Append(tid);
return true;
}
}
@@ -170,10 +184,9 @@ bool ThreadSuspender::SuspendAllThreads() {
// Pointer to the ThreadSuspender instance for use in signal handler.
static ThreadSuspender *thread_suspender_instance = NULL;
-// Signals that should not be blocked (this is used in the parent thread as well
-// as the tracer thread).
-static const int kUnblockedSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV,
- SIGBUS, SIGXCPU, SIGXFSZ };
+// Synchronous signals that should not be blocked.
+static const int kSyncSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS,
+ SIGXCPU, SIGXFSZ };
// Structure for passing arguments into the tracer thread.
struct TracerThreadArgument {
@@ -188,7 +201,7 @@ struct TracerThreadArgument {
static DieCallbackType old_die_callback;
// Signal handler to wake up suspended threads when the tracer thread dies.
-void TracerThreadSignalHandler(int signum, void *siginfo, void *) {
+static void TracerThreadSignalHandler(int signum, void *siginfo, void *) {
if (thread_suspender_instance != NULL) {
if (signum == SIGABRT)
thread_suspender_instance->KillAllThreads();
@@ -228,6 +241,7 @@ static int TracerThread(void* argument) {
tracer_thread_argument->mutex.Lock();
tracer_thread_argument->mutex.Unlock();
+ old_die_callback = GetDieCallback();
SetDieCallback(TracerThreadDieCallback);
ThreadSuspender thread_suspender(internal_getppid());
@@ -242,17 +256,14 @@ static int TracerThread(void* argument) {
handler_stack.ss_size = kHandlerStackSize;
internal_sigaltstack(&handler_stack, NULL);
- // Install our handler for fatal signals. Other signals should be blocked by
- // the mask we inherited from the caller thread.
- for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);
- signal_index++) {
- __sanitizer_sigaction new_sigaction;
- internal_memset(&new_sigaction, 0, sizeof(new_sigaction));
- new_sigaction.sigaction = TracerThreadSignalHandler;
- new_sigaction.sa_flags = SA_ONSTACK | SA_SIGINFO;
- internal_sigfillset(&new_sigaction.sa_mask);
- internal_sigaction_norestorer(kUnblockedSignals[signal_index],
- &new_sigaction, NULL);
+ // Install our handler for synchronous signals. Other signals should be
+ // blocked by the mask we inherited from the parent thread.
+ for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) {
+ __sanitizer_sigaction act;
+ internal_memset(&act, 0, sizeof(act));
+ act.sigaction = TracerThreadSignalHandler;
+ act.sa_flags = SA_ONSTACK | SA_SIGINFO;
+ internal_sigaction_norestorer(kSyncSignals[i], &act, 0);
}
int exit_code = 0;
@@ -265,9 +276,11 @@ 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;
- handler_stack.ss_flags = SS_DISABLE;
- internal_sigaltstack(&handler_stack, NULL);
return exit_code;
}
@@ -299,53 +312,21 @@ class ScopedStackSpaceWithGuard {
// into globals.
static __sanitizer_sigset_t blocked_sigset;
static __sanitizer_sigset_t old_sigset;
-static __sanitizer_sigaction old_sigactions
- [ARRAY_SIZE(kUnblockedSignals)];
class StopTheWorldScope {
public:
StopTheWorldScope() {
- // Block all signals that can be blocked safely, and install
- // default handlers for the remaining signals.
- // We cannot allow user-defined handlers to run while the ThreadSuspender
- // thread is active, because they could conceivably call some libc functions
- // which modify errno (which is shared between the two threads).
- internal_sigfillset(&blocked_sigset);
- for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);
- signal_index++) {
- // Remove the signal from the set of blocked signals.
- internal_sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]);
- // Install the default handler.
- __sanitizer_sigaction new_sigaction;
- internal_memset(&new_sigaction, 0, sizeof(new_sigaction));
- new_sigaction.handler = SIG_DFL;
- internal_sigfillset(&new_sigaction.sa_mask);
- internal_sigaction_norestorer(kUnblockedSignals[signal_index],
- &new_sigaction, &old_sigactions[signal_index]);
- }
- int sigprocmask_status =
- internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);
- CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail
// Make this process dumpable. Processes that are not dumpable cannot be
// attached to.
process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
if (!process_was_dumpable_)
internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
- old_die_callback = GetDieCallback();
}
~StopTheWorldScope() {
- SetDieCallback(old_die_callback);
// Restore the dumpable flag.
if (!process_was_dumpable_)
internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
- // Restore the signal handlers.
- for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals);
- signal_index++) {
- internal_sigaction_norestorer(kUnblockedSignals[signal_index],
- &old_sigactions[signal_index], NULL);
- }
- internal_sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset);
}
private:
@@ -378,11 +359,36 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) {
// Block the execution of TracerThread until after we have set ptrace
// permissions.
tracer_thread_argument.mutex.Lock();
+ // Signal handling story.
+ // We don't want async signals to be delivered to the tracer thread,
+ // so we block all async signals before creating the thread. An async signal
+ // handler can temporary modify errno, which is shared with this thread.
+ // We ought to use pthread_sigmask here, because sigprocmask has undefined
+ // behavior in multithreaded programs. However, on linux sigprocmask is
+ // equivalent to pthread_sigmask with the exception that pthread_sigmask
+ // does not allow to block some signals used internally in pthread
+ // implementation. We are fine with blocking them here, we are really not
+ // going to pthread_cancel the thread.
+ // The tracer thread should not raise any synchronous signals. But in case it
+ // does, we setup a special handler for sync signals that properly kills the
+ // parent as well. Note: we don't pass CLONE_SIGHAND to clone, so handlers
+ // in the tracer thread won't interfere with user program. Double note: if a
+ // user does something along the lines of 'kill -11 pid', that can kill the
+ // process even if user setup own handler for SEGV.
+ // Thing to watch out for: this code should not change behavior of user code
+ // in any observable way. In particular it should not override user signal
+ // handlers.
+ internal_sigfillset(&blocked_sigset);
+ for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++)
+ internal_sigdelset(&blocked_sigset, kSyncSignals[i]);
+ int rv = internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);
+ CHECK_EQ(rv, 0);
uptr tracer_pid = internal_clone(
TracerThread, tracer_stack.Bottom(),
CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED,
&tracer_thread_argument, 0 /* parent_tidptr */, 0 /* newtls */, 0
/* child_tidptr */);
+ internal_sigprocmask(SIG_SETMASK, &old_sigset, 0);
int local_errno = 0;
if (internal_iserror(tracer_pid, &local_errno)) {
VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno);
@@ -459,4 +465,4 @@ uptr SuspendedThreadsList::RegisterCount() {
}
} // namespace __sanitizer
-#endif // SANITIZER_LINUX && defined(__x86_64__)
+#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__))
diff --git a/lib/sanitizer_common/sanitizer_suppressions.cc b/lib/sanitizer_common/sanitizer_suppressions.cc
index 6b75036c7e50..2b697e955709 100644
--- a/lib/sanitizer_common/sanitizer_suppressions.cc
+++ b/lib/sanitizer_common/sanitizer_suppressions.cc
@@ -7,7 +7,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Suppression parsing/matching code shared between TSan and LSan.
+// Suppression parsing/matching code.
//
//===----------------------------------------------------------------------===//
@@ -21,97 +21,43 @@
namespace __sanitizer {
-static const char *const kTypeStrings[SuppressionTypeCount] = {
- "none", "race", "mutex", "thread", "signal", "leak", "called_from_lib",
- "deadlock", "vptr_check", "interceptor_name", "interceptor_via_fun",
- "interceptor_via_lib"};
-
-bool TemplateMatch(char *templ, const char *str) {
- if (str == 0 || str[0] == 0)
- return false;
- bool start = false;
- if (templ && templ[0] == '^') {
- start = true;
- templ++;
- }
- bool asterisk = false;
- while (templ && templ[0]) {
- if (templ[0] == '*') {
- templ++;
- start = false;
- asterisk = true;
- continue;
- }
- if (templ[0] == '$')
- return str[0] == 0 || asterisk;
- if (str[0] == 0)
- return false;
- char *tpos = (char*)internal_strchr(templ, '*');
- char *tpos1 = (char*)internal_strchr(templ, '$');
- if (tpos == 0 || (tpos1 && tpos1 < tpos))
- tpos = tpos1;
- if (tpos != 0)
- tpos[0] = 0;
- const char *str0 = str;
- const char *spos = internal_strstr(str, templ);
- str = spos + internal_strlen(templ);
- templ = tpos;
- if (tpos)
- tpos[0] = tpos == tpos1 ? '$' : '*';
- if (spos == 0)
- return false;
- if (start && spos != str0)
- return false;
- start = false;
- asterisk = false;
- }
- return true;
-}
-
-ALIGNED(64) static char placeholder[sizeof(SuppressionContext)];
-static SuppressionContext *suppression_ctx = 0;
-
-SuppressionContext::SuppressionContext() : suppressions_(1), can_parse_(true) {
- internal_memset(has_suppresson_type_, 0, sizeof(has_suppresson_type_));
-}
-
-SuppressionContext *SuppressionContext::Get() {
- CHECK(suppression_ctx);
- return suppression_ctx;
+SuppressionContext::SuppressionContext(const char *suppression_types[],
+ int suppression_types_num)
+ : suppression_types_(suppression_types),
+ suppression_types_num_(suppression_types_num), suppressions_(1),
+ can_parse_(true) {
+ CHECK_LE(suppression_types_num_, kMaxSuppressionTypes);
+ internal_memset(has_suppression_type_, 0, suppression_types_num_);
}
-void SuppressionContext::InitIfNecessary() {
- if (suppression_ctx)
+void SuppressionContext::ParseFromFile(const char *filename) {
+ if (filename[0] == '\0')
return;
- suppression_ctx = new(placeholder) SuppressionContext;
- if (common_flags()->suppressions[0] == '\0')
- return;
- char *suppressions_from_file;
+ char *file_contents;
uptr buffer_size;
- uptr contents_size =
- ReadFileToBuffer(common_flags()->suppressions, &suppressions_from_file,
- &buffer_size, 1 << 26 /* max_len */);
+ uptr contents_size = ReadFileToBuffer(filename, &file_contents, &buffer_size,
+ 1 << 26 /* max_len */);
if (contents_size == 0) {
Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName,
- common_flags()->suppressions);
+ filename);
Die();
}
- suppression_ctx->Parse(suppressions_from_file);
+ Parse(file_contents);
}
-bool SuppressionContext::Match(const char *str, SuppressionType type,
+bool SuppressionContext::Match(const char *str, const char *type,
Suppression **s) {
- if (!has_suppresson_type_[type])
- return false;
can_parse_ = false;
- uptr i;
- for (i = 0; i < suppressions_.size(); i++)
- if (type == suppressions_[i].type &&
- TemplateMatch(suppressions_[i].templ, str))
- break;
- if (i == suppressions_.size()) return false;
- *s = &suppressions_[i];
- return true;
+ if (!HasSuppressionType(type))
+ return false;
+ for (uptr i = 0; i < suppressions_.size(); i++) {
+ Suppression &cur = suppressions_[i];
+ if (0 == internal_strcmp(cur.type, type) && TemplateMatch(cur.templ, str)) {
+ *s = &cur;
+ return true;
+ }
+ }
+ return false;
}
static const char *StripPrefix(const char *str, const char *prefix) {
@@ -139,26 +85,26 @@ void SuppressionContext::Parse(const char *str) {
while (line != end2 && (end2[-1] == ' ' || end2[-1] == '\t'))
end2--;
int type;
- for (type = 0; type < SuppressionTypeCount; type++) {
- const char *next_char = StripPrefix(line, kTypeStrings[type]);
+ for (type = 0; type < suppression_types_num_; type++) {
+ const char *next_char = StripPrefix(line, suppression_types_[type]);
if (next_char && *next_char == ':') {
line = ++next_char;
break;
}
}
- if (type == SuppressionTypeCount) {
+ if (type == suppression_types_num_) {
Printf("%s: failed to parse suppressions\n", SanitizerToolName);
Die();
}
Suppression s;
- s.type = static_cast<SuppressionType>(type);
+ s.type = suppression_types_[type];
s.templ = (char*)InternalAlloc(end2 - line + 1);
internal_memcpy(s.templ, line, end2 - line);
s.templ[end2 - line] = 0;
s.hit_count = 0;
s.weight = 0;
suppressions_.push_back(s);
- has_suppresson_type_[s.type] = true;
+ has_suppression_type_[type] = true;
}
if (end[0] == 0)
break;
@@ -170,8 +116,12 @@ uptr SuppressionContext::SuppressionCount() const {
return suppressions_.size();
}
-bool SuppressionContext::HasSuppressionType(SuppressionType type) const {
- return has_suppresson_type_[type];
+bool SuppressionContext::HasSuppressionType(const char *type) const {
+ for (int i = 0; i < suppression_types_num_; i++) {
+ if (0 == internal_strcmp(type, suppression_types_[i]))
+ return has_suppression_type_[i];
+ }
+ return false;
}
const Suppression *SuppressionContext::SuppressionAt(uptr i) const {
@@ -186,9 +136,4 @@ void SuppressionContext::GetMatched(
matched->push_back(&suppressions_[i]);
}
-const char *SuppressionTypeString(SuppressionType t) {
- CHECK(t < SuppressionTypeCount);
- return kTypeStrings[t];
-}
-
} // namespace __sanitizer
diff --git a/lib/sanitizer_common/sanitizer_suppressions.h b/lib/sanitizer_common/sanitizer_suppressions.h
index 453731456169..02dbf6f9690b 100644
--- a/lib/sanitizer_common/sanitizer_suppressions.h
+++ b/lib/sanitizer_common/sanitizer_suppressions.h
@@ -7,7 +7,7 @@
//
//===----------------------------------------------------------------------===//
//
-// Suppression parsing/matching code shared between TSan and LSan.
+// Suppression parsing/matching code.
//
//===----------------------------------------------------------------------===//
#ifndef SANITIZER_SUPPRESSIONS_H
@@ -18,24 +18,8 @@
namespace __sanitizer {
-enum SuppressionType {
- SuppressionNone,
- SuppressionRace,
- SuppressionMutex,
- SuppressionThread,
- SuppressionSignal,
- SuppressionLeak,
- SuppressionLib,
- SuppressionDeadlock,
- SuppressionVptrCheck,
- SuppressionInterceptorName,
- SuppressionInterceptorViaFunction,
- SuppressionInterceptorViaLibrary,
- SuppressionTypeCount
-};
-
struct Suppression {
- SuppressionType type;
+ const char *type;
char *templ;
unsigned hit_count;
uptr weight;
@@ -43,33 +27,29 @@ struct Suppression {
class SuppressionContext {
public:
+ // Create new SuppressionContext capable of parsing given suppression types.
+ SuppressionContext(const char *supprression_types[],
+ int suppression_types_num);
+
+ void ParseFromFile(const char *filename);
void Parse(const char *str);
- bool Match(const char* str, SuppressionType type, Suppression **s);
+
+ bool Match(const char *str, const char *type, Suppression **s);
uptr SuppressionCount() const;
- bool HasSuppressionType(SuppressionType type) const;
+ bool HasSuppressionType(const char *type) const;
const Suppression *SuppressionAt(uptr i) const;
void GetMatched(InternalMmapVector<Suppression *> *matched);
- // Create a SuppressionContext singleton if it hasn't been created earlier.
- // Not thread safe. Must be called early during initialization (but after
- // runtime flags are parsed).
- static void InitIfNecessary();
- // Returns a SuppressionContext singleton.
- static SuppressionContext *Get();
-
private:
- SuppressionContext();
+ static const int kMaxSuppressionTypes = 16;
+ const char **const suppression_types_;
+ const int suppression_types_num_;
+
InternalMmapVector<Suppression> suppressions_;
- bool has_suppresson_type_[SuppressionTypeCount];
+ bool has_suppression_type_[kMaxSuppressionTypes];
bool can_parse_;
-
- friend class SuppressionContextTest;
};
-const char *SuppressionTypeString(SuppressionType t);
-
-bool TemplateMatch(char *templ, const char *str);
-
} // namespace __sanitizer
#endif // SANITIZER_SUPPRESSIONS_H
diff --git a/lib/sanitizer_common/sanitizer_symbolizer_win.cc b/lib/sanitizer_common/sanitizer_symbolizer_win.cc
index 6bb7d3805604..ed96a3a895a8 100644
--- a/lib/sanitizer_common/sanitizer_symbolizer_win.cc
+++ b/lib/sanitizer_common/sanitizer_symbolizer_win.cc
@@ -30,25 +30,7 @@ class WinSymbolizer : public Symbolizer {
SymbolizedStack *frame = SymbolizedStack::New(addr);
BlockingMutexLock l(&dbghelp_mu_);
- if (!initialized_) {
- 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;
- }
+ InitializeIfNeeded();
// See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
@@ -100,6 +82,58 @@ class WinSymbolizer : public Symbolizer {
// 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;
+ }
+ }
+
bool TrySymInitialize() {
SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
return SymInitialize(GetCurrentProcess(), 0, TRUE);
diff --git a/lib/sanitizer_common/sanitizer_syscall_generic.inc b/lib/sanitizer_common/sanitizer_syscall_generic.inc
index 88d237f4e3ce..15cf05f06087 100644
--- a/lib/sanitizer_common/sanitizer_syscall_generic.inc
+++ b/lib/sanitizer_common/sanitizer_syscall_generic.inc
@@ -11,13 +11,13 @@
//
//===----------------------------------------------------------------------===//
-#if SANITIZER_FREEBSD
+#if SANITIZER_FREEBSD || SANITIZER_MAC
# define SYSCALL(name) SYS_ ## name
#else
# define SYSCALL(name) __NR_ ## name
#endif
-#if SANITIZER_FREEBSD && defined(__x86_64__)
+#if (SANITIZER_FREEBSD || SANITIZER_MAC) && defined(__x86_64__)
# define internal_syscall __syscall
# else
# define internal_syscall syscall
diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc
index edd4bd11b369..335cecabe118 100644
--- a/lib/sanitizer_common/sanitizer_win.cc
+++ b/lib/sanitizer_common/sanitizer_win.cc
@@ -219,6 +219,7 @@ int CompareModulesBase(const void *pl, const void *pr) {
}
} // namespace
+#ifndef SANITIZER_GO
void DumpProcessMap() {
Report("Dumping process modules:\n");
HANDLE cur_process = GetCurrentProcess();
@@ -263,8 +264,8 @@ void DumpProcessMap() {
for (size_t i = 0; i < num_modules; ++i) {
const ModuleInfo &mi = modules[i];
char module_name[MAX_PATH];
- bool got_module_name = GetModuleFileNameEx(
- cur_process, mi.handle, module_name, sizeof(module_name));
+ bool got_module_name = GetModuleFileNameA(
+ mi.handle, module_name, sizeof(module_name));
if (mi.end_address != 0) {
Printf("\t%p-%p %s\n", mi.base_address, mi.end_address,
got_module_name ? module_name : "[no name]");
@@ -276,6 +277,7 @@ void DumpProcessMap() {
}
UnmapOrDie(modules, num_modules * sizeof(ModuleInfo));
}
+#endif
void DisableCoreDumperIfNecessary() {
// Do nothing.
diff --git a/lib/sanitizer_common/tests/sanitizer_linux_test.cc b/lib/sanitizer_common/tests/sanitizer_linux_test.cc
index 592d9c3eeaf5..11342b775cc7 100644
--- a/lib/sanitizer_common/tests/sanitizer_linux_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_linux_test.cc
@@ -255,6 +255,14 @@ TEST(SanitizerCommon, LibraryNameIs) {
}
}
+#if defined(__mips64)
+// Effectively, this is a test for ThreadDescriptorSize() which is used to
+// compute ThreadSelf().
+TEST(SanitizerLinux, ThreadSelfTest) {
+ ASSERT_EQ(pthread_self(), ThreadSelf());
+}
+#endif
+
} // namespace __sanitizer
#endif // SANITIZER_LINUX
diff --git a/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc
index 0699243283df..e8c30d07e78c 100644
--- a/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc
+++ b/lib/sanitizer_common/tests/sanitizer_suppressions_test.cc
@@ -58,117 +58,77 @@ TEST(Suppressions, Match) {
EXPECT_FALSE(MyMatch("foo$^bar", "foobar"));
}
-TEST(Suppressions, TypeStrings) {
- CHECK(!internal_strcmp(SuppressionTypeString(SuppressionNone), "none"));
- CHECK(!internal_strcmp(SuppressionTypeString(SuppressionRace), "race"));
- CHECK(!internal_strcmp(SuppressionTypeString(SuppressionMutex), "mutex"));
- CHECK(!internal_strcmp(SuppressionTypeString(SuppressionThread), "thread"));
- CHECK(!internal_strcmp(SuppressionTypeString(SuppressionSignal), "signal"));
- CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLeak), "leak"));
- CHECK(!internal_strcmp(SuppressionTypeString(SuppressionLib),
- "called_from_lib"));
- CHECK(
- !internal_strcmp(SuppressionTypeString(SuppressionDeadlock), "deadlock"));
- CHECK(!internal_strcmp(SuppressionTypeString(SuppressionVptrCheck),
- "vptr_check"));
- CHECK(!internal_strcmp(SuppressionTypeString(SuppressionInterceptorName),
- "interceptor_name"));
- CHECK(
- !internal_strcmp(SuppressionTypeString(SuppressionInterceptorViaFunction),
- "interceptor_via_fun"));
- CHECK(
- !internal_strcmp(SuppressionTypeString(SuppressionInterceptorViaLibrary),
- "interceptor_via_lib"));
- // Ensure this test is up-to-date when suppression types are added.
- CHECK_EQ(12, SuppressionTypeCount);
-}
+static const char *kTestSuppressionTypes[] = {"race", "thread", "mutex",
+ "signal"};
class SuppressionContextTest : public ::testing::Test {
public:
- virtual void SetUp() { ctx_ = new(placeholder_) SuppressionContext; }
- virtual void TearDown() { ctx_->~SuppressionContext(); }
+ SuppressionContextTest()
+ : ctx_(kTestSuppressionTypes, ARRAY_SIZE(kTestSuppressionTypes)) {}
protected:
- InternalMmapVector<Suppression> *Suppressions() {
- return &ctx_->suppressions_;
+ SuppressionContext ctx_;
+
+ void CheckSuppressions(unsigned count, std::vector<const char *> types,
+ std::vector<const char *> templs) const {
+ EXPECT_EQ(count, ctx_.SuppressionCount());
+ for (unsigned i = 0; i < count; i++) {
+ const Suppression *s = ctx_.SuppressionAt(i);
+ EXPECT_STREQ(types[i], s->type);
+ EXPECT_STREQ(templs[i], s->templ);
+ }
}
- SuppressionContext *ctx_;
- ALIGNED(64) char placeholder_[sizeof(SuppressionContext)];
};
TEST_F(SuppressionContextTest, Parse) {
- ctx_->Parse(
- "race:foo\n"
- " race:bar\n" // NOLINT
- "race:baz \n" // NOLINT
- "# a comment\n"
- "race:quz\n"
- ); // NOLINT
- EXPECT_EQ((unsigned)4, ctx_->SuppressionCount());
- EXPECT_EQ((*Suppressions())[3].type, SuppressionRace);
- EXPECT_EQ(0, strcmp((*Suppressions())[3].templ, "quz"));
- EXPECT_EQ((*Suppressions())[2].type, SuppressionRace);
- EXPECT_EQ(0, strcmp((*Suppressions())[2].templ, "baz"));
- EXPECT_EQ((*Suppressions())[1].type, SuppressionRace);
- EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar"));
- EXPECT_EQ((*Suppressions())[0].type, SuppressionRace);
- EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo"));
+ ctx_.Parse("race:foo\n"
+ " race:bar\n" // NOLINT
+ "race:baz \n" // NOLINT
+ "# a comment\n"
+ "race:quz\n"); // NOLINT
+ CheckSuppressions(4, {"race", "race", "race", "race"},
+ {"foo", "bar", "baz", "quz"});
}
TEST_F(SuppressionContextTest, Parse2) {
- ctx_->Parse(
+ ctx_.Parse(
" # first line comment\n" // NOLINT
" race:bar \n" // NOLINT
"race:baz* *baz\n"
"# a comment\n"
"# last line comment\n"
); // NOLINT
- EXPECT_EQ((unsigned)2, ctx_->SuppressionCount());
- EXPECT_EQ((*Suppressions())[1].type, SuppressionRace);
- EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "baz* *baz"));
- EXPECT_EQ((*Suppressions())[0].type, SuppressionRace);
- EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "bar"));
+ CheckSuppressions(2, {"race", "race"}, {"bar", "baz* *baz"});
}
TEST_F(SuppressionContextTest, Parse3) {
- ctx_->Parse(
+ ctx_.Parse(
"# last suppression w/o line-feed\n"
"race:foo\n"
"race:bar"
); // NOLINT
- EXPECT_EQ((unsigned)2, ctx_->SuppressionCount());
- EXPECT_EQ((*Suppressions())[1].type, SuppressionRace);
- EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar"));
- EXPECT_EQ((*Suppressions())[0].type, SuppressionRace);
- EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo"));
+ CheckSuppressions(2, {"race", "race"}, {"foo", "bar"});
}
TEST_F(SuppressionContextTest, ParseType) {
- ctx_->Parse(
+ ctx_.Parse(
"race:foo\n"
"thread:bar\n"
"mutex:baz\n"
"signal:quz\n"
); // NOLINT
- EXPECT_EQ((unsigned)4, ctx_->SuppressionCount());
- EXPECT_EQ((*Suppressions())[3].type, SuppressionSignal);
- EXPECT_EQ(0, strcmp((*Suppressions())[3].templ, "quz"));
- EXPECT_EQ((*Suppressions())[2].type, SuppressionMutex);
- EXPECT_EQ(0, strcmp((*Suppressions())[2].templ, "baz"));
- EXPECT_EQ((*Suppressions())[1].type, SuppressionThread);
- EXPECT_EQ(0, strcmp((*Suppressions())[1].templ, "bar"));
- EXPECT_EQ((*Suppressions())[0].type, SuppressionRace);
- EXPECT_EQ(0, strcmp((*Suppressions())[0].templ, "foo"));
+ CheckSuppressions(4, {"race", "thread", "mutex", "signal"},
+ {"foo", "bar", "baz", "quz"});
}
TEST_F(SuppressionContextTest, HasSuppressionType) {
- ctx_->Parse(
+ ctx_.Parse(
"race:foo\n"
"thread:bar\n");
- EXPECT_TRUE(ctx_->HasSuppressionType(SuppressionRace));
- EXPECT_TRUE(ctx_->HasSuppressionType(SuppressionThread));
- EXPECT_FALSE(ctx_->HasSuppressionType(SuppressionMutex));
- EXPECT_FALSE(ctx_->HasSuppressionType(SuppressionSignal));
+ EXPECT_TRUE(ctx_.HasSuppressionType("race"));
+ EXPECT_TRUE(ctx_.HasSuppressionType("thread"));
+ EXPECT_FALSE(ctx_.HasSuppressionType("mutex"));
+ EXPECT_FALSE(ctx_.HasSuppressionType("signal"));
}
} // namespace __sanitizer
diff --git a/lib/tsan/CMakeLists.txt b/lib/tsan/CMakeLists.txt
index baf07a034e3b..9f6d63041a68 100644
--- a/lib/tsan/CMakeLists.txt
+++ b/lib/tsan/CMakeLists.txt
@@ -79,35 +79,40 @@ set(TSAN_HEADERS
set(TSAN_RUNTIME_LIBRARIES)
add_custom_target(tsan)
# TSan is currently supported on 64-bit Linux only.
-if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE)
- 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)
- set(arch "x86_64")
- 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)
+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)
-# Sanity check for Go runtime.
-set(BUILDGO_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/go/buildgo.sh)
-add_custom_target(GotsanRuntimeCheck
- COMMAND CC=${CMAKE_C_COMPILER} IN_TMPDIR=1 SILENT=1 ${BUILDGO_SCRIPT}
- DEPENDS clang_rt.tsan-${arch} ${BUILDGO_SCRIPT}
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/go
- COMMENT "Checking TSan Go runtime..."
- VERBATIM)
# Build libcxx instrumented with TSan.
if(TSAN_SUPPORTED_ARCH AND
diff --git a/lib/tsan/Makefile.mk b/lib/tsan/Makefile.mk
deleted file mode 100644
index 70fb610bdc61..000000000000
--- a/lib/tsan/Makefile.mk
+++ /dev/null
@@ -1,18 +0,0 @@
-#===- lib/tsan/Makefile.mk ---------------------------------*- Makefile -*--===#
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-#===------------------------------------------------------------------------===#
-
-ModuleName := tsan
-SubDirs := rtl
-Sources :=
-ObjNames :=
-Dependencies :=
-
-Implementation := Generic
-
-TsanFunctions :=
diff --git a/lib/tsan/dd/dd_rtl.cc b/lib/tsan/dd/dd_rtl.cc
index 2ba1ee324111..99b8ee597cd1 100644
--- a/lib/tsan/dd/dd_rtl.cc
+++ b/lib/tsan/dd/dd_rtl.cc
@@ -65,8 +65,8 @@ u32 Callback::Unwind() {
return CurrentStackTrace(thr, 3);
}
-void InitializeFlags(Flags *f, const char *env) {
- internal_memset(f, 0, sizeof(*f));
+static void InitializeFlags() {
+ Flags *f = flags();
// Default values.
f->second_deadlock_stack = false;
@@ -84,7 +84,7 @@ void InitializeFlags(Flags *f, const char *env) {
FlagParser parser;
RegisterFlag(&parser, "second_deadlock_stack", "", &f->second_deadlock_stack);
RegisterCommonFlags(&parser);
- parser.ParseString(env);
+ parser.ParseString(GetEnv("DSAN_OPTIONS"));
SetVerbosity(common_flags()->verbosity);
}
@@ -93,7 +93,7 @@ void Initialize() {
ctx = new(ctx_mem) Context();
InitializeInterceptors();
- InitializeFlags(flags(), GetEnv("DSAN_OPTIONS"));
+ InitializeFlags();
ctx->dd = DDetector::Create(flags());
}
diff --git a/lib/tsan/go/build.bat b/lib/tsan/go/build.bat
index 9b350a2d633e..7d393dc0e025 100644
--- a/lib/tsan/go/build.bat
+++ b/lib/tsan/go/build.bat
@@ -1,4 +1,4 @@
-type tsan_go.cc ..\rtl\tsan_interface_atomic.cc ..\rtl\tsan_clock.cc ..\rtl\tsan_flags.cc ..\rtl\tsan_md5.cc ..\rtl\tsan_mutex.cc ..\rtl\tsan_report.cc ..\rtl\tsan_rtl.cc ..\rtl\tsan_rtl_mutex.cc ..\rtl\tsan_rtl_report.cc ..\rtl\tsan_rtl_thread.cc ..\rtl\tsan_stat.cc ..\rtl\tsan_suppressions.cc ..\rtl\tsan_sync.cc ..\rtl\tsan_stack_trace.cc ..\..\sanitizer_common\sanitizer_allocator.cc ..\..\sanitizer_common\sanitizer_common.cc ..\..\sanitizer_common\sanitizer_flags.cc ..\..\sanitizer_common\sanitizer_stacktrace.cc ..\..\sanitizer_common\sanitizer_libc.cc ..\..\sanitizer_common\sanitizer_printf.cc ..\..\sanitizer_common\sanitizer_suppressions.cc ..\..\sanitizer_common\sanitizer_thread_registry.cc ..\rtl\tsan_platform_windows.cc ..\..\sanitizer_common\sanitizer_win.cc ..\..\sanitizer_common\sanitizer_deadlock_detector1.cc ..\..\sanitizer_common\sanitizer_stackdepot.cc ..\..\sanitizer_common\sanitizer_persistent_allocator.cc > gotsan.cc
+type tsan_go.cc ..\rtl\tsan_interface_atomic.cc ..\rtl\tsan_clock.cc ..\rtl\tsan_flags.cc ..\rtl\tsan_md5.cc ..\rtl\tsan_mutex.cc ..\rtl\tsan_report.cc ..\rtl\tsan_rtl.cc ..\rtl\tsan_rtl_mutex.cc ..\rtl\tsan_rtl_report.cc ..\rtl\tsan_rtl_thread.cc ..\rtl\tsan_stat.cc ..\rtl\tsan_suppressions.cc ..\rtl\tsan_sync.cc ..\rtl\tsan_stack_trace.cc ..\..\sanitizer_common\sanitizer_allocator.cc ..\..\sanitizer_common\sanitizer_common.cc ..\..\sanitizer_common\sanitizer_flags.cc ..\..\sanitizer_common\sanitizer_stacktrace.cc ..\..\sanitizer_common\sanitizer_libc.cc ..\..\sanitizer_common\sanitizer_printf.cc ..\..\sanitizer_common\sanitizer_suppressions.cc ..\..\sanitizer_common\sanitizer_thread_registry.cc ..\rtl\tsan_platform_windows.cc ..\..\sanitizer_common\sanitizer_win.cc ..\..\sanitizer_common\sanitizer_deadlock_detector1.cc ..\..\sanitizer_common\sanitizer_stackdepot.cc ..\..\sanitizer_common\sanitizer_persistent_allocator.cc ..\..\sanitizer_common\sanitizer_flag_parser.cc ..\..\sanitizer_common\sanitizer_symbolizer.cc > gotsan.cc
-gcc -c -o race_windows_amd64.syso gotsan.cc -I..\rtl -I..\.. -I..\..\sanitizer_common -I..\..\..\include -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO -Wno-error=attributes -Wno-attributes -Wno-format -DSANITIZER_DEBUG=0 -O3 -fomit-frame-pointer
+gcc -c -o race_windows_amd64.syso gotsan.cc -I..\rtl -I..\.. -I..\..\sanitizer_common -I..\..\..\include -m64 -Wall -fno-exceptions -fno-rtti -DSANITIZER_GO -Wno-error=attributes -Wno-attributes -Wno-format -Wno-maybe-uninitialized -DSANITIZER_DEBUG=0 -O3 -fomit-frame-pointer -std=c++11
diff --git a/lib/tsan/go/buildgo.sh b/lib/tsan/go/buildgo.sh
index e00408cb10f0..5ac60349e081 100755
--- a/lib/tsan/go/buildgo.sh
+++ b/lib/tsan/go/buildgo.sh
@@ -45,7 +45,6 @@ if [ "`uname -a | grep Linux`" != "" ]; then
../../sanitizer_common/sanitizer_procmaps_common.cc
../../sanitizer_common/sanitizer_procmaps_linux.cc
../../sanitizer_common/sanitizer_linux.cc
- ../../sanitizer_common/sanitizer_linux_libcdep.cc
../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
"
elif [ "`uname -a | grep FreeBSD`" != "" ]; then
@@ -60,7 +59,6 @@ elif [ "`uname -a | grep FreeBSD`" != "" ]; then
../../sanitizer_common/sanitizer_procmaps_common.cc
../../sanitizer_common/sanitizer_procmaps_freebsd.cc
../../sanitizer_common/sanitizer_linux.cc
- ../../sanitizer_common/sanitizer_linux_libcdep.cc
../../sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc
"
elif [ "`uname -a | grep Darwin`" != "" ]; then
diff --git a/lib/tsan/rtl/Makefile.mk b/lib/tsan/rtl/Makefile.mk
deleted file mode 100644
index 2687123f731d..000000000000
--- a/lib/tsan/rtl/Makefile.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-#===- lib/tsan/rtl/Makefile.mk -----------------------------*- Makefile -*--===#
-#
-# The LLVM Compiler Infrastructure
-#
-# This file is distributed under the University of Illinois Open Source
-# License. See LICENSE.TXT for details.
-#
-#===------------------------------------------------------------------------===#
-
-ModuleName := tsan
-SubDirs :=
-
-Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file)))
-AsmSources := $(foreach file,$(wildcard $(Dir)/*.S),$(notdir $(file)))
-ObjNames := $(Sources:%.cc=%.o) $(AsmSources:%.S=%.o)
-
-Implementation := Generic
-
-# FIXME: use automatic dependencies?
-Dependencies := $(wildcard $(Dir)/*.h)
-Dependencies += $(wildcard $(Dir)/../../interception/*.h)
-Dependencies += $(wildcard $(Dir)/../../sanitizer_common/*.h)
-
-# Define a convenience variable for all the tsan functions.
-TsanFunctions += $(Sources:%.cc=%) $(AsmSources:%.S=%)
diff --git a/lib/tsan/rtl/tsan_defs.h b/lib/tsan/rtl/tsan_defs.h
index f19aee999332..910a483127d5 100644
--- a/lib/tsan/rtl/tsan_defs.h
+++ b/lib/tsan/rtl/tsan_defs.h
@@ -18,6 +18,15 @@
#include "sanitizer_common/sanitizer_libc.h"
#include "tsan_stat.h"
+// Setup defaults for compile definitions.
+#ifndef TSAN_NO_HISTORY
+# define TSAN_NO_HISTORY 0
+#endif
+
+#ifndef TSAN_COLLECT_STATS
+# define TSAN_COLLECT_STATS 0
+#endif
+
namespace __tsan {
#ifdef SANITIZER_GO
@@ -35,7 +44,11 @@ const char *const kTsanOptionsEnv = "TSAN_OPTIONS";
const int kTidBits = 13;
const unsigned kMaxTid = 1 << kTidBits;
+#ifndef SANITIZER_GO
const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit.
+#else
+const unsigned kMaxTidInClock = kMaxTid; // Go does not track freed memory.
+#endif
const int kClkBits = 42;
const unsigned kMaxTidReuse = (1 << (64 - kClkBits)) - 1;
const uptr kShadowStackSize = 64 * 1024;
@@ -59,18 +72,12 @@ const uptr kMetaShadowCell = 8;
// Size of a single meta shadow value (u32).
const uptr kMetaShadowSize = 4;
-#if defined(TSAN_NO_HISTORY) && TSAN_NO_HISTORY
+#if TSAN_NO_HISTORY
const bool kCollectHistory = false;
#else
const bool kCollectHistory = true;
#endif
-#if defined(TSAN_COLLECT_STATS) && TSAN_COLLECT_STATS
-const bool kCollectStats = true;
-#else
-const bool kCollectStats = false;
-#endif
-
// The following "build consistency" machinery ensures that all source files
// are built in the same configuration. Inconsistent builds lead to
// hard to debug crashes.
diff --git a/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc
index fed3de8db2ed..1e81ef3d0003 100644
--- a/lib/tsan/rtl/tsan_flags.cc
+++ b/lib/tsan/rtl/tsan_flags.cc
@@ -62,7 +62,9 @@ void InitializeFlags(Flags *f, const char *env) {
CommonFlags cf;
cf.CopyFrom(*common_flags());
cf.allow_addr2line = true;
+#ifndef SANITIZER_GO
cf.detect_deadlocks = true;
+#endif
cf.print_suppressions = false;
cf.stack_trace_format = " #%n %f %S %M";
OverrideCommonFlags(cf);
diff --git a/lib/tsan/rtl/tsan_flags.inc b/lib/tsan/rtl/tsan_flags.inc
index 2925f38482a2..e4994685fa0d 100644
--- a/lib/tsan/rtl/tsan_flags.inc
+++ b/lib/tsan/rtl/tsan_flags.inc
@@ -63,7 +63,7 @@ TSAN_FLAG(bool, stop_on_start, false,
TSAN_FLAG(bool, running_on_valgrind, false,
"Controls whether RunningOnValgrind() returns true or false.")
TSAN_FLAG(
- int, history_size, kGoMode ? 1 : 2, // There are a lot of goroutines in Go.
+ int, history_size, kGoMode ? 1 : 3, // There are a lot of goroutines in Go.
"Per-thread history size, controls how many previous memory accesses "
"are remembered per thread. Possible values are [0..7]. "
"history_size=0 amounts to 32K memory accesses. Each next value doubles "
@@ -76,3 +76,4 @@ TSAN_FLAG(int, io_sync, 1,
"2 - global synchronization of all IO operations.")
TSAN_FLAG(bool, die_after_fork, true,
"Die after multi-threaded fork if the child creates new threads.")
+TSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc
index 9a6401167bc1..31ff7d56aac7 100644
--- a/lib/tsan/rtl/tsan_interceptors.cc
+++ b/lib/tsan/rtl/tsan_interceptors.cc
@@ -39,17 +39,27 @@ using namespace __tsan; // NOLINT
#define stderr __stderrp
#endif
+#ifdef __mips__
+const int kSigCount = 129;
+#else
const int kSigCount = 65;
+#endif
struct my_siginfo_t {
// The size is determined by looking at sizeof of real siginfo_t on linux.
u64 opaque[128 / sizeof(u64)];
};
+#ifdef __mips__
+struct ucontext_t {
+ u64 opaque[768 / sizeof(u64) + 1];
+};
+#else
struct ucontext_t {
// The size is determined by looking at sizeof of real ucontext_t on linux.
u64 opaque[936 / sizeof(u64) + 1];
};
+#endif
extern "C" int pthread_attr_init(void *attr);
extern "C" int pthread_attr_destroy(void *attr);
@@ -89,8 +99,13 @@ const int SIGFPE = 8;
const int SIGSEGV = 11;
const int SIGPIPE = 13;
const int SIGTERM = 15;
+#ifdef __mips__
+const int SIGBUS = 10;
+const int SIGSYS = 12;
+#else
const int SIGBUS = 7;
const int SIGSYS = 31;
+#endif
void *const MAP_FAILED = (void*)-1;
const int PTHREAD_BARRIER_SERIAL_THREAD = -1;
const int MAP_FIXED = 0x10;
@@ -108,6 +123,9 @@ typedef void (*sighandler_t)(int sig);
typedef void (*sigactionhandler_t)(int sig, my_siginfo_t *siginfo, void *uctx);
struct sigaction_t {
+#ifdef __mips__
+ u32 sa_flags;
+#endif
union {
sighandler_t sa_handler;
sigactionhandler_t sa_sigaction;
@@ -117,7 +135,9 @@ struct sigaction_t {
__sanitizer_sigset_t sa_mask;
#else
__sanitizer_sigset_t sa_mask;
+#ifndef __mips__
int sa_flags;
+#endif
void (*sa_restorer)();
#endif
};
@@ -125,8 +145,13 @@ 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__
+const int SA_SIGINFO = 8;
+const int SIG_SETMASK = 3;
+#else
const int SA_SIGINFO = 4;
const int SIG_SETMASK = 2;
+#endif
namespace std {
struct nothrow_t {};
@@ -157,7 +182,13 @@ static LibIgnore *libignore() {
}
void InitializeLibIgnore() {
- libignore()->Init(*SuppressionContext::Get());
+ const SuppressionContext &supp = *Suppressions();
+ const uptr n = supp.SuppressionCount();
+ for (uptr i = 0; i < n; i++) {
+ const Suppression *s = supp.SuppressionAt(i);
+ if (0 == internal_strcmp(s->type, kSuppressionLib))
+ libignore()->AddIgnoredLibrary(s->templ);
+ }
libignore()->OnLibraryLoaded(0);
}
@@ -1895,9 +1926,9 @@ static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire,
// signal; and it looks too fragile to intercept all ways to reraise a signal.
if (flags()->report_bugs && !sync && sig != SIGTERM && errno != 99) {
VarSizeStackTrace stack;
- // Add 1 to pc because return address is expected,
- // OutputReport() will undo this.
- ObtainCurrentStack(thr, pc + 1, &stack);
+ // StackTrace::GetNestInstructionPc(pc) is used because return address is
+ // expected, OutputReport() will undo this.
+ ObtainCurrentStack(thr, StackTrace::GetNextInstructionPc(pc), &stack);
ThreadRegistryLock l(ctx->thread_registry);
ScopedReport rep(ReportTypeErrnoInSignal);
if (!IsFiredSuppression(ctx, rep, stack)) {
diff --git a/lib/tsan/rtl/tsan_interface_atomic.cc b/lib/tsan/rtl/tsan_interface_atomic.cc
index 9b699511674a..27031991438c 100644
--- a/lib/tsan/rtl/tsan_interface_atomic.cc
+++ b/lib/tsan/rtl/tsan_interface_atomic.cc
@@ -33,14 +33,14 @@ typedef unsigned short a16; // NOLINT
typedef unsigned int a32;
typedef unsigned long long a64; // NOLINT
#if !defined(SANITIZER_GO) && (defined(__SIZEOF_INT128__) \
- || (__clang_major__ * 100 + __clang_minor__ >= 302))
+ || (__clang_major__ * 100 + __clang_minor__ >= 302)) && !defined(__mips64)
__extension__ typedef __int128 a128;
# define __TSAN_HAS_INT128 1
#else
# define __TSAN_HAS_INT128 0
#endif
-#ifndef SANITIZER_GO
+#if !defined(SANITIZER_GO) && __TSAN_HAS_INT128
// Protects emulation of 128-bit atomic operations.
static StaticSpinMutex mutex128;
#endif
@@ -125,7 +125,8 @@ template<typename T> T func_cas(volatile T *v, T cmp, T xch) {
// Atomic ops are executed under tsan internal mutex,
// here we assume that the atomic variables are not accessed
// from non-instrumented code.
-#if !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) && !defined(SANITIZER_GO)
+#if !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) && !defined(SANITIZER_GO) \
+ && __TSAN_HAS_INT128
a128 func_xchg(volatile a128 *v, a128 op) {
SpinMutexLock lock(&mutex128);
a128 cmp = *v;
diff --git a/lib/tsan/rtl/tsan_platform.h b/lib/tsan/rtl/tsan_platform.h
index 03f95694d26c..135e16027132 100644
--- a/lib/tsan/rtl/tsan_platform.h
+++ b/lib/tsan/rtl/tsan_platform.h
@@ -26,8 +26,9 @@ namespace __tsan {
#if !defined(SANITIZER_GO)
+#if defined(__x86_64__)
/*
-C/C++ on linux and freebsd
+C/C++ on linux/x86_64 and freebsd/x86_64
0000 0000 1000 - 0100 0000 0000: main binary and/or MAP_32BIT mappings
0100 0000 0000 - 0200 0000 0000: -
0200 0000 0000 - 1000 0000 0000: shadow
@@ -40,7 +41,6 @@ C/C++ on linux and freebsd
7e00 0000 0000 - 7e80 0000 0000: -
7e80 0000 0000 - 8000 0000 0000: modules and main thread stack
*/
-
const uptr kMetaShadowBeg = 0x300000000000ull;
const uptr kMetaShadowEnd = 0x400000000000ull;
const uptr kTraceMemBeg = 0x600000000000ull;
@@ -55,6 +55,38 @@ const uptr kHiAppMemBeg = 0x7e8000000000ull;
const uptr kHiAppMemEnd = 0x800000000000ull;
const uptr kAppMemMsk = 0x7c0000000000ull;
const uptr kAppMemXor = 0x020000000000ull;
+const uptr kVdsoBeg = 0xf000000000000000ull;
+#elif defined(__mips64)
+/*
+C/C++ on linux/mips64
+0100 0000 00 - 0200 0000 00: main binary
+0200 0000 00 - 1400 0000 00: -
+1400 0000 00 - 2400 0000 00: shadow
+2400 0000 00 - 3000 0000 00: -
+3000 0000 00 - 4000 0000 00: metainfo (memory blocks and sync objects)
+4000 0000 00 - 6000 0000 00: -
+6000 0000 00 - 6200 0000 00: traces
+6200 0000 00 - fe00 0000 00: -
+fe00 0000 00 - ff00 0000 00: heap
+ff00 0000 00 - ff80 0000 00: -
+ff80 0000 00 - ffff ffff ff: modules and main thread stack
+*/
+const uptr kMetaShadowBeg = 0x3000000000ull;
+const uptr kMetaShadowEnd = 0x4000000000ull;
+const uptr kTraceMemBeg = 0x6000000000ull;
+const uptr kTraceMemEnd = 0x6200000000ull;
+const uptr kShadowBeg = 0x1400000000ull;
+const uptr kShadowEnd = 0x2400000000ull;
+const uptr kHeapMemBeg = 0xfe00000000ull;
+const uptr kHeapMemEnd = 0xff00000000ull;
+const uptr kLoAppMemBeg = 0x0100000000ull;
+const uptr kLoAppMemEnd = 0x0200000000ull;
+const uptr kHiAppMemBeg = 0xff80000000ull;
+const uptr kHiAppMemEnd = 0xffffffffffull;
+const uptr kAppMemMsk = 0xfc00000000ull;
+const uptr kAppMemXor = 0x0400000000ull;
+const uptr kVdsoBeg = 0xfffff00000ull;
+#endif
ALWAYS_INLINE
bool IsAppMem(uptr mem) {
@@ -171,8 +203,8 @@ static USED uptr UserRegions[] = {
0000 1000 0000 - 00f8 0000 0000: -
00c0 0000 0000 - 00e0 0000 0000: heap
00e0 0000 0000 - 0100 0000 0000: -
-0100 0000 0000 - 0380 0000 0000: shadow
-0380 0000 0000 - 0560 0000 0000: -
+0100 0000 0000 - 0500 0000 0000: shadow
+0500 0000 0000 - 0560 0000 0000: -
0560 0000 0000 - 0760 0000 0000: traces
0760 0000 0000 - 07d0 0000 0000: metainfo (memory blocks and sync objects)
07d0 0000 0000 - 8000 0000 0000: -
@@ -183,7 +215,7 @@ const uptr kMetaShadowEnd = 0x07d000000000ull;
const uptr kTraceMemBeg = 0x056000000000ull;
const uptr kTraceMemEnd = 0x076000000000ull;
const uptr kShadowBeg = 0x010000000000ull;
-const uptr kShadowEnd = 0x038000000000ull;
+const uptr kShadowEnd = 0x050000000000ull;
const uptr kAppMemBeg = 0x000000001000ull;
const uptr kAppMemEnd = 0x00e000000000ull;
@@ -205,21 +237,21 @@ bool IsMetaMem(uptr mem) {
ALWAYS_INLINE
uptr MemToShadow(uptr x) {
DCHECK(IsAppMem(x));
- return ((x & ~(kShadowCell - 1)) * kShadowCnt) | kShadowBeg;
+ return ((x & ~(kShadowCell - 1)) * kShadowCnt) + kShadowBeg;
}
ALWAYS_INLINE
u32 *MemToMeta(uptr x) {
DCHECK(IsAppMem(x));
return (u32*)(((x & ~(kMetaShadowCell - 1)) / \
- kMetaShadowCell * kMetaShadowSize) | kMetaShadowEnd);
+ kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg);
}
ALWAYS_INLINE
uptr ShadowToMem(uptr s) {
CHECK(IsShadowMem(s));
// FIXME(dvyukov): this is most likely wrong as the mapping is not bijection.
- return (x & ~kShadowBeg) / kShadowCnt;
+ return (s - kShadowBeg) / kShadowCnt;
}
static USED uptr UserRegions[] = {
diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc
index 7bc28db296ec..659e8d8a8345 100644
--- a/lib/tsan/rtl/tsan_platform_linux.cc
+++ b/lib/tsan/rtl/tsan_platform_linux.cc
@@ -66,8 +66,6 @@ namespace __tsan {
static uptr g_data_start;
static uptr g_data_end;
-const uptr kPageSize = 4096;
-
enum {
MemTotal = 0,
MemShadow = 1,
@@ -173,7 +171,7 @@ static void MapRodata() {
*p = kShadowRodata;
internal_write(fd, marker.data(), marker.size());
// Map the file into memory.
- uptr page = internal_mmap(0, kPageSize, PROT_READ | PROT_WRITE,
+ uptr page = internal_mmap(0, GetPageSizeCached(), PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, fd, 0);
if (internal_iserror(page)) {
internal_close(fd);
@@ -216,8 +214,15 @@ void InitializeShadowMemory() {
// a program uses a small part of large mmap. On some programs
// we see 20% memory usage reduction without huge pages for this range.
// FIXME: don't use constants here.
- NoHugePagesInRegion(MemToShadow(0x7f0000000000ULL),
- 0x10000000000ULL * kShadowMultiplier);
+#if defined(__x86_64__)
+ const uptr kMadviseRangeBeg = 0x7f0000000000ull;
+ const uptr kMadviseRangeSize = 0x010000000000ull;
+#elif defined(__mips64)
+ const uptr kMadviseRangeBeg = 0xff00000000ull;
+ const uptr kMadviseRangeSize = 0x0100000000ull;
+#endif
+ NoHugePagesInRegion(MemToShadow(kMadviseRangeBeg),
+ kMadviseRangeSize * kShadowMultiplier);
if (common_flags()->use_madv_dontdump)
DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg);
DPrintf("memory shadow: %zx-%zx (%zuGB)\n",
@@ -289,9 +294,9 @@ static void CheckAndProtect() {
if (IsAppMem(p))
continue;
if (p >= kHeapMemEnd &&
- p < kHeapMemEnd + PrimaryAllocator::AdditionalSize())
+ p < HeapEnd())
continue;
- if (p >= 0xf000000000000000ull) // vdso
+ if (p >= kVdsoBeg) // vdso
break;
Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n", p, end);
Die();
@@ -304,7 +309,7 @@ static void CheckAndProtect() {
// Protect the whole range for now, so that user does not map something here.
ProtectRange(kTraceMemBeg, kTraceMemEnd);
ProtectRange(kTraceMemEnd, kHeapMemBeg);
- ProtectRange(kHeapMemEnd + PrimaryAllocator::AdditionalSize(), kHiAppMemBeg);
+ ProtectRange(HeapEnd(), kHiAppMemBeg);
}
#endif // #ifndef SANITIZER_GO
diff --git a/lib/tsan/rtl/tsan_report.cc b/lib/tsan/rtl/tsan_report.cc
index c22f12a1bfa5..7e69cb4ecfbc 100644
--- a/lib/tsan/rtl/tsan_report.cc
+++ b/lib/tsan/rtl/tsan_report.cc
@@ -356,8 +356,9 @@ void PrintStack(const ReportStack *ent) {
SymbolizedStack *frame = ent->frames;
for (int i = 0; frame; frame = frame->next, i++) {
const AddressInfo &info = frame->info;
- Printf(" %s()\n %s:%d +0x%zx\n", info.function, info.file, info.line,
- (void *)info.module_offset);
+ Printf(" %s()\n %s:%d +0x%zx\n", info.function,
+ StripPathPrefix(info.file, common_flags()->strip_path_prefix),
+ info.line, (void *)info.module_offset);
}
}
diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc
index b3320aad8038..b76f3e05dde6 100644
--- a/lib/tsan/rtl/tsan_rtl.cc
+++ b/lib/tsan/rtl/tsan_rtl.cc
@@ -67,8 +67,17 @@ 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));
- MapThreadTrace(GetThreadTraceHeader(tid), sizeof(Trace));
- new(ThreadTrace(tid)) Trace();
+ const uptr hdr = GetThreadTraceHeader(tid);
+ MapThreadTrace(hdr, sizeof(Trace));
+ 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.
+ // Unmap the unused part.
+ uptr hdr_end = hdr + sizeof(Trace);
+ hdr_end -= sizeof(TraceHeader) * (kTraceParts - TraceParts());
+ hdr_end = RoundUp(hdr_end, GetPageSizeCached());
+ if (hdr_end < hdr + sizeof(Trace))
+ UnmapOrDie((void*)hdr_end, hdr + sizeof(Trace) - hdr_end);
void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext));
return new(mem) ThreadContext(tid);
}
@@ -117,6 +126,7 @@ ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch,
{
}
+#ifndef SANITIZER_GO
static void MemoryProfiler(Context *ctx, fd_t fd, int i) {
uptr n_threads;
uptr n_running_threads;
@@ -127,13 +137,11 @@ static void MemoryProfiler(Context *ctx, fd_t fd, int i) {
}
static void BackgroundThread(void *arg) {
-#ifndef SANITIZER_GO
// This is a non-initialized non-user thread, nothing to see here.
// We don't use ScopedIgnoreInterceptors, because we want ignores to be
// enabled even when the thread function exits (e.g. during pthread thread
// shutdown code).
cur_thread()->ignore_interceptors++;
-#endif
const u64 kMs2Ns = 1000 * 1000;
fd_t mprof_fd = kInvalidFd;
@@ -191,7 +199,6 @@ static void BackgroundThread(void *arg) {
if (mprof_fd != kInvalidFd)
MemoryProfiler(ctx, mprof_fd, i);
-#ifndef SANITIZER_GO
// Flush symbolizer cache if requested.
if (flags()->flush_symbolizer_ms > 0) {
u64 last = atomic_load(&ctx->last_symbolize_time_ns,
@@ -203,7 +210,6 @@ static void BackgroundThread(void *arg) {
atomic_store(&ctx->last_symbolize_time_ns, 0, memory_order_relaxed);
}
}
-#endif
}
}
@@ -211,13 +217,14 @@ static void StartBackgroundThread() {
ctx->background_thread = internal_start_thread(&BackgroundThread, 0);
}
-#ifndef SANITIZER_GO
+#ifndef __mips__
static void StopBackgroundThread() {
atomic_store(&ctx->stop_background_thread, 1, memory_order_relaxed);
internal_join_thread(ctx->background_thread);
ctx->background_thread = 0;
}
#endif
+#endif
void DontNeedShadowFor(uptr addr, uptr size) {
uptr shadow_beg = MemToShadow(addr);
@@ -282,11 +289,11 @@ static void CheckShadowMapping() {
if (p < beg || p >= end)
continue;
const uptr s = MemToShadow(p);
- VPrintf(3, " checking pointer %p -> %p\n", p, s);
+ const uptr m = (uptr)MemToMeta(p);
+ VPrintf(3, " checking pointer %p: shadow=%p meta=%p\n", p, s, m);
CHECK(IsAppMem(p));
CHECK(IsShadowMem(s));
CHECK_EQ(p & ~(kShadowCell - 1), ShadowToMem(s));
- const uptr m = (uptr)MemToMeta(p);
CHECK(IsMetaMem(m));
}
}
@@ -325,11 +332,14 @@ void Initialize(ThreadState *thr) {
#ifndef SANITIZER_GO
InitializeLibIgnore();
Symbolizer::GetOrInit()->AddHooks(EnterSymbolizer, ExitSymbolizer);
-#endif
+ // On MIPS, TSan initialization is run before
+ // __pthread_initialize_minimal_internal() is finished, so we can not spawn
+ // new threads.
+#ifndef __mips__
StartBackgroundThread();
-#ifndef SANITIZER_GO
SetSandboxingCallback(StopBackgroundThread);
#endif
+#endif
if (common_flags()->detect_deadlocks)
ctx->dd = DDetector::Create(flags());
@@ -394,8 +404,11 @@ int Finalize(ThreadState *thr) {
failed = OnFinalize(failed);
+#if TSAN_COLLECT_STATS
StatAggregate(ctx->stat, thr->stat);
StatOutput(ctx->stat);
+#endif
+
return failed ? flags()->exitcode : 0;
}
@@ -419,7 +432,7 @@ void ForkChildAfter(ThreadState *thr, uptr pc) {
VPrintf(1, "ThreadSanitizer: forked new process with pid %d,"
" parent had %d threads\n", (int)internal_getpid(), (int)nthread);
if (nthread == 1) {
- internal_start_thread(&BackgroundThread, 0);
+ StartBackgroundThread();
} else {
// We've just forked a multi-threaded process. We cannot reasonably function
// after that (some mutexes may be locked before fork). So just enable
@@ -826,7 +839,7 @@ static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size,
}
} else {
// The region is big, reset only beginning and end.
- const uptr kPageSize = 4096;
+ const uptr kPageSize = GetPageSizeCached();
u64 *begin = (u64*)MemToShadow(addr);
u64 *end = begin + size / kShadowCell * kShadowCnt;
u64 *p = begin;
diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h
index 768e8307fcc8..7a60e5c55af4 100644
--- a/lib/tsan/rtl/tsan_rtl.h
+++ b/lib/tsan/rtl/tsan_rtl.h
@@ -54,8 +54,21 @@ namespace __tsan {
#ifndef SANITIZER_GO
struct MapUnmapCallback;
+#ifdef __mips64
+static const uptr kAllocatorSpace = 0;
+static const uptr kAllocatorSize = SANITIZER_MMAP_RANGE_SIZE;
+static const uptr kAllocatorRegionSizeLog = 20;
+static const uptr kAllocatorNumRegions =
+ kAllocatorSize >> kAllocatorRegionSizeLog;
+typedef TwoLevelByteMap<(kAllocatorNumRegions >> 12), 1 << 12,
+ MapUnmapCallback> ByteMap;
+typedef SizeClassAllocator32<kAllocatorSpace, kAllocatorSize, 0,
+ CompactSizeClassMap, kAllocatorRegionSizeLog, ByteMap,
+ MapUnmapCallback> PrimaryAllocator;
+#else
typedef SizeClassAllocator64<kHeapMemBeg, kHeapMemEnd - kHeapMemBeg, 0,
DefaultSizeClassMap, MapUnmapCallback> PrimaryAllocator;
+#endif
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
typedef LargeMmapAllocator<MapUnmapCallback> SecondaryAllocator;
typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
@@ -351,7 +364,9 @@ struct ThreadState {
Vector<JmpBuf> jmp_bufs;
int ignore_interceptors;
#endif
+#if TSAN_COLLECT_STATS
u64 stat[StatCnt];
+#endif
const int tid;
const int unique_id;
bool in_symbolizer;
@@ -365,7 +380,9 @@ struct ThreadState {
const uptr tls_size;
ThreadContext *tctx;
+#if SANITIZER_DEBUG && !SANITIZER_GO
InternalDeadlockDetector internal_deadlock_detector;
+#endif
DDPhysicalThread *dd_pt;
DDLogicalThread *dd_lt;
@@ -539,15 +556,20 @@ void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack) {
}
+#if TSAN_COLLECT_STATS
void StatAggregate(u64 *dst, u64 *src);
void StatOutput(u64 *stat);
+#endif
+
void ALWAYS_INLINE StatInc(ThreadState *thr, StatType typ, u64 n = 1) {
- if (kCollectStats)
- thr->stat[typ] += n;
+#if TSAN_COLLECT_STATS
+ thr->stat[typ] += n;
+#endif
}
void ALWAYS_INLINE StatSet(ThreadState *thr, StatType typ, u64 n) {
- if (kCollectStats)
- thr->stat[typ] = n;
+#if TSAN_COLLECT_STATS
+ thr->stat[typ] = n;
+#endif
}
void MapShadow(uptr addr, uptr size);
@@ -729,6 +751,16 @@ void ALWAYS_INLINE TraceAddEvent(ThreadState *thr, FastState fs,
*evp = ev;
}
+#ifndef SANITIZER_GO
+uptr ALWAYS_INLINE HeapEnd() {
+#if SANITIZER_CAN_USE_ALLOCATOR64
+ return kHeapMemEnd + PrimaryAllocator::AdditionalSize();
+#else
+ return kHeapMemEnd;
+#endif
+}
+#endif
+
} // namespace __tsan
#endif // TSAN_RTL_H
diff --git a/lib/tsan/rtl/tsan_rtl_report.cc b/lib/tsan/rtl/tsan_rtl_report.cc
index d1621454242c..dc9438e6371b 100644
--- a/lib/tsan/rtl/tsan_rtl_report.cc
+++ b/lib/tsan/rtl/tsan_rtl_report.cc
@@ -112,16 +112,10 @@ static ReportStack *SymbolizeStack(StackTrace trace) {
for (uptr si = 0; si < trace.size; si++) {
const uptr pc = trace.trace[si];
uptr pc1 = pc;
-#ifndef SANITIZER_GO
// We obtain the return address, but we're interested in the previous
// instruction.
if ((pc & kExternalPCBit) == 0)
pc1 = StackTrace::GetPreviousInstructionPc(pc);
-#else
- // FIXME(dvyukov): Go sometimes uses address of a function as top pc.
- if (si != trace.size - 1)
- pc1 -= 1;
-#endif
SymbolizedStack *ent = SymbolizeCode(pc1);
CHECK_NE(ent, 0);
SymbolizedStack *last = ent;
diff --git a/lib/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc
index e026217ed171..8ed1fbf2edae 100644
--- a/lib/tsan/rtl/tsan_rtl_thread.cc
+++ b/lib/tsan/rtl/tsan_rtl_thread.cc
@@ -145,7 +145,9 @@ void ThreadContext::OnFinished() {
AllocatorThreadFinish(thr);
#endif
thr->~ThreadState();
+#if TSAN_COLLECT_STATS
StatAggregate(ctx->stat, thr->stat);
+#endif
thr = 0;
}
@@ -239,6 +241,7 @@ void ThreadStart(ThreadState *thr, int tid, uptr os_id) {
uptr stk_size = 0;
uptr tls_addr = 0;
uptr tls_size = 0;
+#ifndef SANITIZER_GO
GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size);
if (tid) {
@@ -259,6 +262,7 @@ void ThreadStart(ThreadState *thr, int tid, uptr os_id) {
thr_end, tls_addr + tls_size - thr_end);
}
}
+#endif
ThreadRegistry *tr = ctx->thread_registry;
OnStartedArgs args = { thr, stk_addr, stk_size, tls_addr, tls_size };
diff --git a/lib/tsan/rtl/tsan_stat.cc b/lib/tsan/rtl/tsan_stat.cc
index 350a2ba48253..15fa43d6f8a1 100644
--- a/lib/tsan/rtl/tsan_stat.cc
+++ b/lib/tsan/rtl/tsan_stat.cc
@@ -15,17 +15,14 @@
namespace __tsan {
+#if TSAN_COLLECT_STATS
+
void StatAggregate(u64 *dst, u64 *src) {
- if (!kCollectStats)
- return;
for (int i = 0; i < StatCnt; i++)
dst[i] += src[i];
}
void StatOutput(u64 *stat) {
- if (!kCollectStats)
- return;
-
stat[StatShadowNonZero] = stat[StatShadowProcessed] - stat[StatShadowZero];
static const char *name[StatCnt] = {};
@@ -176,4 +173,6 @@ void StatOutput(u64 *stat) {
Printf("%s: %16zu\n", name[i], (uptr)stat[i]);
}
+#endif
+
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc
index 299fc80fd262..5413f04af1d2 100644
--- a/lib/tsan/rtl/tsan_suppressions.cc
+++ b/lib/tsan/rtl/tsan_suppressions.cc
@@ -41,63 +41,74 @@ extern "C" const char *WEAK __tsan_default_suppressions() {
namespace __tsan {
-static bool suppressions_inited = false;
+ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = nullptr;
+static const char *kSuppressionTypes[] = {
+ kSuppressionRace, kSuppressionMutex, kSuppressionThread,
+ kSuppressionSignal, kSuppressionLib, kSuppressionDeadlock};
void InitializeSuppressions() {
- CHECK(!suppressions_inited);
- SuppressionContext::InitIfNecessary();
+ CHECK_EQ(nullptr, suppression_ctx);
+ suppression_ctx = new (suppression_placeholder) // NOLINT
+ SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+ suppression_ctx->ParseFromFile(flags()->suppressions);
#ifndef SANITIZER_GO
- SuppressionContext::Get()->Parse(__tsan_default_suppressions());
- SuppressionContext::Get()->Parse(std_suppressions);
+ suppression_ctx->Parse(__tsan_default_suppressions());
+ suppression_ctx->Parse(std_suppressions);
#endif
- suppressions_inited = true;
}
-SuppressionType conv(ReportType typ) {
+SuppressionContext *Suppressions() {
+ CHECK(suppression_ctx);
+ return suppression_ctx;
+}
+
+static const char *conv(ReportType typ) {
if (typ == ReportTypeRace)
- return SuppressionRace;
+ return kSuppressionRace;
else if (typ == ReportTypeVptrRace)
- return SuppressionRace;
+ return kSuppressionRace;
else if (typ == ReportTypeUseAfterFree)
- return SuppressionRace;
+ return kSuppressionRace;
else if (typ == ReportTypeVptrUseAfterFree)
- return SuppressionRace;
+ return kSuppressionRace;
else if (typ == ReportTypeThreadLeak)
- return SuppressionThread;
+ return kSuppressionThread;
else if (typ == ReportTypeMutexDestroyLocked)
- return SuppressionMutex;
+ return kSuppressionMutex;
else if (typ == ReportTypeMutexDoubleLock)
- return SuppressionMutex;
+ return kSuppressionMutex;
else if (typ == ReportTypeMutexBadUnlock)
- return SuppressionMutex;
+ return kSuppressionMutex;
else if (typ == ReportTypeMutexBadReadLock)
- return SuppressionMutex;
+ return kSuppressionMutex;
else if (typ == ReportTypeMutexBadReadUnlock)
- return SuppressionMutex;
+ return kSuppressionMutex;
else if (typ == ReportTypeSignalUnsafe)
- return SuppressionSignal;
+ return kSuppressionSignal;
else if (typ == ReportTypeErrnoInSignal)
- return SuppressionNone;
+ return kSuppressionNone;
else if (typ == ReportTypeDeadlock)
- return SuppressionDeadlock;
+ return kSuppressionDeadlock;
Printf("ThreadSanitizer: unknown report type %d\n", typ),
Die();
}
uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
- if (!SuppressionContext::Get()->SuppressionCount() || stack == 0 ||
+ CHECK(suppression_ctx);
+ if (!suppression_ctx->SuppressionCount() || stack == 0 ||
!stack->suppressable)
return 0;
- SuppressionType stype = conv(typ);
- if (stype == SuppressionNone)
+ 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 (SuppressionContext::Get()->Match(info.function, stype, &s) ||
- SuppressionContext::Get()->Match(info.file, stype, &s) ||
- SuppressionContext::Get()->Match(info.module, stype, &s)) {
+ 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;
@@ -108,16 +119,17 @@ uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
}
uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) {
- if (!SuppressionContext::Get()->SuppressionCount() || loc == 0 ||
+ CHECK(suppression_ctx);
+ if (!suppression_ctx->SuppressionCount() || loc == 0 ||
loc->type != ReportLocationGlobal || !loc->suppressable)
return 0;
- SuppressionType stype = conv(typ);
- if (stype == SuppressionNone)
+ const char *stype = conv(typ);
+ if (0 == internal_strcmp(stype, kSuppressionNone))
return 0;
Suppression *s;
const DataInfo &global = loc->global;
- if (SuppressionContext::Get()->Match(global.name, stype, &s) ||
- SuppressionContext::Get()->Match(global.module, stype, &s)) {
+ if (suppression_ctx->Match(global.name, stype, &s) ||
+ suppression_ctx->Match(global.module, stype, &s)) {
DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ);
s->hit_count++;
*sp = s;
@@ -128,7 +140,8 @@ uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) {
void PrintMatchedSuppressions() {
InternalMmapVector<Suppression *> matched(1);
- SuppressionContext::Get()->GetMatched(&matched);
+ CHECK(suppression_ctx);
+ suppression_ctx->GetMatched(&matched);
if (!matched.size())
return;
int hit_count = 0;
@@ -137,8 +150,8 @@ void PrintMatchedSuppressions() {
Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", hit_count,
(int)internal_getpid());
for (uptr i = 0; i < matched.size(); i++) {
- Printf("%d %s:%s\n", matched[i]->hit_count,
- SuppressionTypeString(matched[i]->type), matched[i]->templ);
+ Printf("%d %s:%s\n", matched[i]->hit_count, matched[i]->type,
+ matched[i]->templ);
}
}
} // namespace __tsan
diff --git a/lib/tsan/rtl/tsan_suppressions.h b/lib/tsan/rtl/tsan_suppressions.h
index c618b3db4c2d..e6d279c33a4e 100644
--- a/lib/tsan/rtl/tsan_suppressions.h
+++ b/lib/tsan/rtl/tsan_suppressions.h
@@ -18,7 +18,16 @@
namespace __tsan {
+const char kSuppressionNone[] = "none";
+const char kSuppressionRace[] = "race";
+const char kSuppressionMutex[] = "mutex";
+const char kSuppressionThread[] = "thread";
+const char kSuppressionSignal[] = "signal";
+const char kSuppressionLib[] = "called_from_lib";
+const char kSuppressionDeadlock[] = "deadlock";
+
void InitializeSuppressions();
+SuppressionContext *Suppressions();
void PrintMatchedSuppressions();
uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp);
uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp);
diff --git a/lib/tsan/rtl/tsan_trace.h b/lib/tsan/rtl/tsan_trace.h
index 1da8752f6fa7..2569c7e42a47 100644
--- a/lib/tsan/rtl/tsan_trace.h
+++ b/lib/tsan/rtl/tsan_trace.h
@@ -20,9 +20,9 @@
namespace __tsan {
-const int kTracePartSizeBits = 14;
+const int kTracePartSizeBits = 13;
const int kTracePartSize = 1 << kTracePartSizeBits;
-const int kTraceParts = 4 * 1024 * 1024 / kTracePartSize;
+const int kTraceParts = 2 * 1024 * 1024 / kTracePartSize;
const int kTraceSize = kTracePartSize * kTraceParts;
// Must fit into 3 bits.
@@ -54,13 +54,15 @@ struct TraceHeader {
};
struct Trace {
- TraceHeader headers[kTraceParts];
Mutex mtx;
#ifndef SANITIZER_GO
// Must be last to catch overflow as paging fault.
// Go shadow stack is dynamically allocated.
uptr shadow_stack[kShadowStackSize];
#endif
+ // Must be the last field, because we unmap the unused part in
+ // CreateThreadContext.
+ TraceHeader headers[kTraceParts];
Trace()
: mtx(MutexTypeTrace, StatMtxTrace) {
diff --git a/lib/tsan/tests/CMakeLists.txt b/lib/tsan/tests/CMakeLists.txt
index cb7e5374ec8a..e0c3f8a1a6d8 100644
--- a/lib/tsan/tests/CMakeLists.txt
+++ b/lib/tsan/tests/CMakeLists.txt
@@ -34,26 +34,28 @@ endmacro()
macro(add_tsan_unittest testname)
# Build unit tests only for 64-bit Linux.
- if(UNIX AND NOT APPLE AND CAN_TARGET_x86_64)
- parse_arguments(TEST "SOURCES;HEADERS" "" ${ARGN})
- set(TEST_OBJECTS)
- foreach(SOURCE ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE})
- tsan_compile(TEST_OBJECTS ${SOURCE} x86_64 ${TEST_HEADERS})
+ if(UNIX AND NOT APPLE)
+ foreach(arch ${TSAN_SUPPORTED_ARCH})
+ 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})
+ endforeach()
+ get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS)
+ set(TEST_DEPS ${TEST_OBJECTS})
+ if(NOT COMPILER_RT_STANDALONE_BUILD)
+ list(APPEND TEST_DEPS tsan)
+ endif()
+ # FIXME: Looks like we should link TSan with just-built runtime,
+ # and not rely on -fsanitize=thread, as these tests are essentially
+ # unit tests.
+ add_compiler_rt_test(TsanUnitTests ${testname}
+ OBJECTS ${TEST_OBJECTS}
+ DEPS ${TEST_DEPS}
+ LINK_FLAGS ${TARGET_LINK_FLAGS}
+ -fsanitize=thread
+ -lstdc++ -lm)
endforeach()
- get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS)
- set(TEST_DEPS ${TEST_OBJECTS})
- if(NOT COMPILER_RT_STANDALONE_BUILD)
- list(APPEND TEST_DEPS tsan)
- endif()
- # FIXME: Looks like we should link TSan with just-built runtime,
- # and not rely on -fsanitize=thread, as these tests are essentially
- # unit tests.
- add_compiler_rt_test(TsanUnitTests ${testname}
- OBJECTS ${TEST_OBJECTS}
- DEPS ${TEST_DEPS}
- LINK_FLAGS ${TARGET_LINK_FLAGS}
- -fsanitize=thread
- -lstdc++ -lm)
endif()
endmacro()
diff --git a/lib/ubsan/CMakeLists.txt b/lib/ubsan/CMakeLists.txt
index 541138044654..09c7a851e075 100644
--- a/lib/ubsan/CMakeLists.txt
+++ b/lib/ubsan/CMakeLists.txt
@@ -17,12 +17,7 @@ include_directories(..)
set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_no_rtti_flag(UBSAN_CFLAGS)
-append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors
- UBSAN_CFLAGS)
-
set(UBSAN_CXXFLAGS ${SANITIZER_COMMON_CFLAGS})
-append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors
- UBSAN_CXXFLAGS)
add_custom_target(ubsan)
diff --git a/lib/ubsan/ubsan_diag.cc b/lib/ubsan/ubsan_diag.cc
index 76ce2bd39996..4f2a2a9f3562 100644
--- a/lib/ubsan/ubsan_diag.cc
+++ b/lib/ubsan/ubsan_diag.cc
@@ -14,9 +14,11 @@
#include "ubsan_diag.h"
#include "ubsan_init.h"
#include "ubsan_flags.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_stacktrace_printer.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include <stdio.h>
@@ -66,39 +68,9 @@ class Decorator : public SanitizerCommonDecorator {
};
}
-Location __ubsan::getCallerLocation(uptr CallerLoc) {
- if (!CallerLoc)
- return Location();
-
- uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc);
- return getFunctionLocation(Loc, 0);
-}
-
-Location __ubsan::getFunctionLocation(uptr Loc, const char **FName) {
- if (!Loc)
- return Location();
+SymbolizedStack *__ubsan::getSymbolizedLocation(uptr PC) {
InitIfNecessary();
-
- SymbolizedStack *Frames = Symbolizer::GetOrInit()->SymbolizePC(Loc);
- const AddressInfo &Info = Frames->info;
-
- if (!Info.module) {
- Frames->ClearAll();
- return Location(Loc);
- }
-
- if (FName && Info.function)
- *FName = internal_strdup(Info.function);
-
- if (!Info.file) {
- ModuleLocation MLoc(internal_strdup(Info.module), Info.module_offset);
- Frames->ClearAll();
- return MLoc;
- }
-
- SourceLocation SLoc(internal_strdup(Info.file), Info.line, Info.column);
- Frames->ClearAll();
- return SLoc;
+ return Symbolizer::GetOrInit()->SymbolizePC(PC);
}
Diag &Diag::operator<<(const TypeDescriptor &V) {
@@ -142,15 +114,22 @@ static void renderLocation(Location Loc) {
SLoc.getColumn(), common_flags()->strip_path_prefix);
break;
}
- case Location::LK_Module: {
- ModuleLocation MLoc = Loc.getModuleLocation();
- RenderModuleLocation(&LocBuffer, MLoc.getModuleName(), MLoc.getOffset(),
- common_flags()->strip_path_prefix);
- break;
- }
case Location::LK_Memory:
LocBuffer.append("%p", Loc.getMemoryLocation());
break;
+ case Location::LK_Symbolized: {
+ const AddressInfo &Info = Loc.getSymbolizedStack()->info;
+ if (Info.file) {
+ RenderSourceLocation(&LocBuffer, Info.file, Info.line, Info.column,
+ common_flags()->strip_path_prefix);
+ } else if (Info.module) {
+ RenderModuleLocation(&LocBuffer, Info.module, Info.module_offset,
+ common_flags()->strip_path_prefix);
+ } else {
+ LocBuffer.append("%p", Info.address);
+ }
+ break;
+ }
case Location::LK_Null:
LocBuffer.append("<unknown>");
break;
@@ -356,11 +335,24 @@ ScopedReport::~ScopedReport() {
Die();
}
-bool __ubsan::MatchSuppression(const char *Str, SuppressionType Type) {
- Suppression *s;
+ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = nullptr;
+static const char kVptrCheck[] = "vptr_check";
+static const char *kSuppressionTypes[] = { kVptrCheck };
+
+void __ubsan::InitializeSuppressions() {
+ CHECK_EQ(nullptr, suppression_ctx);
+ suppression_ctx = new (suppression_placeholder) // NOLINT
+ SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+ suppression_ctx->ParseFromFile(flags()->suppressions);
+}
+
+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();
- return SuppressionContext::Get()->Match(Str, Type, &s);
+ CHECK(suppression_ctx);
+ Suppression *s;
+ return suppression_ctx->Match(TypeName, kVptrCheck, &s);
}
diff --git a/lib/ubsan/ubsan_diag.h b/lib/ubsan/ubsan_diag.h
index 296ec0d3f61a..44dca90b7a53 100644
--- a/lib/ubsan/ubsan_diag.h
+++ b/lib/ubsan/ubsan_diag.h
@@ -15,79 +15,84 @@
#include "ubsan_value.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "sanitizer_common/sanitizer_suppressions.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
namespace __ubsan {
-/// \brief A location within a loaded module in the program. These are used when
-/// the location can't be resolved to a SourceLocation.
-class ModuleLocation {
- const char *ModuleName;
- uptr Offset;
+class SymbolizedStackHolder {
+ SymbolizedStack *Stack;
+
+ void clear() {
+ if (Stack)
+ Stack->ClearAll();
+ }
public:
- ModuleLocation() : ModuleName(0), Offset(0) {}
- ModuleLocation(const char *ModuleName, uptr Offset)
- : ModuleName(ModuleName), Offset(Offset) {}
- const char *getModuleName() const { return ModuleName; }
- uptr getOffset() const { return Offset; }
+ explicit SymbolizedStackHolder(SymbolizedStack *Stack = nullptr)
+ : Stack(Stack) {}
+ ~SymbolizedStackHolder() { clear(); }
+ void reset(SymbolizedStack *S) {
+ if (Stack != S)
+ clear();
+ Stack = S;
+ }
+ const SymbolizedStack *get() const { return Stack; }
};
+SymbolizedStack *getSymbolizedLocation(uptr PC);
+
+inline SymbolizedStack *getCallerLocation(uptr CallerPC) {
+ CHECK(CallerPC);
+ uptr PC = StackTrace::GetPreviousInstructionPc(CallerPC);
+ return getSymbolizedLocation(PC);
+}
+
/// A location of some data within the program's address space.
typedef uptr MemoryLocation;
/// \brief Location at which a diagnostic can be emitted. Either a
-/// SourceLocation, a ModuleLocation, or a MemoryLocation.
+/// SourceLocation, a MemoryLocation, or a SymbolizedStack.
class Location {
public:
- enum LocationKind { LK_Null, LK_Source, LK_Module, LK_Memory };
+ enum LocationKind { LK_Null, LK_Source, LK_Memory, LK_Symbolized };
private:
LocationKind Kind;
// FIXME: In C++11, wrap these in an anonymous union.
SourceLocation SourceLoc;
- ModuleLocation ModuleLoc;
MemoryLocation MemoryLoc;
+ const SymbolizedStack *SymbolizedLoc; // Not owned.
public:
Location() : Kind(LK_Null) {}
Location(SourceLocation Loc) :
Kind(LK_Source), SourceLoc(Loc) {}
- Location(ModuleLocation Loc) :
- Kind(LK_Module), ModuleLoc(Loc) {}
Location(MemoryLocation Loc) :
Kind(LK_Memory), MemoryLoc(Loc) {}
+ // SymbolizedStackHolder must outlive Location object.
+ Location(const SymbolizedStackHolder &Stack) :
+ Kind(LK_Symbolized), SymbolizedLoc(Stack.get()) {}
LocationKind getKind() const { return Kind; }
bool isSourceLocation() const { return Kind == LK_Source; }
- bool isModuleLocation() const { return Kind == LK_Module; }
bool isMemoryLocation() const { return Kind == LK_Memory; }
+ bool isSymbolizedStack() const { return Kind == LK_Symbolized; }
SourceLocation getSourceLocation() const {
CHECK(isSourceLocation());
return SourceLoc;
}
- ModuleLocation getModuleLocation() const {
- CHECK(isModuleLocation());
- return ModuleLoc;
- }
MemoryLocation getMemoryLocation() const {
CHECK(isMemoryLocation());
return MemoryLoc;
}
+ const SymbolizedStack *getSymbolizedStack() const {
+ CHECK(isSymbolizedStack());
+ return SymbolizedLoc;
+ }
};
-/// Try to obtain a location for the caller. This might fail, and produce either
-/// an invalid location or a module location for the caller.
-Location getCallerLocation(uptr CallerLoc = GET_CALLER_PC());
-
-/// Try to obtain a location for the given function pointer. This might fail,
-/// and produce either an invalid location or a module location for the caller.
-/// If FName is non-null and the name of the function is known, set *FName to
-/// the function name, otherwise *FName is unchanged.
-Location getFunctionLocation(uptr Loc, const char **FName);
-
/// A diagnostic severity level.
enum DiagLevel {
DL_Error, ///< An error.
@@ -230,7 +235,8 @@ public:
~ScopedReport();
};
-bool MatchSuppression(const char *Str, SuppressionType Type);
+void InitializeSuppressions();
+bool IsVptrCheckSuppressed(const char *TypeName);
} // namespace __ubsan
diff --git a/lib/ubsan/ubsan_flags.inc b/lib/ubsan/ubsan_flags.inc
index 3260e8e13df8..9ca31d13a9b6 100644
--- a/lib/ubsan/ubsan_flags.inc
+++ b/lib/ubsan/ubsan_flags.inc
@@ -21,4 +21,5 @@ UBSAN_FLAG(bool, halt_on_error, false,
"Crash the program after printing the first error report")
UBSAN_FLAG(bool, print_stacktrace, false,
"Include full stacktrace into an error report")
+UBSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc
index a0ecff943592..78e7508f7f93 100644
--- a/lib/ubsan/ubsan_handlers.cc
+++ b/lib/ubsan/ubsan_handlers.cc
@@ -37,14 +37,17 @@ const char *TypeCheckKinds[] = {
}
static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
- Location FallbackLoc, ReportOptions Opts) {
+ ReportOptions Opts) {
Location Loc = Data->Loc.acquire();
// Use the SourceLocation from Data to track deduplication, even if 'invalid'
if (ignoreReport(Loc.getSourceLocation(), Opts))
return;
- if (Data->Loc.isInvalid())
+ SymbolizedStackHolder FallbackLoc;
+ if (Data->Loc.isInvalid()) {
+ FallbackLoc.reset(getCallerLocation(Opts.pc));
Loc = FallbackLoc;
+ }
ScopedReport R(Opts, Loc);
@@ -67,12 +70,12 @@ static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
void __ubsan::__ubsan_handle_type_mismatch(TypeMismatchData *Data,
ValueHandle Pointer) {
GET_REPORT_OPTIONS(false);
- handleTypeMismatchImpl(Data, Pointer, getCallerLocation(), Opts);
+ handleTypeMismatchImpl(Data, Pointer, Opts);
}
void __ubsan::__ubsan_handle_type_mismatch_abort(TypeMismatchData *Data,
ValueHandle Pointer) {
GET_REPORT_OPTIONS(true);
- handleTypeMismatchImpl(Data, Pointer, getCallerLocation(), Opts);
+ handleTypeMismatchImpl(Data, Pointer, Opts);
Die();
}
@@ -288,7 +291,8 @@ void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data,
static void handleFloatCastOverflow(FloatCastOverflowData *Data,
ValueHandle From, ReportOptions Opts) {
// TODO: Add deduplication once a SourceLocation is generated for this check.
- Location Loc = getCallerLocation();
+ SymbolizedStackHolder CallerLoc(getCallerLocation(Opts.pc));
+ Location Loc = CallerLoc;
ScopedReport R(Opts, Loc);
Diag(Loc, DL_Error,
@@ -337,16 +341,21 @@ void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data,
static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data,
ValueHandle Function,
ReportOptions Opts) {
- const char *FName = "(unknown)";
+ SourceLocation CallLoc = Data->Loc.acquire();
+ if (ignoreReport(CallLoc, Opts))
+ return;
- Location Loc = getFunctionLocation(Function, &FName);
+ ScopedReport R(Opts, CallLoc);
- ScopedReport R(Opts, Loc);
+ SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
+ const char *FName = FLoc.get()->info.function;
+ if (!FName)
+ FName = "(unknown)";
- Diag(Data->Loc, DL_Error,
+ Diag(CallLoc, DL_Error,
"call to function %0 through pointer to incorrect function type %1")
- << FName << Data->Type;
- Diag(Loc, DL_Note, "%0 defined here") << FName;
+ << FName << Data->Type;
+ Diag(FLoc, DL_Note, "%0 defined here") << FName;
}
void
diff --git a/lib/ubsan/ubsan_handlers_cxx.cc b/lib/ubsan/ubsan_handlers_cxx.cc
index 5704c1e6342d..4718e6eacee2 100644
--- a/lib/ubsan/ubsan_handlers_cxx.cc
+++ b/lib/ubsan/ubsan_handlers_cxx.cc
@@ -36,8 +36,7 @@ static void HandleDynamicTypeCacheMiss(
// Check if error report should be suppressed.
DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer);
- if (DTI.isValid() &&
- MatchSuppression(DTI.getMostDerivedTypeName(), SuppressionVptrCheck))
+ if (DTI.isValid() && IsVptrCheckSuppressed(DTI.getMostDerivedTypeName()))
return;
SourceLocation Loc = Data->Loc.acquire();
diff --git a/lib/ubsan/ubsan_init.cc b/lib/ubsan/ubsan_init.cc
index 48fa492486dd..219273d51921 100644
--- a/lib/ubsan/ubsan_init.cc
+++ b/lib/ubsan/ubsan_init.cc
@@ -11,12 +11,12 @@
//
//===----------------------------------------------------------------------===//
+#include "ubsan_diag.h"
#include "ubsan_init.h"
#include "ubsan_flags.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_mutex.h"
-#include "sanitizer_common/sanitizer_suppressions.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
using namespace __ubsan;
@@ -43,7 +43,7 @@ void __ubsan::InitIfNecessary() {
}
// Initialize UBSan-specific flags.
InitializeFlags(standalone);
- SuppressionContext::InitIfNecessary();
+ InitializeSuppressions();
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
ubsan_inited = true;
}
diff --git a/make/platform/clang_linux.mk b/make/platform/clang_linux.mk
index fc4405734840..cb023f3db9cc 100644
--- a/make/platform/clang_linux.mk
+++ b/make/platform/clang_linux.mk
@@ -49,80 +49,34 @@ endif
# Build runtime libraries for i386.
ifeq ($(call contains,$(SupportedArches),i386),true)
-Configs += builtins-i386 profile-i386 san-i386 asan-i386 asan_cxx-i386 \
- ubsan-i386 ubsan_cxx-i386
+Configs += builtins-i386 profile-i386
Arch.builtins-i386 := i386
Arch.profile-i386 := i386
-Arch.san-i386 := i386
-Arch.asan-i386 := i386
-Arch.asan_cxx-i386 := i386
-Arch.ubsan-i386 := i386
-Arch.ubsan_cxx-i386 := i386
endif
# Build runtime libraries for x86_64.
ifeq ($(call contains,$(SupportedArches),x86_64),true)
-Configs += builtins-x86_64 profile-x86_64 san-x86_64 asan-x86_64 asan_cxx-x86_64 \
- tsan-x86_64 msan-x86_64 ubsan-x86_64 ubsan_cxx-x86_64 dfsan-x86_64 \
- lsan-x86_64
+Configs += builtins-x86_64 profile-x86_64
Arch.builtins-x86_64 := x86_64
Arch.profile-x86_64 := x86_64
-Arch.san-x86_64 := x86_64
-Arch.asan-x86_64 := x86_64
-Arch.asan_cxx-x86_64 := x86_64
-Arch.tsan-x86_64 := x86_64
-Arch.msan-x86_64 := x86_64
-Arch.ubsan-x86_64 := x86_64
-Arch.ubsan_cxx-x86_64 := x86_64
-Arch.dfsan-x86_64 := x86_64
-Arch.lsan-x86_64 := x86_64
endif
endif
-ifneq ($(LLVM_ANDROID_TOOLCHAIN_DIR),)
-Configs += asan-arm-android
-Arch.asan-arm-android := arm-android
-endif
-
endif
###
CFLAGS := -Wall -Werror -O3 -fomit-frame-pointer
-SANITIZER_CFLAGS := -fPIE -fno-builtin -gline-tables-only
CFLAGS.builtins-i386 := $(CFLAGS) -m32
CFLAGS.builtins-x86_64 := $(CFLAGS) -m64
CFLAGS.profile-i386 := $(CFLAGS) -m32
CFLAGS.profile-x86_64 := $(CFLAGS) -m64
-CFLAGS.san-i386 := $(CFLAGS) -m32 $(SANITIZER_CFLAGS) -fno-rtti
-CFLAGS.san-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti
-CFLAGS.asan-i386 := $(CFLAGS) -m32 $(SANITIZER_CFLAGS) -fno-rtti
-CFLAGS.asan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti
-CFLAGS.asan_cxx-i386 := $(CFLAGS) -m32 $(SANITIZER_CFLAGS) -fno-rtti
-CFLAGS.asan_cxx-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti
-CFLAGS.tsan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti
-CFLAGS.msan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti
-CFLAGS.ubsan-i386 := $(CFLAGS) -m32 $(SANITIZER_CFLAGS) -fno-rtti
-CFLAGS.ubsan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti
-CFLAGS.ubsan_cxx-i386 := $(CFLAGS) -m32 $(SANITIZER_CFLAGS)
-CFLAGS.ubsan_cxx-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS)
-CFLAGS.dfsan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti
-CFLAGS.lsan-x86_64 := $(CFLAGS) -m64 $(SANITIZER_CFLAGS) -fno-rtti
-
-SHARED_LIBRARY.asan-arm-android := 1
-ANDROID_COMMON_FLAGS := -target arm-linux-androideabi \
- --sysroot=$(LLVM_ANDROID_TOOLCHAIN_DIR)/sysroot \
- -B$(LLVM_ANDROID_TOOLCHAIN_DIR)
-CFLAGS.asan-arm-android := $(CFLAGS) $(SANITIZER_CFLAGS) \
- $(ANDROID_COMMON_FLAGS) -fno-rtti
-LDFLAGS.asan-arm-android := $(LDFLAGS) $(ANDROID_COMMON_FLAGS) -ldl -lm -llog \
- -lstdc++ -Wl,-soname=libclang_rt.asan-arm-android.so -Wl,-z,defs
# Use our stub SDK as the sysroot to support more portable building. For now we
# just do this for the core module, because the stub SDK doesn't have
-# enough support to build the sanitizers or profile runtimes.
+# enough support to build the profile runtime.
CFLAGS.builtins-i386 += --sysroot=$(ProjSrcRoot)/SDKs/linux
CFLAGS.builtins-x86_64 += --sysroot=$(ProjSrcRoot)/SDKs/linux
@@ -132,29 +86,6 @@ FUNCTIONS.profile-i386 := GCDAProfiling InstrProfiling InstrProfilingBuffer \
InstrProfilingFile InstrProfilingPlatformOther \
InstrProfilingRuntime
FUNCTIONS.profile-x86_64 := $(FUNCTIONS.profile-i386)
-FUNCTIONS.san-i386 := $(SanitizerCommonFunctions)
-FUNCTIONS.san-x86_64 := $(SanitizerCommonFunctions)
-FUNCTIONS.asan-i386 := $(AsanFunctions) $(InterceptionFunctions) \
- $(SanitizerCommonFunctions)
-FUNCTIONS.asan-x86_64 := $(AsanFunctions) $(InterceptionFunctions) \
- $(SanitizerCommonFunctions) $(LsanCommonFunctions)
-FUNCTIONS.asan_cxx-i386 := $(AsanCXXFunctions)
-FUNCTIONS.asan_cxx-x86_64 := $(AsanCXXFunctions)
-FUNCTIONS.asan-arm-android := $(AsanFunctions) $(AsanCXXFunctions) \
- $(InterceptionFunctions) \
- $(SanitizerCommonFunctions)
-FUNCTIONS.tsan-x86_64 := $(TsanFunctions) $(InterceptionFunctions) \
- $(SanitizerCommonFunctions)
-FUNCTIONS.msan-x86_64 := $(MsanFunctions) $(InterceptionFunctions) \
- $(SanitizerCommonFunctions)
-FUNCTIONS.ubsan-i386 := $(UbsanFunctions)
-FUNCTIONS.ubsan-x86_64 := $(UbsanFunctions)
-FUNCTIONS.ubsan_cxx-i386 := $(UbsanCXXFunctions)
-FUNCTIONS.ubsan_cxx-x86_64 := $(UbsanCXXFunctions)
-FUNCTIONS.dfsan-x86_64 := $(DfsanFunctions) $(InterceptionFunctions) \
- $(SanitizerCommonFunctions)
-FUNCTIONS.lsan-x86_64 := $(LsanFunctions) $(InterceptionFunctions) \
- $(SanitizerCommonFunctions)
# Always use optimized variants.
OPTIMIZED := 1
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 2ceb86463ae0..85a1735b1cf9 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -7,6 +7,12 @@ configure_lit_site_cfg(
# add_subdirectory(builtins)
set(SANITIZER_COMMON_LIT_TEST_DEPS)
+if(COMPILER_RT_STANDALONE_BUILD)
+ add_executable(FileCheck IMPORTED GLOBAL)
+ set_property(TARGET FileCheck PROPERTY IMPORTED_LOCATION ${LLVM_TOOLS_BINARY_DIR}/FileCheck)
+ list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS FileCheck)
+endif()
+
# When ANDROID, we build tests with the host compiler (i.e. CMAKE_C_COMPILER),
# and run tests with tools from the host toolchain.
if(NOT ANDROID)
@@ -51,6 +57,7 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS)
if(COMPILER_RT_HAS_UBSAN)
add_subdirectory(ubsan)
endif()
+ add_subdirectory(cfi)
endif()
if(COMPILER_RT_STANDALONE_BUILD)
diff --git a/test/asan/CMakeLists.txt b/test/asan/CMakeLists.txt
index 0c46ef7e6e31..e1b81264604d 100644
--- a/test/asan/CMakeLists.txt
+++ b/test/asan/CMakeLists.txt
@@ -55,11 +55,7 @@ foreach(arch ${ASAN_SUPPORTED_ARCH})
endforeach()
set(ASAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
-if(COMPILER_RT_STANDALONE_BUILD)
- add_executable(FileCheck IMPORTED GLOBAL)
- set_property(TARGET FileCheck PROPERTY IMPORTED_LOCATION ${LLVM_TOOLS_BINARY_DIR}/FileCheck)
- list(APPEND ASAN_TEST_DEPS FileCheck)
-else()
+if(NOT COMPILER_RT_STANDALONE_BUILD)
list(APPEND ASAN_TEST_DEPS asan)
endif()
set(ASAN_DYNAMIC_TEST_DEPS ${ASAN_TEST_DEPS})
diff --git a/test/asan/TestCases/Windows/dll_host.cc b/test/asan/TestCases/Windows/dll_host.cc
index d3b4c149d009..71721fe29e88 100644
--- a/test/asan/TestCases/Windows/dll_host.cc
+++ b/test/asan/TestCases/Windows/dll_host.cc
@@ -6,9 +6,14 @@
//
// Get the list of ASan wrappers exported by the main module RTL:
// RUN: dumpbin /EXPORTS %t | grep -o "__asan_wrap[^ ]*" | grep -v @ | sort | uniq > %t.exported_wrappers
+// FIXME: we should really check the other __asan exports too.
+// RUN: dumpbin /EXPORTS %t | grep -o "__sanitizer_[^ ]*" | grep -v @ | sort | uniq >> %t.exported_wrappers
//
// Get the list of ASan wrappers imported by the DLL RTL:
-// RUN: grep INTERCEPT_LIBRARY_FUNCTION %p/../../../../lib/asan/asan_win_dll_thunk.cc | grep -v define | sed "s/.*(\(.*\)).*/__asan_wrap_\1/" | sort | uniq > %t.dll_imports
+// [BEWARE: be really careful with the sed commands, as this test can be run
+// from different environemnts with different shells and seds]
+// RUN: grep INTERCEPT_LIBRARY_FUNCTION %p/../../../../lib/asan/asan_win_dll_thunk.cc | grep -v define | sed -e s/.*(/__asan_wrap_/ | sed -e s/).*// | sort | uniq > %t.dll_imports
+// RUN: grep "^INTERFACE_FUNCTION.*sanitizer" %p/../../../../lib/asan/asan_win_dll_thunk.cc | grep -v define | sed -e s/.*(// | sed -e s/).*// | sort | uniq >> %t.dll_imports
//
// Now make sure the DLL thunk imports everything:
// RUN: echo
diff --git a/test/asan/TestCases/Windows/globals_multiple_dlls.cc b/test/asan/TestCases/Windows/globals_multiple_dlls.cc
new file mode 100644
index 000000000000..634e5782796c
--- /dev/null
+++ b/test/asan/TestCases/Windows/globals_multiple_dlls.cc
@@ -0,0 +1,51 @@
+// Make sure everything works even if the main module doesn't have any stack
+// variables, thus doesn't explicitly reference any symbol exported by the
+// runtime thunk.
+//
+// RUN: %clang_cl_asan -LD -O0 -DDLL %s -Fe%t.dll
+// RUN: %clang_cl_asan -O0 -DEXE %s -Fe%te.exe
+// RUN: env ASAN_OPTIONS=report_globals=2 %run %te.exe %t.dll 2>&1 | FileCheck %s
+
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+
+extern "C" {
+#if defined(EXE)
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ printf("Usage: %s [client].dll\n", argv[0]);
+ return 101;
+ }
+ const char *dll_name = argv[1];
+
+// CHECK: time to load DLL
+ printf("time to load DLL\n");
+ fflush(0);
+
+// On DLL load, the "in DLL\n" string is registered:
+// CHECK: Added Global{{.*}} size=19
+// CHECK: in DLL(reason=1)
+ HMODULE dll = LoadLibrary(dll_name);
+ if (dll == NULL)
+ return 3;
+
+// CHECK: in DLL(reason=0)
+// CHECK-NEXT: Removed Global{{.*}} size=19
+ if (!FreeLibrary(dll))
+ return 4;
+
+// CHECK: bye!
+ printf("bye!\n");
+ fflush(0);
+}
+#elif defined(DLL)
+BOOL WINAPI DllMain(HMODULE, DWORD reason, LPVOID) {
+ printf("in DLL(reason=%d)\n", (int)reason);
+ fflush(0);
+ return TRUE;
+}
+#else
+# error oops!
+#endif
+}
diff --git a/test/asan/TestCases/Windows/oom.cc b/test/asan/TestCases/Windows/oom.cc
new file mode 100644
index 000000000000..b24cddf17a97
--- /dev/null
+++ b/test/asan/TestCases/Windows/oom.cc
@@ -0,0 +1,12 @@
+// RUN: %clang_cl_asan -O0 %s -Fe%t
+// RUN: not %run %t 2>&1 | FileCheck %s
+
+#include <malloc.h>
+
+int main() {
+ while (true) {
+ void *ptr = malloc(200 * 1024 * 1024); // 200MB
+ free(ptr);
+ }
+// CHECK: failed to allocate
+}
diff --git a/test/asan/TestCases/Windows/symbols_path.cc b/test/asan/TestCases/Windows/symbols_path.cc
new file mode 100644
index 000000000000..3c69f8861d78
--- /dev/null
+++ b/test/asan/TestCases/Windows/symbols_path.cc
@@ -0,0 +1,22 @@
+// Make sure symbolization works even if the path to the .exe file changes.
+// RUN: mkdir %t || true
+// RUN: %clang_cl_asan -O0 %s -Fe%t/symbols_path.exe
+// RUN: not %run %t/symbols_path.exe 2>&1 | FileCheck %s
+// RUN: mkdir %t2 || true
+// RUN: mv %t/* %t2
+// RUN: not %run %t2/symbols_path.exe 2>&1 | FileCheck %s
+
+#include <malloc.h>
+
+int main() {
+ char *buffer = (char*)malloc(42);
+ buffer[-1] = 42;
+// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR:0x[0-9a-f]+]]
+// CHECK: WRITE of size 1 at [[ADDR]] thread T0
+// CHECK-NEXT: {{#0 .* main .*symbols_path.cc}}:[[@LINE-3]]
+// CHECK: [[ADDR]] is located 1 bytes to the left of 42-byte region
+// CHECK: allocated by thread T0 here:
+// CHECK-NEXT: {{#0 .* malloc }}
+// CHECK-NEXT: {{#1 .* main .*symbols_path.cc}}:[[@LINE-8]]
+ free(buffer);
+}
diff --git a/test/asan/TestCases/dlclose-test.cc b/test/asan/TestCases/dlclose-test.cc
index 2d31aee5a32f..369abd3127cc 100644
--- a/test/asan/TestCases/dlclose-test.cc
+++ b/test/asan/TestCases/dlclose-test.cc
@@ -33,6 +33,13 @@
#include <string>
+#if defined(__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
+
using std::string;
typedef int *(fun_t)();
diff --git a/test/asan/TestCases/gc-test.cc b/test/asan/TestCases/gc-test.cc
index ffbea85b2650..4ffa51dd22d3 100644
--- a/test/asan/TestCases/gc-test.cc
+++ b/test/asan/TestCases/gc-test.cc
@@ -1,6 +1,9 @@
// RUN: %clangxx_asan %s -pthread -o %t
// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1
// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK0
+// RUN: %clangxx_asan -O3 %s -pthread -o %t
+// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1
+// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK0
// REQUIRES: stable-runtime
#include <assert.h>
@@ -9,6 +12,7 @@
#include <sanitizer/asan_interface.h>
static const int kNumThreads = 2;
+static const int kLeftRedzoneSize = sizeof(void *) * 4;
void *Thread(void *unused) {
void *fake_stack = __asan_get_current_fake_stack();
@@ -23,7 +27,7 @@ void *Thread(void *unused) {
assert(real_stack);
assert((char*)beg <= (char*)&var[0]);
assert((char*)end > (char*)&var[0]);
- for (int i = -32; i < 15; i++) {
+ for (int i = -kLeftRedzoneSize; i < 15; i++) {
void *beg1, *end1;
char *ptr = &var[0] + i;
void *real_stack1 =
diff --git a/test/asan/lit.cfg b/test/asan/lit.cfg
index 8be7062614b3..a6f443cfb6a5 100644
--- a/test/asan/lit.cfg
+++ b/test/asan/lit.cfg
@@ -3,6 +3,8 @@
import os
import platform
+import lit.formats
+
def get_required_attr(config, attr_name):
attr_value = getattr(config, attr_name, None)
if attr_value == None:
@@ -15,6 +17,8 @@ def get_required_attr(config, attr_name):
def push_dynamic_library_lookup_path(config, new_path):
if platform.system() == 'Windows':
dynamic_library_lookup_var = 'PATH'
+ elif platform.system() == 'Darwin':
+ dynamic_library_lookup_var = 'DYLD_LIBRARY_PATH'
else:
dynamic_library_lookup_var = 'LD_LIBRARY_PATH'
@@ -25,6 +29,10 @@ def push_dynamic_library_lookup_path(config, new_path):
# Setup config name.
config.name = 'AddressSanitizer' + config.name_suffix
+# testFormat: The test format to use to interpret tests.
+external_bash = (not sys.platform in ['win32'])
+config.test_format = lit.formats.ShTest(external_bash)
+
# Setup source root.
config.test_source_root = os.path.dirname(__file__)
diff --git a/test/cfi/CMakeLists.txt b/test/cfi/CMakeLists.txt
new file mode 100644
index 000000000000..f519fb089505
--- /dev/null
+++ b/test/cfi/CMakeLists.txt
@@ -0,0 +1,23 @@
+configure_lit_site_cfg(
+ ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
+ ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
+ )
+
+set(CFI_TEST_DEPS)
+if(NOT COMPILER_RT_STANDALONE_BUILD)
+ list(APPEND CFI_TEST_DEPS
+ FileCheck
+ clang
+ not
+ )
+ if(LLVM_ENABLE_PIC AND LLVM_BINUTILS_INCDIR)
+ list(APPEND CFI_TEST_DEPS
+ LLVMgold
+ )
+ endif()
+endif()
+
+add_lit_testsuite(check-cfi "Running the cfi regression tests"
+ ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${CFI_TEST_DEPS})
+set_target_properties(check-cfi PROPERTIES FOLDER "Tests")
diff --git a/test/cfi/anon-namespace.cpp b/test/cfi/anon-namespace.cpp
new file mode 100644
index 000000000000..0c2c689966f1
--- /dev/null
+++ b/test/cfi/anon-namespace.cpp
@@ -0,0 +1,93 @@
+// RUN: %clangxx_cfi -c -DTU1 -o %t1.o %s
+// RUN: %clangxx_cfi -c -DTU2 -o %t2.o %S/../cfi/anon-namespace.cpp
+// RUN: %clangxx_cfi -o %t %t1.o %t2.o
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -c -DTU1 -DB32 -o %t1.o %s
+// RUN: %clangxx_cfi -c -DTU2 -DB32 -o %t2.o %S/../cfi/anon-namespace.cpp
+// RUN: %clangxx_cfi -o %t %t1.o %t2.o
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -c -DTU1 -DB64 -o %t1.o %s
+// RUN: %clangxx_cfi -c -DTU2 -DB64 -o %t2.o %S/../cfi/anon-namespace.cpp
+// RUN: %clangxx_cfi -o %t %t1.o %t2.o
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -c -DTU1 -DBM -o %t1.o %s
+// RUN: %clangxx_cfi -c -DTU2 -DBM -o %t2.o %S/../cfi/anon-namespace.cpp
+// RUN: %clangxx_cfi -o %t %t1.o %t2.o
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx -c -DTU1 -o %t1.o %s
+// RUN: %clangxx -c -DTU2 -o %t2.o %S/../cfi/anon-namespace.cpp
+// RUN: %clangxx -o %t %t1.o %t2.o
+// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
+
+// Tests that the CFI mechanism treats classes in the anonymous namespace in
+// different translation units as having distinct identities. This is done by
+// compiling two translation units TU1 and TU2 containing a class named B in an
+// anonymous namespace, and testing that the program crashes if TU2 attempts to
+// use a TU1 B as a TU2 B.
+
+// FIXME: This test should not require that the paths supplied to the compiler
+// are different. It currently does so because bitset names have global scope
+// so we have to mangle the file path into the bitset name.
+
+#include <stdio.h>
+#include "utils.h"
+
+struct A {
+ virtual void f() = 0;
+};
+
+namespace {
+
+struct B : A {
+ virtual void f() {}
+};
+
+}
+
+A *mkb();
+
+#ifdef TU1
+
+A *mkb() {
+ return new B;
+}
+
+#endif // TU1
+
+#ifdef TU2
+
+int main() {
+#ifdef B32
+ break_optimization(new Deriver<B, 0>);
+#endif
+
+#ifdef B64
+ break_optimization(new Deriver<B, 0>);
+ break_optimization(new Deriver<B, 1>);
+#endif
+
+#ifdef BM
+ break_optimization(new Deriver<B, 0>);
+ break_optimization(new Deriver<B, 1>);
+ break_optimization(new Deriver<B, 2>);
+#endif
+
+ A *a = mkb();
+ break_optimization(a);
+
+ // CFI: 1
+ // NCFI: 1
+ fprintf(stderr, "1\n");
+
+ ((B *)a)->f(); // UB here
+
+ // CFI-NOT: 2
+ // NCFI: 2
+ fprintf(stderr, "2\n");
+}
+
+#endif // TU2
diff --git a/test/cfi/lit.cfg b/test/cfi/lit.cfg
new file mode 100644
index 000000000000..d78820daa055
--- /dev/null
+++ b/test/cfi/lit.cfg
@@ -0,0 +1,35 @@
+import lit.formats
+import os
+import subprocess
+import sys
+
+config.name = 'cfi'
+config.suffixes = ['.cpp']
+config.test_source_root = os.path.dirname(__file__)
+
+def is_darwin_lto_supported():
+ return os.path.exists(os.path.join(config.llvm_shlib_dir, 'libLTO.dylib'))
+
+def is_linux_lto_supported():
+ if not os.path.exists(os.path.join(config.llvm_shlib_dir, 'LLVMgold.so')):
+ return False
+
+ ld_cmd = subprocess.Popen([config.gold_executable, '--help'], stdout = subprocess.PIPE)
+ ld_out = ld_cmd.stdout.read().decode()
+ ld_cmd.wait()
+
+ if not '-plugin' in ld_out:
+ return False
+
+ return True
+
+clangxx = ' '.join([config.clang] + config.cxx_mode_flags)
+
+config.substitutions.append((r"%clangxx ", clangxx + ' '))
+
+if sys.platform == 'darwin' and is_darwin_lto_supported():
+ config.substitutions.append((r"%clangxx_cfi ", 'env DYLD_LIBRARY_PATH=' + config.llvm_shlib_dir + ' ' + clangxx + ' -fsanitize=cfi '))
+elif sys.platform.startswith('linux') and is_linux_lto_supported():
+ config.substitutions.append((r"%clangxx_cfi ", clangxx + ' -fuse-ld=gold -fsanitize=cfi '))
+else:
+ config.unsupported = True
diff --git a/test/cfi/lit.site.cfg.in b/test/cfi/lit.site.cfg.in
new file mode 100644
index 000000000000..76897e701874
--- /dev/null
+++ b/test/cfi/lit.site.cfg.in
@@ -0,0 +1,2 @@
+lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
+lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg")
diff --git a/test/cfi/multiple-inheritance.cpp b/test/cfi/multiple-inheritance.cpp
new file mode 100644
index 000000000000..523af6f72f2f
--- /dev/null
+++ b/test/cfi/multiple-inheritance.cpp
@@ -0,0 +1,82 @@
+// RUN: %clangxx_cfi -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+// RUN: not --crash %t x 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -DB32 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+// RUN: not --crash %t x 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -DB64 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+// RUN: not --crash %t x 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -DBM -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+// RUN: not --crash %t x 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx -o %t %s
+// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
+// RUN: %t x 2>&1 | FileCheck --check-prefix=NCFI %s
+
+// Tests that the CFI mechanism is sensitive to multiple inheritance and only
+// permits calls via virtual tables for the correct base class.
+
+#include <stdio.h>
+#include "utils.h"
+
+struct A {
+ virtual void f() = 0;
+};
+
+struct B {
+ virtual void g() = 0;
+};
+
+struct C : A, B {
+ virtual void f(), g();
+};
+
+void C::f() {}
+void C::g() {}
+
+int main(int argc, char **argv) {
+#ifdef B32
+ break_optimization(new Deriver<A, 0>);
+ break_optimization(new Deriver<B, 0>);
+#endif
+
+#ifdef B64
+ break_optimization(new Deriver<A, 0>);
+ break_optimization(new Deriver<A, 1>);
+ break_optimization(new Deriver<B, 0>);
+ break_optimization(new Deriver<B, 1>);
+#endif
+
+#ifdef BM
+ break_optimization(new Deriver<A, 0>);
+ break_optimization(new Deriver<A, 1>);
+ break_optimization(new Deriver<A, 2>);
+ break_optimization(new Deriver<B, 0>);
+ break_optimization(new Deriver<B, 1>);
+ break_optimization(new Deriver<B, 2>);
+#endif
+
+ C *c = new C;
+ break_optimization(c);
+
+ // CFI: 1
+ // NCFI: 1
+ fprintf(stderr, "1\n");
+
+ if (argc > 1) {
+ A *a = c;
+ ((B *)a)->g(); // UB here
+ } else {
+ B *b = c;
+ ((A *)b)->f(); // UB here
+ }
+
+ // CFI-NOT: 2
+ // NCFI: 2
+ fprintf(stderr, "2\n");
+}
diff --git a/test/cfi/overwrite.cpp b/test/cfi/overwrite.cpp
new file mode 100644
index 000000000000..d7e58d9277e9
--- /dev/null
+++ b/test/cfi/overwrite.cpp
@@ -0,0 +1,67 @@
+// RUN: %clangxx_cfi -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -DB32 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -DB64 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -DBM -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx -o %t %s
+// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
+
+// Tests that the CFI mechanism crashes the program when a virtual table is
+// replaced with a compatible table of function pointers that does not belong to
+// any class, by manually overwriting the virtual table of an object and
+// attempting to make a call through it.
+
+#include <stdio.h>
+#include "utils.h"
+
+struct A {
+ virtual void f();
+};
+
+void A::f() {}
+
+void foo() {
+ fprintf(stderr, "foo\n");
+}
+
+void *fake_vtable[] = { (void *)&foo };
+
+int main() {
+#ifdef B32
+ break_optimization(new Deriver<A, 0>);
+#endif
+
+#ifdef B64
+ break_optimization(new Deriver<A, 0>);
+ break_optimization(new Deriver<A, 1>);
+#endif
+
+#ifdef BM
+ break_optimization(new Deriver<A, 0>);
+ break_optimization(new Deriver<A, 1>);
+ break_optimization(new Deriver<A, 2>);
+#endif
+
+ A *a = new A;
+ *((void **)a) = fake_vtable; // UB here
+ break_optimization(a);
+
+ // CFI: 1
+ // NCFI: 1
+ fprintf(stderr, "1\n");
+
+ // CFI-NOT: foo
+ // NCFI: foo
+ a->f();
+
+ // CFI-NOT: 2
+ // NCFI: 2
+ fprintf(stderr, "2\n");
+}
diff --git a/test/cfi/simple-fail.cpp b/test/cfi/simple-fail.cpp
new file mode 100644
index 000000000000..cf24f86e0064
--- /dev/null
+++ b/test/cfi/simple-fail.cpp
@@ -0,0 +1,99 @@
+// RUN: %clangxx_cfi -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -DB32 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -DB64 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -DBM -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -O1 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -O1 -DB32 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -O1 -DB64 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -O1 -DBM -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -O2 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -O2 -DB32 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -O2 -DB64 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -O2 -DBM -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -O3 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -O3 -DB32 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -O3 -DB64 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -O3 -DBM -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx -o %t %s
+// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
+
+// Tests that the CFI mechanism crashes the program when making a virtual call
+// to an object of the wrong class but with a compatible vtable, by casting a
+// pointer to such an object and attempting to make a call through it.
+
+#include <stdio.h>
+#include "utils.h"
+
+struct A {
+ virtual void f();
+};
+
+void A::f() {}
+
+struct B {
+ virtual void f();
+};
+
+void B::f() {}
+
+int main() {
+#ifdef B32
+ break_optimization(new Deriver<B, 0>);
+#endif
+
+#ifdef B64
+ break_optimization(new Deriver<B, 0>);
+ break_optimization(new Deriver<B, 1>);
+#endif
+
+#ifdef BM
+ break_optimization(new Deriver<B, 0>);
+ break_optimization(new Deriver<B, 1>);
+ break_optimization(new Deriver<B, 2>);
+#endif
+
+ A *a = new A;
+ break_optimization(a);
+
+ // CFI: 1
+ // NCFI: 1
+ fprintf(stderr, "1\n");
+
+ ((B *)a)->f(); // UB here
+
+ // CFI-NOT: 2
+ // NCFI: 2
+ fprintf(stderr, "2\n");
+}
diff --git a/test/cfi/simple-pass.cpp b/test/cfi/simple-pass.cpp
new file mode 100644
index 000000000000..50e7d9256084
--- /dev/null
+++ b/test/cfi/simple-pass.cpp
@@ -0,0 +1,97 @@
+// RUN: %clangxx_cfi -o %t %s
+// RUN: %t
+
+// Tests that the CFI mechanism does not crash the program when making various
+// kinds of valid calls involving classes with various different linkages and
+// types of inheritance.
+
+#include "utils.h"
+
+struct A {
+ virtual void f();
+};
+
+void A::f() {}
+
+struct A2 : A {
+ virtual void f();
+};
+
+void A2::f() {}
+
+struct B {
+ virtual void f() {}
+};
+
+struct B2 : B {
+ virtual void f() {}
+};
+
+namespace {
+
+struct C {
+ virtual void f();
+};
+
+void C::f() {}
+
+struct C2 : C {
+ virtual void f();
+};
+
+void C2::f() {}
+
+struct D {
+ virtual void f() {}
+};
+
+struct D2 : D {
+ virtual void f() {}
+};
+
+}
+
+struct E {
+ virtual void f() {}
+};
+
+struct E2 : virtual E {
+ virtual void f() {}
+};
+
+int main() {
+ A *a = new A;
+ break_optimization(a);
+ a->f();
+ a = new A2;
+ break_optimization(a);
+ a->f();
+
+ B *b = new B;
+ break_optimization(b);
+ b->f();
+ b = new B2;
+ break_optimization(b);
+ b->f();
+
+ C *c = new C;
+ break_optimization(c);
+ c->f();
+ c = new C2;
+ break_optimization(c);
+ c->f();
+
+ D *d = new D;
+ break_optimization(d);
+ d->f();
+ d = new D2;
+ break_optimization(d);
+ d->f();
+
+ E *e = new E;
+ break_optimization(e);
+ e->f();
+ e = new E2;
+ break_optimization(e);
+ e->f();
+}
diff --git a/test/cfi/utils.h b/test/cfi/utils.h
new file mode 100644
index 000000000000..5c290d151069
--- /dev/null
+++ b/test/cfi/utils.h
@@ -0,0 +1,53 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+inline void break_optimization(void *arg) {
+ __asm__ __volatile__("" : : "r" (arg) : "memory");
+}
+
+// Tests will instantiate this class to pad out bit sets to test out the various
+// ways we can represent the bit set (32-bit inline, 64-bit inline, memory).
+// This class has 37 virtual member functions, which forces us to use a
+// pointer-aligned bitset.
+template <typename T, unsigned I>
+class Deriver : T {
+ virtual void f() {}
+ virtual void g() {}
+ virtual void f1() {}
+ virtual void f2() {}
+ virtual void f3() {}
+ virtual void f4() {}
+ virtual void f5() {}
+ virtual void f6() {}
+ virtual void f7() {}
+ virtual void f8() {}
+ virtual void f9() {}
+ virtual void f10() {}
+ virtual void f11() {}
+ virtual void f12() {}
+ virtual void f13() {}
+ virtual void f14() {}
+ virtual void f15() {}
+ virtual void f16() {}
+ virtual void f17() {}
+ virtual void f18() {}
+ virtual void f19() {}
+ virtual void f20() {}
+ virtual void f21() {}
+ virtual void f22() {}
+ virtual void f23() {}
+ virtual void f24() {}
+ virtual void f25() {}
+ virtual void f26() {}
+ virtual void f27() {}
+ virtual void f28() {}
+ virtual void f29() {}
+ virtual void f30() {}
+ virtual void f31() {}
+ virtual void f32() {}
+ virtual void f33() {}
+ virtual void f34() {}
+ virtual void f35() {}
+};
+
+#endif
diff --git a/test/cfi/vdtor.cpp b/test/cfi/vdtor.cpp
new file mode 100644
index 000000000000..e21883c380dd
--- /dev/null
+++ b/test/cfi/vdtor.cpp
@@ -0,0 +1,62 @@
+// RUN: %clangxx_cfi -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -DB32 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -DB64 -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx_cfi -DBM -o %t %s
+// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s
+
+// RUN: %clangxx -o %t %s
+// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s
+
+// Tests that the CFI enforcement also applies to virtual destructor calls made
+// via 'delete'.
+
+#include <stdio.h>
+#include "utils.h"
+
+struct A {
+ virtual ~A();
+};
+
+A::~A() {}
+
+struct B {
+ virtual ~B();
+};
+
+B::~B() {}
+
+int main() {
+#ifdef B32
+ break_optimization(new Deriver<B, 0>);
+#endif
+
+#ifdef B64
+ break_optimization(new Deriver<B, 0>);
+ break_optimization(new Deriver<B, 1>);
+#endif
+
+#ifdef BM
+ break_optimization(new Deriver<B, 0>);
+ break_optimization(new Deriver<B, 1>);
+ break_optimization(new Deriver<B, 2>);
+#endif
+
+ A *a = new A;
+ break_optimization(a);
+
+ // CFI: 1
+ // NCFI: 1
+ fprintf(stderr, "1\n");
+
+ delete (B *)a; // UB here
+
+ // CFI-NOT: 2
+ // NCFI: 2
+ fprintf(stderr, "2\n");
+}
diff --git a/test/lit.common.cfg b/test/lit.common.cfg
index 0ee2b84481da..0a551862c32b 100644
--- a/test/lit.common.cfg
+++ b/test/lit.common.cfg
@@ -61,7 +61,8 @@ path = os.path.pathsep.join((llvm_tools_dir, config.environment['PATH']))
config.environment['PATH'] = path
# Help MSVS link.exe find the standard libraries.
-if platform.system() == 'Windows':
+# Make sure we only try to use it when targetting Windows.
+if platform.system() == 'Windows' and '-win' in config.target_triple:
config.environment['LIB'] = os.environ['LIB']
# Use ugly construction to explicitly prohibit "clang", "clang++" etc.
diff --git a/test/lit.common.configured.in b/test/lit.common.configured.in
index ceab67d4ad07..4a5966e44f32 100644
--- a/test/lit.common.configured.in
+++ b/test/lit.common.configured.in
@@ -18,6 +18,8 @@ set_default("llvm_obj_root", "@LLVM_BINARY_DIR@")
set_default("compiler_rt_src_root", "@COMPILER_RT_SOURCE_DIR@")
set_default("compiler_rt_obj_root", "@COMPILER_RT_BINARY_DIR@")
set_default("llvm_tools_dir", "@LLVM_TOOLS_DIR@")
+set_default("llvm_shlib_dir", "@SHLIBDIR@")
+set_default("gold_executable", "@GOLD_EXECUTABLE@")
set_default("clang", "@COMPILER_RT_TEST_COMPILER@")
set_default("compiler_id", "@COMPILER_RT_TEST_COMPILER_ID@")
set_default("compiler_rt_arch", "@COMPILER_RT_SUPPORTED_ARCH@")
diff --git a/test/lsan/lit.common.cfg b/test/lsan/lit.common.cfg
index bd1aa2769c42..ba9c283ca84a 100644
--- a/test/lsan/lit.common.cfg
+++ b/test/lsan/lit.common.cfg
@@ -44,8 +44,8 @@ config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) )
config.substitutions.append( ("%clang_lsan ", build_invocation(clang_lsan_cflags)) )
config.substitutions.append( ("%clangxx_lsan ", build_invocation(clang_lsan_cxxflags)) )
-# LeakSanitizer tests are currently supported on x86-64 Linux only.
-if config.host_os not in ['Linux'] or config.host_arch not in ['x86_64']:
+# LeakSanitizer tests are currently supported on x86-64 Linux and mips64 Linux only.
+if config.host_os not in ['Linux'] or config.host_arch not in ['x86_64', 'mips64']:
config.unsupported = True
config.suffixes = ['.c', '.cc', '.cpp']
diff --git a/test/msan/mmap_below_shadow.cc b/test/msan/mmap_below_shadow.cc
index 4b5890ba0fb8..0b982d58930d 100644
--- a/test/msan/mmap_below_shadow.cc
+++ b/test/msan/mmap_below_shadow.cc
@@ -15,8 +15,13 @@
int main(void) {
// Hint address just below shadow.
+#if defined(__x86_64__)
uintptr_t hint = 0x4f0000000000ULL;
const uintptr_t app_start = 0x600000000000ULL;
+#elif defined (__mips64)
+ uintptr_t hint = 0x4f00000000ULL;
+ const uintptr_t app_start = 0x6000000000ULL;
+#endif
uintptr_t p = (uintptr_t)mmap(
(void *)hint, 4096, PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | (FIXED ? MAP_FIXED : 0), -1, 0);
diff --git a/test/msan/strlen_of_shadow.cc b/test/msan/strlen_of_shadow.cc
index bb9fe17d41cd..8f1b4e1fc6bb 100644
--- a/test/msan/strlen_of_shadow.cc
+++ b/test/msan/strlen_of_shadow.cc
@@ -9,7 +9,11 @@
#include <string.h>
const char *mem_to_shadow(const char *p) {
+#if defined(__x86_64__)
return (char *)((uintptr_t)p & ~0x400000000000ULL);
+#elif defined (__mips64)
+ return (char *)((uintptr_t)p & ~0x4000000000ULL);
+#endif
}
int main(void) {
diff --git a/test/msan/vector_select.cc b/test/msan/vector_select.cc
index e8d55423293c..afeb1ad50c8b 100644
--- a/test/msan/vector_select.cc
+++ b/test/msan/vector_select.cc
@@ -4,10 +4,18 @@
// Regression test for MemorySanitizer instrumentation of a select instruction
// with vector arguments.
+#if defined(__x86_64__)
#include <emmintrin.h>
__m128d select(bool b, __m128d c, __m128d d)
{
return b ? c : d;
}
+#elif defined (__mips64)
+typedef double __w64d __attribute__ ((vector_size(16)));
+__w64d select(bool b, __w64d c, __w64d d)
+{
+ return b ? c : d;
+}
+#endif
diff --git a/test/sanitizer_common/TestCases/Linux/ptrace.cc b/test/sanitizer_common/TestCases/Linux/ptrace.cc
index 2bf0fd2f0f35..ba318169ee7d 100644
--- a/test/sanitizer_common/TestCases/Linux/ptrace.cc
+++ b/test/sanitizer_common/TestCases/Linux/ptrace.cc
@@ -8,6 +8,10 @@
#include <sys/user.h>
#include <sys/wait.h>
#include <unistd.h>
+#if __mips64
+ #include <asm/ptrace.h>
+ #include <sys/procfs.h>
+#endif
int main(void) {
pid_t pid;
@@ -33,19 +37,23 @@ int main(void) {
printf("%x\n", fpregs.mxcsr);
#endif // __x86_64__
-#if __powerpc64__
+#if (__powerpc64__ || __mips64)
struct pt_regs regs;
res = ptrace((enum __ptrace_request)PTRACE_GETREGS, pid, NULL, &regs);
assert(!res);
+#if (__powerpc64__)
if (regs.nip)
printf("%lx\n", regs.nip);
-
+#else
+ if (regs.cp0_epc)
+ printf("%lx\n", regs.cp0_epc);
+#endif
elf_fpregset_t fpregs;
res = ptrace((enum __ptrace_request)PTRACE_GETFPREGS, pid, NULL, &fpregs);
assert(!res);
if ((elf_greg_t)fpregs[32]) // fpscr
printf("%lx\n", (elf_greg_t)fpregs[32]);
-#endif // __powerpc64__
+#endif // (__powerpc64__ || __mips64)
siginfo_t siginfo;
res = ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo);
diff --git a/test/tsan/global_race.cc b/test/tsan/global_race.cc
index d70bee4a556b..3128ec411749 100644
--- a/test/tsan/global_race.cc
+++ b/test/tsan/global_race.cc
@@ -11,9 +11,9 @@ void *Thread(void *a) {
int main() {
barrier_init(&barrier, 2);
- // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not
- // match to the format used in the diagnotic message.
- fprintf(stderr, "addr=0x%012lx\n", (unsigned long) GlobalData);
+ fprintf(stderr, "addr=");
+ print_address(GlobalData);
+ fprintf(stderr, "\n");
pthread_t t;
pthread_create(&t, 0, Thread, 0);
GlobalData[2] = 43;
diff --git a/test/tsan/global_race2.cc b/test/tsan/global_race2.cc
index 6631008d85a5..4ab2842e7eef 100644
--- a/test/tsan/global_race2.cc
+++ b/test/tsan/global_race2.cc
@@ -11,9 +11,9 @@ void *Thread(void *a) {
int main() {
barrier_init(&barrier, 2);
- // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not
- // match to the format used in the diagnotic message.
- fprintf(stderr, "addr2=0x%012lx\n", (unsigned long) &x);
+ fprintf(stderr, "addr2=");
+ print_address(&x);
+ fprintf(stderr, "\n");
pthread_t t;
pthread_create(&t, 0, Thread, 0);
x = 0;
diff --git a/test/tsan/global_race3.cc b/test/tsan/global_race3.cc
index e7e9a7fef028..1531d7830f78 100644
--- a/test/tsan/global_race3.cc
+++ b/test/tsan/global_race3.cc
@@ -16,9 +16,9 @@ void *Thread(void *a) {
int main() {
barrier_init(&barrier, 2);
- // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not
- // match to the format used in the diagnotic message.
- fprintf(stderr, "addr3=0x%012lx\n", (unsigned long) XXX::YYY::ZZZ);
+ fprintf(stderr, "addr3=");
+ print_address(XXX::YYY::ZZZ);
+ fprintf(stderr, "\n");
pthread_t t;
pthread_create(&t, 0, Thread, 0);
XXX::YYY::ZZZ[0] = 0;
diff --git a/test/tsan/map32bit.cc b/test/tsan/map32bit.cc
index 9dae6880e7fe..d9a04655ddca 100644
--- a/test/tsan/map32bit.cc
+++ b/test/tsan/map32bit.cc
@@ -7,6 +7,9 @@
// Test for issue:
// https://code.google.com/p/thread-sanitizer/issues/detail?id=5
+// MAP_32BIT flag for mmap is supported only for x86_64.
+// XFAIL: mips64
+
void *Thread(void *ptr) {
*(int*)ptr = 42;
barrier_wait(&barrier);
diff --git a/test/tsan/mmap_large.cc b/test/tsan/mmap_large.cc
index e715ea666231..4ae4c0863501 100644
--- a/test/tsan/mmap_large.cc
+++ b/test/tsan/mmap_large.cc
@@ -5,7 +5,11 @@
#include <sys/mman.h>
int main() {
+#ifdef __x86_64__
const size_t kLog2Size = 39;
+#elif defined(__mips64)
+ const size_t kLog2Size = 32;
+#endif
const uintptr_t kLocation = 0x40ULL << kLog2Size;
void *p = mmap(
reinterpret_cast<void*>(kLocation),
diff --git a/test/tsan/signal_segv_handler.cc b/test/tsan/signal_segv_handler.cc
new file mode 100644
index 000000000000..2d806eef6764
--- /dev/null
+++ b/test/tsan/signal_segv_handler.cc
@@ -0,0 +1,39 @@
+// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="flush_memory_ms=1 memory_limit_mb=1" %run %t 2>&1 | FileCheck %s
+
+// JVM uses SEGV to preempt threads. All threads do a load from a known address
+// periodically. When runtime needs to preempt threads, it unmaps the page.
+// Threads start triggering SEGV one by one. The signal handler blocks
+// threads while runtime does its thing. Then runtime maps the page again
+// and resumes the threads.
+// Previously this pattern conflicted with stop-the-world machinery,
+// because it briefly reset SEGV handler to SIG_DFL.
+// As the consequence JVM just silently died.
+
+// This test sets memory flushing rate to maximum, then does series of
+// "benign" SEGVs that are handled by signal handler, and ensures that
+// the process survive.
+
+#include "test.h"
+#include <signal.h>
+#include <sys/mman.h>
+
+void *guard;
+
+void handler(int signo, siginfo_t *info, void *uctx) {
+ mprotect(guard, 4096, PROT_READ | PROT_WRITE);
+}
+
+int main() {
+ struct sigaction a;
+ a.sa_sigaction = handler;
+ a.sa_flags = SA_SIGINFO;
+ sigaction(SIGSEGV, &a, 0);
+ guard = mmap(0, 4096, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
+ for (int i = 0; i < 1000000; i++) {
+ mprotect(guard, 4096, PROT_NONE);
+ *(int*)guard = 1;
+ }
+ fprintf(stderr, "DONE\n");
+}
+
+// CHECK: DONE
diff --git a/test/tsan/test.h b/test/tsan/test.h
index 4496e56cda87..bb861b07745e 100644
--- a/test/tsan/test.h
+++ b/test/tsan/test.h
@@ -29,3 +29,12 @@ void barrier_init(pthread_barrier_t *barrier, unsigned count) {
// Default instance of the barrier, but a test can declare more manually.
pthread_barrier_t barrier;
+void print_address(void *address) {
+// On FreeBSD, the %p conversion specifier works as 0x%x and thus does not match
+// to the format used in the diagnotic message.
+#ifdef __x86_64__
+ fprintf(stderr, "0x%012lx", (unsigned long) address);
+#elif defined(__mips64)
+ fprintf(stderr, "0x%010lx", (unsigned long) address);
+#endif
+}
diff --git a/test/ubsan/TestCases/TypeCheck/Function/function.cpp b/test/ubsan/TestCases/TypeCheck/Function/function.cpp
index deca77d459c0..2609c6a8f666 100644
--- a/test/ubsan/TestCases/TypeCheck/Function/function.cpp
+++ b/test/ubsan/TestCases/TypeCheck/Function/function.cpp
@@ -12,13 +12,24 @@ void f() {}
void g(int x) {}
-int main(void) {
- // CHECK: runtime error: call to function f() through pointer to incorrect function type 'void (*)(int)'
+void make_valid_call() {
+ // CHECK-NOT: runtime error: call to function g
+ reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(g))(42);
+}
+
+void make_invalid_call() {
+ // CHECK: function.cpp:25:3: runtime error: call to function f() through pointer to incorrect function type 'void (*)(int)'
// CHECK-NEXT: function.cpp:11: note: f() defined here
- // NOSYM: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int)'
+ // NOSYM: function.cpp:25:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int)'
// NOSYM-NEXT: ({{.*}}+0x{{.*}}): note: (unknown) defined here
reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(f))(42);
+}
- // CHECK-NOT: runtime error: call to function g
- reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(g))(42);
+int main(void) {
+ make_valid_call();
+ make_invalid_call();
+ // Check that no more errors will be printed.
+ // CHECK-NOT: runtime error: call to function
+ // NOSYM-NOT: runtime error: call to function
+ make_invalid_call();
}
diff --git a/test/ubsan/TestCases/TypeCheck/null.cpp b/test/ubsan/TestCases/TypeCheck/null.cpp
index 2a90f7fb956b..190dc30e5dfe 100644
--- a/test/ubsan/TestCases/TypeCheck/null.cpp
+++ b/test/ubsan/TestCases/TypeCheck/null.cpp
@@ -18,21 +18,21 @@ int main(int, char **argv) {
switch (argv[1][0]) {
case 'l':
- // CHECK-LOAD: null.cpp:22:12: runtime error: load of null pointer of type 'int'
+ // CHECK-LOAD: null.cpp:[[@LINE+1]]:12: runtime error: load of null pointer of type 'int'
return *p;
case 's':
- // CHECK-STORE: null.cpp:25:5: runtime error: store to null pointer of type 'int'
+ // CHECK-STORE: null.cpp:[[@LINE+1]]:5: runtime error: store to null pointer of type 'int'
*p = 1;
break;
case 'r':
- // CHECK-REFERENCE: null.cpp:29:15: runtime error: reference binding to null pointer of type 'int'
+ // CHECK-REFERENCE: null.cpp:[[@LINE+1]]:15: runtime error: reference binding to null pointer of type 'int'
{int &r = *p;}
break;
case 'm':
- // CHECK-MEMBER: null.cpp:33:15: runtime error: member access within null pointer of type 'S'
+ // CHECK-MEMBER: null.cpp:[[@LINE+1]]:15: runtime error: member access within null pointer of type 'S'
return s->k;
case 'f':
- // CHECK-MEMFUN: null.cpp:36:12: runtime error: member call on null pointer of type 'S'
+ // CHECK-MEMFUN: null.cpp:[[@LINE+1]]:12: runtime error: member call on null pointer of type 'S'
return s->f();
}
}
diff --git a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp
index 033da24451c2..806e45c7d357 100644
--- a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp
+++ b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp
@@ -1,4 +1,4 @@
-// RUN: %clangxx -fsanitize=vptr -fno-sanitize-recover=vptr -g %s -O3 -o %t
+// RUN: %clangxx -frtti -fsanitize=vptr -fno-sanitize-recover=vptr -g %s -O3 -o %t
// RUN: not %run %t 2>&1 | FileCheck %s
// FIXME: This test produces linker errors on Darwin.
diff --git a/test/ubsan/TestCases/TypeCheck/vptr.cpp b/test/ubsan/TestCases/TypeCheck/vptr.cpp
index 98eac271c3a1..1f8ee02641a8 100644
--- a/test/ubsan/TestCases/TypeCheck/vptr.cpp
+++ b/test/ubsan/TestCases/TypeCheck/vptr.cpp
@@ -1,4 +1,4 @@
-// RUN: %clangxx -fsanitize=vptr -g %s -O3 -o %t
+// RUN: %clangxx -frtti -fsanitize=vptr -g %s -O3 -o %t
// RUN: %run %t rT && %run %t mT && %run %t fT && %run %t cT
// RUN: %run %t rU && %run %t mU && %run %t fU && %run %t cU
// RUN: %run %t rS && %run %t rV && %run %t oV
@@ -12,16 +12,16 @@
// RUN: %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --strict-whitespace
// RUN: (echo "vptr_check:S"; echo "vptr_check:T"; echo "vptr_check:U") > %t.supp
-// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t mS 2>&1
-// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t fS 2>&1
-// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t cS 2>&1
-// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t mV 2>&1
-// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t fV 2>&1
-// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t cV 2>&1
-// RUN: ASAN_OPTIONS="suppressions='%t.supp'" UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t oU 2>&1
+// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t mS 2>&1
+// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t fS 2>&1
+// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t cS 2>&1
+// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t mV 2>&1
+// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t fV 2>&1
+// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t cV 2>&1
+// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t oU 2>&1
// RUN: echo "vptr_check:S" > %t.loc-supp
-// RUN: ASAN_OPTIONS="suppressions='%t.loc-supp'" UBSAN_OPTIONS="suppressions='%t.loc-supp':halt_on_error=1" not %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS
+// RUN: UBSAN_OPTIONS="suppressions='%t.loc-supp':halt_on_error=1" not %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS
// FIXME: This test produces linker errors on Darwin.
// XFAIL: darwin