1 // Copyright (c) 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_
6 #define THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_
7 
8 #include <errno.h>
9 #include <sys/mman.h>
10 
11 #include "build/build_config.h"
12 
13 #if defined(OS_APPLE)
14 #include <mach/mach.h>
15 #endif
16 #if defined(OS_ANDROID)
17 #include <sys/prctl.h>
18 #endif
19 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD)
20 #include <sys/resource.h>
21 
22 #include <algorithm>
23 #endif
24 
25 #include "third_party/base/allocator/partition_allocator/page_allocator.h"
26 #include "third_party/base/check.h"
27 #include "third_party/base/notreached.h"
28 
29 #ifndef MAP_ANONYMOUS
30 #define MAP_ANONYMOUS MAP_ANON
31 #endif
32 
33 namespace pdfium {
34 namespace base {
35 
36 #if defined(OS_ANDROID)
37 namespace {
PageTagToName(PageTag tag)38 const char* PageTagToName(PageTag tag) {
39   // Important: All the names should be string literals. As per prctl.h in
40   // //third_party/android_ndk the kernel keeps a pointer to the name instead
41   // of copying it.
42   //
43   // Having the name in .rodata ensures that the pointer remains valid as
44   // long as the mapping is alive.
45   switch (tag) {
46     case PageTag::kBlinkGC:
47       return "blink_gc";
48     case PageTag::kPartitionAlloc:
49       return "partition_alloc";
50     case PageTag::kChromium:
51       return "chromium";
52     case PageTag::kV8:
53       return "v8";
54     default:
55       DCHECK(false);
56       return "";
57   }
58 }
59 }  // namespace
60 #endif  // defined(OS_ANDROID)
61 
62 // |mmap| uses a nearby address if the hint address is blocked.
63 constexpr bool kHintIsAdvisory = true;
64 std::atomic<int32_t> s_allocPageErrorCode{0};
65 
GetAccessFlags(PageAccessibilityConfiguration accessibility)66 int GetAccessFlags(PageAccessibilityConfiguration accessibility) {
67   switch (accessibility) {
68     case PageRead:
69       return PROT_READ;
70     case PageReadWrite:
71       return PROT_READ | PROT_WRITE;
72     case PageReadExecute:
73       return PROT_READ | PROT_EXEC;
74     case PageReadWriteExecute:
75       return PROT_READ | PROT_WRITE | PROT_EXEC;
76     default:
77       NOTREACHED();
78       FALLTHROUGH;
79     case PageInaccessible:
80       return PROT_NONE;
81   }
82 }
83 
SystemAllocPagesInternal(void * hint,size_t length,PageAccessibilityConfiguration accessibility,PageTag page_tag,bool commit)84 void* SystemAllocPagesInternal(void* hint,
85                                size_t length,
86                                PageAccessibilityConfiguration accessibility,
87                                PageTag page_tag,
88                                bool commit) {
89 #if defined(OS_APPLE)
90   // Use a custom tag to make it easier to distinguish Partition Alloc regions
91   // in vmmap(1). Tags between 240-255 are supported.
92   DCHECK(PageTag::kFirst <= page_tag);
93   DCHECK(PageTag::kLast >= page_tag);
94   int fd = VM_MAKE_TAG(static_cast<int>(page_tag));
95 #else
96   int fd = -1;
97 #endif
98 
99   int access_flag = GetAccessFlags(accessibility);
100   int map_flags = MAP_ANONYMOUS | MAP_PRIVATE;
101 
102   // TODO(https://crbug.com/927411): Remove once Fuchsia uses a native page
103   // allocator, rather than relying on POSIX compatibility.
104 #if defined(OS_FUCHSIA)
105   if (page_tag == PageTag::kV8) {
106     map_flags |= MAP_JIT;
107   }
108 #endif
109 
110   void* ret = mmap(hint, length, access_flag, map_flags, fd, 0);
111   if (ret == MAP_FAILED) {
112     s_allocPageErrorCode = errno;
113     ret = nullptr;
114   }
115 
116 #if defined(OS_ANDROID)
117   // On Android, anonymous mappings can have a name attached to them. This is
118   // useful for debugging, and double-checking memory attribution.
119   if (ret) {
120     // No error checking on purpose, testing only.
121     prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ret, length,
122           PageTagToName(page_tag));
123   }
124 #endif
125 
126   return ret;
127 }
128 
TrimMappingInternal(void * base,size_t base_length,size_t trim_length,PageAccessibilityConfiguration accessibility,bool commit,size_t pre_slack,size_t post_slack)129 void* TrimMappingInternal(void* base,
130                           size_t base_length,
131                           size_t trim_length,
132                           PageAccessibilityConfiguration accessibility,
133                           bool commit,
134                           size_t pre_slack,
135                           size_t post_slack) {
136   void* ret = base;
137   // We can resize the allocation run. Release unneeded memory before and after
138   // the aligned range.
139   if (pre_slack) {
140     int res = munmap(base, pre_slack);
141     CHECK(!res);
142     ret = reinterpret_cast<char*>(base) + pre_slack;
143   }
144   if (post_slack) {
145     int res = munmap(reinterpret_cast<char*>(ret) + trim_length, post_slack);
146     CHECK(!res);
147   }
148   return ret;
149 }
150 
TrySetSystemPagesAccessInternal(void * address,size_t length,PageAccessibilityConfiguration accessibility)151 bool TrySetSystemPagesAccessInternal(
152     void* address,
153     size_t length,
154     PageAccessibilityConfiguration accessibility) {
155   return 0 == mprotect(address, length, GetAccessFlags(accessibility));
156 }
157 
SetSystemPagesAccessInternal(void * address,size_t length,PageAccessibilityConfiguration accessibility)158 void SetSystemPagesAccessInternal(
159     void* address,
160     size_t length,
161     PageAccessibilityConfiguration accessibility) {
162   CHECK_EQ(0, mprotect(address, length, GetAccessFlags(accessibility)));
163 }
164 
FreePagesInternal(void * address,size_t length)165 void FreePagesInternal(void* address, size_t length) {
166   CHECK(!munmap(address, length));
167 }
168 
DecommitSystemPagesInternal(void * address,size_t length)169 void DecommitSystemPagesInternal(void* address, size_t length) {
170   // In POSIX, there is no decommit concept. Discarding is an effective way of
171   // implementing the Windows semantics where the OS is allowed to not swap the
172   // pages in the region.
173   //
174   // TODO(ajwong): Also explore setting PageInaccessible to make the protection
175   // semantics consistent between Windows and POSIX. This might have a perf cost
176   // though as both decommit and recommit would incur an extra syscall.
177   // http://crbug.com/766882
178   DiscardSystemPages(address, length);
179 }
180 
RecommitSystemPagesInternal(void * address,size_t length,PageAccessibilityConfiguration accessibility)181 bool RecommitSystemPagesInternal(void* address,
182                                  size_t length,
183                                  PageAccessibilityConfiguration accessibility) {
184 #if defined(OS_APPLE)
185   // On macOS, to update accounting, we need to make another syscall. For more
186   // details, see https://crbug.com/823915.
187   madvise(address, length, MADV_FREE_REUSE);
188 #endif
189 
190   // On POSIX systems, the caller need simply read the memory to recommit it.
191   // This has the correct behavior because the API requires the permissions to
192   // be the same as before decommitting and all configurations can read.
193   return true;
194 }
195 
DiscardSystemPagesInternal(void * address,size_t length)196 void DiscardSystemPagesInternal(void* address, size_t length) {
197 #if defined(OS_APPLE)
198   int ret = madvise(address, length, MADV_FREE_REUSABLE);
199   if (ret) {
200     // MADV_FREE_REUSABLE sometimes fails, so fall back to MADV_DONTNEED.
201     ret = madvise(address, length, MADV_DONTNEED);
202   }
203   CHECK(0 == ret);
204 #else
205   // We have experimented with other flags, but with suboptimal results.
206   //
207   // MADV_FREE (Linux): Makes our memory measurements less predictable;
208   // performance benefits unclear.
209   //
210   // Therefore, we just do the simple thing: MADV_DONTNEED.
211   CHECK(!madvise(address, length, MADV_DONTNEED));
212 #endif
213 }
214 
215 }  // namespace base
216 }  // namespace pdfium
217 
218 #endif  // THIRD_PARTY_BASE_ALLOCATOR_PARTITION_ALLOCATOR_PAGE_ALLOCATOR_INTERNALS_POSIX_H_
219