Index: cmake/config-ix.cmake =================================================================== --- cmake/config-ix.cmake +++ cmake/config-ix.cmake @@ -246,6 +246,7 @@ set(ALL_CFI_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${MIPS64}) set(ALL_ESAN_SUPPORTED_ARCH ${X86_64} ${MIPS64}) set(ALL_SCUDO_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64} ${PPC64}) +set(ALL_SCUDO_STANDALONE_SUPPORTED_ARCH ${X86} ${X86_64}) if(APPLE) set(ALL_XRAY_SUPPORTED_ARCH ${X86_64}) else() @@ -459,6 +460,9 @@ list_intersect(SCUDO_SUPPORTED_ARCH ALL_SCUDO_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) + list_intersect(SCUDO_STANDALONE_SUPPORTED_ARCH + ALL_SCUDO_STANDALONE_SUPPORTED_ARCH + SANITIZER_COMMON_SUPPORTED_ARCH) list_intersect(FUZZER_SUPPORTED_ARCH ALL_FUZZER_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) @@ -493,6 +497,7 @@ filter_available_targets(CFI_SUPPORTED_ARCH ${ALL_CFI_SUPPORTED_ARCH}) filter_available_targets(ESAN_SUPPORTED_ARCH ${ALL_ESAN_SUPPORTED_ARCH}) filter_available_targets(SCUDO_SUPPORTED_ARCH ${ALL_SCUDO_SUPPORTED_ARCH}) + filter_available_targets(SCUDO_STANDALONE_SUPPORTED_ARCH ${ALL_SCUDO_SUPPORTED_ARCH}) filter_available_targets(XRAY_SUPPORTED_ARCH ${ALL_XRAY_SUPPORTED_ARCH}) filter_available_targets(SHADOWCALLSTACK_SUPPORTED_ARCH ${ALL_SHADOWCALLSTACK_SUPPORTED_ARCH}) @@ -634,6 +639,13 @@ set(COMPILER_RT_HAS_ESAN FALSE) endif() +#TODO(kostyak): add back Android & Fuchsia when the code settles a bit. +if (SCUDO_STANDALONE_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux") + set(COMPILER_RT_HAS_SCUDO_STANDALONE TRUE) +else() + set(COMPILER_RT_HAS_SCUDO_STANDALONE FALSE) +endif() + if (COMPILER_RT_HAS_SANITIZER_COMMON AND SCUDO_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux|Android|Fuchsia") set(COMPILER_RT_HAS_SCUDO TRUE) Index: lib/CMakeLists.txt =================================================================== --- lib/CMakeLists.txt +++ lib/CMakeLists.txt @@ -24,6 +24,9 @@ if(${runtime} STREQUAL tsan) add_subdirectory(tsan/dd) endif() + if(${runtime} STREQUAL scudo) + add_subdirectory(scudo/standalone) + endif() endif() endfunction() Index: lib/scudo/standalone/CMakeLists.txt =================================================================== --- lib/scudo/standalone/CMakeLists.txt +++ lib/scudo/standalone/CMakeLists.txt @@ -0,0 +1,54 @@ +add_compiler_rt_component(scudo_standalone) + +include_directories(../..) + +set(SCUDO_CFLAGS) + +list(APPEND SCUDO_CFLAGS + -Wall + -Werror + -nostdinc++) + +append_list_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding SCUDO_CFLAGS) + +append_list_if(COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG -fvisibility=hidden SCUDO_CFLAGS) + +if(COMPILER_RT_DEBUG) + list(APPEND SCUDO_CFLAGS -O0) +else() + list(APPEND SCUDO_CFLAGS -O3) +endif() + +set(SCUDO_LINK_FLAGS) + +list(APPEND SCUDO_LINK_FLAGS -Wl,-z,defs,-z,now,-z,relro) + +append_list_if(COMPILER_RT_HAS_NODEFAULTLIBS_FLAG -nodefaultlibs SCUDO_LINK_FLAGS) + +if(ANDROID) +# Put the shared library in the global group. For more details, see +# android-changes-for-ndk-developers.md#changes-to-library-search-order + append_list_if(COMPILER_RT_HAS_Z_GLOBAL -Wl,-z,global SCUDO_LINK_FLAGS) +endif() + +set(SCUDO_SOURCES empty.cc) + +set(SCUDO_HEADERS + platform.h + internal_defs.h + atomic_helpers.h + list.h) + +if(COMPILER_RT_HAS_SCUDO_STANDALONE) + add_compiler_rt_runtime(clang_rt.scudo_standalone + STATIC + ARCHS ${SCUDO_SUPPORTED_ARCH} + SOURCES ${SCUDO_SOURCES} + ADDITIONAL_HEADERS ${SCUDO_HEADERS} + CFLAGS ${SCUDO_CFLAGS} + PARENT_TARGET scudo_standalone) + + if(COMPILER_RT_INCLUDE_TESTS) + add_subdirectory(tests) + endif() +endif() Index: lib/scudo/standalone/atomic_helpers.h =================================================================== --- lib/scudo/standalone/atomic_helpers.h +++ lib/scudo/standalone/atomic_helpers.h @@ -0,0 +1,131 @@ +//===-- atomic_helpers.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_ATOMIC_H_ +#define SCUDO_ATOMIC_H_ + +#include "internal_defs.h" + +namespace scudo { + +enum memory_order { + memory_order_relaxed = 0, + memory_order_consume = 1, + memory_order_acquire = 2, + memory_order_release = 3, + memory_order_acq_rel = 4, + memory_order_seq_cst = 5 +}; +COMPILER_CHECK(memory_order_relaxed == __ATOMIC_RELAXED); +COMPILER_CHECK(memory_order_consume == __ATOMIC_CONSUME); +COMPILER_CHECK(memory_order_acquire == __ATOMIC_ACQUIRE); +COMPILER_CHECK(memory_order_release == __ATOMIC_RELEASE); +COMPILER_CHECK(memory_order_acq_rel == __ATOMIC_ACQ_REL); +COMPILER_CHECK(memory_order_seq_cst == __ATOMIC_SEQ_CST); + +struct atomic_u8 { + typedef u8 Type; + volatile Type ValDoNotUse; +}; + +struct atomic_u16 { + typedef u16 Type; + volatile Type ValDoNotUse; +}; + +struct atomic_s32 { + typedef s32 Type; + volatile Type ValDoNotUse; +}; + +struct atomic_u32 { + typedef u32 Type; + volatile Type ValDoNotUse; +}; + +struct atomic_u64 { + typedef u64 Type; + // On 32-bit platforms u64 is not necessarily aligned on 8 bytes. + volatile ALIGNED(8) Type ValDoNotUse; +}; + +struct atomic_uptr { + typedef uptr Type; + volatile Type ValDoNotUse; +}; + +template +INLINE typename T::Type atomic_load(const volatile T *A, memory_order MO) { + DCHECK(!(reinterpret_cast(A) % sizeof(*A))); + typename T::Type V; + __atomic_load(&A->ValDoNotUse, &V, MO); + return V; +} + +template +INLINE void atomic_store(volatile T *A, typename T::Type V, memory_order MO) { + DCHECK(!(reinterpret_cast(A) % sizeof(*A))); + __atomic_store(&A->ValDoNotUse, &V, MO); +} + +INLINE void atomic_thread_fence(memory_order) { __sync_synchronize(); } + +template +INLINE typename T::Type atomic_fetch_add(volatile T *A, typename T::Type V, + memory_order MO) { + DCHECK(!(reinterpret_cast(A) % sizeof(*A))); + return __atomic_fetch_add(&A->ValDoNotUse, V, MO); +} + +template +INLINE typename T::Type atomic_fetch_sub(volatile T *A, typename T::Type V, + memory_order MO) { + DCHECK(!(reinterpret_cast(A) % sizeof(*A))); + return __atomic_fetch_sub(&A->ValDoNotUse, V, MO); +} + +template +INLINE typename T::Type atomic_exchange(volatile T *A, typename T::Type V, + memory_order MO) { + DCHECK(!(reinterpret_cast(A) % sizeof(*A))); + typename T::Type R; + __atomic_exchange(&A->ValDoNotUse, &V, &R, MO); + return R; +} + +template +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 +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); +} + +// Clutter-reducing helpers. + +template +INLINE typename T::Type atomic_load_relaxed(const volatile T *A) { + return atomic_load(A, memory_order_relaxed); +} + +template +INLINE void atomic_store_relaxed(volatile T *A, typename T::Type V) { + atomic_store(A, V, memory_order_relaxed); +} + +} // namespace scudo + +#endif // SCUDO_ATOMIC_H_ Index: lib/scudo/standalone/empty.cc =================================================================== --- lib/scudo/standalone/empty.cc +++ lib/scudo/standalone/empty.cc @@ -0,0 +1,10 @@ +//===-- empty.cc ------------------------------------------------*- 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 "atomic_helpers.h" +#include "list.h" Index: lib/scudo/standalone/internal_defs.h =================================================================== --- lib/scudo/standalone/internal_defs.h +++ lib/scudo/standalone/internal_defs.h @@ -0,0 +1,135 @@ +//===-- internal_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 +// +//===----------------------------------------------------------------------===// + +#ifndef SCUDO_INTERNAL_DEFS_H_ +#define SCUDO_INTERNAL_DEFS_H_ + +#include "platform.h" + +#ifndef SCUDO_DEBUG +#define SCUDO_DEBUG 0 +#endif + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +// String related macros. + +#define STRINGIFY_(S) #S +#define STRINGIFY(S) STRINGIFY_(S) +#define CONCATENATE_(S, C) S##C +#define CONCATENATE(S, C) CONCATENATE_(S, C) + +// Attributes & builtins related macros. + +#define INTERFACE __attribute__((visibility("default"))) +#define WEAK __attribute__((weak)) +#define INLINE inline +#define ALWAYS_INLINE inline __attribute__((always_inline)) +#define ALIAS(x) __attribute__((alias(x))) +#define ALIGNED(x) __attribute__((aligned(x))) +#define FORMAT(f, a) __attribute__((format(printf, f, a))) +#define NOINLINE __attribute__((noinline)) +#define NORETURN __attribute__((noreturn)) +#define THREADLOCAL __thread +#define LIKELY(x) __builtin_expect(!!(x), 1) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#if defined(__i386__) || defined(__x86_64__) +// __builtin_prefetch(x) generates prefetchnt0 on x86 +#define PREFETCH(x) __asm__("prefetchnta (%0)" : : "r"(x)) +#else +#define PREFETCH(x) __builtin_prefetch(x) +#endif +#define UNUSED __attribute__((unused)) +#define USED __attribute__((used)) +#define NOEXCEPT noexcept + +namespace scudo { + +typedef unsigned long uptr; +typedef signed long sptr; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; + +// Various integer constants. + +#undef __INT64_C +#undef __UINT64_C +#undef UINTPTR_MAX +#if SCUDO_WORDSIZE == 64U +#define __INT64_C(c) c##L +#define __UINT64_C(c) c##UL +#define UINTPTR_MAX (18446744073709551615UL) +#else +#define __INT64_C(c) c##LL +#define __UINT64_C(c) c##ULL +#define UINTPTR_MAX (4294967295U) +#endif // SCUDO_WORDSIZE == 64U +#undef INT32_MIN +#define INT32_MIN (-2147483647 - 1) +#undef INT32_MAX +#define INT32_MAX (2147483647) +#undef UINT32_MAX +#define UINT32_MAX (4294967295U) +#undef INT64_MIN +#define INT64_MIN (-__INT64_C(9223372036854775807) - 1) +#undef INT64_MAX +#define INT64_MAX (__INT64_C(9223372036854775807)) +#undef UINT64_MAX +#define UINT64_MAX (__UINT64_C(18446744073709551615)) + +enum LinkerInitialized { LINKER_INITIALIZED = 0 }; + +// Various CHECK related macros. + +#define COMPILER_CHECK(Pred) static_assert(Pred, "") + +// TODO(kostyak): implement at a later check-in. +#define CHECK_IMPL(c1, op, c2) \ + do { \ + } while (false) + +#define CHECK(a) CHECK_IMPL((a), !=, 0) +#define CHECK_EQ(a, b) CHECK_IMPL((a), ==, (b)) +#define CHECK_NE(a, b) CHECK_IMPL((a), !=, (b)) +#define CHECK_LT(a, b) CHECK_IMPL((a), <, (b)) +#define CHECK_LE(a, b) CHECK_IMPL((a), <=, (b)) +#define CHECK_GT(a, b) CHECK_IMPL((a), >, (b)) +#define CHECK_GE(a, b) CHECK_IMPL((a), >=, (b)) + +#if SCUDO_DEBUG +#define DCHECK(a) CHECK(a) +#define DCHECK_EQ(a, b) CHECK_EQ(a, b) +#define DCHECK_NE(a, b) CHECK_NE(a, b) +#define DCHECK_LT(a, b) CHECK_LT(a, b) +#define DCHECK_LE(a, b) CHECK_LE(a, b) +#define DCHECK_GT(a, b) CHECK_GT(a, b) +#define DCHECK_GE(a, b) CHECK_GE(a, b) +#else +#define DCHECK(a) +#define DCHECK_EQ(a, b) +#define DCHECK_NE(a, b) +#define DCHECK_LT(a, b) +#define DCHECK_LE(a, b) +#define DCHECK_GT(a, b) +#define DCHECK_GE(a, b) +#endif + +// TODO(kostyak): implement at a later check-in. +#define UNREACHABLE(msg) \ + do { \ + } while (false) + +} // namespace scudo + +#endif // SCUDO_INTERNAL_DEFS_H_ Index: lib/scudo/standalone/list.h =================================================================== --- lib/scudo/standalone/list.h +++ lib/scudo/standalone/list.h @@ -0,0 +1,156 @@ +//===-- list.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_LIST_H_ +#define SCUDO_LIST_H_ + +#include "internal_defs.h" + +namespace scudo { + +// Intrusive POD singly-linked list. +// An object with all zero fields should represent a valid empty list. clear() +// should be called on all non-zero-initialized objects before using. +template struct IntrusiveList { + friend class Iterator; + + void clear() { + First = Last = nullptr; + Size = 0; + } + + bool empty() const { return Size == 0; } + uptr size() const { return Size; } + + void push_back(Item *X) { + if (empty()) { + X->Next = nullptr; + First = Last = X; + Size = 1; + } else { + X->Next = nullptr; + Last->Next = X; + Last = X; + Size++; + } + } + + void push_front(Item *X) { + if (empty()) { + X->Next = nullptr; + First = Last = X; + Size = 1; + } else { + X->Next = First; + First = X; + Size++; + } + } + + void pop_front() { + DCHECK(!empty()); + First = First->Next; + if (!First) + Last = nullptr; + Size--; + } + + void extract(Item *Prev, Item *X) { + DCHECK(!empty()); + DCHECK_NE(Prev, nullptr); + DCHECK_NE(X, nullptr); + DCHECK_EQ(Prev->Next, X); + Prev->Next = X->Next; + if (Last == X) + Last = Prev; + Size--; + } + + Item *front() { return First; } + const Item *front() const { return First; } + Item *back() { return Last; } + const Item *back() const { return Last; } + + void append_front(IntrusiveList *L) { + DCHECK_NE(this, L); + if (L->empty()) + return; + if (empty()) { + *this = *L; + } else if (!L->empty()) { + L->Last->Next = First; + First = L->First; + Size += L->size(); + } + L->clear(); + } + + void append_back(IntrusiveList *L) { + DCHECK_NE(this, L); + if (L->empty()) + return; + if (empty()) { + *this = *L; + } else { + Last->Next = L->First; + Last = L->Last; + Size += L->size(); + } + L->clear(); + } + + void checkConsistency() { + if (Size == 0) { + CHECK_EQ(First, 0); + CHECK_EQ(Last, 0); + } else { + uptr count = 0; + for (Item *i = First;; i = i->Next) { + count++; + if (i == Last) + break; + } + CHECK_EQ(size(), count); + CHECK_EQ(Last->Next, 0); + } + } + + template class IteratorBase { + public: + explicit IteratorBase(ItemT *CurrentItem) : Current(CurrentItem) {} + IteratorBase &operator++() { + Current = Current->Next; + return *this; + } + bool operator!=(IteratorBase Other) const { + return Current != Other.Current; + } + ItemT &operator*() { return *Current; } + + private: + ItemT *Current; + }; + + typedef IteratorBase Iterator; + typedef IteratorBase ConstIterator; + + Iterator begin() { return Iterator(First); } + Iterator end() { return Iterator(nullptr); } + + ConstIterator begin() const { return ConstIterator(First); } + ConstIterator end() const { return ConstIterator(nullptr); } + +private: + uptr Size; + Item *First; + Item *Last; +}; + +} // namespace scudo + +#endif // SCUDO_LIST_H_ Index: lib/scudo/standalone/platform.h =================================================================== --- lib/scudo/standalone/platform.h +++ lib/scudo/standalone/platform.h @@ -0,0 +1,70 @@ +//===-- platform.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_PLATFORM_H_ +#define SCUDO_PLATFORM_H_ + +#if defined(__linux__) +#define SCUDO_LINUX 1 +#else +#define SCUDO_LINUX 0 +#endif + +#if defined(__ANDROID__) +#define SCUDO_ANDROID 1 +#else +#define SCUDO_ANDROID 0 +#endif + +#if defined(__Fuchsia__) +#define SCUDO_FUCHSIA 1 +#else +#define SCUDO_FUCHSIA 0 +#endif + +#if __LP64__ +#define SCUDO_WORDSIZE 64U +#else +#define SCUDO_WORDSIZE 32U +#endif + +#if SCUDO_WORDSIZE == 64U +#define FIRST_32_SECOND_64(a, b) (b) +#else +#define FIRST_32_SECOND_64(a, b) (a) +#endif + +#ifndef SCUDO_CAN_USE_PRIMARY64 +#define SCUDO_CAN_USE_PRIMARY64 (SCUDO_WORDSIZE == 64U) +#endif + +#ifndef SCUDO_MIN_ALIGNMENT_LOG +// We force malloc-type functions to be aligned to std::max_align_t, but there +// is no reason why the minimum alignment for all other functions can't be 8 +// bytes. Except obviously for applications making incorrect assumptions. +// TODO(kostyak): define SCUDO_MIN_ALIGNMENT_LOG 3 +#define SCUDO_MIN_ALIGNMENT_LOG FIRST_32_SECOND_64(3, 4) +#endif + +#if defined(__aarch64__) +#define SCUDO_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 48) +#else +#define SCUDO_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47) +#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__) +#define SCUDO_CACHE_LINE_SIZE 128 +#else +#define SCUDO_CACHE_LINE_SIZE 64 +#endif + +#define SCUDO_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12) + +#endif // SCUDO_PLATFORM_H_ Index: lib/scudo/standalone/tests/CMakeLists.txt =================================================================== --- lib/scudo/standalone/tests/CMakeLists.txt +++ lib/scudo/standalone/tests/CMakeLists.txt @@ -0,0 +1,50 @@ +include_directories(..) + +add_custom_target(ScudoUnitTests) +set_target_properties(ScudoUnitTests PROPERTIES + FOLDER "Compiler-RT Tests") + +set(SCUDO_UNITTEST_CFLAGS + ${COMPILER_RT_UNITTEST_CFLAGS} + ${COMPILER_RT_GTEST_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/include + -I${COMPILER_RT_SOURCE_DIR}/lib + -I${COMPILER_RT_SOURCE_DIR}/lib/scudo/standalone + -DGTEST_HAS_RTTI=0) + +set(SCUDO_TEST_ARCH ${SCUDO_SUPPORTED_ARCH}) + +# gtests requires c++ +set(LINK_FLAGS -lstdc++ -pthread) + +set(TEST_HEADERS) +foreach (header ${SCUDO_HEADERS}) + list(APPEND TEST_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../${header}) +endforeach() + +# add_scudo_unittest( +# SOURCES +# HEADERS ) +macro(add_scudo_unittest testname) + cmake_parse_arguments(TEST "" "" "SOURCES;HEADERS" ${ARGN}) + if(COMPILER_RT_HAS_SCUDO_STANDALONE) + foreach(arch ${SCUDO_TEST_ARCH}) + set(ScudoUnitTestsObjects) + generate_compiler_rt_tests(ScudoUnitTestsObjects ScudoUnitTests + "${testname}-${arch}-Test" ${arch} + SOURCES ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE} + COMPILE_DEPS ${TEST_HEADERS} + DEPS gtest scudo_standalone + CFLAGS ${SCUDO_UNITTEST_CFLAGS} + LINK_FLAGS ${LINK_FLAGS}) + endforeach() + endif() +endmacro() + +set(SCUDO_UNIT_TEST_SOURCES + atomic_test.cc + list_test.cc + scudo_unit_test_main.cc) + +add_scudo_unittest(ScudoUnitTest + SOURCES ${SCUDO_UNIT_TEST_SOURCES}) Index: lib/scudo/standalone/tests/atomic_test.cc =================================================================== --- lib/scudo/standalone/tests/atomic_test.cc +++ lib/scudo/standalone/tests/atomic_test.cc @@ -0,0 +1,112 @@ +//===-- atomic_test.cc ------------------------------------------*- 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 "scudo/standalone/atomic_helpers.h" +#include "gtest/gtest.h" + +namespace scudo { + +template struct ValAndMagic { + typename T::Type Magic0; + T A; + typename T::Type Magic1; + + static ValAndMagic *Sink; +}; + +template ValAndMagic *ValAndMagic::Sink; + +template +void checkStoreLoad() { + typedef typename T::Type Type; + ValAndMagic Val; + // Prevent the compiler from scalarizing the struct. + ValAndMagic::Sink = &Val; + // Ensure that surrounding memory is not overwritten. + Val.Magic0 = Val.Magic1 = (Type)-3; + for (u64 I = 0; I < 100; I++) { + // Generate A value that occupies all bytes of the variable. + u64 V = I; + V |= V << 8; + V |= V << 16; + V |= V << 32; + Val.A.ValDoNotUse = (Type)V; + EXPECT_EQ(atomic_load(&Val.A, LoadMO), (Type)V); + Val.A.ValDoNotUse = (Type)-1; + atomic_store(&Val.A, (Type)V, StoreMO); + EXPECT_EQ(Val.A.ValDoNotUse, (Type)V); + } + EXPECT_EQ(Val.Magic0, (Type)-3); + EXPECT_EQ(Val.Magic1, (Type)-3); +} + +TEST(ScudoStandalone, AtomicStoreLoad) { + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); + checkStoreLoad(); +} + +template void checkAtomicCompareExchange() { + typedef typename T::Type Type; + { + Type OldVal = 42; + Type NewVal = 24; + Type V = OldVal; + EXPECT_TRUE(atomic_compare_exchange_strong( + reinterpret_cast(&V), &OldVal, NewVal, memory_order_relaxed)); + EXPECT_FALSE(atomic_compare_exchange_strong( + reinterpret_cast(&V), &OldVal, NewVal, memory_order_relaxed)); + EXPECT_EQ(NewVal, OldVal); + } + { + Type OldVal = 42; + Type NewVal = 24; + Type V = OldVal; + EXPECT_TRUE(atomic_compare_exchange_weak(reinterpret_cast(&V), &OldVal, + NewVal, memory_order_relaxed)); + EXPECT_FALSE(atomic_compare_exchange_weak( + reinterpret_cast(&V), &OldVal, NewVal, memory_order_relaxed)); + EXPECT_EQ(NewVal, OldVal); + } +} + +TEST(ScudoStandalone, AtomicCompareExchangeTest) { + checkAtomicCompareExchange(); + checkAtomicCompareExchange(); + checkAtomicCompareExchange(); + checkAtomicCompareExchange(); + checkAtomicCompareExchange(); +} + +} // namespace scudo Index: lib/scudo/standalone/tests/list_test.cc =================================================================== --- lib/scudo/standalone/tests/list_test.cc +++ lib/scudo/standalone/tests/list_test.cc @@ -0,0 +1,185 @@ +//===-- list_test.cc --------------------------------------------*- 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 "scudo/standalone/list.h" +#include "gtest/gtest.h" + +struct ListItem { + ListItem *Next; +}; + +typedef scudo::IntrusiveList List; + +static List StaticList; + +static void setList(List *L, ListItem *X = nullptr, ListItem *Y = nullptr, + ListItem *Z = nullptr) { + L->clear(); + if (X) + L->push_back(X); + if (Y) + L->push_back(Y); + if (Z) + L->push_back(Z); +} + +static void checkList(List *L, ListItem *I1, ListItem *I2 = nullptr, + ListItem *I3 = nullptr, ListItem *I4 = nullptr, + ListItem *I5 = nullptr, ListItem *I6 = nullptr) { + if (I1) { + EXPECT_EQ(L->front(), I1); + L->pop_front(); + } + if (I2) { + EXPECT_EQ(L->front(), I2); + L->pop_front(); + } + if (I3) { + EXPECT_EQ(L->front(), I3); + L->pop_front(); + } + if (I4) { + EXPECT_EQ(L->front(), I4); + L->pop_front(); + } + if (I5) { + EXPECT_EQ(L->front(), I5); + L->pop_front(); + } + if (I6) { + EXPECT_EQ(L->front(), I6); + L->pop_front(); + } + EXPECT_TRUE(L->empty()); +} + +TEST(ScudoSandalone, IntrusiveList) { + ListItem Items[6]; + EXPECT_EQ(StaticList.size(), 0U); + + List L; + L.clear(); + + ListItem *X = &Items[0]; + ListItem *Y = &Items[1]; + ListItem *Z = &Items[2]; + ListItem *A = &Items[3]; + ListItem *B = &Items[4]; + ListItem *C = &Items[5]; + + EXPECT_EQ(L.size(), 0U); + L.push_back(X); + EXPECT_EQ(L.size(), 1U); + EXPECT_EQ(L.back(), X); + EXPECT_EQ(L.front(), X); + L.pop_front(); + EXPECT_TRUE(L.empty()); + L.checkConsistency(); + + L.push_front(X); + EXPECT_EQ(L.size(), 1U); + EXPECT_EQ(L.back(), X); + EXPECT_EQ(L.front(), X); + L.pop_front(); + EXPECT_TRUE(L.empty()); + L.checkConsistency(); + + L.push_front(X); + L.push_front(Y); + L.push_front(Z); + EXPECT_EQ(L.size(), 3U); + EXPECT_EQ(L.front(), Z); + EXPECT_EQ(L.back(), X); + L.checkConsistency(); + + L.pop_front(); + EXPECT_EQ(L.size(), 2U); + EXPECT_EQ(L.front(), Y); + EXPECT_EQ(L.back(), X); + L.pop_front(); + L.pop_front(); + EXPECT_TRUE(L.empty()); + L.checkConsistency(); + + L.push_back(X); + L.push_back(Y); + L.push_back(Z); + EXPECT_EQ(L.size(), 3U); + EXPECT_EQ(L.front(), X); + EXPECT_EQ(L.back(), Z); + L.checkConsistency(); + + L.pop_front(); + EXPECT_EQ(L.size(), 2U); + EXPECT_EQ(L.front(), Y); + EXPECT_EQ(L.back(), Z); + L.pop_front(); + L.pop_front(); + EXPECT_TRUE(L.empty()); + L.checkConsistency(); + + L.push_back(X); + L.push_back(Y); + L.push_back(Z); + L.extract(X, Y); + EXPECT_EQ(L.size(), 2U); + EXPECT_EQ(L.front(), X); + EXPECT_EQ(L.back(), Z); + L.checkConsistency(); + L.extract(X, Z); + EXPECT_EQ(L.size(), 1U); + EXPECT_EQ(L.front(), X); + EXPECT_EQ(L.back(), X); + L.checkConsistency(); + L.pop_front(); + EXPECT_TRUE(L.empty()); + + List L1, L2; + L1.clear(); + L2.clear(); + + L1.append_front(&L2); + EXPECT_TRUE(L1.empty()); + EXPECT_TRUE(L2.empty()); + + L1.append_back(&L2); + EXPECT_TRUE(L1.empty()); + EXPECT_TRUE(L2.empty()); + + setList(&L1, X); + checkList(&L1, X); + + setList(&L1, X, Y, Z); + setList(&L2, A, B, C); + L1.append_back(&L2); + checkList(&L1, X, Y, Z, A, B, C); + EXPECT_TRUE(L2.empty()); + + setList(&L1, X, Y); + setList(&L2); + L1.append_front(&L2); + checkList(&L1, X, Y); + EXPECT_TRUE(L2.empty()); +} + +TEST(ScudoStandalone, IntrusiveListAppendEmpty) { + ListItem I; + List L; + L.clear(); + L.push_back(&I); + List L2; + L2.clear(); + L.append_back(&L2); + EXPECT_EQ(L.back(), &I); + EXPECT_EQ(L.front(), &I); + EXPECT_EQ(L.size(), 1U); + L.append_front(&L2); + EXPECT_EQ(L.back(), &I); + EXPECT_EQ(L.front(), &I); + EXPECT_EQ(L.size(), 1U); +} Index: lib/scudo/standalone/tests/scudo_unit_test_main.cc =================================================================== --- lib/scudo/standalone/tests/scudo_unit_test_main.cc +++ lib/scudo/standalone/tests/scudo_unit_test_main.cc @@ -0,0 +1,14 @@ +//===-- scudo_unit_test_main.cc ---------------------------------*- 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 "gtest/gtest.h" + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} Index: test/scudo/CMakeLists.txt =================================================================== --- test/scudo/CMakeLists.txt +++ test/scudo/CMakeLists.txt @@ -35,6 +35,8 @@ list(APPEND SCUDO_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}) endforeach() +add_subdirectory(standalone) + add_lit_testsuite(check-scudo "Running the Scudo Hardened Allocator tests" ${SCUDO_TESTSUITES} DEPENDS ${SCUDO_TEST_DEPS}) Index: test/scudo/standalone/CMakeLists.txt =================================================================== --- test/scudo/standalone/CMakeLists.txt +++ test/scudo/standalone/CMakeLists.txt @@ -0,0 +1,15 @@ +if(COMPILER_RT_INCLUDE_TESTS AND COMPILER_RT_HAS_SCUDO_STANDALONE) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/unit/lit.site.cfg) + list(APPEND SCUDO_STANDALONE_TEST_DEPS ScudoUnitTests) + list(APPEND SCUDO_STANDALONE_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/unit) +endif() + +add_lit_testsuite(check-scudo_standalone + "Running Scudo Standalone tests" + ${SCUDO_STANDALONE_TESTSUITES} + DEPENDS ${SCUDO_STANDALONE_TEST_DEPS}) + +set_target_properties(check-scudo_standalone + PROPERTIES FOLDER "Compiler-RT Tests") Index: test/scudo/standalone/unit/lit.site.cfg.in =================================================================== --- test/scudo/standalone/unit/lit.site.cfg.in +++ test/scudo/standalone/unit/lit.site.cfg.in @@ -0,0 +1,12 @@ +@LIT_SITE_CFG_IN_HEADER@ + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured") + +# Setup config name. +config.name = 'ScudoStandalone-Unit' + +# Setup test source and exec root. +# For unit tests, we define it as build directory with unit tests. +config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/scudo/standalone/tests" +config.test_source_root = config.test_exec_root