Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Tell cmake that Scudo is supported on Windows

Tell cmake that Scudo is supported on Linux

Tell MSVC that Scudo is supported

Need to check that this works on Windows

Change Windows allocator size to be 256GB

Enable scudo on Windows properly

Change literal type to avoid cl warning

Auto-add scudo and scudo_cxx when using -sanitize=scudo to help testing.

This is enough for the tests, though doesn't help as much with large programs like LLVM which drive the linker directly
Use same format for adding libraries as asan above

Avoid adding --gc-sections on Windows

Avoid overriding computeHardwareCRC32 on Windows.

This may cause lower performance on Windows, may need another way or
providing this on Windows.

Only enable builtins (actually required for scudo standalone) if compiler supports them

Tests
Rename getpagesize() to avoid naming conflict on Linux

Move setting up signal handler later as this was firing on the malloc

Remove stray change in SanitizerArgs.cpp

Port threads test from pthreads to c++11 threads for portability as new
test cxx_threads.cpp

Moved mutex.lock() up before starting other threads or they may get the
lock first.

Change to mark as unsupported on windows, not just win32

Convert some more tests to unsupported on windows instead of win32

Mark tests as unsupported

Test for minimal runtime unsupported on Windows

Unsupported on Windows as relies on LD_LIBRARY_PATH.
Or maybe could have parts of this running on Windows

Mark dealloc-race.c as unsupported on Windows as requried fork()

Clang format tests
  • Loading branch information
rgal committed Jan 7, 2021
1 parent ebcc8dc commit 083d151
Show file tree
Hide file tree
Showing 27 changed files with 282 additions and 49 deletions.
8 changes: 8 additions & 0 deletions clang/lib/Driver/ToolChains/MSVC.cpp
Expand Up @@ -446,6 +446,13 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
}
}

if (TC.getSanitizerArgs().needsScudoRt()) {
for (const auto &Lib : {"scudo", "scudo_cxx"}) {
CmdArgs.push_back(TC.getCompilerRTArgString(Args, Lib));
}
CmdArgs.push_back(Args.MakeArgString("-include:malloc"));
}

Args.AddAllArgValues(CmdArgs, options::OPT__SLASH_link);

// Control Flow Guard checks
Expand Down Expand Up @@ -1403,6 +1410,7 @@ SanitizerMask MSVCToolChain::getSupportedSanitizers() const {
Res |= SanitizerKind::PointerSubtract;
Res |= SanitizerKind::Fuzzer;
Res |= SanitizerKind::FuzzerNoLink;
Res |= SanitizerKind::Scudo;
Res &= ~SanitizerKind::CFIMFCall;
return Res;
}
Expand Down
3 changes: 2 additions & 1 deletion compiler-rt/cmake/config-ix.cmake
Expand Up @@ -58,6 +58,7 @@ check_c_compiler_flag(-ffreestanding COMPILER_RT_HAS_FFREESTANDING_FLAG)
check_c_compiler_flag(-std=c11 COMPILER_RT_HAS_STD_C11_FLAG)
check_cxx_compiler_flag(-fPIC COMPILER_RT_HAS_FPIC_FLAG)
check_cxx_compiler_flag(-fPIE COMPILER_RT_HAS_FPIE_FLAG)
check_cxx_compiler_flag(-fbuiltin COMPILER_RT_HAS_FBUILTIN_FLAG)
check_cxx_compiler_flag(-fno-builtin COMPILER_RT_HAS_FNO_BUILTIN_FLAG)
check_cxx_compiler_flag(-fno-exceptions COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG)
check_cxx_compiler_flag(-fomit-frame-pointer COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG)
Expand Down Expand Up @@ -764,7 +765,7 @@ else()
endif()

if (COMPILER_RT_HAS_SANITIZER_COMMON AND SCUDO_SUPPORTED_ARCH AND
OS_NAME MATCHES "Linux|Android|Fuchsia")
OS_NAME MATCHES "Linux|Android|Fuchsia|Windows")
set(COMPILER_RT_HAS_SCUDO TRUE)
else()
set(COMPILER_RT_HAS_SCUDO FALSE)
Expand Down
13 changes: 11 additions & 2 deletions compiler-rt/lib/sanitizer_common/sanitizer_win.cpp
Expand Up @@ -21,6 +21,13 @@
#include <psapi.h>
#include <stdlib.h>

// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the
// "Community Additions" comment on MSDN here:
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx
#define SystemFunction036 NTAPI SystemFunction036
#include <NTSecAPI.h>
#undef SystemFunction036

#include "sanitizer_common.h"
#include "sanitizer_file.h"
#include "sanitizer_libc.h"
Expand Down Expand Up @@ -1106,9 +1113,11 @@ void CheckNoDeepBind(const char *filename, int flag) {
// Do nothing.
}

// FIXME: implement on this platform.
#pragma comment(lib, "advapi32.lib")
bool GetRandom(void *buffer, uptr length, bool blocking) {
UNIMPLEMENTED();
if (!buffer || !length || length > 256)
return false;
return RtlGenRandom(buffer, length) != FALSE;
}

u32 GetNumberOfCPUs() {
Expand Down
8 changes: 6 additions & 2 deletions compiler-rt/lib/scudo/CMakeLists.txt
Expand Up @@ -4,7 +4,9 @@ include_directories(..)

set(SCUDO_CFLAGS ${SANITIZER_COMMON_CFLAGS})
# SANITIZER_COMMON_CFLAGS include -fno-builtin, but we actually want builtins!
list(APPEND SCUDO_CFLAGS -fbuiltin)
if (COMPILER_RT_HAS_FBUILTIN_FLAG)
list(APPEND SCUDO_CFLAGS -fbuiltin)
endif()
append_rtti_flag(OFF SCUDO_CFLAGS)

set(SCUDO_MINIMAL_DYNAMIC_LIBS ${SANITIZER_COMMON_LINK_LIBS})
Expand All @@ -17,7 +19,9 @@ append_list_if(COMPILER_RT_HAS_OMIT_FRAME_POINTER_FLAG -fno-omit-frame-pointer

set(SCUDO_DYNAMIC_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS})
# Use gc-sections by default to avoid unused code being pulled in.
list(APPEND SCUDO_DYNAMIC_LINK_FLAGS -Wl,--gc-sections)
if (!WIN32)
list(APPEND SCUDO_DYNAMIC_LINK_FLAGS -Wl,--gc-sections)
endif()

if(ANDROID)
# Put most Sanitizer shared libraries in the global group. For more details, see
Expand Down
8 changes: 7 additions & 1 deletion compiler-rt/lib/scudo/scudo_allocator.cpp
Expand Up @@ -44,6 +44,12 @@ static u32 Cookie;
// at compilation or at runtime.
static atomic_uint8_t HashAlgorithm = { CRC32Software };

#if !SANITIZER_SUPPORTS_WEAK_HOOKS
SANITIZER_WEAK_ATTRIBUTE u32 computeHardwareCRC32(u32 Crc, uptr Data) {
return computeSoftwareCRC32(Crc, Data);
}
#endif

inline u32 computeCRC32(u32 Crc, uptr Value, uptr *Array, uptr ArraySize) {
// If the hardware CRC32 feature is defined here, it was enabled everywhere,
// as opposed to only for scudo_crc32.cpp. This means that other hardware
Expand Down Expand Up @@ -609,7 +615,7 @@ NOINLINE void Allocator::performSanityChecks() {
// last size class minus the header size, in multiples of MinAlignment.
UnpackedHeader Header = {};
const uptr MaxPrimaryAlignment =
1 << MostSignificantSetBitIndex(SizeClassMap::kMaxSize - MinAlignment);
(uptr)1U << MostSignificantSetBitIndex(SizeClassMap::kMaxSize - MinAlignment);
const uptr MaxOffset =
(MaxPrimaryAlignment - Chunk::getHeaderSize()) >> MinAlignmentLog;
Header.Offset = MaxOffset;
Expand Down
3 changes: 3 additions & 0 deletions compiler-rt/lib/scudo/scudo_crc32.cpp
Expand Up @@ -15,10 +15,13 @@

namespace __scudo {

// Can't override this with weak symbols on Windows
#if !defined(_WIN32)
#if defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32)
u32 computeHardwareCRC32(u32 Crc, uptr Data) {
return CRC32_INTRINSIC(Crc, Data);
}
#endif // defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32)
#endif // defined(_WIN32)

} // namespace __scudo
25 changes: 25 additions & 0 deletions compiler-rt/lib/scudo/scudo_new_delete.cpp
Expand Up @@ -19,7 +19,32 @@

using namespace __scudo;

// C++ operators can't have dllexport attributes on Windows. We export them
// anyway by passing extra -export flags to the linker, which is exactly that
// dllexport would normally do. We need to export them in order to make the
// VS2015 dynamic CRT (MD) work.
#if SANITIZER_WINDOWS
#define CXX_OPERATOR_ATTRIBUTE
#define COMMENT_EXPORT(sym) __pragma(comment(linker, "/export:" sym))
#ifdef _WIN64
COMMENT_EXPORT("??2@YAPEAX_K@Z") // operator new
COMMENT_EXPORT("??2@YAPEAX_KAEBUnothrow_t@std@@@Z") // operator new nothrow
COMMENT_EXPORT("??3@YAXPEAX@Z") // operator delete
COMMENT_EXPORT("??3@YAXPEAX_K@Z") // sized operator delete
COMMENT_EXPORT("??_U@YAPEAX_K@Z") // operator new[]
COMMENT_EXPORT("??_V@YAXPEAX@Z") // operator delete[]
#else
COMMENT_EXPORT("??2@YAPAXI@Z") // operator new
COMMENT_EXPORT("??2@YAPAXIABUnothrow_t@std@@@Z") // operator new nothrow
COMMENT_EXPORT("??3@YAXPAX@Z") // operator delete
COMMENT_EXPORT("??3@YAXPAXI@Z") // sized operator delete
COMMENT_EXPORT("??_U@YAPAXI@Z") // operator new[]
COMMENT_EXPORT("??_V@YAXPAX@Z") // operator delete[]
#endif
#undef COMMENT_EXPORT
#else
#define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE
#endif

// Fake std::nothrow_t to avoid including <new>.
namespace std {
Expand Down
21 changes: 10 additions & 11 deletions compiler-rt/lib/scudo/scudo_platform.h
Expand Up @@ -16,29 +16,26 @@

#include "sanitizer_common/sanitizer_allocator.h"

#if !SANITIZER_LINUX && !SANITIZER_FUCHSIA
#if !SANITIZER_LINUX && !SANITIZER_FUCHSIA && !SANITIZER_WINDOWS
# error "The Scudo hardened allocator is not supported on this platform."
#endif

#define SCUDO_TSD_EXCLUSIVE_SUPPORTED (!SANITIZER_ANDROID && !SANITIZER_FUCHSIA)
#define SCUDO_TSD_EXCLUSIVE_SUPPORTED \
(!SANITIZER_ANDROID && !SANITIZER_FUCHSIA && !SANITIZER_WINDOWS)

#ifndef SCUDO_TSD_EXCLUSIVE
// SCUDO_TSD_EXCLUSIVE wasn't defined, use a default TSD model for the platform.
# if SANITIZER_ANDROID || SANITIZER_FUCHSIA
// Android and Fuchsia use a pool of TSDs shared between threads.
# define SCUDO_TSD_EXCLUSIVE 0
# elif SANITIZER_LINUX && !SANITIZER_ANDROID
// Non-Android Linux use an exclusive TSD per thread.
#if SCUDO_TSD_EXCLUSIVE_SUPPORTED
# define SCUDO_TSD_EXCLUSIVE 1
# else
# error "No default TSD model defined for this platform."
# endif // SANITIZER_ANDROID || SANITIZER_FUCHSIA
#endif // SCUDO_TSD_EXCLUSIVE

#define SCUDO_TSD_EXCLUSIVE 0
#endif // SCUDO_TSD_EXCLUSIVE_SUPPORTED
#else
// If the exclusive TSD model is chosen, make sure the platform supports it.
#if SCUDO_TSD_EXCLUSIVE && !SCUDO_TSD_EXCLUSIVE_SUPPORTED
# error "The exclusive TSD model is not supported on this platform."
#endif
#endif // SCUDO_TSD_EXCLUSIVE

// Maximum number of TSDs that can be created for the Shared model.
#ifndef SCUDO_SHARED_TSD_POOL_SIZE
Expand Down Expand Up @@ -71,6 +68,8 @@ namespace __scudo {
const uptr AllocatorSize = 0x4000000000ULL; // 256G.
# elif defined(__aarch64__)
const uptr AllocatorSize = 0x10000000000ULL; // 1T.
#elif SANITIZER_WINDOWS
const uptr AllocatorSize = 0x4000000000ULL; // 256G.
# else
const uptr AllocatorSize = 0x40000000000ULL; // 4T.
# endif
Expand Down
4 changes: 4 additions & 0 deletions compiler-rt/lib/scudo/scudo_tsd.h
Expand Up @@ -18,7 +18,11 @@
#include "scudo_allocator.h"
#include "scudo_utils.h"

#if !SANITIZER_WINDOWS
#include <pthread.h>
#else
#include <windows.h>
#endif // SANITIZER_WINDOWS

namespace __scudo {

Expand Down
26 changes: 25 additions & 1 deletion compiler-rt/lib/scudo/scudo_tsd_shared.cpp
Expand Up @@ -16,8 +16,13 @@

namespace __scudo {

#if !SANITIZER_WINDOWS
static pthread_once_t GlobalInitialized = PTHREAD_ONCE_INIT;
pthread_key_t PThreadKey;
#else
DWORD TlsIndex = TLS_OUT_OF_INDEXES;
static INIT_ONCE InitOnce = INIT_ONCE_STATIC_INIT;
#endif

static atomic_uint32_t CurrentIndex;
static ScudoTSD *TSDs;
Expand All @@ -31,7 +36,12 @@ THREADLOCAL ScudoTSD *CurrentTSD;
#endif

static void initOnce() {
#if SANITIZER_WINDOWS
TlsIndex = TlsAlloc();
CHECK_NE(TlsIndex, TLS_OUT_OF_INDEXES);
#elif !SANITIZER_ANDROID
CHECK_EQ(pthread_key_create(&PThreadKey, NULL), 0);
#endif
initScudo();
NumberOfTSDs = Min(Max(1U, GetNumberOfCPUsCached()),
static_cast<u32>(SCUDO_SHARED_TSD_POOL_SIZE));
Expand All @@ -48,7 +58,9 @@ static void initOnce() {
}

ALWAYS_INLINE void setCurrentTSD(ScudoTSD *TSD) {
#if SANITIZER_ANDROID
#if SANITIZER_WINDOWS
CHECK(TlsSetValue(TlsIndex, reinterpret_cast<LPVOID>(TSD)));
#elif SANITIZER_ANDROID
*get_android_tls_ptr() = reinterpret_cast<uptr>(TSD);
#elif SANITIZER_LINUX
CurrentTSD = TSD;
Expand All @@ -57,8 +69,20 @@ ALWAYS_INLINE void setCurrentTSD(ScudoTSD *TSD) {
#endif // SANITIZER_ANDROID
}

#if SANITIZER_WINDOWS
static BOOL CALLBACK handleInit(PINIT_ONCE InitOnce, PVOID Parameter,
PVOID *Context) {
initOnce();
return TRUE;
}
#endif

void initThread(bool MinimalInit) {
#if SANITIZER_WINDOWS
CHECK(InitOnceExecuteOnce(&InitOnce, handleInit, nullptr, nullptr));
#else
pthread_once(&GlobalInitialized, initOnce);
#endif
// Initial context assignment is done in a plain round-robin fashion.
u32 Index = atomic_fetch_add(&CurrentIndex, 1, memory_order_relaxed);
setCurrentTSD(&TSDs[Index % NumberOfTSDs]);
Expand Down
8 changes: 7 additions & 1 deletion compiler-rt/lib/scudo/scudo_tsd_shared.inc
Expand Up @@ -16,15 +16,21 @@

#if !SCUDO_TSD_EXCLUSIVE

#if SANITIZER_WINDOWS
extern DWORD TlsIndex;
#elif !SANITIZER_ANDROID
extern pthread_key_t PThreadKey;
#endif

#if SANITIZER_LINUX && !SANITIZER_ANDROID
__attribute__((tls_model("initial-exec")))
extern THREADLOCAL ScudoTSD *CurrentTSD;
#endif

ALWAYS_INLINE ScudoTSD* getCurrentTSD() {
#if SANITIZER_ANDROID
#if SANITIZER_WINDOWS
return reinterpret_cast<ScudoTSD *>(TlsGetValue(TlsIndex));
#elif SANITIZER_ANDROID
return reinterpret_cast<ScudoTSD *>(*get_android_tls_ptr());
#elif SANITIZER_LINUX
return CurrentTSD;
Expand Down
73 changes: 73 additions & 0 deletions compiler-rt/test/scudo/cxx_threads.cpp
@@ -0,0 +1,73 @@
// RUN: %clangxx_scudo %s -o %t
// RUN: %env_scudo_opts="QuarantineSizeKb=0:ThreadLocalQuarantineSizeKb=0" %run %t 5 1000000 2>&1
// RUN: %env_scudo_opts="QuarantineSizeKb=1024:ThreadLocalQuarantineSizeKb=64" %run %t 5 1000000 2>&1

// Tests parallel allocations and deallocations of memory chunks from a number
// of concurrent threads, with and without quarantine.
// This test passes if everything executes properly without crashing.

#include <assert.h>
#include <condition_variable>
#include <stdio.h>
#include <stdlib.h>
#include <thread>

#include <sanitizer/allocator_interface.h>

int num_threads;
int total_num_alloc;
const int kMaxNumThreads = 500;
std::thread thread[kMaxNumThreads];

std::condition_variable cond;
std::mutex mutex;
char go = 0;

void *thread_fun(void *arg) {
mutex.lock();
while (!go) {
std::unique_lock<std::mutex> lk(mutex);
cond.wait(lk);
}

mutex.unlock();
for (int i = 0; i < total_num_alloc / num_threads; i++) {
void *p = malloc(10);
__asm__ __volatile__(""
:
: "r"(p)
: "memory");
free(p);
}
return 0;
}

int main(int argc, char **argv) {
assert(argc == 3);
num_threads = atoi(argv[1]);
assert(num_threads > 0);
assert(num_threads <= kMaxNumThreads);
total_num_alloc = atoi(argv[2]);
assert(total_num_alloc > 0);

printf("%d threads, %d allocations in each\n", num_threads,
total_num_alloc / num_threads);
fprintf(stderr, "Heap size before: %zd\n", __sanitizer_get_heap_size());
fprintf(stderr, "Allocated bytes before: %zd\n",
__sanitizer_get_current_allocated_bytes());

mutex.lock();
for (int i = 0; i < num_threads; i++)
thread[i] = std::thread(thread_fun, (void *)0);
go = 1;
cond.notify_all();
mutex.unlock();
for (int i = 0; i < num_threads; i++)
thread[i].join();

fprintf(stderr, "Heap size after: %zd\n", __sanitizer_get_heap_size());
fprintf(stderr, "Allocated bytes after: %zd\n",
__sanitizer_get_current_allocated_bytes());

return 0;
}
2 changes: 2 additions & 0 deletions compiler-rt/test/scudo/dealloc-race.c
@@ -1,3 +1,5 @@
// UNSUPPORTED: windows

// RUN: %clang_scudo %s -O2 -o %t
// RUN: %env_scudo_opts="QuarantineChunksUpToSize=0" %run %t 2>&1

Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/test/scudo/fsanitize.c
@@ -1,5 +1,7 @@
// Test various -fsanitize= additional flags combinations.

// UNSUPPORTED: windows

// RUN: %clang_scudo %s -o %t
// RUN: not %run %t 2>&1 | FileCheck %s

Expand Down

0 comments on commit 083d151

Please sign in to comment.