Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / wtf / PageAllocator.cpp
blob5f5eb440e4bfa5c87cc5b0ea2ee1093bc78a1d31
1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include "config.h"
32 #include "wtf/PageAllocator.h"
34 #include "wtf/AddressSpaceRandomization.h"
35 #include "wtf/Assertions.h"
37 #include <limits.h>
39 #if OS(POSIX)
41 #include <sys/mman.h>
43 #ifndef MADV_FREE
44 #define MADV_FREE MADV_DONTNEED
45 #endif
47 #ifndef MAP_ANONYMOUS
48 #define MAP_ANONYMOUS MAP_ANON
49 #endif
51 #elif OS(WIN)
53 #include <windows.h>
55 #else
56 #error Unknown OS
57 #endif // OS(POSIX)
59 namespace WTF {
61 // This simple internal function wraps the OS-specific page allocation call so
62 // that it behaves consistently: the address is a hint and if it cannot be used,
63 // the allocation will be placed elsewhere.
64 static void* systemAllocPages(void* addr, size_t len, PageAccessibilityConfiguration pageAccessibility)
66 ASSERT(!(len & kPageAllocationGranularityOffsetMask));
67 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & kPageAllocationGranularityOffsetMask));
68 void* ret;
69 #if OS(WIN)
70 int accessFlag = pageAccessibility == PageAccessible ? PAGE_READWRITE : PAGE_NOACCESS;
71 ret = VirtualAlloc(addr, len, MEM_RESERVE | MEM_COMMIT, accessFlag);
72 if (!ret)
73 ret = VirtualAlloc(0, len, MEM_RESERVE | MEM_COMMIT, accessFlag);
74 #else
75 int accessFlag = pageAccessibility == PageAccessible ? (PROT_READ | PROT_WRITE) : PROT_NONE;
76 ret = mmap(addr, len, accessFlag, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
77 if (ret == MAP_FAILED)
78 ret = 0;
79 #endif
80 return ret;
83 static bool trimMapping(void* baseAddr, size_t baseLen, void* trimAddr, size_t trimLen)
85 #if OS(WIN)
86 return false;
87 #else
88 char* basePtr = static_cast<char*>(baseAddr);
89 char* trimPtr = static_cast<char*>(trimAddr);
90 ASSERT(trimPtr >= basePtr);
91 ASSERT(trimPtr + trimLen <= basePtr + baseLen);
92 size_t preLen = trimPtr - basePtr;
93 if (preLen) {
94 int ret = munmap(basePtr, preLen);
95 RELEASE_ASSERT(!ret);
97 size_t postLen = (basePtr + baseLen) - (trimPtr + trimLen);
98 if (postLen) {
99 int ret = munmap(trimPtr + trimLen, postLen);
100 RELEASE_ASSERT(!ret);
102 return true;
103 #endif
106 void* allocPages(void* addr, size_t len, size_t align, PageAccessibilityConfiguration pageAccessibility)
108 ASSERT(len >= kPageAllocationGranularity);
109 ASSERT(!(len & kPageAllocationGranularityOffsetMask));
110 ASSERT(align >= kPageAllocationGranularity);
111 ASSERT(!(align & kPageAllocationGranularityOffsetMask));
112 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & kPageAllocationGranularityOffsetMask));
113 size_t alignOffsetMask = align - 1;
114 size_t alignBaseMask = ~alignOffsetMask;
115 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & alignOffsetMask));
116 // If the client passed null as the address, choose a good one.
117 if (!addr) {
118 addr = getRandomPageBase();
119 addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & alignBaseMask);
122 // The common case, which is also the least work we can do, is that the
123 // address and length are suitable. Just try it.
124 void* ret = systemAllocPages(addr, len, pageAccessibility);
125 // If the alignment is to our liking, we're done.
126 if (!ret || !(reinterpret_cast<uintptr_t>(ret) & alignOffsetMask))
127 return ret;
129 // Annoying. Unmap and map a larger range to be sure to succeed on the
130 // second, slower attempt.
131 freePages(ret, len);
133 size_t tryLen = len + (align - kPageAllocationGranularity);
134 RELEASE_ASSERT(tryLen > len);
136 // We loop to cater for the unlikely case where another thread maps on top
137 // of the aligned location we choose.
138 int count = 0;
139 while (count++ < 100) {
140 ret = systemAllocPages(addr, tryLen, pageAccessibility);
141 if (!ret)
142 return 0;
143 // We can now try and trim out a subset of the mapping.
144 addr = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(ret) + alignOffsetMask) & alignBaseMask);
146 // On POSIX systems, we can trim the oversized mapping to fit exactly.
147 // This will always work on POSIX systems.
148 if (trimMapping(ret, tryLen, addr, len))
149 return addr;
151 // On Windows, you can't trim an existing mapping so we unmap and remap
152 // a subset. We used to do for all platforms, but OSX 10.8 has a
153 // broken mmap() that ignores address hints for valid, unused addresses.
154 freePages(ret, tryLen);
155 ret = systemAllocPages(addr, len, pageAccessibility);
156 if (ret == addr || !ret)
157 return ret;
159 // Unlikely race / collision. Do the simple thing and just start again.
160 freePages(ret, len);
161 addr = getRandomPageBase();
162 addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & alignBaseMask);
164 IMMEDIATE_CRASH();
165 return 0;
168 void freePages(void* addr, size_t len)
170 ASSERT(!(reinterpret_cast<uintptr_t>(addr) & kPageAllocationGranularityOffsetMask));
171 ASSERT(!(len & kPageAllocationGranularityOffsetMask));
172 #if OS(POSIX)
173 int ret = munmap(addr, len);
174 RELEASE_ASSERT(!ret);
175 #else
176 BOOL ret = VirtualFree(addr, 0, MEM_RELEASE);
177 RELEASE_ASSERT(ret);
178 #endif
181 void setSystemPagesInaccessible(void* addr, size_t len)
183 ASSERT(!(len & kSystemPageOffsetMask));
184 #if OS(POSIX)
185 int ret = mprotect(addr, len, PROT_NONE);
186 RELEASE_ASSERT(!ret);
187 #else
188 BOOL ret = VirtualFree(addr, len, MEM_DECOMMIT);
189 RELEASE_ASSERT(ret);
190 #endif
193 bool setSystemPagesAccessible(void* addr, size_t len)
195 ASSERT(!(len & kSystemPageOffsetMask));
196 #if OS(POSIX)
197 return !mprotect(addr, len, PROT_READ | PROT_WRITE);
198 #else
199 return !!VirtualAlloc(addr, len, MEM_COMMIT, PAGE_READWRITE);
200 #endif
203 void decommitSystemPages(void* addr, size_t len)
205 ASSERT(!(len & kSystemPageOffsetMask));
206 #if OS(POSIX)
207 int ret = madvise(addr, len, MADV_FREE);
208 RELEASE_ASSERT(!ret);
209 #else
210 setSystemPagesInaccessible(addr, len);
211 #endif
214 void recommitSystemPages(void* addr, size_t len)
216 ASSERT(!(len & kSystemPageOffsetMask));
217 #if OS(POSIX)
218 (void) addr;
219 #else
220 RELEASE_ASSERT(setSystemPagesAccessible(addr, len));
221 #endif
224 void discardSystemPages(void* addr, size_t len)
226 ASSERT(!(len & kSystemPageOffsetMask));
227 #if OS(POSIX)
228 // On POSIX, the implementation detail is that discard and decommit are the
229 // same, and lead to pages that are returned to the system immediately and
230 // get replaced with zeroed pages when touched. So we just call
231 // decommitSystemPages() here to avoid code duplication.
232 decommitSystemPages(addr, len);
233 #else
234 (void) addr;
235 (void) len;
236 // TODO(cevans): implement this using MEM_RESET for Windows, once we've
237 // decided that the semantics are a match.
238 #endif
241 } // namespace WTF