summaryrefslogtreecommitdiffstats
path: root/compiler-rt/lib
diff options
context:
space:
mode:
Diffstat (limited to 'compiler-rt/lib')
-rw-r--r--compiler-rt/lib/CMakeLists.txt4
-rw-r--r--compiler-rt/lib/scudo/CMakeLists.txt32
-rw-r--r--compiler-rt/lib/scudo/scudo_allocator.cpp635
-rw-r--r--compiler-rt/lib/scudo/scudo_allocator.h63
-rw-r--r--compiler-rt/lib/scudo/scudo_flags.cpp81
-rw-r--r--compiler-rt/lib/scudo/scudo_flags.h33
-rw-r--r--compiler-rt/lib/scudo/scudo_flags.inc35
-rw-r--r--compiler-rt/lib/scudo/scudo_interceptors.cpp75
-rw-r--r--compiler-rt/lib/scudo/scudo_new_delete.cpp69
-rw-r--r--compiler-rt/lib/scudo/scudo_termination.cpp41
-rw-r--r--compiler-rt/lib/scudo/scudo_utils.cpp133
-rw-r--r--compiler-rt/lib/scudo/scudo_utils.h59
12 files changed, 1260 insertions, 0 deletions
diff --git a/compiler-rt/lib/CMakeLists.txt b/compiler-rt/lib/CMakeLists.txt
index 7a8210e4241..a2b55c4e35c 100644
--- a/compiler-rt/lib/CMakeLists.txt
+++ b/compiler-rt/lib/CMakeLists.txt
@@ -52,4 +52,8 @@ if(COMPILER_RT_BUILD_SANITIZERS)
if(COMPILER_RT_HAS_ESAN)
add_subdirectory(esan)
endif()
+
+ if(COMPILER_RT_HAS_SCUDO)
+ add_subdirectory(scudo)
+ endif()
endif()
diff --git a/compiler-rt/lib/scudo/CMakeLists.txt b/compiler-rt/lib/scudo/CMakeLists.txt
new file mode 100644
index 00000000000..ebf241eb451
--- /dev/null
+++ b/compiler-rt/lib/scudo/CMakeLists.txt
@@ -0,0 +1,32 @@
+add_custom_target(scudo)
+
+include_directories(..)
+
+set(SCUDO_CFLAGS ${SANITIZER_COMMON_CFLAGS})
+append_rtti_flag(OFF SCUDO_CFLAGS)
+list(APPEND SCUDO_CFLAGS -msse4.2 -mcx16)
+
+set(SCUDO_SOURCES
+ scudo_allocator.cpp
+ scudo_flags.cpp
+ scudo_interceptors.cpp
+ scudo_new_delete.cpp
+ scudo_termination.cpp
+ scudo_utils.cpp)
+
+if(COMPILER_RT_HAS_SCUDO)
+ foreach(arch ${SCUDO_SUPPORTED_ARCH})
+ add_compiler_rt_runtime(clang_rt.scudo
+ STATIC
+ ARCHS ${arch}
+ SOURCES ${SCUDO_SOURCES}
+ $<TARGET_OBJECTS:RTInterception.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonNoTermination.${arch}>
+ $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
+ CFLAGS ${SCUDO_CFLAGS}
+ PARENT_TARGET scudo)
+ endforeach()
+endif()
+
+add_dependencies(compiler-rt scudo)
+
diff --git a/compiler-rt/lib/scudo/scudo_allocator.cpp b/compiler-rt/lib/scudo/scudo_allocator.cpp
new file mode 100644
index 00000000000..3ad499aed10
--- /dev/null
+++ b/compiler-rt/lib/scudo/scudo_allocator.cpp
@@ -0,0 +1,635 @@
+//===-- scudo_allocator.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// Scudo Hardened Allocator implementation.
+/// It uses the sanitizer_common allocator as a base and aims at mitigating
+/// heap corruption vulnerabilities. It provides a checksum-guarded chunk
+/// header, a delayed free list, and additional sanity checks.
+///
+//===----------------------------------------------------------------------===//
+
+#include "scudo_allocator.h"
+#include "scudo_utils.h"
+
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_quarantine.h"
+
+#include <limits.h>
+#include <pthread.h>
+#include <smmintrin.h>
+
+#include <atomic>
+#include <cstring>
+
+namespace __scudo {
+
+const uptr AllocatorSpace = ~0ULL;
+const uptr AllocatorSize = 0x10000000000ULL;
+const uptr MinAlignmentLog = 4; // 16 bytes for x64
+const uptr MaxAlignmentLog = 24;
+
+typedef DefaultSizeClassMap SizeClassMap;
+typedef SizeClassAllocator64<AllocatorSpace, AllocatorSize, 0, SizeClassMap>
+ PrimaryAllocator;
+typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
+typedef LargeMmapAllocator<> SecondaryAllocator;
+typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, SecondaryAllocator>
+ ScudoAllocator;
+
+static ScudoAllocator &getAllocator();
+
+static thread_local Xorshift128Plus Prng;
+// Global static cookie, initialized at start-up.
+static u64 Cookie;
+
+enum ChunkState : u8 {
+ ChunkAvailable = 0,
+ ChunkAllocated = 1,
+ ChunkQuarantine = 2
+};
+
+typedef unsigned __int128 PackedHeader;
+typedef std::atomic<PackedHeader> AtomicPackedHeader;
+
+// Our header requires 128-bit of storage on x64 (the only platform supported
+// as of now), which fits nicely with the alignment requirements.
+// Having the offset saves us from using functions such as GetBlockBegin, that
+// is fairly costly. Our first implementation used the MetaData as well, which
+// offers the advantage of being stored away from the chunk itself, but
+// accessing it was costly as well.
+// The header will be atomically loaded and stored using the 16-byte primitives
+// offered by the platform (likely requires cmpxchg16b support).
+struct UnpackedHeader {
+ // 1st 8 bytes
+ u16 Checksum : 16;
+ u64 RequestedSize : 40; // Needed for reallocation purposes.
+ u8 State : 2; // available, allocated, or quarantined
+ u8 AllocType : 2; // malloc, new, new[], or memalign
+ u8 Unused_0_ : 4;
+ // 2nd 8 bytes
+ u64 Offset : 20; // Offset from the beginning of the backend
+ // allocation to the beginning chunk itself, in
+ // multiples of MinAlignment. See comment about its
+ // maximum value and test in Initialize.
+ u64 Unused_1_ : 28;
+ u16 Salt : 16;
+};
+
+COMPILER_CHECK(sizeof(UnpackedHeader) == sizeof(PackedHeader));
+
+const uptr ChunkHeaderSize = sizeof(PackedHeader);
+
+struct ScudoChunk : UnpackedHeader {
+ // We can't use the offset member of the chunk itself, as we would double
+ // fetch it without any warranty that it wouldn't have been tampered. To
+ // prevent this, we work with a local copy of the header.
+ void *AllocBeg(UnpackedHeader *Header) {
+ return reinterpret_cast<void *>(
+ reinterpret_cast<uptr>(this) - (Header->Offset << MinAlignmentLog));
+ }
+
+ // CRC32 checksum of the Chunk pointer and its ChunkHeader.
+ // It currently uses the Intel Nehalem SSE4.2 crc32 64-bit instruction.
+ u16 Checksum(UnpackedHeader *Header) const {
+ u64 HeaderHolder[2];
+ memcpy(HeaderHolder, Header, sizeof(HeaderHolder));
+ u64 Crc = _mm_crc32_u64(Cookie, reinterpret_cast<uptr>(this));
+ // This is somewhat of a shortcut. The checksum is stored in the 16 least
+ // significant bits of the first 8 bytes of the header, hence zero-ing
+ // those bits out. It would be more valid to zero the checksum field of the
+ // UnpackedHeader, but would require holding an additional copy of it.
+ Crc = _mm_crc32_u64(Crc, HeaderHolder[0] & 0xffffffffffff0000ULL);
+ Crc = _mm_crc32_u64(Crc, HeaderHolder[1]);
+ return static_cast<u16>(Crc);
+ }
+
+ // Loads and unpacks the header, verifying the checksum in the process.
+ void loadHeader(UnpackedHeader *NewUnpackedHeader) const {
+ const AtomicPackedHeader *AtomicHeader =
+ reinterpret_cast<const AtomicPackedHeader *>(this);
+ PackedHeader NewPackedHeader =
+ AtomicHeader->load(std::memory_order_relaxed);
+ *NewUnpackedHeader = bit_cast<UnpackedHeader>(NewPackedHeader);
+ if ((NewUnpackedHeader->Unused_0_ != 0) ||
+ (NewUnpackedHeader->Unused_1_ != 0) ||
+ (NewUnpackedHeader->Checksum != Checksum(NewUnpackedHeader))) {
+ dieWithMessage("ERROR: corrupted chunk header at address %p\n", this);
+ }
+ }
+
+ // Packs and stores the header, computing the checksum in the process.
+ void storeHeader(UnpackedHeader *NewUnpackedHeader) {
+ NewUnpackedHeader->Checksum = Checksum(NewUnpackedHeader);
+ PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader);
+ AtomicPackedHeader *AtomicHeader =
+ reinterpret_cast<AtomicPackedHeader *>(this);
+ AtomicHeader->store(NewPackedHeader, std::memory_order_relaxed);
+ }
+
+ // Packs and stores the header, computing the checksum in the process. We
+ // compare the current header with the expected provided one to ensure that
+ // we are not being raced by a corruption occurring in another thread.
+ void compareExchangeHeader(UnpackedHeader *NewUnpackedHeader,
+ UnpackedHeader *OldUnpackedHeader) {
+ NewUnpackedHeader->Checksum = Checksum(NewUnpackedHeader);
+ PackedHeader NewPackedHeader = bit_cast<PackedHeader>(*NewUnpackedHeader);
+ PackedHeader OldPackedHeader = bit_cast<PackedHeader>(*OldUnpackedHeader);
+ AtomicPackedHeader *AtomicHeader =
+ reinterpret_cast<AtomicPackedHeader *>(this);
+ if (!AtomicHeader->compare_exchange_strong(OldPackedHeader,
+ NewPackedHeader,
+ std::memory_order_relaxed,
+ std::memory_order_relaxed)) {
+ dieWithMessage("ERROR: race on chunk header at address %p\n", this);
+ }
+ }
+};
+
+static bool ScudoInitIsRunning = false;
+
+static pthread_once_t GlobalInited = PTHREAD_ONCE_INIT;
+static pthread_key_t pkey;
+
+static thread_local bool ThreadInited = false;
+static thread_local bool ThreadTornDown = false;
+static thread_local AllocatorCache Cache;
+
+static void teardownThread(void *p) {
+ uptr v = reinterpret_cast<uptr>(p);
+ // The glibc POSIX thread-local-storage deallocation routine calls user
+ // provided destructors in a loop of PTHREAD_DESTRUCTOR_ITERATIONS.
+ // We want to be called last since other destructors might call free and the
+ // like, so we wait until PTHREAD_DESTRUCTOR_ITERATIONS before draining the
+ // quarantine and swallowing the cache.
+ if (v < PTHREAD_DESTRUCTOR_ITERATIONS) {
+ pthread_setspecific(pkey, reinterpret_cast<void *>(v + 1));
+ return;
+ }
+ drainQuarantine();
+ getAllocator().DestroyCache(&Cache);
+ ThreadTornDown = true;
+}
+
+static void initInternal() {
+ SanitizerToolName = "Scudo";
+ CHECK(!ScudoInitIsRunning && "Scudo init calls itself!");
+ ScudoInitIsRunning = true;
+
+ initFlags();
+
+ AllocatorOptions Options;
+ Options.setFrom(getFlags(), common_flags());
+ initAllocator(Options);
+
+ ScudoInitIsRunning = false;
+}
+
+static void initGlobal() {
+ pthread_key_create(&pkey, teardownThread);
+ initInternal();
+}
+
+static void NOINLINE initThread() {
+ pthread_once(&GlobalInited, initGlobal);
+ pthread_setspecific(pkey, reinterpret_cast<void *>(1));
+ getAllocator().InitCache(&Cache);
+ ThreadInited = true;
+}
+
+struct QuarantineCallback {
+ explicit QuarantineCallback(AllocatorCache *Cache)
+ : Cache_(Cache) {}
+
+ // Chunk recycling function, returns a quarantined chunk to the backend.
+ void Recycle(ScudoChunk *Chunk) {
+ UnpackedHeader Header;
+ Chunk->loadHeader(&Header);
+ if (Header.State != ChunkQuarantine) {
+ dieWithMessage("ERROR: invalid chunk state when recycling address %p\n",
+ Chunk);
+ }
+ void *Ptr = Chunk->AllocBeg(&Header);
+ getAllocator().Deallocate(Cache_, Ptr);
+ }
+
+ /// Internal quarantine allocation and deallocation functions.
+ void *Allocate(uptr Size) {
+ // The internal quarantine memory cannot be protected by us. But the only
+ // structures allocated are QuarantineBatch, that are 8KB for x64. So we
+ // will use mmap for those, and given that Deallocate doesn't pass a size
+ // in, we enforce the size of the allocation to be sizeof(QuarantineBatch).
+ // TODO(kostyak): switching to mmap impacts greatly performances, we have
+ // to find another solution
+ // CHECK_EQ(Size, sizeof(QuarantineBatch));
+ // return MmapOrDie(Size, "QuarantineBatch");
+ return getAllocator().Allocate(Cache_, Size, 1, false);
+ }
+
+ void Deallocate(void *Ptr) {
+ // UnmapOrDie(Ptr, sizeof(QuarantineBatch));
+ getAllocator().Deallocate(Cache_, Ptr);
+ }
+
+ AllocatorCache *Cache_;
+};
+
+typedef Quarantine<QuarantineCallback, ScudoChunk> ScudoQuarantine;
+typedef ScudoQuarantine::Cache QuarantineCache;
+static thread_local QuarantineCache ThreadQuarantineCache;
+
+void AllocatorOptions::setFrom(const Flags *f, const CommonFlags *cf) {
+ MayReturnNull = cf->allocator_may_return_null;
+ QuarantineSizeMb = f->QuarantineSizeMb;
+ ThreadLocalQuarantineSizeKb = f->ThreadLocalQuarantineSizeKb;
+ DeallocationTypeMismatch = f->DeallocationTypeMismatch;
+ DeleteSizeMismatch = f->DeleteSizeMismatch;
+ ZeroContents = f->ZeroContents;
+}
+
+void AllocatorOptions::copyTo(Flags *f, CommonFlags *cf) const {
+ cf->allocator_may_return_null = MayReturnNull;
+ f->QuarantineSizeMb = QuarantineSizeMb;
+ f->ThreadLocalQuarantineSizeKb = ThreadLocalQuarantineSizeKb;
+ f->DeallocationTypeMismatch = DeallocationTypeMismatch;
+ f->DeleteSizeMismatch = DeleteSizeMismatch;
+ f->ZeroContents = ZeroContents;
+}
+
+struct Allocator {
+ static const uptr MaxAllowedMallocSize = 1ULL << 40;
+ static const uptr MinAlignment = 1 << MinAlignmentLog;
+ static const uptr MaxAlignment = 1 << MaxAlignmentLog; // 16 MB
+
+ ScudoAllocator BackendAllocator;
+ ScudoQuarantine AllocatorQuarantine;
+
+ // The fallback caches are used when the thread local caches have been
+ // 'detroyed' on thread tear-down. They are protected by a Mutex as they can
+ // be accessed by different threads.
+ StaticSpinMutex FallbackMutex;
+ AllocatorCache FallbackAllocatorCache;
+ QuarantineCache FallbackQuarantineCache;
+
+ bool DeallocationTypeMismatch;
+ bool ZeroContents;
+ bool DeleteSizeMismatch;
+
+ explicit Allocator(LinkerInitialized)
+ : AllocatorQuarantine(LINKER_INITIALIZED),
+ FallbackQuarantineCache(LINKER_INITIALIZED) {}
+
+ void init(const AllocatorOptions &Options) {
+ // Currently SSE 4.2 support is required. This might change later.
+ CHECK(testCPUFeature(SSE4_2)); // for crc32
+
+ // Verify that the header offset field can hold the maximum offset. In the
+ // worst case scenario, the backend allocation is already aligned on
+ // MaxAlignment, so in order to store the header and still be aligned, we
+ // add an extra MaxAlignment. As a result, the offset from the beginning of
+ // the backend allocation to the chunk will be MaxAlignment -
+ // ChunkHeaderSize.
+ UnpackedHeader Header = {};
+ uptr MaximumOffset = (MaxAlignment - ChunkHeaderSize) >> MinAlignmentLog;
+ Header.Offset = MaximumOffset;
+ if (Header.Offset != MaximumOffset) {
+ dieWithMessage("ERROR: the maximum possible offset doesn't fit in the "
+ "header\n");
+ }
+
+ DeallocationTypeMismatch = Options.DeallocationTypeMismatch;
+ DeleteSizeMismatch = Options.DeleteSizeMismatch;
+ ZeroContents = Options.ZeroContents;
+ BackendAllocator.Init(Options.MayReturnNull);
+ AllocatorQuarantine.Init(static_cast<uptr>(Options.QuarantineSizeMb) << 20,
+ static_cast<uptr>(
+ Options.ThreadLocalQuarantineSizeKb) << 10);
+ BackendAllocator.InitCache(&FallbackAllocatorCache);
+ Cookie = Prng.Next();
+ }
+
+ // Allocates a chunk.
+ void *allocate(uptr Size, uptr Alignment, AllocType Type) {
+ if (UNLIKELY(!ThreadInited))
+ initThread();
+ if (!IsPowerOfTwo(Alignment)) {
+ dieWithMessage("ERROR: malloc alignment is not a power of 2\n");
+ }
+ if (Alignment > MaxAlignment)
+ return BackendAllocator.ReturnNullOrDie();
+ if (Alignment < MinAlignment)
+ Alignment = MinAlignment;
+ if (Size == 0)
+ Size = 1;
+ if (Size >= MaxAllowedMallocSize)
+ return BackendAllocator.ReturnNullOrDie();
+ uptr RoundedSize = RoundUpTo(Size, MinAlignment);
+ uptr ExtraBytes = ChunkHeaderSize;
+ if (Alignment > MinAlignment)
+ ExtraBytes += Alignment;
+ uptr NeededSize = RoundedSize + ExtraBytes;
+ if (NeededSize >= MaxAllowedMallocSize)
+ return BackendAllocator.ReturnNullOrDie();
+
+ void *Ptr;
+ if (LIKELY(!ThreadTornDown)) {
+ Ptr = BackendAllocator.Allocate(&Cache, NeededSize, MinAlignment);
+ } else {
+ SpinMutexLock l(&FallbackMutex);
+ Ptr = BackendAllocator.Allocate(&FallbackAllocatorCache, NeededSize,
+ MinAlignment);
+ }
+ if (!Ptr)
+ return BackendAllocator.ReturnNullOrDie();
+
+ // If requested, we will zero out the entire contents of the returned chunk.
+ if (ZeroContents && BackendAllocator.FromPrimary(Ptr))
+ memset(Ptr, 0, BackendAllocator.GetActuallyAllocatedSize(Ptr));
+
+ uptr AllocBeg = reinterpret_cast<uptr>(Ptr);
+ uptr ChunkBeg = AllocBeg + ChunkHeaderSize;
+ if (!IsAligned(ChunkBeg, Alignment))
+ ChunkBeg = RoundUpTo(ChunkBeg, Alignment);
+ CHECK_LE(ChunkBeg + Size, AllocBeg + NeededSize);
+ ScudoChunk *Chunk =
+ reinterpret_cast<ScudoChunk *>(ChunkBeg - ChunkHeaderSize);
+ UnpackedHeader Header = {};
+ Header.State = ChunkAllocated;
+ Header.Offset = (ChunkBeg - ChunkHeaderSize - AllocBeg) >> MinAlignmentLog;
+ Header.AllocType = Type;
+ Header.RequestedSize = Size;
+ Header.Salt = static_cast<u16>(Prng.Next());
+ Chunk->storeHeader(&Header);
+ void *UserPtr = reinterpret_cast<void *>(ChunkBeg);
+ // TODO(kostyak): hooks sound like a terrible idea security wise but might
+ // be needed for things to work properly?
+ // if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(UserPtr, Size);
+ return UserPtr;
+ }
+
+ // Deallocates a Chunk, which means adding it to the delayed free list (or
+ // Quarantine).
+ void deallocate(void *UserPtr, uptr DeleteSize, AllocType Type) {
+ if (UNLIKELY(!ThreadInited))
+ initThread();
+ // TODO(kostyak): see hook comment above
+ // if (&__sanitizer_free_hook) __sanitizer_free_hook(UserPtr);
+ if (!UserPtr)
+ return;
+ uptr ChunkBeg = reinterpret_cast<uptr>(UserPtr);
+ if (!IsAligned(ChunkBeg, MinAlignment)) {
+ dieWithMessage("ERROR: attempted to deallocate a chunk not properly "
+ "aligned at address %p\n", UserPtr);
+ }
+ ScudoChunk *Chunk =
+ reinterpret_cast<ScudoChunk *>(ChunkBeg - ChunkHeaderSize);
+ UnpackedHeader OldHeader;
+ Chunk->loadHeader(&OldHeader);
+ if (OldHeader.State != ChunkAllocated) {
+ dieWithMessage("ERROR: invalid chunk state when deallocating address "
+ "%p\n", Chunk);
+ }
+ UnpackedHeader NewHeader = OldHeader;
+ NewHeader.State = ChunkQuarantine;
+ Chunk->compareExchangeHeader(&NewHeader, &OldHeader);
+ if (DeallocationTypeMismatch) {
+ // The deallocation type has to match the allocation one.
+ if (NewHeader.AllocType != Type) {
+ // With the exception of memalign'd Chunks, that can be still be free'd.
+ if (NewHeader.AllocType != FromMemalign || Type != FromMalloc) {
+ dieWithMessage("ERROR: allocation type mismatch on address %p\n",
+ Chunk);
+ }
+ }
+ }
+ uptr Size = NewHeader.RequestedSize;
+ if (DeleteSizeMismatch) {
+ if (DeleteSize && DeleteSize != Size) {
+ dieWithMessage("ERROR: invalid sized delete on chunk at address %p\n",
+ Chunk);
+ }
+ }
+ if (LIKELY(!ThreadTornDown)) {
+ AllocatorQuarantine.Put(&ThreadQuarantineCache,
+ QuarantineCallback(&Cache), Chunk, Size);
+ } else {
+ SpinMutexLock l(&FallbackMutex);
+ AllocatorQuarantine.Put(&FallbackQuarantineCache,
+ QuarantineCallback(&FallbackAllocatorCache),
+ Chunk, Size);
+ }
+ }
+
+ // Returns the actual usable size of a chunk. Since this requires loading the
+ // header, we will return it in the second parameter, as it can be required
+ // by the caller to perform additional processing.
+ uptr getUsableSize(const void *Ptr, UnpackedHeader *Header) {
+ if (UNLIKELY(!ThreadInited))
+ initThread();
+ if (!Ptr)
+ return 0;
+ uptr ChunkBeg = reinterpret_cast<uptr>(Ptr);
+ ScudoChunk *Chunk =
+ reinterpret_cast<ScudoChunk *>(ChunkBeg - ChunkHeaderSize);
+ Chunk->loadHeader(Header);
+ // Getting the usable size of a chunk only makes sense if it's allocated.
+ if (Header->State != ChunkAllocated) {
+ dieWithMessage("ERROR: attempted to size a non-allocated chunk at "
+ "address %p\n", Chunk);
+ }
+ uptr Size =
+ BackendAllocator.GetActuallyAllocatedSize(Chunk->AllocBeg(Header));
+ // UsableSize works as malloc_usable_size, which is also what (AFAIU)
+ // tcmalloc's MallocExtension::GetAllocatedSize aims at providing. This
+ // means we will return the size of the chunk from the user beginning to
+ // the end of the 'user' allocation, hence us subtracting the header size
+ // and the offset from the size.
+ if (Size == 0)
+ return Size;
+ return Size - ChunkHeaderSize - (Header->Offset << MinAlignmentLog);
+ }
+
+ // Helper function that doesn't care about the header.
+ uptr getUsableSize(const void *Ptr) {
+ UnpackedHeader Header;
+ return getUsableSize(Ptr, &Header);
+ }
+
+ // Reallocates a chunk. We can save on a new allocation if the new requested
+ // size still fits in the chunk.
+ void *reallocate(void *OldPtr, uptr NewSize) {
+ if (UNLIKELY(!ThreadInited))
+ initThread();
+ UnpackedHeader OldHeader;
+ uptr Size = getUsableSize(OldPtr, &OldHeader);
+ uptr ChunkBeg = reinterpret_cast<uptr>(OldPtr);
+ ScudoChunk *Chunk =
+ reinterpret_cast<ScudoChunk *>(ChunkBeg - ChunkHeaderSize);
+ if (OldHeader.AllocType != FromMalloc) {
+ dieWithMessage("ERROR: invalid chunk type when reallocating address %p\n",
+ Chunk);
+ }
+ UnpackedHeader NewHeader = OldHeader;
+ // The new size still fits in the current chunk.
+ if (NewSize <= Size) {
+ NewHeader.RequestedSize = NewSize;
+ Chunk->compareExchangeHeader(&NewHeader, &OldHeader);
+ return OldPtr;
+ }
+ // Otherwise, we have to allocate a new chunk and copy the contents of the
+ // old one.
+ void *NewPtr = allocate(NewSize, MinAlignment, FromMalloc);
+ if (NewPtr) {
+ uptr OldSize = OldHeader.RequestedSize;
+ memcpy(NewPtr, OldPtr, Min(NewSize, OldSize));
+ NewHeader.State = ChunkQuarantine;
+ Chunk->compareExchangeHeader(&NewHeader, &OldHeader);
+ if (LIKELY(!ThreadTornDown)) {
+ AllocatorQuarantine.Put(&ThreadQuarantineCache,
+ QuarantineCallback(&Cache), Chunk, OldSize);
+ } else {
+ SpinMutexLock l(&FallbackMutex);
+ AllocatorQuarantine.Put(&FallbackQuarantineCache,
+ QuarantineCallback(&FallbackAllocatorCache),
+ Chunk, OldSize);
+ }
+ }
+ return NewPtr;
+ }
+
+ void *calloc(uptr NMemB, uptr Size) {
+ if (UNLIKELY(!ThreadInited))
+ initThread();
+ uptr Total = NMemB * Size;
+ if (Size != 0 && Total / Size != NMemB) // Overflow check
+ return BackendAllocator.ReturnNullOrDie();
+ void *Ptr = allocate(Total, MinAlignment, FromMalloc);
+ // If ZeroContents, the content of the chunk has already been zero'd out.
+ if (!ZeroContents && Ptr && BackendAllocator.FromPrimary(Ptr))
+ memset(Ptr, 0, getUsableSize(Ptr));
+ return Ptr;
+ }
+
+ void drainQuarantine() {
+ AllocatorQuarantine.Drain(&ThreadQuarantineCache,
+ QuarantineCallback(&Cache));
+ }
+};
+
+static Allocator Instance(LINKER_INITIALIZED);
+
+static ScudoAllocator &getAllocator() {
+ return Instance.BackendAllocator;
+}
+
+void initAllocator(const AllocatorOptions &Options) {
+ Instance.init(Options);
+}
+
+void drainQuarantine() {
+ Instance.drainQuarantine();
+}
+
+void *scudoMalloc(uptr Size, AllocType Type) {
+ return Instance.allocate(Size, Allocator::MinAlignment, Type);
+}
+
+void scudoFree(void *Ptr, AllocType Type) {
+ Instance.deallocate(Ptr, 0, Type);
+}
+
+void scudoSizedFree(void *Ptr, uptr Size, AllocType Type) {
+ Instance.deallocate(Ptr, Size, Type);
+}
+
+void *scudoRealloc(void *Ptr, uptr Size) {
+ if (!Ptr)
+ return Instance.allocate(Size, Allocator::MinAlignment, FromMalloc);
+ if (Size == 0) {
+ Instance.deallocate(Ptr, 0, FromMalloc);
+ return nullptr;
+ }
+ return Instance.reallocate(Ptr, Size);
+}
+
+void *scudoCalloc(uptr NMemB, uptr Size) {
+ return Instance.calloc(NMemB, Size);
+}
+
+void *scudoValloc(uptr Size) {
+ return Instance.allocate(Size, GetPageSizeCached(), FromMemalign);
+}
+
+void *scudoMemalign(uptr Alignment, uptr Size) {
+ return Instance.allocate(Size, Alignment, FromMemalign);
+}
+
+void *scudoPvalloc(uptr Size) {
+ uptr PageSize = GetPageSizeCached();
+ Size = RoundUpTo(Size, PageSize);
+ if (Size == 0) {
+ // pvalloc(0) should allocate one page.
+ Size = PageSize;
+ }
+ return Instance.allocate(Size, PageSize, FromMemalign);
+}
+
+int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size) {
+ *MemPtr = Instance.allocate(Size, Alignment, FromMemalign);
+ return 0;
+}
+
+void *scudoAlignedAlloc(uptr Alignment, uptr Size) {
+ // size must be a multiple of the alignment. To avoid a division, we first
+ // make sure that alignment is a power of 2.
+ CHECK(IsPowerOfTwo(Alignment));
+ CHECK_EQ((Size & (Alignment - 1)), 0);
+ return Instance.allocate(Size, Alignment, FromMalloc);
+}
+
+uptr scudoMallocUsableSize(void *Ptr) {
+ return Instance.getUsableSize(Ptr);
+}
+
+} // namespace __scudo
+
+using namespace __scudo;
+
+// MallocExtension helper functions
+
+uptr __sanitizer_get_current_allocated_bytes() {
+ uptr stats[AllocatorStatCount];
+ getAllocator().GetStats(stats);
+ return stats[AllocatorStatAllocated];
+}
+
+uptr __sanitizer_get_heap_size() {
+ uptr stats[AllocatorStatCount];
+ getAllocator().GetStats(stats);
+ return stats[AllocatorStatMapped];
+}
+
+uptr __sanitizer_get_free_bytes() {
+ return 1;
+}
+
+uptr __sanitizer_get_unmapped_bytes() {
+ return 1;
+}
+
+uptr __sanitizer_get_estimated_allocated_size(uptr size) {
+ return size;
+}
+
+int __sanitizer_get_ownership(const void *p) {
+ return Instance.getUsableSize(p) != 0;
+}
+
+uptr __sanitizer_get_allocated_size(const void *p) {
+ return Instance.getUsableSize(p);
+}
diff --git a/compiler-rt/lib/scudo/scudo_allocator.h b/compiler-rt/lib/scudo/scudo_allocator.h
new file mode 100644
index 00000000000..7e9c7886055
--- /dev/null
+++ b/compiler-rt/lib/scudo/scudo_allocator.h
@@ -0,0 +1,63 @@
+//===-- scudo_allocator.h ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// Header for scudo_allocator.cpp.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef SCUDO_ALLOCATOR_H_
+#define SCUDO_ALLOCATOR_H_
+
+#ifndef __x86_64__
+# error "The Scudo hardened allocator currently only supports x86_64."
+#endif
+
+#include "scudo_flags.h"
+
+#include "sanitizer_common/sanitizer_allocator.h"
+
+namespace __scudo {
+
+enum AllocType : u8 {
+ FromMalloc = 0, // Memory block came from malloc, realloc, calloc, etc.
+ FromNew = 1, // Memory block came from operator new.
+ FromNewArray = 2, // Memory block came from operator new [].
+ FromMemalign = 3, // Memory block came from memalign, posix_memalign, etc.
+};
+
+struct AllocatorOptions {
+ u32 QuarantineSizeMb;
+ u32 ThreadLocalQuarantineSizeKb;
+ bool MayReturnNull;
+ bool DeallocationTypeMismatch;
+ bool DeleteSizeMismatch;
+ bool ZeroContents;
+
+ void setFrom(const Flags *f, const CommonFlags *cf);
+ void copyTo(Flags *f, CommonFlags *cf) const;
+};
+
+void initAllocator(const AllocatorOptions &options);
+void drainQuarantine();
+
+void *scudoMalloc(uptr Size, AllocType Type);
+void scudoFree(void *Ptr, AllocType Type);
+void scudoSizedFree(void *Ptr, uptr Size, AllocType Type);
+void *scudoRealloc(void *Ptr, uptr Size);
+void *scudoCalloc(uptr NMemB, uptr Size);
+void *scudoMemalign(uptr Alignment, uptr Size);
+void *scudoValloc(uptr Size);
+void *scudoPvalloc(uptr Size);
+int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size);
+void *scudoAlignedAlloc(uptr Alignment, uptr Size);
+uptr scudoMallocUsableSize(void *Ptr);
+
+} // namespace __scudo
+
+#endif // SCUDO_ALLOCATOR_H_
diff --git a/compiler-rt/lib/scudo/scudo_flags.cpp b/compiler-rt/lib/scudo/scudo_flags.cpp
new file mode 100644
index 00000000000..430dcd2995d
--- /dev/null
+++ b/compiler-rt/lib/scudo/scudo_flags.cpp
@@ -0,0 +1,81 @@
+//===-- scudo_flags.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// Hardened Allocator flag parsing logic.
+///
+//===----------------------------------------------------------------------===//
+
+#include "scudo_flags.h"
+#include "scudo_utils.h"
+
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+
+namespace __scudo {
+
+Flags scudo_flags_dont_use_directly; // use via flags().
+
+void Flags::setDefaults() {
+#define SCUDO_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "scudo_flags.inc"
+#undef SCUDO_FLAG
+}
+
+static void RegisterScudoFlags(FlagParser *parser, Flags *f) {
+#define SCUDO_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(parser, #Name, Description, &f->Name);
+#include "scudo_flags.inc"
+#undef SCUDO_FLAG
+}
+
+void initFlags() {
+ SetCommonFlagsDefaults();
+ {
+ CommonFlags cf;
+ cf.CopyFrom(*common_flags());
+ cf.exitcode = 1;
+ OverrideCommonFlags(cf);
+ }
+ Flags *f = getFlags();
+ f->setDefaults();
+
+ FlagParser scudo_parser;
+ RegisterScudoFlags(&scudo_parser, f);
+ RegisterCommonFlags(&scudo_parser);
+
+ scudo_parser.ParseString(GetEnv("SCUDO_OPTIONS"));
+
+ InitializeCommonFlags();
+
+ // Sanity checks and default settings for the Quarantine parameters.
+
+ if (f->QuarantineSizeMb < 0) {
+ const int DefaultQuarantineSizeMb = 64;
+ f->QuarantineSizeMb = DefaultQuarantineSizeMb;
+ }
+ // We enforce an upper limit for the quarantine size of 4Gb.
+ if (f->QuarantineSizeMb > (4 * 1024)) {
+ dieWithMessage("ERROR: the quarantine size is too large\n");
+ }
+ if (f->ThreadLocalQuarantineSizeKb < 0) {
+ const int DefaultThreadLocalQuarantineSizeKb = 1024;
+ f->ThreadLocalQuarantineSizeKb = DefaultThreadLocalQuarantineSizeKb;
+ }
+ // And an upper limit of 128Mb for the thread quarantine cache.
+ if (f->ThreadLocalQuarantineSizeKb > (128 * 1024)) {
+ dieWithMessage("ERROR: the per thread quarantine cache size is too "
+ "large\n");
+ }
+}
+
+Flags *getFlags() {
+ return &scudo_flags_dont_use_directly;
+}
+
+}
diff --git a/compiler-rt/lib/scudo/scudo_flags.h b/compiler-rt/lib/scudo/scudo_flags.h
new file mode 100644
index 00000000000..c16f635d366
--- /dev/null
+++ b/compiler-rt/lib/scudo/scudo_flags.h
@@ -0,0 +1,33 @@
+//===-- scudo_flags.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// Header for scudo_flags.cpp.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef SCUDO_FLAGS_H_
+#define SCUDO_FLAGS_H_
+
+namespace __scudo {
+
+struct Flags {
+#define SCUDO_FLAG(Type, Name, DefaultValue, Description) Type Name;
+#include "scudo_flags.inc"
+#undef SCUDO_FLAG
+
+ void setDefaults();
+};
+
+Flags *getFlags();
+
+void initFlags();
+
+} // namespace __scudo
+
+#endif // SCUDO_FLAGS_H_
diff --git a/compiler-rt/lib/scudo/scudo_flags.inc b/compiler-rt/lib/scudo/scudo_flags.inc
new file mode 100644
index 00000000000..c7a2acf146c
--- /dev/null
+++ b/compiler-rt/lib/scudo/scudo_flags.inc
@@ -0,0 +1,35 @@
+//===-- scudo_flags.inc -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// Hardened Allocator runtime flags.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef SCUDO_FLAG
+# error "Define SCUDO_FLAG prior to including this file!"
+#endif
+
+SCUDO_FLAG(int, QuarantineSizeMb, 64,
+ "Size (in Mb) of quarantine used to delay the actual deallocation "
+ "of chunks. Lower value may reduce memory usage but decrease the "
+ "effectiveness of the mitigation.")
+
+SCUDO_FLAG(int, ThreadLocalQuarantineSizeKb, 1024,
+ "Size (in Kb) of per-thread cache used to offload the global "
+ "quarantine. Lower value may reduce memory usage but might increase "
+ "the contention on the global quarantine.")
+
+SCUDO_FLAG(bool, DeallocationTypeMismatch, true,
+ "Report errors on malloc/delete, new/free, new/delete[], etc.")
+
+SCUDO_FLAG(bool, DeleteSizeMismatch, true,
+ "Report errors on mismatch between size of new and delete.")
+
+SCUDO_FLAG(bool, ZeroContents, false,
+ "Zero chunk contents on allocation and deallocation.")
diff --git a/compiler-rt/lib/scudo/scudo_interceptors.cpp b/compiler-rt/lib/scudo/scudo_interceptors.cpp
new file mode 100644
index 00000000000..9204652d8b7
--- /dev/null
+++ b/compiler-rt/lib/scudo/scudo_interceptors.cpp
@@ -0,0 +1,75 @@
+//===-- scudo_interceptors.cpp ----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// Linux specific malloc interception functions.
+///
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_LINUX
+
+#include "scudo_allocator.h"
+
+#include "interception/interception.h"
+
+using namespace __scudo;
+
+INTERCEPTOR(void, free, void *ptr) {
+ scudoFree(ptr, FromMalloc);
+}
+
+INTERCEPTOR(void, cfree, void *ptr) {
+ scudoFree(ptr, FromMalloc);
+}
+
+INTERCEPTOR(void*, malloc, uptr size) {
+ return scudoMalloc(size, FromMalloc);
+}
+
+INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
+ return scudoRealloc(ptr, size);
+}
+
+INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
+ return scudoCalloc(nmemb, size);
+}
+
+INTERCEPTOR(void*, valloc, uptr size) {
+ return scudoValloc(size);
+}
+
+INTERCEPTOR(void*, memalign, uptr alignment, uptr size) {
+ return scudoMemalign(alignment, size);
+}
+
+INTERCEPTOR(void*, __libc_memalign, uptr alignment, uptr size) {
+ return scudoMemalign(alignment, size);
+}
+
+INTERCEPTOR(void*, pvalloc, uptr size) {
+ return scudoPvalloc(size);
+}
+
+INTERCEPTOR(void*, aligned_alloc, uptr alignment, uptr size) {
+ return scudoAlignedAlloc(alignment, size);
+}
+
+INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
+ return scudoPosixMemalign(memptr, alignment, size);
+}
+
+INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
+ return scudoMallocUsableSize(ptr);
+}
+
+INTERCEPTOR(int, mallopt, int cmd, int value) {
+ return -1;
+}
+
+#endif // SANITIZER_LINUX
diff --git a/compiler-rt/lib/scudo/scudo_new_delete.cpp b/compiler-rt/lib/scudo/scudo_new_delete.cpp
new file mode 100644
index 00000000000..172f5659c93
--- /dev/null
+++ b/compiler-rt/lib/scudo/scudo_new_delete.cpp
@@ -0,0 +1,69 @@
+//===-- scudo_new_delete.cpp ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// Interceptors for operators new and delete.
+///
+//===----------------------------------------------------------------------===//
+
+#include "scudo_allocator.h"
+
+#include "interception/interception.h"
+
+#include <cstddef>
+
+using namespace __scudo;
+
+#define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE
+
+// Fake std::nothrow_t to avoid including <new>.
+namespace std {
+struct nothrow_t {};
+} // namespace std
+
+CXX_OPERATOR_ATTRIBUTE
+void *operator new(size_t size) {
+ return scudoMalloc(size, FromNew);
+}
+CXX_OPERATOR_ATTRIBUTE
+void *operator new[](size_t size) {
+ return scudoMalloc(size, FromNewArray);
+}
+CXX_OPERATOR_ATTRIBUTE
+void *operator new(size_t size, std::nothrow_t const&) {
+ return scudoMalloc(size, FromNew);
+}
+CXX_OPERATOR_ATTRIBUTE
+void *operator new[](size_t size, std::nothrow_t const&) {
+ return scudoMalloc(size, FromNewArray);
+}
+
+CXX_OPERATOR_ATTRIBUTE
+void operator delete(void *ptr) NOEXCEPT {
+ return scudoFree(ptr, FromNew);
+}
+CXX_OPERATOR_ATTRIBUTE
+void operator delete[](void *ptr) NOEXCEPT {
+ return scudoFree(ptr, FromNewArray);
+}
+CXX_OPERATOR_ATTRIBUTE
+void operator delete(void *ptr, std::nothrow_t const&) NOEXCEPT {
+ return scudoFree(ptr, FromNew);
+}
+CXX_OPERATOR_ATTRIBUTE
+void operator delete[](void *ptr, std::nothrow_t const&) NOEXCEPT {
+ return scudoFree(ptr, FromNewArray);
+}
+CXX_OPERATOR_ATTRIBUTE
+void operator delete(void *ptr, size_t size) NOEXCEPT {
+ scudoSizedFree(ptr, size, FromNew);
+}
+CXX_OPERATOR_ATTRIBUTE
+void operator delete[](void *ptr, size_t size) NOEXCEPT {
+ scudoSizedFree(ptr, size, FromNewArray);
+}
diff --git a/compiler-rt/lib/scudo/scudo_termination.cpp b/compiler-rt/lib/scudo/scudo_termination.cpp
new file mode 100644
index 00000000000..32421d3d810
--- /dev/null
+++ b/compiler-rt/lib/scudo/scudo_termination.cpp
@@ -0,0 +1,41 @@
+//===-- scudo_termination.cpp -----------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// This file contains bare-bones termination functions to replace the
+/// __sanitizer ones, in order to avoid any potential abuse of the callbacks
+/// functionality.
+///
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_common.h"
+
+namespace __sanitizer {
+
+bool AddDieCallback(DieCallbackType callback) { return true; }
+
+bool RemoveDieCallback(DieCallbackType callback) { return true; }
+
+void SetUserDieCallback(DieCallbackType callback) {}
+
+void NORETURN Die() {
+ if (common_flags()->abort_on_error)
+ Abort();
+ internal__exit(common_flags()->exitcode);
+}
+
+void SetCheckFailedCallback(CheckFailedCallbackType callback) {}
+
+void NORETURN CheckFailed(const char *file, int line, const char *cond,
+ u64 v1, u64 v2) {
+ Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond,
+ v1, v2);
+ Die();
+}
+
+} // namespace __sanitizer
diff --git a/compiler-rt/lib/scudo/scudo_utils.cpp b/compiler-rt/lib/scudo/scudo_utils.cpp
new file mode 100644
index 00000000000..6b96e84ee9d
--- /dev/null
+++ b/compiler-rt/lib/scudo/scudo_utils.cpp
@@ -0,0 +1,133 @@
+//===-- scudo_utils.cpp -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// Platform specific utility functions.
+///
+//===----------------------------------------------------------------------===//
+
+#include "scudo_utils.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <unistd.h>
+
+#include <cstring>
+
+// TODO(kostyak): remove __sanitizer *Printf uses in favor for our own less
+// complicated string formatting code. The following is a
+// temporary workaround to be able to use __sanitizer::VSNPrintf.
+namespace __sanitizer {
+
+extern int VSNPrintf(char *buff, int buff_length, const char *format,
+ va_list args);
+
+} // namespace __sanitizer
+
+namespace __scudo {
+
+FORMAT(1, 2)
+void dieWithMessage(const char *Format, ...) {
+ // Our messages are tiny, 128 characters is more than enough.
+ char Message[128];
+ va_list Args;
+ va_start(Args, Format);
+ __sanitizer::VSNPrintf(Message, sizeof(Message), Format, Args);
+ va_end(Args);
+ RawWrite(Message);
+ Die();
+}
+
+typedef struct {
+ u32 Eax;
+ u32 Ebx;
+ u32 Ecx;
+ u32 Edx;
+} CPUIDInfo;
+
+static void getCPUID(CPUIDInfo *info, u32 leaf, u32 subleaf)
+{
+ asm volatile("cpuid"
+ : "=a" (info->Eax), "=b" (info->Ebx), "=c" (info->Ecx), "=d" (info->Edx)
+ : "a" (leaf), "c" (subleaf)
+ );
+}
+
+// Returns true is the CPU is a "GenuineIntel" or "AuthenticAMD"
+static bool isSupportedCPU()
+{
+ CPUIDInfo Info;
+
+ getCPUID(&Info, 0, 0);
+ if (memcmp(reinterpret_cast<char *>(&Info.Ebx), "Genu", 4) == 0 &&
+ memcmp(reinterpret_cast<char *>(&Info.Edx), "ineI", 4) == 0 &&
+ memcmp(reinterpret_cast<char *>(&Info.Ecx), "ntel", 4) == 0) {
+ return true;
+ }
+ if (memcmp(reinterpret_cast<char *>(&Info.Ebx), "Auth", 4) == 0 &&
+ memcmp(reinterpret_cast<char *>(&Info.Edx), "enti", 4) == 0 &&
+ memcmp(reinterpret_cast<char *>(&Info.Ecx), "cAMD", 4) == 0) {
+ return true;
+ }
+ return false;
+}
+
+bool testCPUFeature(CPUFeature feature)
+{
+ static bool InfoInitialized = false;
+ static CPUIDInfo CPUInfo = {};
+
+ if (InfoInitialized == false) {
+ if (isSupportedCPU() == true)
+ getCPUID(&CPUInfo, 1, 0);
+ else
+ UNIMPLEMENTED();
+ InfoInitialized = true;
+ }
+ switch (feature) {
+ case SSE4_2:
+ return ((CPUInfo.Ecx >> 20) & 0x1) != 0;
+ default:
+ break;
+ }
+ return false;
+}
+
+// readRetry will attempt to read Count bytes from the Fd specified, and if
+// interrupted will retry to read additional bytes to reach Count.
+static ssize_t readRetry(int Fd, u8 *Buffer, size_t Count) {
+ ssize_t AmountRead = 0;
+ while (static_cast<size_t>(AmountRead) < Count) {
+ ssize_t Result = read(Fd, Buffer + AmountRead, Count - AmountRead);
+ if (Result > 0)
+ AmountRead += Result;
+ else if (!Result)
+ break;
+ else if (errno != EINTR) {
+ AmountRead = -1;
+ break;
+ }
+ }
+ return AmountRead;
+}
+
+// Default constructor for Xorshift128Plus seeds the state with /dev/urandom
+Xorshift128Plus::Xorshift128Plus() {
+ int Fd = open("/dev/urandom", O_RDONLY);
+ bool Success = readRetry(Fd, reinterpret_cast<u8 *>(&State_0_),
+ sizeof(State_0_)) == sizeof(State_0_);
+ Success &= readRetry(Fd, reinterpret_cast<u8 *>(&State_1_),
+ sizeof(State_1_)) == sizeof(State_1_);
+ close(Fd);
+ if (!Success) {
+ dieWithMessage("ERROR: failed to read enough data from /dev/urandom.\n");
+ }
+}
+
+} // namespace __scudo
diff --git a/compiler-rt/lib/scudo/scudo_utils.h b/compiler-rt/lib/scudo/scudo_utils.h
new file mode 100644
index 00000000000..07394ff4b91
--- /dev/null
+++ b/compiler-rt/lib/scudo/scudo_utils.h
@@ -0,0 +1,59 @@
+//===-- scudo_utils.h -------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// Header for scudo_utils.cpp.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef SCUDO_UTILS_H_
+#define SCUDO_UTILS_H_
+
+#include <string.h>
+
+#include "sanitizer_common/sanitizer_common.h"
+
+namespace __scudo {
+
+template <class Dest, class Source>
+inline Dest bit_cast(const Source& source) {
+ static_assert(sizeof(Dest) == sizeof(Source), "Sizes are not equal!");
+ Dest dest;
+ memcpy(&dest, &source, sizeof(dest));
+ return dest;
+}
+
+void dieWithMessage(const char *Format, ...);
+
+enum CPUFeature {
+ SSE4_2 = 0,
+ ENUM_CPUFEATURE_MAX
+};
+bool testCPUFeature(CPUFeature feature);
+
+// Tiny PRNG based on https://en.wikipedia.org/wiki/Xorshift#xorshift.2B
+// The state (128 bits) will be stored in thread local storage.
+struct Xorshift128Plus {
+ public:
+ Xorshift128Plus();
+ u64 Next() {
+ u64 x = State_0_;
+ const u64 y = State_1_;
+ State_0_ = y;
+ x ^= x << 23;
+ State_1_ = x ^ y ^ (x >> 17) ^ (y >> 26);
+ return State_1_ + y;
+ }
+ private:
+ u64 State_0_;
+ u64 State_1_;
+};
+
+} // namespace __scudo
+
+#endif // SCUDO_UTILS_H_
OpenPOWER on IntegriCloud