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 @@ -149,11 +149,6 @@ // Unreserve the guarded slot. void freeSlot(size_t SlotIndex); - // Returns the offset (in bytes) between the start of a guarded slot and where - // the start of the allocation should take place. Determined using the size of - // the allocation and the options provided at init-time. - uintptr_t allocationSlotOffset(size_t AllocationSize) const; - // Raise a SEGV and set the corresponding fields in the Allocator's State in // order to tell the crash handler what happened. Used when errors are // detected internally (Double Free, Invalid Free). 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 @@ -8,9 +8,10 @@ #include "gwp_asan/guarded_pool_allocator.h" +#include "gwp_asan/optional/segv_handler.h" #include "gwp_asan/options.h" +#include "gwp_asan/random.h" #include "gwp_asan/utilities.h" -#include "optional/segv_handler.h" // RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this // macro is defined before including . @@ -175,7 +176,14 @@ return nullptr; uintptr_t Ptr = State.slotToAddr(Index); - Ptr += allocationSlotOffset(Size); + // 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); // If a slot is multiple pages in size, and the allocation takes up a single @@ -278,26 +286,6 @@ FreeSlots[FreeSlotsLength++] = SlotIndex; } -uintptr_t GuardedPoolAllocator::allocationSlotOffset(size_t Size) const { - assert(Size > 0); - - bool ShouldRightAlign = getRandomUnsigned32() % 2 == 0; - if (!ShouldRightAlign) - return 0; - - uintptr_t Offset = State.maximumAllocationSize(); - if (!PerfectlyRightAlign) { - if (Size == 3) - Size = 4; - else if (Size > 4 && Size <= 8) - Size = 8; - else if (Size > 8 && (Size % 16) != 0) - Size += 16 - (Size % 16); - } - Offset -= Size; - return Offset; -} - GWP_ASAN_TLS_INITIAL_EXEC GuardedPoolAllocator::ThreadLocalPackedVariables GuardedPoolAllocator::ThreadLocals; 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 @@ -17,9 +17,10 @@ "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. 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.") + "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 " diff --git a/compiler-rt/lib/gwp_asan/platform_specific/utilities_posix.cpp b/compiler-rt/lib/gwp_asan/platform_specific/utilities_posix.cpp --- a/compiler-rt/lib/gwp_asan/platform_specific/utilities_posix.cpp +++ b/compiler-rt/lib/gwp_asan/platform_specific/utilities_posix.cpp @@ -9,16 +9,18 @@ #include "gwp_asan/definitions.h" #include "gwp_asan/utilities.h" -#ifdef ANDROID +#include + +#ifdef __BIONIC__ #include extern "C" GWP_ASAN_WEAK void android_set_abort_message(const char *); -#else // ANDROID +#else // __BIONIC__ #include #endif namespace gwp_asan { -#ifdef ANDROID +#ifdef __BIONIC__ void Check(bool Condition, const char *Message) { if (Condition) return; @@ -26,13 +28,62 @@ android_set_abort_message(Message); abort(); } -#else // ANDROID +#else // __BIONIC__ void Check(bool Condition, const char *Message) { if (Condition) return; fprintf(stderr, "%s", Message); __builtin_trap(); } -#endif // ANDROID +#endif // __BIONIC__ + +// 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::ANDROID; +#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(); + } +} } // namespace gwp_asan 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 @@ -7,21 +7,38 @@ //===----------------------------------------------------------------------===// #include "gwp_asan/tests/harness.h" +#include "gwp_asan/utilities.h" -TEST_F(DefaultGuardedPoolAllocator, BasicAllocation) { - std::vector> AllocSizeToAlignment = { +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, 16}, {31, 16}, - {32, 16}, {33, 16}, {4095, 4096}, {4096, 4096}, + {8, 8}, {9, 16}, {15, 16}, {16, 16}, {17, 32}, {31, 32}, + {32, 32}, {33, 48}, {4095, 4096}, {4096, 4096}, }; - for (const auto &KV : AllocSizeToAlignment) { - void *Ptr = GPA.allocate(KV.first); - EXPECT_NE(nullptr, Ptr); + for (const auto &KV : AskedSizeToAlignedSize) { + EXPECT_EQ(KV.second, + gwp_asan::rightAlignedAllocationSize( + KV.first, gwp_asan::AlignmentStrategy::POWER_OF_TWO)); + } +} + +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}, + }; - // Check the alignment of the pointer is as expected. - EXPECT_EQ(0u, reinterpret_cast(Ptr) % KV.second); + for (const auto &KV : AskedSizeToAlignedSize) { + EXPECT_EQ(KV.second, gwp_asan::rightAlignedAllocationSize( + KV.first, gwp_asan::AlignmentStrategy::BIONIC)); + } +} - GPA.deallocate(Ptr); +TEST(AlignmentTest, PerfectAlignment) { + for (size_t i = 1; i <= 4096; ++i) { + EXPECT_EQ(i, gwp_asan::rightAlignedAllocationSize( + i, gwp_asan::AlignmentStrategy::PERFECT)); } } 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 @@ -8,8 +8,24 @@ #include "gwp_asan/definitions.h" +#include +#include + namespace gwp_asan { // Checks that `Condition` is true, otherwise fails in a platform-specific way // with `Message`. void Check(bool Condition, const char *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