Commit e1440f59 authored by Mitch Phillips's avatar Mitch Phillips
Browse files

[GWP-ASan] Add names to anonymous mappings.

Summary:
Adds names to anonymous GWP-ASan mappings. This helps Android with debugging
via. /proc/maps, as GWP-ASan-allocated mappings are now easily identifyable.

Reviewers: eugenis, cferris

Reviewed By: eugenis

Subscribers: merge_guards_bot, #sanitizers, llvm-commits, cryptoad, pcc

Tags: #sanitizers, #llvm

Differential Revision: https://reviews.llvm.org/D73373
parent daee63f9
Loading
Loading
Loading
Loading
+16 −11
Original line number Diff line number Diff line
@@ -144,16 +144,18 @@ void GuardedPoolAllocator::init(const options::Options &Opts) {
  size_t PoolBytesRequired =
      PageSize * (1 + MaxSimultaneousAllocations) +
      MaxSimultaneousAllocations * maximumAllocationSize();
  void *GuardedPoolMemory = mapMemory(PoolBytesRequired);
  void *GuardedPoolMemory = mapMemory(PoolBytesRequired, kGwpAsanGuardPageName);

  size_t BytesRequired = MaxSimultaneousAllocations * sizeof(*Metadata);
  Metadata = reinterpret_cast<AllocationMetadata *>(mapMemory(BytesRequired));
  markReadWrite(Metadata, BytesRequired);
  Metadata = reinterpret_cast<AllocationMetadata *>(
      mapMemory(BytesRequired, kGwpAsanMetadataName));
  markReadWrite(Metadata, BytesRequired, kGwpAsanMetadataName);

  // Allocate memory and set up the free pages queue.
  BytesRequired = MaxSimultaneousAllocations * sizeof(*FreeSlots);
  FreeSlots = reinterpret_cast<size_t *>(mapMemory(BytesRequired));
  markReadWrite(FreeSlots, BytesRequired);
  FreeSlots = reinterpret_cast<size_t *>(
      mapMemory(BytesRequired, kGwpAsanFreeSlotsName));
  markReadWrite(FreeSlots, BytesRequired, kGwpAsanFreeSlotsName);

  // Multiply the sample rate by 2 to give a good, fast approximation for (1 /
  // SampleRate) chance of sampling.
@@ -183,16 +185,18 @@ void GuardedPoolAllocator::enable() { PoolMutex.unlock(); }
void GuardedPoolAllocator::uninitTestOnly() {
  if (GuardedPagePool) {
    unmapMemory(reinterpret_cast<void *>(GuardedPagePool),
                GuardedPagePoolEnd - GuardedPagePool);
                GuardedPagePoolEnd - GuardedPagePool, kGwpAsanGuardPageName);
    GuardedPagePool = 0;
    GuardedPagePoolEnd = 0;
  }
  if (Metadata) {
    unmapMemory(Metadata, MaxSimultaneousAllocations * sizeof(*Metadata));
    unmapMemory(Metadata, MaxSimultaneousAllocations * sizeof(*Metadata),
                kGwpAsanMetadataName);
    Metadata = nullptr;
  }
  if (FreeSlots) {
    unmapMemory(FreeSlots, MaxSimultaneousAllocations * sizeof(*FreeSlots));
    unmapMemory(FreeSlots, MaxSimultaneousAllocations * sizeof(*FreeSlots),
                kGwpAsanFreeSlotsName);
    FreeSlots = nullptr;
  }
  uninstallSignalHandlers();
@@ -228,7 +232,8 @@ void *GuardedPoolAllocator::allocate(size_t Size) {
  // 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.
  markReadWrite(reinterpret_cast<void *>(getPageAddr(Ptr)), Size);
  markReadWrite(reinterpret_cast<void *>(getPageAddr(Ptr)), Size,
                kGwpAsanAliveSlotName);

  Meta->RecordAllocation(Ptr, Size, Backtrace);

@@ -260,8 +265,8 @@ void GuardedPoolAllocator::deallocate(void *Ptr) {
    Meta->RecordDeallocation(Backtrace);
  }

  markInaccessible(reinterpret_cast<void *>(SlotStart),
                   maximumAllocationSize());
  markInaccessible(reinterpret_cast<void *>(SlotStart), maximumAllocationSize(),
                   kGwpAsanGuardPageName);

  // And finally, lock again to release the slot back into the pool.
  ScopedLock L(PoolMutex);
+18 −5
Original line number Diff line number Diff line
@@ -28,6 +28,9 @@ namespace gwp_asan {
// otherwise).
class GuardedPoolAllocator {
public:
  // Name of the GWP-ASan mapping that for `Metadata`.
  static constexpr const char *kGwpAsanMetadataName = "GWP-ASan Metadata";

  static constexpr uint64_t kInvalidThreadID = UINT64_MAX;

  enum class Error {
@@ -154,6 +157,14 @@ public:
  static uint64_t getThreadID();

private:
  // Name of actively-occupied slot mappings.
  static constexpr const char *kGwpAsanAliveSlotName = "GWP-ASan Alive Slot";
  // Name of the guard pages. This includes all slots that are not actively in
  // use (i.e. were never used, or have been free()'d).)
  static constexpr const char *kGwpAsanGuardPageName = "GWP-ASan Guard Page";
  // Name of the mapping for `FreeSlots`.
  static constexpr const char *kGwpAsanFreeSlotsName = "GWP-ASan Metadata";

  static constexpr size_t kInvalidSlotID = SIZE_MAX;

  // These functions anonymously map memory or change the permissions of mapped
@@ -162,11 +173,13 @@ private:
  // return on error, instead electing to kill the calling process on failure.
  // Note that memory is initially mapped inaccessible. In order for RW
  // mappings, call mapMemory() followed by markReadWrite() on the returned
  // pointer.
  void *mapMemory(size_t Size) const;
  void unmapMemory(void *Addr, size_t Size) const;
  void markReadWrite(void *Ptr, size_t Size) const;
  void markInaccessible(void *Ptr, size_t Size) const;
  // pointer. Each mapping is named on platforms that support it, primarily
  // Android. This name must be a statically allocated string, as the Android
  // kernel uses the string pointer directly.
  void *mapMemory(size_t Size, const char *Name) const;
  void unmapMemory(void *Ptr, size_t Size, const char *Name) const;
  void markReadWrite(void *Ptr, size_t Size, const char *Name) const;
  void markInaccessible(void *Ptr, size_t Size, const char *Name) const;

  // Get the page size from the platform-specific implementation. Only needs to
  // be called once, and the result should be cached in PageSize in this class.
+27 −7
Original line number Diff line number Diff line
@@ -17,9 +17,22 @@
#include <sys/types.h>
#include <unistd.h>

namespace gwp_asan {
#ifdef ANDROID
#include <sys/prctl.h>
#define PR_SET_VMA 0x53564d41
#define PR_SET_VMA_ANON_NAME 0
#endif // ANDROID

void MaybeSetMappingName(void *Mapping, size_t Size, const char *Name) {
#ifdef ANDROID
  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Mapping, Size, Name);
#endif // ANDROID
  // Anonymous mapping names are only supported on Android.
  return;
}

void *GuardedPoolAllocator::mapMemory(size_t Size) const {
namespace gwp_asan {
void *GuardedPoolAllocator::mapMemory(size_t Size, const char *Name) const {
  void *Ptr =
      mmap(nullptr, Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);

@@ -28,29 +41,35 @@ void *GuardedPoolAllocator::mapMemory(size_t Size) const {
    Printf("  mmap(nullptr, %zu, ...) failed.\n", Size);
    exit(EXIT_FAILURE);
  }
  MaybeSetMappingName(Ptr, Size, Name);
  return Ptr;
}

void GuardedPoolAllocator::unmapMemory(void *Addr, size_t Size) const {
  int Res = munmap(Addr, Size);
void GuardedPoolAllocator::unmapMemory(void *Ptr, size_t Size,
                                       const char *Name) const {
  int Res = munmap(Ptr, Size);

  if (Res != 0) {
    Printf("Failed to unmap guarded pool allocator memory, errno: %d\n", errno);
    Printf("  unmmap(%p, %zu, ...) failed.\n", Addr, Size);
    Printf("  unmmap(%p, %zu, ...) failed.\n", Ptr, Size);
    exit(EXIT_FAILURE);
  }
  MaybeSetMappingName(Ptr, Size, Name);
}

void GuardedPoolAllocator::markReadWrite(void *Ptr, size_t Size) const {
void GuardedPoolAllocator::markReadWrite(void *Ptr, size_t Size,
                                         const char *Name) const {
  if (mprotect(Ptr, Size, PROT_READ | PROT_WRITE) != 0) {
    Printf("Failed to set guarded pool allocator memory at as RW, errno: %d\n",
           errno);
    Printf("  mprotect(%p, %zu, RW) failed.\n", Ptr, Size);
    exit(EXIT_FAILURE);
  }
  MaybeSetMappingName(Ptr, Size, Name);
}

void GuardedPoolAllocator::markInaccessible(void *Ptr, size_t Size) const {
void GuardedPoolAllocator::markInaccessible(void *Ptr, size_t Size,
                                            const char *Name) const {
  // mmap() a PROT_NONE page over the address to release it to the system, if
  // we used mprotect() here the system would count pages in the quarantine
  // against the RSS.
@@ -62,6 +81,7 @@ void GuardedPoolAllocator::markInaccessible(void *Ptr, size_t Size) const {
    Printf("  mmap(%p, %zu, NONE, ...) failed.\n", Ptr, Size);
    exit(EXIT_FAILURE);
  }
  MaybeSetMappingName(Ptr, Size, Name);
}

size_t GuardedPoolAllocator::getPlatformPageSize() {