14 changes: 10 additions & 4 deletions compiler-rt/lib/scudo/standalone/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
message(INFO "*** In scudo/standalone/CMakeLists.txt ***")
add_compiler_rt_component(scudo_standalone)
if (COMPILER_RT_HAS_GWP_ASAN)
message(INFO "*** Have GWP ASAN ***")
add_dependencies(scudo_standalone gwp_asan)
endif()

include_directories(../.. include)

set(SCUDO_CFLAGS)

list(APPEND SCUDO_CFLAGS
-Werror=conversion
-Wall
-nostdinc++)
#list(APPEND SCUDO_CFLAGS
# -Werror=conversion
# -Wall
# -nostdinc++)

# Remove -stdlib= which is unused when passing -nostdinc++.
string(REGEX REPLACE "-stdlib=[a-zA-Z+]*" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
Expand Down Expand Up @@ -80,6 +82,8 @@ set(SCUDO_HEADERS
vector.h
wrappers_c_checks.h
wrappers_c.h
win.h
win_defs.h

include/scudo/interface.h
)
Expand All @@ -95,6 +99,7 @@ set(SCUDO_SOURCES
release.cpp
report.cpp
string_utils.cpp
win.cpp
)

# Enable the SSE 4.2 instruction set for crc32_hw.cpp, if available.
Expand Down Expand Up @@ -125,6 +130,7 @@ if (COMPILER_RT_HAS_GWP_ASAN)
endif()

if(COMPILER_RT_HAS_SCUDO_STANDALONE)
message(INFO "*** COMPILER_RT_HAS_SCUDO_STANDALONE ***")
add_compiler_rt_object_libraries(RTScudoStandalone
ARCHS ${SCUDO_STANDALONE_SUPPORTED_ARCH}
SOURCES ${SCUDO_SOURCES}
Expand Down
254 changes: 200 additions & 54 deletions compiler-rt/lib/scudo/standalone/atomic_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,46 @@

#include "internal_defs.h"

extern "C" void _ReadWriteBarrier();
#pragma intrinsic(_ReadWriteBarrier)
extern "C" void _mm_mfence();
#pragma intrinsic(_mm_mfence)
extern "C" void _mm_pause();
#pragma intrinsic(_mm_pause)
extern "C" char _InterlockedExchange8(char volatile *Addend, char Value);
#pragma intrinsic(_InterlockedExchange8)
extern "C" short _InterlockedExchange16(short volatile *Addend, short Value);
#pragma intrinsic(_InterlockedExchange16)
extern "C" long _InterlockedExchange(long volatile *Addend, long Value);
#pragma intrinsic(_InterlockedExchange)
extern "C" long _InterlockedExchangeAdd(long volatile *Addend, long Value);
#pragma intrinsic(_InterlockedExchangeAdd)
extern "C" char _InterlockedCompareExchange8(char volatile *Destination,
char Exchange, char Comparand);
#pragma intrinsic(_InterlockedCompareExchange8)
extern "C" short _InterlockedCompareExchange16(short volatile *Destination,
short Exchange, short Comparand);
#pragma intrinsic(_InterlockedCompareExchange16)
extern "C" long long
_InterlockedCompareExchange64(long long volatile *Destination,
long long Exchange, long long Comparand);
#pragma intrinsic(_InterlockedCompareExchange64)
extern "C" void *_InterlockedCompareExchangePointer(void *volatile *Destination,
void *Exchange,
void *Comparand);
#pragma intrinsic(_InterlockedCompareExchangePointer)
extern "C" long __cdecl _InterlockedCompareExchange(long volatile *Destination,
long Exchange,
long Comparand);
#pragma intrinsic(_InterlockedCompareExchange)

#ifdef _WIN64
extern "C" long long _InterlockedExchangeAdd64(long long volatile *Addend,
long long Value);
#pragma intrinsic(_InterlockedExchangeAdd64)
#endif


namespace scudo {

enum memory_order {
Expand All @@ -21,12 +61,12 @@ enum memory_order {
memory_order_acq_rel = 4,
memory_order_seq_cst = 5
};
static_assert(memory_order_relaxed == __ATOMIC_RELAXED, "");
static_assert(memory_order_consume == __ATOMIC_CONSUME, "");
static_assert(memory_order_acquire == __ATOMIC_ACQUIRE, "");
static_assert(memory_order_release == __ATOMIC_RELEASE, "");
static_assert(memory_order_acq_rel == __ATOMIC_ACQ_REL, "");
static_assert(memory_order_seq_cst == __ATOMIC_SEQ_CST, "");
//static_assert(memory_order_relaxed == __ATOMIC_RELAXED, "");
//static_assert(memory_order_consume == __ATOMIC_CONSUME, "");
//static_assert(memory_order_acquire == __ATOMIC_ACQUIRE, "");
//static_assert(memory_order_release == __ATOMIC_RELEASE, "");
//static_assert(memory_order_acq_rel == __ATOMIC_ACQ_REL, "");
//static_assert(memory_order_seq_cst == __ATOMIC_SEQ_CST, "");

struct atomic_u8 {
typedef u8 Type;
Expand Down Expand Up @@ -59,64 +99,170 @@ struct atomic_uptr {
volatile Type ValDoNotUse;
};

template <typename T>
inline typename T::Type atomic_load(const volatile T *A, memory_order MO) {
DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
typename T::Type V;
__atomic_load(&A->ValDoNotUse, &V, MO);
return V;
//template <typename T>
//inline typename T::Type atomic_load(const volatile T *A, memory_order MO) {
// DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
// typename T::Type V;
// __atomic_load(&A->ValDoNotUse, &V, MO);
// return V;
//}
//
//template <typename T>
//inline void atomic_store(volatile T *A, typename T::Type V, memory_order MO) {
// DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
// __atomic_store(&A->ValDoNotUse, &V, MO);
//}
//
//inline void atomic_thread_fence(memory_order) { __sync_synchronize(); }


INLINE u32 atomic_fetch_add(volatile atomic_u32 *a, u32 v,
memory_order mo) {
(void)mo;
DCHECK(!((uptr)a % sizeof(*a)));
return (u32)_InterlockedExchangeAdd((volatile long *)&a->ValDoNotUse,
(long)v);
}

template <typename T>
inline void atomic_store(volatile T *A, typename T::Type V, memory_order MO) {
DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
__atomic_store(&A->ValDoNotUse, &V, MO);
INLINE uptr atomic_fetch_add(volatile atomic_uptr *a, uptr v,
memory_order mo) {
(void)mo;
DCHECK(!((uptr)a % sizeof(*a)));
#ifdef _WIN64
return (uptr)_InterlockedExchangeAdd64((volatile long long *)&a->ValDoNotUse,
(long long)v);
#else
return (uptr)_InterlockedExchangeAdd((volatile long *)&a->ValDoNotUse,
(long)v);
#endif
}

inline void atomic_thread_fence(memory_order) { __sync_synchronize(); }


//template <typename T>
//inline typename T::Type atomic_fetch_add(volatile T *A, typename T::Type V,
// memory_order MO) {
// DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
// return __atomic_fetch_add(&A->ValDoNotUse, V, MO);
//}
//
//template <typename T>
//inline typename T::Type atomic_fetch_sub(volatile T *A, typename T::Type V,
// memory_order MO) {
// DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
// return __atomic_fetch_sub(&A->ValDoNotUse, V, MO);
//}
//
//template <typename T>
//inline typename T::Type atomic_fetch_and(volatile T *A, typename T::Type V,
// memory_order MO) {
// DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
// return __atomic_fetch_and(&A->ValDoNotUse, V, MO);
//}
//
//template <typename T>
//inline typename T::Type atomic_fetch_or(volatile T *A, typename T::Type V,
// memory_order MO) {
// DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
// return __atomic_fetch_or(&A->ValDoNotUse, V, MO);
//}
//
//template <typename T>
//inline typename T::Type atomic_exchange(volatile T *A, typename T::Type V,
// memory_order MO) {
// DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
// typename T::Type R;
// __atomic_exchange(&A->ValDoNotUse, &V, &R, MO);
// return R;
//}
//
template <typename T>
inline typename T::Type atomic_fetch_add(volatile T *A, typename T::Type V,
memory_order MO) {
DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
return __atomic_fetch_add(&A->ValDoNotUse, V, MO);
inline bool atomic_compare_exchange_strong(volatile T *A, typename T::Type *Cmp,
typename T::Type Xchg,
memory_order MO) {
return __atomic_compare_exchange(&A->ValDoNotUse, Cmp, &Xchg, false, MO,
__ATOMIC_RELAXED);
}

template <typename T>
inline typename T::Type atomic_fetch_sub(volatile T *A, typename T::Type V,
memory_order MO) {
DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
return __atomic_fetch_sub(&A->ValDoNotUse, V, MO);
INLINE bool atomic_compare_exchange_strong(volatile atomic_u8 *a, u8 *cmp,
u8 xchgv, memory_order mo) {
(void)mo;
DCHECK(!((uptr)a % sizeof(*a)));
u8 cmpv = *cmp;
#ifdef _WIN64
u8 prev = (u8)_InterlockedCompareExchange8((volatile char *)&a->ValDoNotUse,
(char)xchgv, (char)cmpv);
#else
u8 prev;
__asm {
mov al, cmpv
mov ecx, a
mov dl, xchgv
lock cmpxchg [ecx], dl
mov prev, al
}
#endif
if (prev == cmpv)
return true;
*cmp = prev;
return false;
}

template <typename T>
inline typename T::Type atomic_fetch_and(volatile T *A, typename T::Type V,
memory_order MO) {
DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
return __atomic_fetch_and(&A->ValDoNotUse, V, MO);
INLINE bool atomic_compare_exchange_strong(volatile atomic_uptr *a,
uptr *cmp, uptr xchg,
memory_order mo) {
uptr cmpv = *cmp;
uptr prev = (uptr)_InterlockedCompareExchangePointer(
(void *volatile *)&a->ValDoNotUse, (void *)xchg, (void *)cmpv);
if (prev == cmpv)
return true;
*cmp = prev;
return false;
}

template <typename T>
inline typename T::Type atomic_fetch_or(volatile T *A, typename T::Type V,
memory_order MO) {
DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
return __atomic_fetch_or(&A->ValDoNotUse, V, MO);
INLINE bool atomic_compare_exchange_strong(volatile atomic_u16 *a,
u16 *cmp, u16 xchg,
memory_order mo) {
u16 cmpv = *cmp;
u16 prev = (u16)_InterlockedCompareExchange16(
(volatile short *)&a->ValDoNotUse, (short)xchg, (short)cmpv);
if (prev == cmpv)
return true;
*cmp = prev;
return false;
}

template <typename T>
inline typename T::Type atomic_exchange(volatile T *A, typename T::Type V,
memory_order MO) {
DCHECK(!(reinterpret_cast<uptr>(A) % sizeof(*A)));
typename T::Type R;
__atomic_exchange(&A->ValDoNotUse, &V, &R, MO);
return R;
INLINE bool atomic_compare_exchange_strong(volatile atomic_u32 *a,
u32 *cmp, u32 xchg,
memory_order mo) {
u32 cmpv = *cmp;
u32 prev = (u32)_InterlockedCompareExchange((volatile long *)&a->ValDoNotUse,
(long)xchg, (long)cmpv);
if (prev == cmpv)
return true;
*cmp = prev;
return false;
}

INLINE bool atomic_compare_exchange_strong(volatile atomic_u64 *a,
u64 *cmp, u64 xchg,
memory_order mo) {
u64 cmpv = *cmp;
u64 prev = (u64)_InterlockedCompareExchange64(
(volatile long long *)&a->ValDoNotUse, (long long)xchg, (long long)cmpv);
if (prev == cmpv)
return true;
*cmp = prev;
return false;
}



template <typename T>
inline bool atomic_compare_exchange_strong(volatile T *A, typename T::Type *Cmp,
typename T::Type Xchg,
memory_order MO) {
return __atomic_compare_exchange(&A->ValDoNotUse, Cmp, &Xchg, false, MO,
inline bool atomic_compare_exchange_weak(volatile T *A, typename T::Type *Cmp,
typename T::Type Xchg,
memory_order MO) {
return __atomic_compare_exchange(&A->ValDoNotUse, Cmp, &Xchg, true, MO,
__ATOMIC_RELAXED);
}

Expand All @@ -132,14 +278,14 @@ inline void atomic_store_relaxed(volatile T *A, typename T::Type V) {
atomic_store(A, V, memory_order_relaxed);
}

template <typename T>
inline typename T::Type atomic_compare_exchange(volatile T *A,
typename T::Type Cmp,
typename T::Type Xchg) {
atomic_compare_exchange_strong(A, &Cmp, Xchg, memory_order_acquire);
return Cmp;
template <typename t>
INLINE typename t::type atomic_compare_exchange(volatile t *a,
typename t::type cmp,
typename t::type xchg) {
atomic_compare_exchange_strong(a, &cmp, xchg, memory_order_acquire);
return cmp;
}

} // namespace scudo

#endif // SCUDO_ATOMIC_H_
#endif // SCUDO_ATOMIC_H_
13 changes: 7 additions & 6 deletions compiler-rt/lib/scudo/standalone/chunk.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,14 @@ enum State : u8 { Available = 0, Allocated = 1, Quarantined = 2 };
typedef u64 PackedHeader;
// Update the 'Mask' constants to reflect changes in this structure.
struct UnpackedHeader {
uptr ClassId : 8;
u8 State : 2;
u64 ClassId : 8;
u64 State : 2;
// Origin if State == Allocated, or WasZeroed otherwise.
u8 OriginOrWasZeroed : 2;
uptr SizeOrUnusedBytes : 20;
uptr Offset : 16;
uptr Checksum : 16;
u64 OriginOrWasZeroed : 2;
u64 Origin : 2;
u64 SizeOrUnusedBytes : 20;
u64 Offset : 16;
u64 Checksum : 16;
};
typedef atomic_u64 AtomicPackedHeader;
static_assert(sizeof(UnpackedHeader) == sizeof(PackedHeader), "");
Expand Down
54 changes: 50 additions & 4 deletions compiler-rt/lib/scudo/standalone/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@

#include "fuchsia.h"
#include "linux.h"
#include "win.h"

#include <stddef.h>
#include <string.h>

#ifdef SCUDO_WINDOWS
extern "C" void _mm_pause();
#pragma intrinsic(_mm_pause)
#endif
namespace scudo {

template <class Dest, class Source> inline Dest bit_cast(const Source &S) {
Expand Down Expand Up @@ -50,9 +55,33 @@ template <class T> void Swap(T &A, T &B) {

inline bool isPowerOfTwo(uptr X) { return (X & (X - 1)) == 0; }

// Math
#if SCUDO_WINDOWS && !defined(__clang__) && !defined(__GNUC__)
extern "C" {
unsigned char _BitScanForward(unsigned long *index, unsigned long mask);
unsigned char _BitScanReverse(unsigned long *index, unsigned long mask);
#if defined(_WIN64)
unsigned char _BitScanForward64(unsigned long *index, unsigned __int64 mask);
unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask);
#endif
}
#endif

inline uptr getMostSignificantSetBitIndex(uptr X) {
DCHECK_NE(X, 0U);
return SCUDO_WORDSIZE - 1U - static_cast<uptr>(__builtin_clzl(X));
unsigned long up;
#if !SCUDO_WINDOWS || defined(__clang__) || defined(__GNUC__)
#ifdef _WIN64
up = SCUDO_WORDSIZE - 1 - __builtin_clzll(X);
#else
up = SCUDO_WORDSIZE - 1 - __builtin_clzl(X);
#endif
#elif defined(_WIN64)
_BitScanReverse64(&up, X);
#else
_BitScanReverse(&up, X);
#endif
return up;
}

inline uptr roundUpToPowerOfTwo(uptr Size) {
Expand All @@ -62,12 +91,24 @@ inline uptr roundUpToPowerOfTwo(uptr Size) {
const uptr Up = getMostSignificantSetBitIndex(Size);
DCHECK_LT(Size, (1UL << (Up + 1)));
DCHECK_GT(Size, (1UL << Up));
return 1UL << (Up + 1);
return 1ULL << (Up + 1);
}

inline uptr getLeastSignificantSetBitIndex(uptr X) {
DCHECK_NE(X, 0U);
return static_cast<uptr>(__builtin_ctzl(X));
unsigned long up;
#if !SCUDO_WINDOWS || defined(__clang__) || defined(__GNUC__)
#ifdef _WIN64
up = __builtin_ctzll(X);
#else
up = __builtin_ctzl(X);
#endif
#elif defined(_WIN64)
_BitScanForward64(&up, X);
#else
_BitScanForward(&up, X);
#endif
return up;
}

inline uptr getLog2(uptr X) {
Expand Down Expand Up @@ -101,6 +142,10 @@ template <typename T> inline void shuffle(T *A, u32 N, u32 *RandState) {
// Hardware specific inlinable functions.

inline void yieldProcessor(u8 Count) {
#if SCUDO_WINDOWS
for (int i = 0; i < Count; i++)
_mm_pause();
#else
#if defined(__i386__) || defined(__x86_64__)
__asm__ __volatile__("" ::: "memory");
for (u8 I = 0; I < Count; I++)
Expand All @@ -111,6 +156,7 @@ inline void yieldProcessor(u8 Count) {
__asm__ __volatile__("yield");
#endif
__asm__ __volatile__("" ::: "memory");
#endif // SCUDO_WINDOWS
}

// Platform specific functions.
Expand All @@ -133,7 +179,7 @@ const char *getEnv(const char *Name);

u64 getMonotonicTime();

u32 getThreadID();
tid_t getThreadID();

// Our randomness gathering function is limited to 256 bytes to ensure we get
// as many bytes as requested, and avoid interruptions (on Linux).
Expand Down
6 changes: 6 additions & 0 deletions compiler-rt/lib/scudo/standalone/flags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,9 @@ void initFlags() {
}

} // namespace scudo

#if !SCUDO_SUPPORTS_WEAK_HOOKS
SCUDO_INTERFACE_WEAK_DEF(const char *, __scudo_default_options, void) {
return "";
}
#endif
11 changes: 6 additions & 5 deletions compiler-rt/lib/scudo/standalone/include/scudo/interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@
#include <stddef.h>
#include <stdint.h>

extern "C" {
#include "internal_defs.h"

__attribute__((weak)) const char *__scudo_default_options();
extern "C" {
INTERFACE WEAK const char *__scudo_default_options();

// Post-allocation & pre-deallocation hooks.
// They must be thread-safe and not use heap related functions.
__attribute__((weak)) void __scudo_allocate_hook(void *ptr, size_t size);
__attribute__((weak)) void __scudo_deallocate_hook(void *ptr);
WEAK void __scudo_allocate_hook(void *ptr, size_t size);
WEAK void __scudo_deallocate_hook(void *ptr);

void __scudo_print_stats(void);
INTERFACE void __scudo_print_stats(void);

typedef void (*iterate_callback)(uintptr_t base, size_t size, void *arg);

Expand Down
113 changes: 103 additions & 10 deletions compiler-rt/lib/scudo/standalone/internal_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,83 @@
#define CONCATENATE(S, C) CONCATENATE_(S, C)

// Attributes & builtins related macros.

#if SCUDO_WINDOWS
#if SCUDO_IMPORT_INTERFACE
#define INTERFACE __declspec(dllimport)
#else
#define INTERFACE __declspec(dllexport)
#endif
#else
#define INTERFACE __attribute__((visibility("default")))
#endif

#define HIDDEN __attribute__((visibility("hidden")))

//--------------------------- WEAK FUNCTIONS ---------------------------------//
// When working with weak functions, to simplify the code and make it more
// portable, when possible define a default implementation using this macro:
//
// SCUDO_INTERFACE_WEAK_DEF(<return_type>, <name>, <parameter list>)
//
// For example:
// SCUDO_INTERFACE_WEAK_DEF(bool, compare, int a, int b) { return a > b; }
//
#if SCUDO_WINDOWS
#include "win_defs.h"
#define SCUDO_INTERFACE_WEAK_DEF(ReturnType, Name, ...) \
WIN_WEAK_EXPORT_DEF(ReturnType, Name, __VA_ARGS__)
#else
#define SCUDO_INTERFACE_WEAK_DEF(ReturnType, Name, ...) \
extern "C" SCUDO_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE ReturnType \
Name(__VA_ARGS__)
#endif

// SCUDO_SUPPORTS_WEAK_HOOKS means that we support real weak functions that
// will evaluate to a null pointer when not defined.
#ifndef SCUDO_SUPPORTS_WEAK_HOOKS
#if SCUDO_LINUX
#define SCUDO_SUPPORTS_WEAK_HOOKS 1
#else
#define SCUDO_SUPPORTS_WEAK_HOOKS 0
#endif
#endif // SCUDO_SUPPORTS_WEAK_HOOKS
// For some weak hooks that will be called very often and we want to avoid the
// overhead of executing the default implementation when it is not necessary,
// we can use the flag SANITIZER_SUPPORTS_WEAK_HOOKS to only define the default
// implementation for platforms that doesn't support weak symbols. For example:
//
// #if !SANITIZER_SUPPORT_WEAK_HOOKS
// SANITIZER_INTERFACE_WEAK_DEF(bool, compare_hook, int a, int b) {
// return a > b;
// }
// #endif
//
// And then use it as: if (compare_hook) compare_hook(a, b);
//----------------------------------------------------------------------------//

#if SCUDO_WINDOWS
#define WEAK
#else
#define WEAK __attribute__((weak))
#endif

// Common defs
#ifndef INLINE
#define INLINE inline
#endif

// Platform-specific defs.
#if defined(_MSC_VER)
#define ALWAYS_INLINE __forceinline
#define ALIAS(x)
#define FORMAT(f, a)
#define NOINLINE __declspec(noinline)
#define NORETURN __declspec(noreturn)
#define THREADLOCAL __declspec(thread)
#define LIKELY(x) (x)
#define UNLIKELY(x) (x)
#define PREFETCH(x) /* _mm_prefetch(x, _MM_HINT_NTA) */ (void)0
#else // _MSC_VER
#define ALWAYS_INLINE inline __attribute__((always_inline))
#define ALIAS(X) __attribute__((alias(X)))
#define FORMAT(F, A) __attribute__((format(printf, F, A)))
Expand All @@ -44,22 +117,42 @@
#else
#define PREFETCH(X) __builtin_prefetch(X)
#endif
#endif // _MSC_VER

#if !defined(_MSC_VER) || defined(__clang__)
#define UNUSED __attribute__((unused))
#define USED __attribute__((used))
#define NOEXCEPT noexcept
#else
#define UNUSED
#define USED
#endif

#if !defined(_MSC_VER) || defined(__clang__) || MSC_PREREQ(1900)
#define NOEXCEPT noexcept
#else
#define NOEXCEPT throw()
#endif
namespace scudo {

#if defined(_WIN64)
// 64-bit Windows uses LLP64 data model.
typedef unsigned long long uptr;
typedef signed long long sptr;
#else
typedef unsigned long uptr;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef signed long sptr;
typedef signed char s8;
typedef signed short s16;
typedef signed int s32;
typedef signed long long s64;
#endif // defined(_WIN64)
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t s8;
typedef int16_t s16;
typedef int32_t s32;
typedef int64_t s64;


typedef u64 tid_t;

// The following two functions have platform specific implementations.
void outputRaw(const char *Buffer);
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/scudo/standalone/mutex.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class HybridMutex {
atomic_u32 M;
#elif SCUDO_FUCHSIA
sync_mutex_t M;
#elif SCUDO_WINDOWS
atomic_u32 M;
#endif

void lockSlow();
Expand Down
31 changes: 30 additions & 1 deletion compiler-rt/lib/scudo/standalone/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,25 @@
#define SCUDO_FUCHSIA 0
#endif

#if __LP64__
#if defined(_WIN32)
#define SCUDO_WINDOWS 1
#else
#define SCUDO_WINDOWS 0
#endif

#if defined(_WIN64)
#define SCUDO_WINDOWS64 1
#else
#define SCUDO_WINDOWS64 0
#endif

#if SCUDO_WINDOWS && !SCUDO_WINDOWS64
#error "Don't support Win32"
#endif

#if defined(_WIN64)
#define SCUDO_WORDSIZE 64U
#elif __LP64__
#define SCUDO_WORDSIZE 64U
#else
#define SCUDO_WORDSIZE 32U
Expand Down Expand Up @@ -61,6 +79,17 @@
#define SCUDO_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47)
#endif

/// \macro MSC_PREREQ
/// \brief Is the compiler MSVC of at least the specified version?
/// The common \param version values to check for are:
/// * 1800: Microsoft Visual Studio 2013 / 12.0
/// * 1900: Microsoft Visual Studio 2015 / 14.0
#ifdef _MSC_VER
#define MSC_PREREQ(version) (_MSC_VER >= (version))
#else
#define MSC_PREREQ(version) 0
#endif

// Older gcc have issues aligning to a constexpr, and require an integer.
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56859 among others.
#if defined(__powerpc__) || defined(__powerpc64__)
Expand Down
8 changes: 7 additions & 1 deletion compiler-rt/lib/scudo/standalone/report.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,14 @@ class ScopedErrorReport {
private:
ScopedString Message;
};
#if !defined(_MSC_VER) || defined(__clang__)
INLINE void NORETURN trap() { __builtin_trap(); }
#else
extern "C" void __ud2(void);
#pragma intrinsic(__ud2)
INLINE void trap() { __ud2(); }
#endif

inline void NORETURN trap() { __builtin_trap(); }

// This could potentially be called recursively if a CHECK fails in the reports.
void NORETURN reportCheckFailed(const char *File, int Line,
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/scudo/standalone/size_class_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace scudo {

inline uptr scaledLog2(uptr Size, uptr ZeroLog, uptr LogBits) {
const uptr L = getMostSignificantSetBitIndex(Size);
const uptr LBits = (Size >> (L - LogBits)) - (1 << LogBits);
const uptr LBits = (Size >> (L - LogBits)) - (1ULL << LogBits);
const uptr HBits = (L - ZeroLog) << LogBits;
return LBits + HBits;
}
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/scudo/standalone/tsd.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
#include "mutex.h"

#include <limits.h> // for PTHREAD_DESTRUCTOR_ITERATIONS
#ifndef _WIN64
#include <pthread.h>
#endif

// With some build setups, this might still not be defined.
#ifndef PTHREAD_DESTRUCTOR_ITERATIONS
Expand Down
5 changes: 4 additions & 1 deletion compiler-rt/lib/scudo/standalone/tsd_exclusive.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,11 @@ template <class Allocator> struct TSDRegistryExT {
Instance->callPostInitCallback();
}

#if !SCUDO_WINDOWS
pthread_key_t PThreadKey;
#else

#endif
bool Initialized;
atomic_u8 Disabled;
TSD<Allocator> FallbackTSD;
Expand Down Expand Up @@ -137,5 +141,4 @@ template <class Allocator> void teardownThread(void *Ptr) {
}

} // namespace scudo

#endif // SCUDO_TSD_EXCLUSIVE_H_
198 changes: 198 additions & 0 deletions compiler-rt/lib/scudo/standalone/win.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
//===-- win.cpp --------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "platform.h"

#if SCUDO_WINDOWS

#include "common.h"
#include "win.h"
#include "mutex.h"
#include "string_utils.h"
#include "atomic_helpers.h"

#include <chrono>

#include <errno.h>
#include <fcntl.h>
//#include <linux/futex.h>
//#include <sched.h>
#include <stdlib.h>
#include <string.h>
//#include <sys/mman.h>
#include <sys/stat.h>
//#include <sys/syscall.h>
//#include <sys/time.h>
#include <time.h>
//#include <unistd.h>

#include <windows.h>
#include <io.h>
#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

namespace scudo {

uptr getPageSize() {
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
}

void NORETURN die() { abort(); }

void *map(void *Addr, uptr Size, UNUSED const char *Name, uptr Flags,
UNUSED MapPlatformData *Data) {
return nullptr;
// int MmapFlags = MAP_PRIVATE | MAP_ANONYMOUS;
// int MmapProt;
// if (Flags & MAP_NOACCESS) {
// MmapFlags |= MAP_NORESERVE;
// MmapProt = PROT_NONE;
// } else {
// MmapProt = PROT_READ | PROT_WRITE;
// }
// if (Addr) {
// // Currently no scenario for a noaccess mapping with a fixed address.
// DCHECK_EQ(Flags & MAP_NOACCESS, 0);
// MmapFlags |= MAP_FIXED;
// }
// void *P = mmap(Addr, Size, MmapProt, MmapFlags, -1, 0);
// if (P == MAP_FAILED) {
// if (!(Flags & MAP_ALLOWNOMEM) || errno != ENOMEM)
// dieOnMapUnmapError(errno == ENOMEM);
// return nullptr;
// }
// return P;
}

void unmap(void *Addr, uptr Size, UNUSED uptr Flags,
UNUSED MapPlatformData *Data) {
// if (munmap(Addr, Size) != 0)
// dieOnMapUnmapError();
}

void releasePagesToOS(uptr BaseAddress, uptr Offset, uptr Size,
UNUSED MapPlatformData *Data) {
// void *Addr = reinterpret_cast<void *>(BaseAddress + Offset);
// while (madvise(Addr, Size, MADV_DONTNEED) == -1 && errno == EAGAIN) {
//}
}

// Calling getenv should be fine (c)(tm) at any time.
const char *getEnv(const char *Name) { return getenv(Name); }

namespace {
enum State : u32 { Unlocked = 0, Locked = 1, Sleeping = 2 };
}

bool HybridMutex::tryLock() {
return atomic_compare_exchange(&M, Unlocked, Locked) == Unlocked;
}

// The following is based on https://akkadia.org/drepper/futex.pdf.
void HybridMutex::lockSlow() {
// u32 V = atomic_compare_exchange(&M, Unlocked, Locked);
// if (V == Unlocked)
// return;
// if (V != Sleeping)
// V = atomic_exchange(&M, Sleeping, memory_order_acquire);
// while (V != Unlocked) {
// syscall(SYS_futex, reinterpret_cast<uptr>(&M), FUTEX_WAIT_PRIVATE, Sleeping,
// nullptr, nullptr, 0);
// V = atomic_exchange(&M, Sleeping, memory_order_acquire);
// }
}

void HybridMutex::unlock() {
// if (atomic_fetch_sub(&M, 1U, memory_order_release) != Locked) {
// atomic_store(&M, Unlocked, memory_order_release);
// syscall(SYS_futex, reinterpret_cast<uptr>(&M), FUTEX_WAKE_PRIVATE, 1,
// nullptr, nullptr, 0);
// }
}

u64 getMonotonicTime() {
static LARGE_INTEGER frequency = {};
LARGE_INTEGER counter;
if (UNLIKELY(frequency.QuadPart == 0)) {
QueryPerformanceFrequency(&frequency);
CHECK_NE(frequency.QuadPart, 0);
}
QueryPerformanceCounter(&counter);
counter.QuadPart *= 1000ULL * 1000000ULL;
counter.QuadPart /= frequency.QuadPart;
return counter.QuadPart;

// Would like to use this but it seems slower on Windows
//using namespace std::chrono;
//return time_point_cast<nanoseconds>(
// steady_clock::now())
// .time_since_epoch()
// .count();
}

u32 getNumberOfCPUs() {
SYSTEM_INFO sysinfo = {};
GetNativeSystemInfo(&sysinfo);
return sysinfo.dwNumberOfProcessors;
}

tid_t getThreadID() {
return GetCurrentThreadId();
}
#pragma comment(lib, "advapi32.lib")
// Blocking is possibly unused if the getrandom block is not compiled in.
bool getRandom(void *Buffer, uptr Length, UNUSED bool Blocking) {
if (!Buffer || !Length || Length > 256)
return false;
return RtlGenRandom(Buffer, Length) != FALSE;
}

// Allocation free syslog-like API.
extern "C" WEAK int async_safe_write_log(int pri, const char *tag,
const char *msg);

void outputRaw(const char *Buffer) {
// if (&async_safe_write_log) {
// constexpr s32 AndroidLogInfo = 4;
// constexpr uptr MaxLength = 1024U;
// char LocalBuffer[MaxLength];
// while (strlen(Buffer) > MaxLength) {
// uptr P;
// for (P = MaxLength - 1; P > 0; P--) {
// if (Buffer[P] == '\n') {
// memcpy(LocalBuffer, Buffer, P);
// LocalBuffer[P] = '\0';
// async_safe_write_log(AndroidLogInfo, "scudo", LocalBuffer);
// Buffer = &Buffer[P + 1];
// break;
// }
// }
// // If no newline was found, just log the buffer.
// if (P == 0)
// break;
// }
// async_safe_write_log(AndroidLogInfo, "scudo", Buffer);
// } else {
// write(2, Buffer, strlen(Buffer));
// }
}

void setAbortMessage(const char *Message) {}

} // namespace scudo

#endif // SCUDO_LINUX
25 changes: 25 additions & 0 deletions compiler-rt/lib/scudo/standalone/win.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//===-- win.h ----------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef SCUDO_WIN_H_
#define SCUDO_WIN_H_

#include "platform.h"

#if SCUDO_WINDOWS

namespace scudo {

// MapPlatformData is unused on Windows, define it as a minimally sized structure.
struct MapPlatformData {};

} // namespace scudo

#endif // SCUDO_WINDOWS

#endif // SCUDO_WIN_H_
163 changes: 163 additions & 0 deletions compiler-rt/lib/scudo/standalone/win_defs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
//===-- win_defs.h ------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Common definitions for Windows-specific code.
//
//===----------------------------------------------------------------------===//
#ifndef SCUDO_WIN_DEFS_H
#define SCUDO_WIN_DEFS_H

#include "platform.h"
#if SCUDO_WINDOWS

//#ifndef WINAPI
//#if defined(_M_IX86) || defined(__i386__)
//#define WINAPI __stdcall
//#else
//#define WINAPI
//#endif
//#endif

#if defined(_M_IX86) || defined(__i386__)
#define WIN_SYM_PREFIX "_"
#else
#define WIN_SYM_PREFIX
#endif

// For MinGW, the /export: directives contain undecorated symbols, contrary to
// link/lld-link. The GNU linker doesn't support /alternatename and /include
// though, thus lld-link in MinGW mode interprets them in the same way as
// in the default mode.
#ifdef __MINGW32__
#define WIN_EXPORT_PREFIX
#else
#define WIN_EXPORT_PREFIX WIN_SYM_PREFIX
#endif

// Intermediate macro to ensure the parameter is expanded before stringified.
#define STRINGIFY_(A) #A
#define STRINGIFY(A) STRINGIFY_(A)

// ----------------- A workaround for the absence of weak symbols --------------
// We don't have a direct equivalent of weak symbols when using MSVC, but we can
// use the /alternatename directive to tell the linker to default a specific
// symbol to a specific value.
// Take into account that this is a pragma directive for the linker, so it will
// be ignored by the compiler and the function will be marked as UNDEF in the
// symbol table of the resulting object file. The linker won't find the default
// implementation until it links with that object file.
// So, suppose we provide a default implementation "fundef" for "fun", and this
// is compiled into the object file "test.obj" including the pragma directive.
// If we have some code with references to "fun" and we link that code with
// "test.obj", it will work because the linker always link object files.
// But, if "test.obj" is included in a static library, like "test.lib", then the
// liker will only link to "test.obj" if necessary. If we only included the
// definition of "fun", it won't link to "test.obj" (from test.lib) because
// "fun" appears as UNDEF, so it doesn't resolve the symbol "fun", and will
// result in a link error (the linker doesn't find the pragma directive).
// So, a workaround is to force linkage with the modules that include weak
// definitions, with the following macro: WIN_FORCE_LINK()

#define WIN_WEAK_ALIAS(Name, Default) \
__pragma(comment(linker, "/alternatename:" WIN_SYM_PREFIX STRINGIFY(Name) "="\
WIN_SYM_PREFIX STRINGIFY(Default)))

#define WIN_FORCE_LINK(Name) \
__pragma(comment(linker, "/include:" WIN_SYM_PREFIX STRINGIFY(Name)))

#define WIN_EXPORT(ExportedName, Name) \
__pragma(comment(linker, "/export:" WIN_EXPORT_PREFIX STRINGIFY(ExportedName)\
"=" WIN_EXPORT_PREFIX STRINGIFY(Name)))

// We cannot define weak functions on Windows, but we can use WIN_WEAK_ALIAS()
// which defines an alias to a default implementation, and only works when
// linking statically.
// So, to define a weak function "fun", we define a default implementation with
// a different name "fun__def" and we create a "weak alias" fun = fun__def.
// Then, users can override it just defining "fun".
// We impose "extern "C"" because otherwise WIN_WEAK_ALIAS() will fail because
// of name mangling.

// Dummy name for default implementation of weak function.
# define WEAK_DEFAULT_NAME(Name) Name##__def
// Name for exported implementation of weak function.
# define WEAK_EXPORT_NAME(Name) Name##__dll

// Use this macro when you need to define and export a weak function from a
// library. For example:
// WIN_WEAK_EXPORT_DEF(bool, compare, int a, int b) { return a > b; }
# define WIN_WEAK_EXPORT_DEF(ReturnType, Name, ...) \
WIN_WEAK_ALIAS(Name, WEAK_DEFAULT_NAME(Name)) \
WIN_EXPORT(WEAK_EXPORT_NAME(Name), Name) \
extern "C" ReturnType Name(__VA_ARGS__); \
extern "C" ReturnType WEAK_DEFAULT_NAME(Name)(__VA_ARGS__)

// Use this macro when you need to import a weak function from a library. It
// defines a weak alias to the imported function from the dll. For example:
// WIN_WEAK_IMPORT_DEF(compare)
# define WIN_WEAK_IMPORT_DEF(Name) \
WIN_WEAK_ALIAS(Name, WEAK_EXPORT_NAME(Name))

// So, for Windows we provide something similar to weak symbols in Linux, with
// some differences:
// + A default implementation must always be provided.
//
// + When linking statically it works quite similarly. For example:
//
// // libExample.cc
// WIN_WEAK_EXPORT_DEF(bool, compare, int a, int b) { return a > b; }
//
// // client.cc
// // We can use the default implementation from the library:
// compare(1, 2);
// // Or we can override it:
// extern "C" bool compare (int a, int b) { return a >= b; }
//
// And it will work fine. If we don't override the function, we need to ensure
// that the linker includes the object file with the default implementation.
// We can do so with the linker option "-wholearchive:".
//
// + When linking dynamically with a library (dll), weak functions are exported
// with "__dll" suffix. Clients can use the macro WIN_WEAK_IMPORT_DEF(fun)
// which defines a "weak alias" fun = fun__dll.
//
// // libExample.cc
// WIN_WEAK_EXPORT_DEF(bool, compare, int a, int b) { return a > b; }
//
// // client.cc
// WIN_WEAK_IMPORT_DEF(compare)
// // We can use the default implementation from the library:
// compare(1, 2);
// // Or we can override it:
// extern "C" bool compare (int a, int b) { return a >= b; }
//
// But if we override the function, the dlls don't have access to it (which
// is different in linux). If that is desired, the strong definition must be
// exported and interception can be used from the rest of the dlls.
//
// // libExample.cc
// WIN_WEAK_EXPORT_DEF(bool, compare, int a, int b) { return a > b; }
// // When initialized, check if the main executable defined "compare".
// int libExample_init() {
// uptr fnptr = __interception::InternalGetProcAddress(
// (void *)GetModuleHandleA(0), "compare");
// if (fnptr && !__interception::OverrideFunction((uptr)compare, fnptr, 0))
// abort();
// return 0;
// }
//
// // client.cc
// WIN_WEAK_IMPORT_DEF(compare)
// // We override and export compare:
// extern "C" __declspec(dllexport) bool compare (int a, int b) {
// return a >= b;
// }
//

#endif // SCUDO_WINDOWS
#endif // SCUDO_WIN_DEFS_H
2 changes: 1 addition & 1 deletion compiler-rt/lib/scudo/standalone/wrappers_c.inc
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ INTERFACE WEAK int SCUDO_PREFIX(malloc_info)(UNUSED int options, FILE *stream) {
fputs("<malloc version=\"scudo-1\">\n", stream);
for (scudo::uptr i = 0; i != max_size; ++i)
if (sizes[i])
fprintf(stream, "<alloc size=\"%lu\" count=\"%lu\"/>\n", i, sizes[i]);
fprintf(stream, "<alloc size=\"%llu\" count=\"%llu\"/>\n", i, sizes[i]);
fputs("</malloc>\n", stream);
SCUDO_PREFIX(free)(sizes);
return 0;
Expand Down
73 changes: 73 additions & 0 deletions compiler-rt/test/scudo/cxx_threads.cpp
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
21 changes: 19 additions & 2 deletions compiler-rt/test/scudo/interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,30 @@
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#if defined(_WIN32)
#include <windows.h>
#else
#include <unistd.h>
#endif

#include <vector>

#include <sanitizer/allocator_interface.h>
#include <sanitizer/scudo_interface.h>

#if defined(_MSC_VER)
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif

void sleep_ms(unsigned int ms) {
#if defined(_WIN32)
Sleep(ms);
#else
usleep(ms * 1000U);
#endif
}

int main(int argc, char **argv)
{
assert(argc == 2);
Expand Down Expand Up @@ -57,7 +74,7 @@ int main(int argc, char **argv)
}
// Set the soft RSS limit to 1Mb.
__scudo_set_rss_limit(1, 0);
usleep(20000);
sleep_ms(200);
// The following allocation should return NULL.
void *p = malloc(size);
assert(!p);
Expand All @@ -83,7 +100,7 @@ int main(int argc, char **argv)
}
// Set the hard RSS limit to 1Mb
__scudo_set_rss_limit(1, 1);
usleep(20000);
sleep_ms(200);
// The following should trigger our death.
void *p = malloc(size);
}
Expand Down
29 changes: 16 additions & 13 deletions compiler-rt/test/scudo/lit.cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,22 @@

# C & CXX flags.
c_flags = ([config.target_cflags] +
["-pthread",
"-fPIE",
"-pie",
"-O0",
"-UNDEBUG",
"-ldl",
"-Wl,--gc-sections"])
["-O0",
"-UNDEBUG"])

# Android doesn't want -lrt.
if not config.android:
c_flags += ["-lrt"]
if config.host_os != 'Windows':
c_flags += ["-pthread",
"-fPIE",
"-pie",
"-ldl",
"-Wl,--gc-sections"]
# Android doesn't want -lrt.
if not config.android:
c_flags += ["-lrt"]

cxx_flags = (c_flags + config.cxx_mode_flags + ["-std=c++11"])
cxx_flags = (c_flags + config.cxx_mode_flags)
if config.host_os != 'Windows':
cxx_flags += ["-std=c++11"]

scudo_flags = ["-fsanitize=scudo"]

Expand Down Expand Up @@ -59,6 +62,6 @@ def build_invocation(compile_flags):
config.substitutions.append(('%env_scudo_opts=',
'env SCUDO_OPTIONS=' + default_scudo_opts))

# Hardened Allocator tests are currently supported on Linux only.
if config.host_os not in ['Linux']:
# Hardened Allocator tests are currently supported on Linux and Windows only.
if config.host_os not in ['Linux', 'Windows']:
config.unsupported = True
5 changes: 5 additions & 0 deletions compiler-rt/test/scudo/malloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@

#include <vector>

#if defined(_MSC_VER)
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif

int main(int argc, char **argv)
{
void *p;
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/test/scudo/memalign.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// RUN: not %run %t double-free 2>&1 | FileCheck --check-prefix=CHECK-double-free %s
// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t realloc 2>&1 | FileCheck --check-prefix=CHECK-realloc %s
// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t realloc 2>&1
// UNSUPPORTED: windows

// Tests that the various aligned allocation functions work as intended. Also
// tests for the condition where the alignment is not a power of 2.
Expand Down
1 change: 0 additions & 1 deletion compiler-rt/test/scudo/mismatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,3 @@ int main(int argc, char **argv)
}

// CHECK-dealloc: ERROR: allocation type mismatch when deallocating address
// CHECK-realloc: ERROR: allocation type mismatch when reallocating address
5 changes: 5 additions & 0 deletions compiler-rt/test/scudo/overflow.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@
#include <stdlib.h>
#include <string.h>

#if defined(_MSC_VER)
#include <BaseTsd.h>
typedef SSIZE_T ssize_t;
#endif

int main(int argc, char **argv)
{
ssize_t offset = sizeof(void *) == 8 ? 8 : 0;
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/test/scudo/preload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// RUN: env LD_PRELOAD=%shared_minlibscudo not %run %t 2>&1 | FileCheck %s

// This way of setting LD_PRELOAD does not work with Android test runner.
// REQUIRES: !android
// UNSUPPORTED: android, windows

#include <assert.h>

Expand Down
14 changes: 13 additions & 1 deletion compiler-rt/test/scudo/rss.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,31 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32)
#include <windows.h>
#else
#include <unistd.h>
#endif

static const size_t kNumAllocs = 64;
static const size_t kAllocSize = 1 << 20; // 1MB.

static void *allocs[kNumAllocs];

void sleep_ms(unsigned int ms) {
#if defined(_WIN32)
Sleep(ms);
#else
usleep(ms * 1000U);
#endif
}

int main(int argc, char *argv[]) {
int returned_null = 0;
for (int i = 0; i < kNumAllocs; i++) {
// sleep for 100ms every 8 allocations, to allow the RSS check to catch up.
if (i != 0 && (i & 0x7) == 0)
usleep(100000);
sleep_ms(100);
allocs[i] = malloc(kAllocSize);
if (allocs[i])
memset(allocs[i], 0xff, kAllocSize); // Dirty the pages.
Expand Down
43 changes: 33 additions & 10 deletions compiler-rt/test/scudo/secondary.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,60 @@
// allocated by the Secondary allocator, or writing too far in front of it.

#include <assert.h>
#include <malloc.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <signal.h>
#include <unistd.h>
#endif

#ifdef _WIN32
DWORD getsystempagesize() {
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
}
LONG WINAPI handler(EXCEPTION_POINTERS *ExceptionInfo) {
fprintf(stderr, "AccessViolation\n");
ExitProcess(0);
}
#else
void handler(int signo, siginfo_t *info, void *uctx) {
if (info->si_code == SEGV_ACCERR) {
fprintf(stderr, "SCUDO SIGSEGV\n");
fprintf(stderr, "AccessViolation\n");
exit(0);
}
exit(1);
}
long getsystempagesize() {
return sysconf(_SC_PAGESIZE);
}
#endif

int main(int argc, char **argv)
{
// The size must be large enough to be serviced by the secondary allocator.
long page_size = sysconf(_SC_PAGESIZE);
size_t size = (1U << 17) + page_size;
struct sigaction a;
long page_size = getsystempagesize();
size_t size = (1U << 19) + page_size;

assert(argc == 2);
memset(&a, 0, sizeof(a));
a.sa_sigaction = handler;
a.sa_flags = SA_SIGINFO;

char *p = (char *)malloc(size);
assert(p);
memset(p, 'A', size); // This should not trigger anything.
// Set up the SIGSEGV handler now, as the rest should trigger an AV.
#ifdef _WIN32
SetUnhandledExceptionFilter(handler);
#else
struct sigaction a = {0};
a.sa_sigaction = handler;
a.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &a, NULL);
#endif

if (!strcmp(argv[1], "after")) {
for (int i = 0; i < page_size; i++)
p[size + i] = 'A';
Expand All @@ -50,4 +73,4 @@ int main(int argc, char **argv)
return 1; // A successful test means we shouldn't reach this.
}

// CHECK: SCUDO SIGSEGV
// CHECK: AccessViolation
2 changes: 1 addition & 1 deletion compiler-rt/test/scudo/symbols.test
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
UNSUPPORTED: android
UNSUPPORTED: android, windows

Verify that various functions are *not* present in the minimal binary. Presence
of those symbols in the minimal runtime would mean that the split code made it
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/test/scudo/threads.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// RUN: %clang_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
// UNSUPPORTED: windows

// Tests parallel allocations and deallocations of memory chunks from a number
// of concurrent threads, with and without quarantine.
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/test/scudo/tsd_destruction.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// RUN: %clang_scudo %s -o %t
// RUN: %run %t 2>&1
// UNSUPPORTED: windows

#include <locale.h>
#include <pthread.h>
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/test/scudo/valloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// RUN: %run %t valid 2>&1
// RUN: not %run %t invalid 2>&1 | FileCheck %s
// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t invalid 2>&1
// UNSUPPORTED: android
// UNSUPPORTED: android, windows

// Tests that valloc and pvalloc work as intended.

Expand Down
3 changes: 3 additions & 0 deletions llvm/cmake/modules/HandleLLVMOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,9 @@ if(LLVM_USE_SANITIZER)
elseif (LLVM_USE_SANITIZER STREQUAL "Leaks")
append_common_sanitizer_flags()
append("-fsanitize=leak" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
elseif (LLVM_USE_SANITIZER STREQUAL "Scudo")
append_common_sanitizer_flags()
append("-fsanitize=scudo" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
else()
message(FATAL_ERROR "Unsupported value of LLVM_USE_SANITIZER: ${LLVM_USE_SANITIZER}")
endif()
Expand Down
4 changes: 3 additions & 1 deletion llvm/lib/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ if(LLVM_INTEGRATED_CRT_ALLOC)
string(REGEX REPLACE "(/|\\\\)$" "" LLVM_INTEGRATED_CRT_ALLOC "${LLVM_INTEGRATED_CRT_ALLOC}")

if(NOT EXISTS "${LLVM_INTEGRATED_CRT_ALLOC}")
message(FATAL_ERROR "Cannot find the path to `git clone` for the CRT allocator! (${LLVM_INTEGRATED_CRT_ALLOC}). Currently, rpmalloc, snmalloc and mimalloc are supported.")
message(FATAL_ERROR "Cannot find the path to `git clone` for the CRT allocator! (${LLVM_INTEGRATED_CRT_ALLOC}). Currently, rpmalloc, snmalloc, mimalloc and scudo are supported.")
endif()

if(LLVM_INTEGRATED_CRT_ALLOC MATCHES "rpmalloc$")
Expand All @@ -73,6 +73,8 @@ if(LLVM_INTEGRATED_CRT_ALLOC)
message(FATAL_ERROR "Cannot find the mimalloc static library. To build it, first apply the patch from https://github.com/microsoft/mimalloc/issues/268 then build the Release x64 target through ${LLVM_INTEGRATED_CRT_ALLOC}\\ide\\vs2019\\mimalloc.sln")
endif()
set(system_libs ${system_libs} "${MIMALLOC_LIB}" "-INCLUDE:malloc")
elseif(LLVM_INTEGRATED_CRT_ALLOC MATCHES "scudo")
set(system_libs ${system_libs} "${LLVM_INTEGRATED_CRT_ALLOC}" "-INCLUDE:malloc")
endif()
endif()

Expand Down