From 00ac45086dd1f773431990f88336e44795c1fbe9 Mon Sep 17 00:00:00 2001 From: Steve Harter Date: Wed, 16 Dec 2015 14:27:26 -0600 Subject: [PATCH] Reduce clr startup noise when using Clang sanitizers --- CMakeLists.txt | 2 +- enablesanitizers.sh | 26 ++++++++++++++++++-------- sanitizerblacklist.txt | 14 ++++++++++++++ src/gc/handletable.cpp | 8 ++++---- src/gc/handletablecore.cpp | 10 ++++++---- src/inc/clrhost.h | 16 +++++++++++----- src/vm/vars.hpp | 5 +++-- 7 files changed, 57 insertions(+), 24 deletions(-) create mode 100644 sanitizerblacklist.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index c7b53ef911..64a6eb912f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -321,7 +321,7 @@ if (CLR_CMAKE_PLATFORM_UNIX) string(FIND "$ENV{DEBUG_SANITIZERS}" "asan" __ASAN_POS) string(FIND "$ENV{DEBUG_SANITIZERS}" "ubsan" __UBSAN_POS) if ((${__ASAN_POS} GREATER -1) OR (${__UBSAN_POS} GREATER -1)) - set(CLR_SANITIZE_CXX_FLAGS "${CLR_SANITIZE_CXX_FLAGS} -fsanitize=") + set(CLR_SANITIZE_CXX_FLAGS "${CLR_SANITIZE_CXX_FLAGS} -fsanitize-blacklist=${CMAKE_CURRENT_SOURCE_DIR}/sanitizerblacklist.txt -fsanitize=") set(CLR_SANITIZE_LINK_FLAGS "${CLR_SANITIZE_LINK_FLAGS} -fsanitize=") if (${__ASAN_POS} GREATER -1) set(CLR_SANITIZE_CXX_FLAGS "${CLR_SANITIZE_CXX_FLAGS}address,") diff --git a/enablesanitizers.sh b/enablesanitizers.sh index 0afec5d1ee..70555aa70e 100755 --- a/enablesanitizers.sh +++ b/enablesanitizers.sh @@ -10,15 +10,17 @@ if [ $# -eq 0 ]; then echo " cd $(dirname $0);. enablesanitizers.sh [options]; cd -" fi echo "Usage: [asan] [ubsan] [lsan] [all] [off] [clangx.y]" - echo "asan: optional argument to enable Address Sanitizer." - echo "ubsan: optional argument to enable Undefined Behavior Sanitizer." - echo "lsan - optional argument to enable memory Leak Sanitizer." - echo "all - optional argument to enable asan, ubsan and lsan." - echo "off - optional argument to turn off all sanitizers." - echo "clangx.y - optional argument to specify clang version x.y. which is used to resolve stack traces." + echo " asan: optional argument to enable Address Sanitizer." + echo " ubsan: optional argument to enable Undefined Behavior Sanitizer." + echo " lsan - optional argument to enable memory Leak Sanitizer." + echo " all - optional argument to enable asan, ubsan and lsan." + echo " off - optional argument to turn off all sanitizers." + echo " clangx.y - optional argument to specify clang version x.y. which is used to resolve stack traces. Default is 3.6" else + # default to clang 3.6 instead of 3.5 because it supports print_stacktrace (otherwise only one stack frame) __ClangMajorVersion=3 - __ClangMinorVersion=5 + __ClangMinorVersion=6 + __EnableASan=0 __EnableUBSan=0 __EnableLSan=0 @@ -69,7 +71,10 @@ else unset DEBUG_SANITIZERS echo "Setting DEBUG_SANITIZERS=" else - ASAN_OPTIONS="symbolize=1" + # for now, specify alloc_dealloc_mismatch=0 as there are too many error reports that are not an issue + ASAN_OPTIONS="symbolize=1 alloc_dealloc_mismatch=0" + # when Clang 3.8 available, add: suppressions=$(readlink -f sanitizersuppressions.txt) + UBSAN_OPTIONS="print_stacktrace=1" if [ $__EnableASan == 1 ]; then __Options="$__Options asan" @@ -91,10 +96,15 @@ else export ASAN_OPTIONS echo "Setting ASAN_OPTIONS=$ASAN_OPTIONS" + UBSAN_OPTIONS="\"$UBSAN_OPTIONS\"" + export UBSAN_OPTIONS + echo "Setting UBSAN_OPTIONS=$UBSAN_OPTIONS" + # used by ASan at run-time ASAN_SYMBOLIZER_PATH="/usr/bin/llvm-symbolizer-$__ClangMajorVersion.$__ClangMinorVersion" export ASAN_SYMBOLIZER_PATH echo "Setting ASAN_SYMBOLIZER_PATH=$ASAN_SYMBOLIZER_PATH" + echo "Done. You can now run: build.sh Debug clang$__ClangMajorVersion.$__ClangMinorVersion" fi unset __ClangMajorVersion diff --git a/sanitizerblacklist.txt b/sanitizerblacklist.txt new file mode 100644 index 0000000000..ec3313973b --- /dev/null +++ b/sanitizerblacklist.txt @@ -0,0 +1,14 @@ +# This file has exclusions to the Clang address sanitizer to suppress error reports +# When Clang 3.8 is available, convert these to suppression list instead as that is preferred for internal code + +# CMiniMdBase::UsesAllocatedMemory - suppress stack-buffer-underflow (code backs up pointer by -1 to check allocation ownership) +fun:_ZN11CMiniMdBase19UsesAllocatedMemoryEP11CMiniColDef + +# JIT_InitPInvokeFrame - suppress unknown sanitizer issue causing SEGV on unknown address 0x000000000000 +# 0 0x4e8a0c in __ubsan::checkDynamicType(void*, void*, unsigned long) +# 1 0x4e807f in HandleDynamicTypeCacheMiss(__ubsan::DynamicTypeCacheMissData*, unsigned long, unsigned long, __ubsan::ReportOptions) +# 2 0x4e8051 in __ubsan_handle_dynamic_type_cache_miss +# 3 0x7f02ce676cd8 in JIT_InitPInvokeFrame(InlinedCallFrame*, void*) /home/steveharter/git/dotnet_coreclr/src/vm/jithelpers.cpp:6491:9 +# 4 0x7f0252bbceb2 () +fun:_Z20JIT_InitPInvokeFrameP16InlinedCallFramePv + diff --git a/src/gc/handletable.cpp b/src/gc/handletable.cpp index 7f855bba29..7ded783211 100644 --- a/src/gc/handletable.cpp +++ b/src/gc/handletable.cpp @@ -391,11 +391,11 @@ void ValidateFetchObjrefForHandle(OBJECTREF objref, ADIndex appDomainIndex) _ASSERTE(!pDomain->NoAccessToHandleTable()); #if CHECK_APP_DOMAIN_LEAKS - if (g_pConfig->AppDomainLeaks()) + if (g_pConfig->AppDomainLeaks() && objref != NULL) { if (appDomainIndex.m_dwIndex) objref->TryAssignAppDomain(pDomain); - else if (objref != 0) + else objref->TrySetAppDomainAgile(); } #endif @@ -421,11 +421,11 @@ void ValidateAssignObjrefForHandle(OBJECTREF objref, ADIndex appDomainIndex) _ASSERTE(!pDomain->NoAccessToHandleTable()); #if CHECK_APP_DOMAIN_LEAKS - if (g_pConfig->AppDomainLeaks()) + if (g_pConfig->AppDomainLeaks() && objref != NULL) { if (appDomainIndex.m_dwIndex) objref->TryAssignAppDomain(pDomain); - else if (objref != 0) + else objref->TrySetAppDomainAgile(); } #endif diff --git a/src/gc/handletablecore.cpp b/src/gc/handletablecore.cpp index d302087ec9..10c66497d0 100644 --- a/src/gc/handletablecore.cpp +++ b/src/gc/handletablecore.cpp @@ -1984,15 +1984,17 @@ uint32_t BlockAllocHandlesInitial(TableSegment *pSegment, uint32_t uType, uint32 uint32_t uAlloc = uRemain; // compute the default mask based on that count - uint32_t dwNewMask = (MASK_EMPTY << uAlloc); - + uint32_t dwNewMask; // are we allocating all of them? if (uAlloc >= HANDLE_HANDLES_PER_MASK) { - // shift above has unpredictable results in this case - dwNewMask = MASK_FULL; + dwNewMask = MASK_FULL; // avoid unpredictable shift uAlloc = HANDLE_HANDLES_PER_MASK; } + else + { + dwNewMask = (MASK_EMPTY << uAlloc); + } // set the free mask *pdwMask = dwNewMask; diff --git a/src/inc/clrhost.h b/src/inc/clrhost.h index 8268c9a69b..3d1a8fae04 100644 --- a/src/inc/clrhost.h +++ b/src/inc/clrhost.h @@ -85,6 +85,8 @@ void ClrFlsAssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback); // Function pointer for fast TLS fetch - do not use directly typedef LPVOID (*POPTIMIZEDTLSGETTER)(); +typedef LPVOID* (*CLRFLSGETBLOCK)(); + extern POPTIMIZEDTLSGETTER __ClrFlsGetBlock; #ifndef CLR_STANDALONE_BINDER @@ -98,8 +100,9 @@ inline void ClrFlsIncrementValue(DWORD slot, int increment) STATIC_CONTRACT_SO_TOLERANT; _ASSERTE(increment != 0); - - void **block = (void **) (*__ClrFlsGetBlock)(); + + CLRFLSGETBLOCK clrFlsGetBlockFn = (CLRFLSGETBLOCK)__ClrFlsGetBlock; + void **block = (*clrFlsGetBlockFn)(); size_t value; if (block != NULL) @@ -134,7 +137,8 @@ inline void * ClrFlsGetValue (DWORD slot) STATIC_CONTRACT_CANNOT_TAKE_LOCK; STATIC_CONTRACT_SO_TOLERANT; - void **block = (void **) (*__ClrFlsGetBlock)(); + CLRFLSGETBLOCK clrFlsGetBlockFn = (CLRFLSGETBLOCK)__ClrFlsGetBlock; + void **block = (*clrFlsGetBlockFn)(); if (block != NULL) { return block[slot]; @@ -159,7 +163,8 @@ inline BOOL ClrFlsCheckValue(DWORD slot, void ** pValue) #ifdef _DEBUG *pValue = ULongToPtr(0xcccccccc); #endif //_DEBUG - void **block = (void **) (*__ClrFlsGetBlock)(); + CLRFLSGETBLOCK clrFlsGetBlockFn = (CLRFLSGETBLOCK)__ClrFlsGetBlock; + void **block = (*clrFlsGetBlockFn)(); if (block != NULL) { *pValue = block[slot]; @@ -181,7 +186,8 @@ inline void ClrFlsSetValue(DWORD slot, void *pData) STATIC_CONTRACT_CANNOT_TAKE_LOCK; STATIC_CONTRACT_SO_TOLERANT; - void **block = (void **) (*__ClrFlsGetBlock)(); + CLRFLSGETBLOCK clrFlsGetBlockFn = (CLRFLSGETBLOCK)__ClrFlsGetBlock; + void **block = (*clrFlsGetBlockFn)(); if (block != NULL) { block[slot] = pData; diff --git a/src/vm/vars.hpp b/src/vm/vars.hpp index cfb4a93233..5286930f4c 100644 --- a/src/vm/vars.hpp +++ b/src/vm/vars.hpp @@ -341,8 +341,9 @@ class REF : public OBJECTREF }; -#define VALIDATEOBJECTREF(objref) ((objref).Validate()) -#define VALIDATEOBJECT(obj) obj->Validate() +// the while (0) syntax below is to force a trailing semicolon on users of the macro +#define VALIDATEOBJECTREF(objref) do {if ((objref) != NULL) (objref).Validate();} while (0) +#define VALIDATEOBJECT(obj) do {if ((obj) != NULL) (obj)->Validate();} while (0) #define ObjectToOBJECTREF(obj) (OBJECTREF(obj)) #define OBJECTREFToObject(objref) ((objref).operator-> ()) -- 2.34.1