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 @@ -10,7 +10,8 @@ #include "gwp_asan/options.h" #include "gwp_asan/utilities.h" -#include "optional/segv_handler.h" +#include "gwp_asan/optional/segv_handler.h" +#include "gwp_asan/random.h" // RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this // macro is defined before including . @@ -175,7 +176,13 @@ return nullptr; uintptr_t Ptr = State.slotToAddr(Index); - Ptr += allocationSlotOffset(Size); + // Should we right-align this allocation? + if (getRandomUnsigned32() % 2 == 0) { + Alignment Align = Alignment::DEFAULT; + if (PerfectlyRightAlign) + Align = Alignment::PERFECT; + Ptr += State.maximumAllocationSize() - alignedAllocationSize(Size, Align); + } AllocationMetadata *Meta = addrToMetadata(Ptr); // If a slot is multiple pages in size, and the allocation takes up a single @@ -278,26 +285,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,11 @@ "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. On Android, we use 8-byte alignment " + "for < 8 byte allocations, and 16-byte alignment otherwise 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,52 @@ 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__ + +size_t alignEight(size_t RealAllocationSize) { + if (RealAllocationSize <= 8) + return 8; + if (RealAllocationSize % 16 == 0) + return RealAllocationSize; + return RealAllocationSize + 16 - (RealAllocationSize % 16); +} + +size_t alignPowerOfTwo(size_t RealAllocationSize) { + if (RealAllocationSize <= 2) + return RealAllocationSize; + if (RealAllocationSize <= 4) + return 4; + return alignEight(RealAllocationSize); +} + +#ifdef __BIONIC__ +Alignment PlatformDefaultAlignment = Alignment::EIGHT_THEN_SIXTEEN; +#else // __BIONIC__ +Alignment PlatformDefaultAlignment = Alignment::POWER_OF_TWO; +#endif // __BIONIC__ + +size_t alignedAllocationSize(size_t RealAllocationSize, Alignment Align) { + assert(RealAllocationSize > 0); + if (Align == Alignment::DEFAULT) + Align = PlatformDefaultAlignment; + + switch (Align) { + case Alignment::EIGHT_THEN_SIXTEEN: + return alignEight(RealAllocationSize); + case Alignment::POWER_OF_TWO: + return alignPowerOfTwo(RealAllocationSize); + case Alignment::PERFECT: + return RealAllocationSize; + case Alignment::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> AllocSizeToAlignment = { {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); + EXPECT_EQ(KV.second, gwp_asan::alignedAllocationSize( + KV.first, gwp_asan::Alignment::POWER_OF_TWO)); + } +} + +TEST(AlignmentTest, AlignToEight) { + std::vector> AllocSizeToAlignment = { + {1, 8}, {2, 8}, {3, 8}, {4, 8}, {5, 8}, {7, 8}, + {8, 8}, {9, 16}, {15, 16}, {16, 16}, {17, 32}, {31, 32}, + {32, 32}, {33, 48}, {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 : AllocSizeToAlignment) { + EXPECT_EQ(KV.second, + gwp_asan::alignedAllocationSize( + KV.first, gwp_asan::Alignment::EIGHT_THEN_SIXTEEN)); + } +} - GPA.deallocate(Ptr); +TEST(AlignmentTest, PerfectAlignment) { + for (size_t i = 1; i <= 4096; ++i) { + EXPECT_EQ(i, + gwp_asan::alignedAllocationSize(i, gwp_asan::Alignment::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,23 @@ #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 Alignment { + // Default => POWER_OF_TWO on most platforms, EIGHT_THEN_SIXTEEN on Android. + DEFAULT, + POWER_OF_TWO, + EIGHT_THEN_SIXTEEN, + PERFECT, +}; + +// Returns the aligned size of an allocation. +size_t alignedAllocationSize(size_t RealAllocationSize, + Alignment Align = Alignment::DEFAULT); } // namespace gwp_asan