Import compiler-rt trunk r230183.

https://llvm.org/svn/llvm-project/compiler-rt/trunk@230183
This commit is contained in:
Dimitry Andric 2015-02-22 22:43:40 +00:00
parent 476c4db3dc
commit cd2dd3df15
126 changed files with 2225 additions and 1037 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.")

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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.
}

View File

@ -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.

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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.
}

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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) {

View File

@ -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=%)

View File

@ -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

View File

@ -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=%)

View File

@ -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();

View File

@ -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,

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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.")

View File

@ -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=%)

View File

@ -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");

View File

@ -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__;

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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);
}

View File

@ -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.

View File

@ -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

View File

@ -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 <>

View File

@ -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;

View File

@ -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(

View 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

View File

@ -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 -------------

View File

@ -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) {

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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__))

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 :=

View File

@ -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());
}

View File

@ -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

View File

@ -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

View File

@ -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=%)

View File

@ -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.

View File

@ -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);

View File

@ -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.")

View File

@ -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)) {

View File

@ -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;

View File

@ -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[] = {

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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 };

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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) {

View File

@ -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()

View File

@ -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)

View File

@ -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);
}

View File

@ -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

View File

@ -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.")

View File

@ -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

View File

@ -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();

View File

@ -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;
}

View File

@ -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

View File

@ -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)

View File

@ -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})

View File

@ -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

View 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
}

View 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
}

View 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);
}

View File

@ -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)();

View File

@ -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 =

View File

@ -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
View 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")

View 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