diff --git a/compiler-rt/lib/gwp_asan/CMakeLists.txt b/compiler-rt/lib/gwp_asan/CMakeLists.txt --- a/compiler-rt/lib/gwp_asan/CMakeLists.txt +++ b/compiler-rt/lib/gwp_asan/CMakeLists.txt @@ -11,7 +11,6 @@ platform_specific/utilities_posix.cpp guarded_pool_allocator.cpp stack_trace_compressor.cpp - utilities.cpp ) set(GWP_ASAN_HEADERS diff --git a/compiler-rt/lib/gwp_asan/common.h b/compiler-rt/lib/gwp_asan/common.h --- a/compiler-rt/lib/gwp_asan/common.h +++ b/compiler-rt/lib/gwp_asan/common.h @@ -49,7 +49,7 @@ static constexpr size_t kMaxTraceLengthToCollect = 128; // Records the given allocation metadata into this struct. - void RecordAllocation(uintptr_t Addr, size_t Size); + void RecordAllocation(uintptr_t Addr, size_t RequestedSize); // Record that this allocation is now deallocated. void RecordDeallocation(); @@ -70,7 +70,7 @@ // valid, as the allocation has never occurred. uintptr_t Addr = 0; // Represents the actual size of the allocation. - size_t Size = 0; + size_t RequestedSize = 0; CallSiteInfo AllocationTrace; CallSiteInfo DeallocationTrace; diff --git a/compiler-rt/lib/gwp_asan/common.cpp b/compiler-rt/lib/gwp_asan/common.cpp --- a/compiler-rt/lib/gwp_asan/common.cpp +++ b/compiler-rt/lib/gwp_asan/common.cpp @@ -40,7 +40,7 @@ void AllocationMetadata::RecordAllocation(uintptr_t AllocAddr, size_t AllocSize) { Addr = AllocAddr; - Size = AllocSize; + RequestedSize = AllocSize; IsDeallocated = false; AllocationTrace.ThreadID = getThreadID(); diff --git a/compiler-rt/lib/gwp_asan/crash_handler.cpp b/compiler-rt/lib/gwp_asan/crash_handler.cpp --- a/compiler-rt/lib/gwp_asan/crash_handler.cpp +++ b/compiler-rt/lib/gwp_asan/crash_handler.cpp @@ -103,7 +103,7 @@ size_t __gwp_asan_get_allocation_size( const gwp_asan::AllocationMetadata *AllocationMeta) { - return AllocationMeta->Size; + return AllocationMeta->RequestedSize; } uint64_t __gwp_asan_get_allocation_thread_id( diff --git a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h --- a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h +++ b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h @@ -19,6 +19,7 @@ #include #include +// IWYU pragma: no_include <__stddef_max_align_t.h> namespace gwp_asan { // This class is the primary implementation of the allocator portion of GWP- @@ -93,10 +94,13 @@ return State.pointerIsMine(Ptr); } - // Allocate memory in a guarded slot, and return a pointer to the new - // allocation. Returns nullptr if the pool is empty, the requested size is too - // large for this pool to handle, or the requested size is zero. - void *allocate(size_t Size); + // Allocate memory in a guarded slot, with the specified `Alignment`. Returns + // nullptr if the pool is empty, if the alignnment is not a power of two, or + // if the size/alignment makes the allocation too large for this pool to + // handle. By default, uses strong alignment (i.e. `max_align_t`), see + // http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2293.htm for discussion of + // alignment issues in the standard. + void *allocate(size_t Size, size_t Alignment = alignof(max_align_t)); // Deallocate memory in a guarded slot. The provided pointer must have been // allocated using this pool. This will set the guarded slot as inaccessible. @@ -111,6 +115,18 @@ // Returns a pointer to the AllocatorState region. const AllocatorState *getAllocatorState() const { return &State; } + // Exposed as protected for testing. +protected: + // Returns the actual allocation size required to service an allocation with + // the provided Size and Alignment. + static size_t getRequiredBackingSize(size_t Size, size_t Alignment, + size_t PageSize); + + // Returns the provided pointer that meets the specified alignment, depending + // on whether it's left or right aligned. + static uintptr_t getAlignedPtr(uintptr_t Ptr, size_t Alignment, + bool IsRightAligned); + private: // Name of actively-occupied slot mappings. static constexpr const char *kGwpAsanAliveSlotName = "GWP-ASan Alive Slot"; diff --git a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp --- a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp +++ b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp @@ -12,6 +12,7 @@ #include "gwp_asan/utilities.h" #include +#include using AllocationMetadata = gwp_asan::AllocationMetadata; using Error = gwp_asan::Error; @@ -32,6 +33,11 @@ uintptr_t getPageAddr(uintptr_t Ptr, uintptr_t PageSize) { return Ptr & ~(PageSize - 1); } + +// Unused on -DNDEBUG builds. +__attribute__((unused)) bool IsPowerOfTwo(uintptr_t x) { + return (x & (x - 1)) == 0; +} } // anonymous namespace // Gets the singleton implementation of this class. Thread-compatible until @@ -63,8 +69,6 @@ assert((PageSize & (PageSize - 1)) == 0); State.PageSize = PageSize; - PerfectlyRightAlign = Opts.PerfectlyRightAlign; - size_t PoolBytesRequired = PageSize * (1 + State.MaxSimultaneousAllocations) + State.MaxSimultaneousAllocations * State.maximumAllocationSize(); @@ -113,7 +117,7 @@ const AllocationMetadata &Meta = Metadata[i]; if (Meta.Addr && !Meta.IsDeallocated && Meta.Addr >= Start && Meta.Addr < Start + Size) - Cb(Meta.Addr, Meta.Size, Arg); + Cb(Meta.Addr, Meta.RequestedSize, Arg); } } @@ -138,7 +142,42 @@ *getThreadLocals() = ThreadLocalPackedVariables(); } -void *GuardedPoolAllocator::allocate(size_t Size) { +// Note, minimum backing allocation size in GWP-ASan is always one page, and +// each slot could potentially be multiple pages (but always in +// page-increments). Thus, for anything that requires less than page size +// alignment, we don't need to allocate extra padding to ensure the alignment +// can be met. +size_t GuardedPoolAllocator::getRequiredBackingSize(size_t Size, + size_t Alignment, + size_t PageSize) { + assert(IsPowerOfTwo(Alignment) && "Alignment must be a power of two!"); + assert(Alignment != 0 && "Alignment should be non-zero"); + assert(Size != 0 && "Alignment should be non-zero"); + + Size = Size ?: 1; + Alignment = Alignment ?: 1; + + if (Alignment <= PageSize) + return roundUpTo(Size, Alignment); + + return Size + Alignment; +} + +uintptr_t GuardedPoolAllocator::getAlignedPtr(uintptr_t Ptr, size_t Alignment, + bool IsRightAligned) { + assert(IsPowerOfTwo(Alignment) && "Alignment must be a power of two!"); + assert(Alignment != 0 && "Alignment should be non-zero"); + if ((Ptr & (Alignment - 1)) == 0) + return Ptr; + + if (IsRightAligned) + Ptr -= Ptr & (Alignment - 1); + else + Ptr += Alignment - (Ptr & (Alignment - 1)); + return Ptr; +} + +void *GuardedPoolAllocator::allocate(size_t Size, size_t Alignment) { // GuardedPagePoolEnd == 0 when GWP-ASan is disabled. If we are disabled, fall // back to the supporting allocator. if (State.GuardedPagePoolEnd == 0) { @@ -148,14 +187,22 @@ return nullptr; } + Size = Size ?: 1; + Alignment = Alignment ?: 1; + + if (!IsPowerOfTwo(Alignment) || Alignment > State.maximumAllocationSize() || + Size > State.maximumAllocationSize()) + return nullptr; + + size_t BackingSize = getRequiredBackingSize(Size, Alignment, State.PageSize); + if (BackingSize > State.maximumAllocationSize()) + return nullptr; + // Protect against recursivity. if (getThreadLocals()->RecursiveGuard) return nullptr; ScopedRecursiveGuard SRG; - if (Size == 0 || Size > State.maximumAllocationSize()) - return nullptr; - size_t Index; { ScopedLock L(PoolMutex); @@ -165,28 +212,32 @@ if (Index == kInvalidSlotID) return nullptr; - uintptr_t Ptr = State.slotToAddr(Index); - // Should we right-align this allocation? - if (getRandomUnsigned32() % 2 == 0) { - AlignmentStrategy Align = AlignmentStrategy::DEFAULT; - if (PerfectlyRightAlign) - Align = AlignmentStrategy::PERFECT; - Ptr += - State.maximumAllocationSize() - rightAlignedAllocationSize(Size, Align); - } - AllocationMetadata *Meta = addrToMetadata(Ptr); + uintptr_t SlotStart = State.slotToAddr(Index); + AllocationMetadata *Meta = addrToMetadata(SlotStart); + uintptr_t SlotEnd = State.slotToAddr(Index) + State.maximumAllocationSize(); + uintptr_t UserPtr; + // Randomly choose whether to left-align or right-align the allocation, and + // then apply the necessary adjustments to get an aligned pointer. + if (getRandomUnsigned32() % 2 == 0) + UserPtr = getAlignedPtr(SlotStart, Alignment, /* IsRightAligned */ false); + else + UserPtr = + getAlignedPtr(SlotEnd - Size, Alignment, /* IsRightAligned */ true); + + assert(UserPtr >= SlotStart && UserPtr < SlotEnd); // If a slot is multiple pages in size, and the allocation takes up a single // page, we can improve overflow detection by leaving the unused pages as // unmapped. const size_t PageSize = State.PageSize; - allocateInGuardedPool(reinterpret_cast(getPageAddr(Ptr, PageSize)), - roundUpTo(Size, PageSize)); + allocateInGuardedPool( + reinterpret_cast(getPageAddr(UserPtr, PageSize)), + roundUpTo(Size, PageSize)); - Meta->RecordAllocation(Ptr, Size); + Meta->RecordAllocation(UserPtr, Size); Meta->AllocationTrace.RecordBacktrace(Backtrace); - return reinterpret_cast(Ptr); + return reinterpret_cast(UserPtr); } void GuardedPoolAllocator::trapOnAddress(uintptr_t Address, Error E) { @@ -250,7 +301,7 @@ ScopedLock L(PoolMutex); AllocationMetadata *Meta = addrToMetadata(reinterpret_cast(Ptr)); assert(Meta->Addr == reinterpret_cast(Ptr)); - return Meta->Size; + return Meta->RequestedSize; } AllocationMetadata *GuardedPoolAllocator::addrToMetadata(uintptr_t Ptr) const { diff --git a/compiler-rt/lib/gwp_asan/options.inc b/compiler-rt/lib/gwp_asan/options.inc --- a/compiler-rt/lib/gwp_asan/options.inc +++ b/compiler-rt/lib/gwp_asan/options.inc @@ -23,16 +23,6 @@ "Is GWP-ASan enabled? Defaults to " GWP_ASAN_STRINGIFY( GWP_ASAN_DEFAULT_ENABLED) ".") -GWP_ASAN_OPTION( - bool, PerfectlyRightAlign, false, - "When allocations are right-aligned, should we perfectly align them up to " - "the page boundary? By default (false), we round up allocation size to the " - "nearest power of two (1, 2, 4, 8, 16) up to a maximum of 16-byte " - "alignment for performance reasons. For Bionic, we use 8-byte alignment by " - "default. Setting this to true can find single byte buffer-overflows for " - "multibyte allocations at the cost of performance, and may be incompatible " - "with some architectures.") - GWP_ASAN_OPTION(int, MaxSimultaneousAllocations, 16, "Number of simultaneously-guarded allocations available in the " "pool. Defaults to 16.") diff --git a/compiler-rt/lib/gwp_asan/tests/alignment.cpp b/compiler-rt/lib/gwp_asan/tests/alignment.cpp --- a/compiler-rt/lib/gwp_asan/tests/alignment.cpp +++ b/compiler-rt/lib/gwp_asan/tests/alignment.cpp @@ -6,41 +6,83 @@ // //===----------------------------------------------------------------------===// +#include "gwp_asan/guarded_pool_allocator.h" #include "gwp_asan/tests/harness.h" -#include "gwp_asan/utilities.h" -#include +class AlignmentTestGPA : public gwp_asan::GuardedPoolAllocator { +public: + static size_t getRequiredBackingSize(size_t Size, size_t Alignment, + size_t PageSize) { + return GuardedPoolAllocator::getRequiredBackingSize(Size, Alignment, + PageSize); + } + static uintptr_t getAlignedPtr(uintptr_t Ptr, size_t Alignment, + bool IsRightAligned) { + return GuardedPoolAllocator::getAlignedPtr(Ptr, Alignment, IsRightAligned); + } +}; -TEST(AlignmentTest, PowerOfTwo) { - std::vector> AskedSizeToAlignedSize = { - {1, 1}, {2, 2}, {3, 4}, {4, 4}, {5, 8}, {7, 8}, - {8, 8}, {9, 16}, {15, 16}, {16, 16}, {17, 32}, {31, 32}, - {32, 32}, {33, 48}, {4095, 4096}, {4096, 4096}, - }; +// Assume 4KiB pages across all these tests. +constexpr size_t kPageSize = 0x1000; - for (const auto &KV : AskedSizeToAlignedSize) { - EXPECT_EQ(KV.second, - gwp_asan::rightAlignedAllocationSize( - KV.first, gwp_asan::AlignmentStrategy::POWER_OF_TWO)); - } -} +TEST(AlignmentTest, PointerAlignment) { -TEST(AlignmentTest, AlignBionic) { - std::vector> AskedSizeToAlignedSize = { - {1, 8}, {2, 8}, {3, 8}, {4, 8}, {5, 8}, {7, 8}, - {8, 8}, {9, 16}, {15, 16}, {16, 16}, {17, 24}, {31, 32}, - {32, 32}, {33, 40}, {4095, 4096}, {4096, 4096}, - }; + EXPECT_EQ(0x1000, AlignmentTestGPA::getAlignedPtr( + /* Ptr */ 0x1000, /* Alignment */ 0x1, + /* IsRightAligned */ false)); + EXPECT_EQ(0x1000, AlignmentTestGPA::getAlignedPtr( + /* Ptr */ 0x1000, /* Alignment */ 0x1000, + /* IsRightAligned */ false)); - for (const auto &KV : AskedSizeToAlignedSize) { - EXPECT_EQ(KV.second, gwp_asan::rightAlignedAllocationSize( - KV.first, gwp_asan::AlignmentStrategy::BIONIC)); - } + EXPECT_EQ(0x1000, AlignmentTestGPA::getAlignedPtr( + /* Ptr */ 0x1000, /* Alignment */ 0x1, + /* IsRightAligned */ true)); + EXPECT_EQ(0x1000, AlignmentTestGPA::getAlignedPtr( + /* Ptr */ 0x1000, /* Alignment */ 0x1000, + /* IsRightAligned */ false)); + + EXPECT_EQ(0x2000, AlignmentTestGPA::getAlignedPtr( + /* Ptr */ 0x3000, /* Alignment */ 0x2000, + /* IsRightAligned */ true)); + EXPECT_EQ(0x4000, AlignmentTestGPA::getAlignedPtr( + /* Ptr */ 0x3000, /* Alignment */ 0x2000, + /* IsRightAligned */ false)); + + EXPECT_EQ(0x3000, AlignmentTestGPA::getAlignedPtr( + /* Ptr */ 0x3000, /* Alignment */ 0x800, + /* IsRightAligned */ true)); + EXPECT_EQ(0x3000, AlignmentTestGPA::getAlignedPtr( + /* Ptr */ 0x3000, /* Alignment */ 0x800, + /* IsRightAligned */ false)); + + EXPECT_EQ(0x3000, AlignmentTestGPA::getAlignedPtr( + /* Ptr */ 0x3137, /* Alignment */ 0x1000, + /* IsRightAligned */ true)); + EXPECT_EQ(0x4000, AlignmentTestGPA::getAlignedPtr( + /* Ptr */ 0x3137, /* Alignment */ 0x1000, + /* IsRightAligned */ false)); } -TEST(AlignmentTest, PerfectAlignment) { - for (size_t i = 1; i <= 4096; ++i) { - EXPECT_EQ(i, gwp_asan::rightAlignedAllocationSize( - i, gwp_asan::AlignmentStrategy::PERFECT)); - } +TEST(AlignmentTest, RequiredBackingSize) { + EXPECT_EQ(kPageSize, AlignmentTestGPA::getRequiredBackingSize( + /* Size */ 1, /* Alignment */ kPageSize, kPageSize)); + EXPECT_EQ(kPageSize, + AlignmentTestGPA::getRequiredBackingSize( + /* Size */ 1337, /* Alignment */ kPageSize, kPageSize)); + EXPECT_EQ(kPageSize, + AlignmentTestGPA::getRequiredBackingSize( + /* Size */ kPageSize, /* Alignment */ kPageSize, kPageSize)); + + EXPECT_EQ(kPageSize, AlignmentTestGPA::getRequiredBackingSize( + /* Size */ kPageSize, /* Alignment */ 1, kPageSize)); + EXPECT_EQ(kPageSize * 2 + 1, + AlignmentTestGPA::getRequiredBackingSize( + /* Size */ 1, /* Alignment */ kPageSize * 2, kPageSize)); + EXPECT_EQ(kPageSize * 2, AlignmentTestGPA::getRequiredBackingSize( + /* Size */ kPageSize * 2, + /* Alignment */ kPageSize, kPageSize)); + + EXPECT_EQ(kPageSize * 2, AlignmentTestGPA::getRequiredBackingSize( + /* Size */ kPageSize + 1, + /* Alignment */ kPageSize, kPageSize)); } diff --git a/compiler-rt/lib/gwp_asan/tests/backtrace.cpp b/compiler-rt/lib/gwp_asan/tests/backtrace.cpp --- a/compiler-rt/lib/gwp_asan/tests/backtrace.cpp +++ b/compiler-rt/lib/gwp_asan/tests/backtrace.cpp @@ -48,6 +48,7 @@ TEST_F(BacktraceGuardedPoolAllocator, UseAfterFree) { void *Ptr = AllocateMemory(GPA); DeallocateMemory(GPA, Ptr); + std::cerr << "Got ptr: " << Ptr << "\n"; std::string DeathRegex = "Use After Free.*"; DeathRegex.append("TouchMemory.*"); diff --git a/compiler-rt/lib/gwp_asan/tests/basic.cpp b/compiler-rt/lib/gwp_asan/tests/basic.cpp --- a/compiler-rt/lib/gwp_asan/tests/basic.cpp +++ b/compiler-rt/lib/gwp_asan/tests/basic.cpp @@ -39,6 +39,28 @@ TEST_F(DefaultGuardedPoolAllocator, TooLargeAllocation) { EXPECT_EQ(nullptr, GPA.allocate(GPA.getAllocatorState()->maximumAllocationSize() + 1)); + EXPECT_EQ(nullptr, GPA.allocate(SIZE_MAX, 0)); + EXPECT_EQ(nullptr, GPA.allocate(SIZE_MAX, 1)); + EXPECT_EQ(nullptr, GPA.allocate(0, SIZE_MAX / 2)); + EXPECT_EQ(nullptr, GPA.allocate(1, SIZE_MAX / 2)); + EXPECT_EQ(nullptr, GPA.allocate(SIZE_MAX, SIZE_MAX / 2)); +} + +TEST_F(DefaultGuardedPoolAllocator, ZeroSizeAndAlignmentAllocations) { + void *P; + EXPECT_NE(nullptr, (P = GPA.allocate(0, 0))); + GPA.deallocate(P); + EXPECT_NE(nullptr, (P = GPA.allocate(1, 0))); + GPA.deallocate(P); + EXPECT_NE(nullptr, (P = GPA.allocate(0, 1))); + GPA.deallocate(P); +} + +TEST_F(DefaultGuardedPoolAllocator, NonPowerOfTwoAlignment) { + EXPECT_EQ(nullptr, GPA.allocate(0, 3)); + EXPECT_EQ(nullptr, GPA.allocate(1, 3)); + EXPECT_EQ(nullptr, GPA.allocate(0, SIZE_MAX)); + EXPECT_EQ(nullptr, GPA.allocate(1, SIZE_MAX)); } TEST_F(CustomGuardedPoolAllocator, AllocAllSlots) { diff --git a/compiler-rt/lib/gwp_asan/tests/crash_handler_api.cpp b/compiler-rt/lib/gwp_asan/tests/crash_handler_api.cpp --- a/compiler-rt/lib/gwp_asan/tests/crash_handler_api.cpp +++ b/compiler-rt/lib/gwp_asan/tests/crash_handler_api.cpp @@ -29,7 +29,7 @@ size_t Slot = State.getNearestSlot(Addr); Metadata[Slot].Addr = Addr; - Metadata[Slot].Size = Size; + Metadata[Slot].RequestedSize = Size; Metadata[Slot].IsDeallocated = IsDeallocated; Metadata[Slot].AllocationTrace.ThreadID = 123; Metadata[Slot].DeallocationTrace.ThreadID = 321; @@ -80,7 +80,8 @@ __gwp_asan_get_metadata(&State, Metadata, ErrorPtr); EXPECT_NE(nullptr, Meta); EXPECT_EQ(Metadata[Index].Addr, __gwp_asan_get_allocation_address(Meta)); - EXPECT_EQ(Metadata[Index].Size, __gwp_asan_get_allocation_size(Meta)); + EXPECT_EQ(Metadata[Index].RequestedSize, + __gwp_asan_get_allocation_size(Meta)); EXPECT_EQ(Metadata[Index].AllocationTrace.ThreadID, __gwp_asan_get_allocation_thread_id(Meta)); diff --git a/compiler-rt/lib/gwp_asan/utilities.h b/compiler-rt/lib/gwp_asan/utilities.h --- a/compiler-rt/lib/gwp_asan/utilities.h +++ b/compiler-rt/lib/gwp_asan/utilities.h @@ -23,19 +23,6 @@ return; die(Message); } - -enum class AlignmentStrategy { - // Default => POWER_OF_TWO on most platforms, BIONIC for Android Bionic. - DEFAULT, - POWER_OF_TWO, - BIONIC, - PERFECT, -}; - -// Returns the real size of a right-aligned allocation. -size_t rightAlignedAllocationSize( - size_t RealAllocationSize, - AlignmentStrategy Align = AlignmentStrategy::DEFAULT); } // namespace gwp_asan #endif // GWP_ASAN_UTILITIES_H_ diff --git a/compiler-rt/lib/gwp_asan/utilities.cpp b/compiler-rt/lib/gwp_asan/utilities.cpp deleted file mode 100644 --- a/compiler-rt/lib/gwp_asan/utilities.cpp +++ /dev/null @@ -1,63 +0,0 @@ -//===-- utilities.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 "gwp_asan/utilities.h" - -#include - -namespace gwp_asan { -// See `bionic/tests/malloc_test.cpp` in the Android source for documentation -// regarding their alignment guarantees. We always round up to the closest -// 8-byte window. As GWP-ASan's malloc(X) can always get exactly an X-sized -// allocation, an allocation that rounds up to 16-bytes will always be given a -// 16-byte aligned allocation. -static size_t alignBionic(size_t RealAllocationSize) { - if (RealAllocationSize % 8 == 0) - return RealAllocationSize; - return RealAllocationSize + 8 - (RealAllocationSize % 8); -} - -static size_t alignPowerOfTwo(size_t RealAllocationSize) { - if (RealAllocationSize <= 2) - return RealAllocationSize; - if (RealAllocationSize <= 4) - return 4; - if (RealAllocationSize <= 8) - return 8; - if (RealAllocationSize % 16 == 0) - return RealAllocationSize; - return RealAllocationSize + 16 - (RealAllocationSize % 16); -} - -#ifdef __BIONIC__ -static constexpr AlignmentStrategy PlatformDefaultAlignment = - AlignmentStrategy::BIONIC; -#else // __BIONIC__ -static constexpr AlignmentStrategy PlatformDefaultAlignment = - AlignmentStrategy::POWER_OF_TWO; -#endif // __BIONIC__ - -size_t rightAlignedAllocationSize(size_t RealAllocationSize, - AlignmentStrategy Align) { - assert(RealAllocationSize > 0); - if (Align == AlignmentStrategy::DEFAULT) - Align = PlatformDefaultAlignment; - - switch (Align) { - case AlignmentStrategy::BIONIC: - return alignBionic(RealAllocationSize); - case AlignmentStrategy::POWER_OF_TWO: - return alignPowerOfTwo(RealAllocationSize); - case AlignmentStrategy::PERFECT: - return RealAllocationSize; - case AlignmentStrategy::DEFAULT: - __builtin_unreachable(); - } - __builtin_unreachable(); -} -} // namespace gwp_asan diff --git a/compiler-rt/lib/scudo/scudo_allocator.cpp b/compiler-rt/lib/scudo/scudo_allocator.cpp --- a/compiler-rt/lib/scudo/scudo_allocator.cpp +++ b/compiler-rt/lib/scudo/scudo_allocator.cpp @@ -303,18 +303,23 @@ bool ForceZeroContents = false) { initThreadMaybe(); + if (UNLIKELY(Alignment > MaxAlignment)) { + if (AllocatorMayReturnNull()) + return nullptr; + reportAllocationAlignmentTooBig(Alignment, MaxAlignment); + } + #ifdef GWP_ASAN_HOOKS if (UNLIKELY(GuardedAlloc.shouldSample())) { - if (void *Ptr = GuardedAlloc.allocate(Size)) + if (void *Ptr = GuardedAlloc.allocate(Size, Alignment)) { + if (SCUDO_CAN_USE_HOOKS && &__sanitizer_malloc_hook) + __sanitizer_malloc_hook(Ptr, Size); + return Ptr; + } } #endif // GWP_ASAN_HOOKS - if (UNLIKELY(Alignment > MaxAlignment)) { - if (AllocatorMayReturnNull()) - return nullptr; - reportAllocationAlignmentTooBig(Alignment, MaxAlignment); - } if (UNLIKELY(Alignment < MinAlignment)) Alignment = MinAlignment; diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h --- a/compiler-rt/lib/scudo/standalone/combined.h +++ b/compiler-rt/lib/scudo/standalone/combined.h @@ -280,8 +280,12 @@ #ifdef GWP_ASAN_HOOKS if (UNLIKELY(GuardedAlloc.shouldSample())) { - if (void *Ptr = GuardedAlloc.allocate(roundUpTo(Size, Alignment))) + if (void *Ptr = GuardedAlloc.allocate(Size, Alignment)) { + if (UNLIKELY(&__scudo_allocate_hook)) + __scudo_allocate_hook(Ptr, Size); + Stats.add(StatAllocated, Size); return Ptr; + } } #endif // GWP_ASAN_HOOKS @@ -480,18 +484,20 @@ // being destroyed properly. Any other heap operation will do a full init. initThreadMaybe(/*MinimalInit=*/true); + if (UNLIKELY(&__scudo_deallocate_hook)) + __scudo_deallocate_hook(Ptr); + + if (UNLIKELY(!Ptr)) + return; + #ifdef GWP_ASAN_HOOKS if (UNLIKELY(GuardedAlloc.pointerIsMine(Ptr))) { GuardedAlloc.deallocate(Ptr); + Stats.add(StatFree, GuardedAlloc.getSize(Ptr)); return; } #endif // GWP_ASAN_HOOKS - if (UNLIKELY(&__scudo_deallocate_hook)) - __scudo_deallocate_hook(Ptr); - - if (UNLIKELY(!Ptr)) - return; if (UNLIKELY(!isAligned(reinterpret_cast(Ptr), MinAlignment))) reportMisalignedPointer(AllocatorAction::Deallocating, Ptr); @@ -547,6 +553,7 @@ if (NewPtr) memcpy(NewPtr, OldPtr, (NewSize < OldSize) ? NewSize : OldSize); GuardedAlloc.deallocate(OldPtr); + Stats.add(StatFree, OldSize); return NewPtr; } #endif // GWP_ASAN_HOOKS diff --git a/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp b/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp --- a/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp +++ b/compiler-rt/lib/scudo/standalone/tests/wrappers_c_test.cpp @@ -83,6 +83,18 @@ EXPECT_EQ(errno, ENOMEM); } +TEST(ScudoWrappersCTest, SmallAlign) { + void *P; + for (size_t Size = 1; Size <= 0x10000; Size <<= 1) { + for (size_t Align = 1; Align <= 0x10000; Align <<= 1) { + for (size_t Count = 0; Count < 3; ++Count) { + P = memalign(Align, Size); + EXPECT_TRUE(reinterpret_cast(P) % Align == 0); + } + } + } +} + TEST(ScudoWrappersCTest, Memalign) { void *P; for (size_t I = FIRST_32_SECOND_64(2U, 3U); I <= 18U; I++) {