1 //===-- guarded_pool_allocator_posix.cpp ------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "gwp_asan/common.h" 10 #include "gwp_asan/guarded_pool_allocator.h" 11 #include "gwp_asan/platform_specific/guarded_pool_allocator_tls.h" 12 #include "gwp_asan/utilities.h" 13 14 #include <assert.h> 15 #include <pthread.h> 16 #include <stdint.h> 17 #include <stdlib.h> 18 #include <sys/mman.h> 19 #include <time.h> 20 #include <unistd.h> 21 22 #ifdef ANDROID 23 #include <sys/prctl.h> 24 #define PR_SET_VMA 0x53564d41 25 #define PR_SET_VMA_ANON_NAME 0 26 #endif // ANDROID 27 28 namespace { 29 void MaybeSetMappingName(void *Mapping, size_t Size, const char *Name) { 30 #ifdef ANDROID 31 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Mapping, Size, Name); 32 #endif // ANDROID 33 // Anonymous mapping names are only supported on Android. 34 return; 35 } 36 } // anonymous namespace 37 38 namespace gwp_asan { 39 40 void GuardedPoolAllocator::initPRNG() { 41 getThreadLocals()->RandomState = 42 static_cast<uint32_t>(time(nullptr) + getThreadID()); 43 } 44 45 void *GuardedPoolAllocator::map(size_t Size, const char *Name) const { 46 assert((Size % State.PageSize) == 0); 47 void *Ptr = mmap(nullptr, Size, PROT_READ | PROT_WRITE, 48 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 49 Check(Ptr != MAP_FAILED, "Failed to map guarded pool allocator memory"); 50 MaybeSetMappingName(Ptr, Size, Name); 51 return Ptr; 52 } 53 54 void GuardedPoolAllocator::unmap(void *Ptr, size_t Size) const { 55 assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0); 56 assert((Size % State.PageSize) == 0); 57 Check(munmap(Ptr, Size) == 0, 58 "Failed to unmap guarded pool allocator memory."); 59 } 60 61 void *GuardedPoolAllocator::reserveGuardedPool(size_t Size) { 62 assert((Size % State.PageSize) == 0); 63 void *Ptr = 64 mmap(nullptr, Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 65 Check(Ptr != MAP_FAILED, "Failed to reserve guarded pool allocator memory"); 66 MaybeSetMappingName(Ptr, Size, kGwpAsanGuardPageName); 67 return Ptr; 68 } 69 70 void GuardedPoolAllocator::unreserveGuardedPool() { 71 unmap(reinterpret_cast<void *>(State.GuardedPagePool), 72 State.GuardedPagePoolEnd - State.GuardedPagePool); 73 } 74 75 void GuardedPoolAllocator::allocateInGuardedPool(void *Ptr, size_t Size) const { 76 assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0); 77 assert((Size % State.PageSize) == 0); 78 Check(mprotect(Ptr, Size, PROT_READ | PROT_WRITE) == 0, 79 "Failed to allocate in guarded pool allocator memory"); 80 MaybeSetMappingName(Ptr, Size, kGwpAsanAliveSlotName); 81 } 82 83 void GuardedPoolAllocator::deallocateInGuardedPool(void *Ptr, 84 size_t Size) const { 85 assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0); 86 assert((Size % State.PageSize) == 0); 87 // mmap() a PROT_NONE page over the address to release it to the system, if 88 // we used mprotect() here the system would count pages in the quarantine 89 // against the RSS. 90 Check(mmap(Ptr, Size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 91 0) != MAP_FAILED, 92 "Failed to deallocate in guarded pool allocator memory"); 93 MaybeSetMappingName(Ptr, Size, kGwpAsanGuardPageName); 94 } 95 96 size_t GuardedPoolAllocator::getPlatformPageSize() { 97 return sysconf(_SC_PAGESIZE); 98 } 99 100 void GuardedPoolAllocator::installAtFork() { 101 auto Disable = []() { 102 if (auto *S = getSingleton()) 103 S->disable(); 104 }; 105 auto Enable = []() { 106 if (auto *S = getSingleton()) 107 S->enable(); 108 }; 109 pthread_atfork(Disable, Enable, Enable); 110 } 111 } // namespace gwp_asan 112