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 <errno.h> |
16 | #include <pthread.h> |
17 | #include <stdint.h> |
18 | #include <stdlib.h> |
19 | #include <sys/mman.h> |
20 | #include <time.h> |
21 | #include <unistd.h> |
22 | |
23 | #ifdef ANDROID |
24 | #include <sys/prctl.h> |
25 | #define PR_SET_VMA 0x53564d41 |
26 | #define PR_SET_VMA_ANON_NAME 0 |
27 | #endif // ANDROID |
28 | |
29 | namespace { |
30 | void MaybeSetMappingName(void *Mapping, size_t Size, const char *Name) { |
31 | #ifdef ANDROID |
32 | prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Mapping, Size, Name); |
33 | #endif // ANDROID |
34 | // Anonymous mapping names are only supported on Android. |
35 | return; |
36 | } |
37 | } // anonymous namespace |
38 | |
39 | namespace gwp_asan { |
40 | |
41 | void GuardedPoolAllocator::initPRNG() { |
42 | getThreadLocals()->RandomState = |
43 | static_cast<uint32_t>(time(timer: nullptr) + getThreadID()); |
44 | } |
45 | |
46 | void *GuardedPoolAllocator::map(size_t Size, const char *Name) const { |
47 | assert((Size % State.PageSize) == 0); |
48 | void *Ptr = mmap(addr: nullptr, len: Size, PROT_READ | PROT_WRITE, |
49 | MAP_ANONYMOUS | MAP_PRIVATE, fd: -1, offset: 0); |
50 | checkWithErrorCode(Condition: Ptr != MAP_FAILED, |
51 | Message: "Failed to map guarded pool allocator memory" , errno); |
52 | MaybeSetMappingName(Mapping: Ptr, Size, Name); |
53 | return Ptr; |
54 | } |
55 | |
56 | void GuardedPoolAllocator::unmap(void *Ptr, size_t Size) const { |
57 | assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0); |
58 | assert((Size % State.PageSize) == 0); |
59 | checkWithErrorCode(Condition: munmap(addr: Ptr, len: Size) == 0, |
60 | Message: "Failed to unmap guarded pool allocator memory." , errno); |
61 | } |
62 | |
63 | void *GuardedPoolAllocator::reserveGuardedPool(size_t Size) { |
64 | assert((Size % State.PageSize) == 0); |
65 | void *Ptr = |
66 | mmap(addr: nullptr, len: Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, fd: -1, offset: 0); |
67 | checkWithErrorCode(Condition: Ptr != MAP_FAILED, |
68 | Message: "Failed to reserve guarded pool allocator memory" , errno); |
69 | MaybeSetMappingName(Mapping: Ptr, Size, Name: kGwpAsanGuardPageName); |
70 | return Ptr; |
71 | } |
72 | |
73 | void GuardedPoolAllocator::unreserveGuardedPool() { |
74 | unmap(Ptr: reinterpret_cast<void *>(State.GuardedPagePool), |
75 | Size: State.GuardedPagePoolEnd - State.GuardedPagePool); |
76 | } |
77 | |
78 | void GuardedPoolAllocator::allocateInGuardedPool(void *Ptr, size_t Size) const { |
79 | assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0); |
80 | assert((Size % State.PageSize) == 0); |
81 | checkWithErrorCode(Condition: mprotect(addr: Ptr, len: Size, PROT_READ | PROT_WRITE) == 0, |
82 | Message: "Failed to allocate in guarded pool allocator memory" , |
83 | errno); |
84 | MaybeSetMappingName(Mapping: Ptr, Size, Name: kGwpAsanAliveSlotName); |
85 | } |
86 | |
87 | void GuardedPoolAllocator::deallocateInGuardedPool(void *Ptr, |
88 | size_t Size) const { |
89 | assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0); |
90 | assert((Size % State.PageSize) == 0); |
91 | // mmap() a PROT_NONE page over the address to release it to the system, if |
92 | // we used mprotect() here the system would count pages in the quarantine |
93 | // against the RSS. |
94 | checkWithErrorCode( |
95 | Condition: mmap(addr: Ptr, len: Size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, fd: -1, |
96 | offset: 0) != MAP_FAILED, |
97 | Message: "Failed to deallocate in guarded pool allocator memory" , errno); |
98 | MaybeSetMappingName(Mapping: Ptr, Size, Name: kGwpAsanGuardPageName); |
99 | } |
100 | |
101 | size_t GuardedPoolAllocator::getPlatformPageSize() { |
102 | return sysconf(_SC_PAGESIZE); |
103 | } |
104 | |
105 | void GuardedPoolAllocator::installAtFork() { |
106 | static bool AtForkInstalled = false; |
107 | if (AtForkInstalled) |
108 | return; |
109 | AtForkInstalled = true; |
110 | auto Disable = []() { |
111 | if (auto *S = getSingleton()) |
112 | S->disable(); |
113 | }; |
114 | auto Enable = []() { |
115 | if (auto *S = getSingleton()) |
116 | S->enable(); |
117 | }; |
118 | pthread_atfork(prepare: Disable, parent: Enable, child: Enable); |
119 | } |
120 | } // namespace gwp_asan |
121 | |