[compiler-rt] 5f2a74c - [GWP-ASan] Update alignment on Android.

Mitch Phillips via llvm-commits llvm-commits at lists.llvm.org
Wed Feb 12 15:25:48 PST 2020


Author: Mitch Phillips
Date: 2020-02-12T15:24:58-08:00
New Revision: 5f2a74c87abc4ed926e93e75d1c3178723a47322

URL: https://github.com/llvm/llvm-project/commit/5f2a74c87abc4ed926e93e75d1c3178723a47322
DIFF: https://github.com/llvm/llvm-project/commit/5f2a74c87abc4ed926e93e75d1c3178723a47322.diff

LOG: [GWP-ASan] Update alignment on Android.

Summary:
Android has different alignment requirements. You can read more about
them here
(https://cs.android.com/android/platform/superproject/+/master:bionic/tests/malloc_test.cpp;l=808),
but the general gist is that for malloc(x <= 8), we do malloc(8), and
for everything else, we do 16-byte alignment.

Reviewers: eugenis, morehouse, cferris

Reviewed By: eugenis, morehouse

Subscribers: #sanitizers, llvm-commits, pcc

Tags: #sanitizers, #llvm

Differential Revision: https://reviews.llvm.org/D74364

Added: 
    

Modified: 
    compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp
    compiler-rt/lib/gwp_asan/guarded_pool_allocator.h
    compiler-rt/lib/gwp_asan/options.inc
    compiler-rt/lib/gwp_asan/platform_specific/utilities_posix.cpp
    compiler-rt/lib/gwp_asan/tests/alignment.cpp
    compiler-rt/lib/gwp_asan/utilities.h

Removed: 
    


################################################################################
diff  --git a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.cpp
index 7af99e482f3d..b2602e4caa59 100644
--- 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 <inttypes.h>.
@@ -175,7 +176,14 @@ void *GuardedPoolAllocator::allocate(size_t Size) {
     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 @@ void GuardedPoolAllocator::freeSlot(size_t SlotIndex) {
   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/guarded_pool_allocator.h b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h
index 53d1635346f0..ae00506c5692 100644
--- a/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h
+++ b/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h
@@ -149,11 +149,6 @@ class GuardedPoolAllocator {
   // 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/options.inc b/compiler-rt/lib/gwp_asan/options.inc
index 3b941b19af5e..6cdddfbad84d 100644
--- a/compiler-rt/lib/gwp_asan/options.inc
+++ b/compiler-rt/lib/gwp_asan/options.inc
@@ -17,9 +17,10 @@ GWP_ASAN_OPTION(
     "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
index 68d7ee7867a9..2e7403458af5 100644
--- 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 <assert.h>
+
+#ifdef __BIONIC__
 #include <stdlib.h>
 extern "C" GWP_ASAN_WEAK void android_set_abort_message(const char *);
-#else // ANDROID
+#else // __BIONIC__
 #include <stdio.h>
 #endif
 
 namespace gwp_asan {
 
-#ifdef ANDROID
+#ifdef __BIONIC__
 void Check(bool Condition, const char *Message) {
   if (Condition)
     return;
@@ -26,13 +28,62 @@ void Check(bool Condition, const char *Message) {
     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
index 8b1ce8ccd712..bf98f1f58338 100644
--- 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<std::pair<int, int>> AllocSizeToAlignment = {
+TEST(AlignmentTest, PowerOfTwo) {
+  std::vector<std::pair<size_t, size_t>> 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<std::pair<size_t, size_t>> 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<uintptr_t>(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
index c307b381fe5f..71d525f9e14c 100644
--- 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 <stddef.h>
+#include <stdint.h>
+
 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


        


More information about the llvm-commits mailing list