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 while (madvise(addr: result, len: bytes, MADV_DONTNEED)) {
118 if (errno != EAGAIN)
119 CRASH();
120 }
121
122 if (fd != -1)
123 close(fd: fd);
124#else
125 void* result = reserveAndCommit(bytes, usage, writable, executable);
126#if HAVE(MADV_FREE_REUSE)
127 // To support the "reserve then commit" model, we have to initially decommit.
128 while (madvise(result, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { }
129#endif
130
131#endif // OS(QNX)
132
133 return result;
134}
135
136void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages)
137{
138 // All POSIX reservations start out logically committed.
139 int protection = PROT_READ;
140 if (writable)
141 protection |= PROT_WRITE;
142 if (executable)
143 protection |= PROT_EXEC;
144
145 int flags = MAP_PRIVATE | MAP_ANON;
146#if PLATFORM(IOS)
147 if (executable)
148 flags |= MAP_JIT;
149#endif
150
151#if OS(DARWIN)
152 int fd = usage;
153#elif OS(LINUX)
154 int fd = memfdForUsage(bytes, usage);
155 if (fd != -1)
156 flags &= ~MAP_ANON;
157#else
158 UNUSED_PARAM(usage);
159 int fd = -1;
160#endif
161
162 void* result = 0;
163#if (OS(DARWIN) && CPU(X86_64))
164 if (executable) {
165 ASSERT(includesGuardPages);
166 // Cook up an address to allocate at, using the following recipe:
167 // 17 bits of zero, stay in userspace kids.
168 // 26 bits of randomness for ASLR.
169 // 21 bits of zero, at least stay aligned within one level of the pagetables.
170 //
171 // But! - as a temporary workaround for some plugin problems (rdar://problem/6812854),
172 // for now instead of 2^26 bits of ASLR lets stick with 25 bits of randomization plus
173 // 2^24, which should put up somewhere in the middle of userspace (in the address range
174 // 0x200000000000 .. 0x5fffffffffff).
175 intptr_t randomLocation = 0;
176 randomLocation = arc4random() & ((1 << 25) - 1);
177 randomLocation += (1 << 24);
178 randomLocation <<= 21;
179 result = reinterpret_cast<void*>(randomLocation);
180 }
181#endif
182
183 result = mmap(addr: result, len: bytes, prot: protection, flags: flags, fd: fd, offset: 0);
184 if (result == MAP_FAILED) {
185#if ENABLE(LLINT)
186 if (executable)
187 result = 0;
188 else
189#endif
190 CRASH();
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 CRASH();
255 }
256 if (mprotect(addr: address, len: bytes, PROT_NONE))
257 CRASH();
258#elif HAVE(MADV_FREE_REUSE)
259 while (madvise(address, bytes, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN) { }
260#elif HAVE(MADV_FREE)
261 while (madvise(address, bytes, MADV_FREE) == -1 && errno == EAGAIN) { }
262#elif HAVE(MADV_DONTNEED)
263 while (madvise(address, bytes, MADV_DONTNEED) == -1 && errno == EAGAIN) { }
264#else
265 UNUSED_PARAM(address);
266 UNUSED_PARAM(bytes);
267#endif
268}
269
270void OSAllocator::releaseDecommitted(void* address, size_t bytes)
271{
272 int result = munmap(addr: address, len: bytes);
273 if (result == -1)
274 CRASH();
275}
276
277bool OSAllocator::canAllocateExecutableMemory()
278{
279 int flags = MAP_PRIVATE | MAP_ANON;
280#if PLATFORM(IOS)
281 if (executable)
282 flags |= MAP_JIT;
283#endif
284 const auto size = pageSize();
285 void *testPage = mmap(addr: nullptr, len: size, PROT_READ | PROT_WRITE | PROT_EXEC, flags: flags, /*fd*/-1, /*offset*/0);
286 if (testPage == MAP_FAILED)
287 return false;
288 munmap(addr: testPage, len: size);
289 return true;
290}
291
292} // namespace WTF
293
294#endif // OS(UNIX)
295

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