Import compiler-rt trunk r230183.
https://llvm.org/svn/llvm-project/compiler-rt/trunk@230183
This commit is contained in:
parent
476c4db3dc
commit
cd2dd3df15
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
// Set the ASan-specific SEH handler at the end of CRT initialization of each
|
||||
// module (see asan_win.cc for the details).
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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=%)
|
@ -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
|
||||
|
@ -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=%)
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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.")
|
||||
|
@ -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=%)
|
@ -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");
|
||||
|
@ -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__;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 <>
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
58
lib/sanitizer_common/sanitizer_interface_internal.h
Normal file
58
lib/sanitizer_common/sanitizer_interface_internal.h
Normal file
@ -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
|
@ -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 -------------
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
@ -223,6 +239,13 @@ uptr ThreadDescriptorSize() {
|
||||
atomic_store(&kThreadDescriptorSize, val, memory_order_relaxed);
|
||||
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) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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__))
|
||||
|
@ -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;
|
||||
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_);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 :=
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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=%)
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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.")
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
@ -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[] = {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,10 +332,13 @@ 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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 };
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 @@ class ScopedReport {
|
||||
~ScopedReport();
|
||||
};
|
||||
|
||||
bool MatchSuppression(const char *Str, SuppressionType Type);
|
||||
void InitializeSuppressions();
|
||||
bool IsVptrCheckSuppressed(const char *TypeName);
|
||||
|
||||
} // namespace __ubsan
|
||||
|
||||
|
@ -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.")
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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})
|
||||
|
@ -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
|
||||
|
51
test/asan/TestCases/Windows/globals_multiple_dlls.cc
Normal file
51
test/asan/TestCases/Windows/globals_multiple_dlls.cc
Normal file
@ -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
|
||||
}
|
12
test/asan/TestCases/Windows/oom.cc
Normal file
12
test/asan/TestCases/Windows/oom.cc
Normal file
@ -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
|
||||
}
|
22
test/asan/TestCases/Windows/symbols_path.cc
Normal file
22
test/asan/TestCases/Windows/symbols_path.cc
Normal file
@ -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);
|
||||
}
|
@ -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)();
|
||||
|
@ -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 =
|
||||
|
@ -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__)
|
||||
|
||||
|
23
test/cfi/CMakeLists.txt
Normal file
23
test/cfi/CMakeLists.txt
Normal file
@ -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")
|
93
test/cfi/anon-namespace.cpp
Normal file
93
test/cfi/anon-namespace.cpp
Normal file
@ -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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user