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(timer: 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(addr: nullptr, len: Size, PROT_READ | PROT_WRITE, |
48 | MAP_ANONYMOUS | MAP_PRIVATE, fd: -1, offset: 0); |
49 | Check(Condition: Ptr != MAP_FAILED, Message: "Failed to map guarded pool allocator memory" ); |
50 | MaybeSetMappingName(Mapping: 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(Condition: munmap(addr: Ptr, len: Size) == 0, |
58 | Message: "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(addr: nullptr, len: Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, fd: -1, offset: 0); |
65 | Check(Condition: Ptr != MAP_FAILED, Message: "Failed to reserve guarded pool allocator memory" ); |
66 | MaybeSetMappingName(Mapping: Ptr, Size, Name: kGwpAsanGuardPageName); |
67 | return Ptr; |
68 | } |
69 | |
70 | void GuardedPoolAllocator::unreserveGuardedPool() { |
71 | unmap(Ptr: reinterpret_cast<void *>(State.GuardedPagePool), |
72 | Size: 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(Condition: mprotect(addr: Ptr, len: Size, PROT_READ | PROT_WRITE) == 0, |
79 | Message: "Failed to allocate in guarded pool allocator memory" ); |
80 | MaybeSetMappingName(Mapping: Ptr, Size, Name: 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(Condition: mmap(addr: Ptr, len: Size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, fd: -1, |
91 | offset: 0) != MAP_FAILED, |
92 | Message: "Failed to deallocate in guarded pool allocator memory" ); |
93 | MaybeSetMappingName(Mapping: Ptr, Size, Name: kGwpAsanGuardPageName); |
94 | } |
95 | |
96 | size_t GuardedPoolAllocator::getPlatformPageSize() { |
97 | return sysconf(_SC_PAGESIZE); |
98 | } |
99 | |
100 | void GuardedPoolAllocator::installAtFork() { |
101 | static bool AtForkInstalled = false; |
102 | if (AtForkInstalled) |
103 | return; |
104 | AtForkInstalled = true; |
105 | auto Disable = []() { |
106 | if (auto *S = getSingleton()) |
107 | S->disable(); |
108 | }; |
109 | auto Enable = []() { |
110 | if (auto *S = getSingleton()) |
111 | S->enable(); |
112 | }; |
113 | pthread_atfork(prepare: Disable, parent: Enable, child: Enable); |
114 | } |
115 | } // namespace gwp_asan |
116 | |