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 OS(DARWIN)
142 if (executable || usage == OSAllocator::JSJITCodePages)
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 OS(VXWORKS) && defined(QT_RTP_MEM_FILL) && QT_RTP_MEM_FILL
188 // page allocator expects mmap'ed memory to be zero'ed
189 memset(result, 0, bytes);
190#endif
191
192 if (result && includesGuardPages) {
193 // We use mmap to remap the guardpages rather than using mprotect as
194 // mprotect results in multiple references to the code region. This
195 // breaks the madvise based mechanism we use to return physical memory
196 // to the OS.
197 mmap(addr: result, len: pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd: fd, offset: 0);
198 mmap(addr: static_cast<char*>(result) + bytes - pageSize(), len: pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd: fd, offset: 0);
199 }
200
201#if OS(LINUX)
202 if (fd != -1)
203 close(fd: fd);
204#endif
205
206 return result;
207}
208
209void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable)
210{
211#if OS(QNX)
212 int protection = PROT_READ;
213 if (writable)
214 protection |= PROT_WRITE;
215 if (executable)
216 protection |= PROT_EXEC;
217 if (MAP_FAILED == mmap(address, bytes, protection, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0))
218 CRASH();
219#elif OS(LINUX)
220 int protection = PROT_READ;
221 if (writable)
222 protection |= PROT_WRITE;
223 if (executable)
224 protection |= PROT_EXEC;
225 if (mprotect(addr: address, len: bytes, prot: protection))
226 CRASH();
227
228 while (madvise(addr: address, len: bytes, MADV_WILLNEED)) {
229 if (errno != EAGAIN)
230 break; // We don't have to crash here. MADV_WILLNEED is only advisory
231 }
232
233#elif HAVE(MADV_FREE_REUSE)
234 UNUSED_PARAM(writable);
235 UNUSED_PARAM(executable);
236 while (madvise(address, bytes, MADV_FREE_REUSE) == -1 && errno == EAGAIN) { }
237#else
238 // Non-MADV_FREE_REUSE reservations automatically commit on demand.
239 UNUSED_PARAM(address);
240 UNUSED_PARAM(bytes);
241 UNUSED_PARAM(writable);
242 UNUSED_PARAM(executable);
243#endif
244}
245
246void OSAllocator::decommit(void* address, size_t bytes)
247{
248#if OS(QNX)
249 // Use PROT_NONE and MAP_LAZY to decommit the pages.
250 mmap(address, bytes, PROT_NONE, MAP_FIXED | MAP_LAZY | MAP_PRIVATE | MAP_ANON, -1, 0);
251#elif OS(LINUX)
252 while (madvise(addr: address, len: bytes, MADV_DONTNEED)) {
253 if (errno != EAGAIN) {
254 memset(s: address, c: 0, n: bytes); // We rely on madvise to zero-out the memory
255 break;
256 }
257 }
258 if (mprotect(addr: address, len: bytes, PROT_NONE))
259 CRASH();
260#elif HAVE(MADV_FREE_REUSE)
261 while (madvise(address, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { }
262#elif HAVE(MADV_FREE)
263 while (madvise(address, bytes, MADV_FREE) == -1 && errno == EAGAIN) { }
264#elif HAVE(MADV_DONTNEED)
265 while (madvise(address, bytes, MADV_DONTNEED) == -1 && errno == EAGAIN) { }
266#else
267 UNUSED_PARAM(address);
268 UNUSED_PARAM(bytes);
269#endif
270}
271
272void OSAllocator::releaseDecommitted(void* address, size_t bytes)
273{
274 int result = munmap(addr: address, len: bytes);
275 if (result == -1)
276 CRASH();
277}
278
279#if OS(MAC_OS_X) && CPU(X86_64)
280
281// For unknown reasons, the correct detection (see below) causes crashes on macOS on x86_64.
282// Use the known good one from Qt 6.8.0 for now.
283// TODO: Delete this when macOS on x86_64 is not a thing anymore.
284bool OSAllocator::canAllocateExecutableMemory()
285{
286 int flags = MAP_PRIVATE | MAP_ANON;
287 const auto size = pageSize();
288 void *testPage = mmap(
289 nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, flags, /*fd*/-1, /*offset*/0);
290 if (testPage == MAP_FAILED)
291 return false;
292 munmap(testPage, size);
293 return true;
294}
295
296#else
297
298bool OSAllocator::canAllocateExecutableMemory()
299{
300 int flags = MAP_PRIVATE;
301#if OS(DARWIN)
302 flags |= MAP_JIT;
303#endif
304
305 // Get a read/write memfd page
306 const auto size = pageSize();
307#if OS(LINUX)
308 const int fd = memfdForUsage(bytes: size, usage: OSAllocator::JSJITCodePages);
309#else
310 const int fd = -1;
311#endif
312 if (fd == -1)
313 flags |= MAP_ANON;
314 void *testPage = mmap(addr: nullptr, len: size, PROT_READ | PROT_WRITE, flags: flags, fd: fd, /*offset*/0);
315 if (fd != -1)
316 close(fd: fd);
317
318 if (testPage == MAP_FAILED)
319 return false;
320
321 // Write something into the page, to trigger CoW
322 memset(s: testPage, c: 0xab, n: sizeof(quintptr));
323
324 // Then make it executable
325 const bool result = mprotect(addr: testPage, len: size, PROT_READ | PROT_EXEC) == 0;
326
327 munmap(addr: testPage, len: size);
328 return result;
329}
330
331#endif
332
333} // namespace WTF
334
335#endif // OS(UNIX)
336

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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