1/*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "OSAllocator.h"
28
29#if OS(UNIX)
30
31#include <cstdlib>
32
33#include "PageAllocation.h"
34#include <errno.h>
35#include <sys/mman.h>
36#include <wtf/Assertions.h>
37#include <wtf/UnusedParam.h>
38
39#if OS(LINUX)
40#include <sys/syscall.h>
41#ifndef MFD_CLOEXEC
42#define MFD_CLOEXEC 0x0001U
43#endif
44#endif
45
46#if defined(__ANDROID__) && defined(SYS_memfd_create)
47 // On Android it's been observed that permissions of memory mappings
48 // backed by a memfd could not be changed via mprotect for no obvious
49 // reason.
50# undef SYS_memfd_create
51#endif
52
53namespace WTF {
54
55#ifdef SYS_memfd_create
56static int memfdForUsage(size_t bytes, OSAllocator::Usage usage)
57{
58 const char *type = "unknown-usage:";
59 switch (usage) {
60 case OSAllocator::UnknownUsage:
61 break;
62 case OSAllocator::FastMallocPages:
63 type = "fastmalloc:";
64 break;
65 case OSAllocator::JSGCHeapPages:
66 type = "JSGCHeap:";
67 break;
68 case OSAllocator::JSVMStackPages:
69 type = "JSVMStack:";
70 break;
71 case OSAllocator::JSJITCodePages:
72 type = "JITCode:";
73 break;
74 }
75
76 char buf[PATH_MAX];
77 strcpy(dest: buf, src: type);
78 strcat(dest: buf, src: "QtQml");
79
80 int fd = syscall(SYS_memfd_create, buf, MFD_CLOEXEC);
81 if (fd != -1) {
82 if (ftruncate(fd: fd, length: bytes) == 0)
83 return fd;
84 }
85 close(fd: fd);
86 return -1;
87}
88#elif OS(LINUX)
89static int memfdForUsage(size_t bytes, OSAllocator::Usage usage)
90{
91 UNUSED_PARAM(bytes);
92 UNUSED_PARAM(usage);
93 return -1;
94}
95#endif
96
97void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable)
98{
99#if OS(QNX)
100 UNUSED_PARAM(usage);
101 UNUSED_PARAM(writable);
102 UNUSED_PARAM(executable);
103 // Reserve memory with PROT_NONE and MAP_LAZY so it isn't committed now.
104 void* result = mmap(0, bytes, PROT_NONE, MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0);
105 if (result == MAP_FAILED)
106 CRASH();
107#elif OS(LINUX)
108 UNUSED_PARAM(writable);
109 UNUSED_PARAM(executable);
110 int fd = memfdForUsage(bytes, usage);
111
112 void* result = mmap(addr: 0, len: bytes, PROT_NONE, MAP_NORESERVE | MAP_PRIVATE |
113 (fd == -1 ? MAP_ANON : 0), fd: fd, offset: 0);
114 if (result == MAP_FAILED)
115 CRASH();
116
117 if (fd != -1)
118 close(fd: fd);
119#else
120 void* result = reserveAndCommit(bytes, usage, writable, executable);
121#if HAVE(MADV_FREE_REUSE)
122 // To support the "reserve then commit" model, we have to initially decommit.
123 while (madvise(result, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { }
124#endif
125
126#endif // OS(QNX)
127
128 return result;
129}
130
131void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages)
132{
133 // All POSIX reservations start out logically committed.
134 int protection = PROT_READ;
135 if (writable)
136 protection |= PROT_WRITE;
137 if (executable)
138 protection |= PROT_EXEC;
139
140 int flags = MAP_PRIVATE | MAP_ANON;
141#if PLATFORM(IOS)
142 if (executable)
143 flags |= MAP_JIT;
144#endif
145
146#if OS(DARWIN)
147 int fd = usage;
148#elif OS(LINUX)
149 int fd = memfdForUsage(bytes, usage);
150 if (fd != -1)
151 flags &= ~MAP_ANON;
152#else
153 UNUSED_PARAM(usage);
154 int fd = -1;
155#endif
156
157 void* result = 0;
158#if (OS(DARWIN) && CPU(X86_64))
159 if (executable) {
160 ASSERT(includesGuardPages);
161 // Cook up an address to allocate at, using the following recipe:
162 // 17 bits of zero, stay in userspace kids.
163 // 26 bits of randomness for ASLR.
164 // 21 bits of zero, at least stay aligned within one level of the pagetables.
165 //
166 // But! - as a temporary workaround for some plugin problems (rdar://problem/6812854),
167 // for now instead of 2^26 bits of ASLR lets stick with 25 bits of randomization plus
168 // 2^24, which should put up somewhere in the middle of userspace (in the address range
169 // 0x200000000000 .. 0x5fffffffffff).
170 intptr_t randomLocation = 0;
171 randomLocation = arc4random() & ((1 << 25) - 1);
172 randomLocation += (1 << 24);
173 randomLocation <<= 21;
174 result = reinterpret_cast<void*>(randomLocation);
175 }
176#endif
177
178 result = mmap(addr: result, len: bytes, prot: protection, flags: flags, fd: fd, offset: 0);
179 if (result == MAP_FAILED) {
180#if ENABLE(LLINT)
181 if (executable)
182 result = 0;
183 else
184#endif
185 CRASH();
186 }
187 if (result && includesGuardPages) {
188 // We use mmap to remap the guardpages rather than using mprotect as
189 // mprotect results in multiple references to the code region. This
190 // breaks the madvise based mechanism we use to return physical memory
191 // to the OS.
192 mmap(addr: result, len: pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd: fd, offset: 0);
193 mmap(addr: static_cast<char*>(result) + bytes - pageSize(), len: pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd: fd, offset: 0);
194 }
195
196#if OS(LINUX)
197 if (fd != -1)
198 close(fd: fd);
199#endif
200
201 return result;
202}
203
204void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable)
205{
206#if OS(QNX)
207 int protection = PROT_READ;
208 if (writable)
209 protection |= PROT_WRITE;
210 if (executable)
211 protection |= PROT_EXEC;
212 if (MAP_FAILED == mmap(address, bytes, protection, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0))
213 CRASH();
214#elif OS(LINUX)
215 int protection = PROT_READ;
216 if (writable)
217 protection |= PROT_WRITE;
218 if (executable)
219 protection |= PROT_EXEC;
220 if (mprotect(addr: address, len: bytes, prot: protection))
221 CRASH();
222
223 while (madvise(addr: address, len: bytes, MADV_WILLNEED)) {
224 if (errno != EAGAIN)
225 break; // We don't have to crash here. MADV_WILLNEED is only advisory
226 }
227
228#elif HAVE(MADV_FREE_REUSE)
229 UNUSED_PARAM(writable);
230 UNUSED_PARAM(executable);
231 while (madvise(address, bytes, MADV_FREE_REUSE) == -1 && errno == EAGAIN) { }
232#else
233 // Non-MADV_FREE_REUSE reservations automatically commit on demand.
234 UNUSED_PARAM(address);
235 UNUSED_PARAM(bytes);
236 UNUSED_PARAM(writable);
237 UNUSED_PARAM(executable);
238#endif
239}
240
241void OSAllocator::decommit(void* address, size_t bytes)
242{
243#if OS(QNX)
244 // Use PROT_NONE and MAP_LAZY to decommit the pages.
245 mmap(address, bytes, PROT_NONE, MAP_FIXED | MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0);
246#elif OS(LINUX)
247 while (madvise(addr: address, len: bytes, MADV_DONTNEED)) {
248 if (errno != EAGAIN) {
249 memset(s: address, c: 0, n: bytes); // We rely on madvise to zero-out the memory
250 break;
251 }
252 }
253 if (mprotect(addr: address, len: bytes, PROT_NONE))
254 CRASH();
255#elif HAVE(MADV_FREE_REUSE)
256 while (madvise(address, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { }
257#elif HAVE(MADV_FREE)
258 while (madvise(address, bytes, MADV_FREE) == -1 && errno == EAGAIN) { }
259#elif HAVE(MADV_DONTNEED)
260 while (madvise(address, bytes, MADV_DONTNEED) == -1 && errno == EAGAIN) { }
261#else
262 UNUSED_PARAM(address);
263 UNUSED_PARAM(bytes);
264#endif
265}
266
267void OSAllocator::releaseDecommitted(void* address, size_t bytes)
268{
269 int result = munmap(addr: address, len: bytes);
270 if (result == -1)
271 CRASH();
272}
273
274bool OSAllocator::canAllocateExecutableMemory()
275{
276 int flags = MAP_PRIVATE;
277#if PLATFORM(IOS)
278 if (executable)
279 flags |= MAP_JIT;
280#endif
281
282 // Get a read/write memfd page
283 const auto size = pageSize();
284#if OS(LINUX)
285 const int fd = memfdForUsage(bytes: size, usage: OSAllocator::JSJITCodePages);
286#else
287 const int fd = -1;
288#endif
289 if (fd == -1)
290 flags |= MAP_ANON;
291 void *testPage = mmap(addr: nullptr, len: size, PROT_READ | PROT_WRITE, flags: flags, fd: fd, /*offset*/0);
292 if (fd != -1)
293 close(fd: fd);
294
295 if (testPage == MAP_FAILED)
296 return false;
297
298 // Write something into the page, to trigger CoW
299 memset(s: testPage, c: 0xab, n: sizeof(quintptr));
300
301 // Then make it executable
302 const bool result = mprotect(addr: testPage, len: size, PROT_READ | PROT_EXEC) == 0;
303
304 munmap(addr: testPage, len: size);
305 return result;
306}
307
308} // namespace WTF
309
310#endif // OS(UNIX)
311

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtdeclarative/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp