• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 Google 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 are
6  * met:
7  *
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.
17  *
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.
29  */
30 
31 #include "config.h"
32 #include "heap/Heap.h"
33 
34 #include "heap/ThreadState.h"
35 
36 #if OS(POSIX)
37 #include <sys/mman.h>
38 #include <unistd.h>
39 #elif OS(WIN)
40 #include <windows.h>
41 #endif
42 
43 namespace WebCore {
44 
45 #if OS(WIN)
IsPowerOf2(size_t power)46 static bool IsPowerOf2(size_t power)
47 {
48     return !((power - 1) & power);
49 }
50 #endif
51 
roundToBlinkPageBoundary(void * base)52 static Address roundToBlinkPageBoundary(void* base)
53 {
54     return reinterpret_cast<Address>((reinterpret_cast<uintptr_t>(base) + blinkPageOffsetMask) & blinkPageBaseMask);
55 }
56 
roundToOsPageSize(size_t size)57 static size_t roundToOsPageSize(size_t size)
58 {
59     return (size + osPageSize() - 1) & ~(osPageSize() - 1);
60 }
61 
osPageSize()62 size_t osPageSize()
63 {
64 #if OS(POSIX)
65     static const size_t pageSize = getpagesize();
66 #else
67     static size_t pageSize = 0;
68     if (!pageSize) {
69         SYSTEM_INFO info;
70         GetSystemInfo(&info);
71         pageSize = info.dwPageSize;
72         ASSERT(IsPowerOf2(pageSize));
73     }
74 #endif
75     return pageSize;
76 }
77 
78 class MemoryRegion {
79 public:
MemoryRegion(Address base,size_t size)80     MemoryRegion(Address base, size_t size) : m_base(base), m_size(size) { ASSERT(size > 0); }
81 
contains(Address addr) const82     bool contains(Address addr) const
83     {
84         return m_base <= addr && addr < (m_base + m_size);
85     }
86 
87 
contains(const MemoryRegion & other) const88     bool contains(const MemoryRegion& other) const
89     {
90         return contains(other.m_base) && contains(other.m_base + other.m_size - 1);
91     }
92 
release()93     void release()
94     {
95 #if OS(POSIX)
96         int err = munmap(m_base, m_size);
97         RELEASE_ASSERT(!err);
98 #else
99         bool success = VirtualFree(m_base, 0, MEM_RELEASE);
100         RELEASE_ASSERT(success);
101 #endif
102     }
103 
commit()104     WARN_UNUSED_RETURN bool commit()
105     {
106 #if OS(POSIX)
107         int err = mprotect(m_base, m_size, PROT_READ | PROT_WRITE);
108         if (!err) {
109             madvise(m_base, m_size, MADV_NORMAL);
110             return true;
111         }
112         return false;
113 #else
114         void* result = VirtualAlloc(m_base, m_size, MEM_COMMIT, PAGE_READWRITE);
115         return !!result;
116 #endif
117     }
118 
decommit()119     void decommit()
120     {
121 #if OS(POSIX)
122         int err = mprotect(m_base, m_size, PROT_NONE);
123         RELEASE_ASSERT(!err);
124         // FIXME: Consider using MADV_FREE on MacOS.
125         madvise(m_base, m_size, MADV_DONTNEED);
126 #else
127         bool success = VirtualFree(m_base, m_size, MEM_DECOMMIT);
128         RELEASE_ASSERT(success);
129 #endif
130     }
131 
base() const132     Address base() const { return m_base; }
133 
134 private:
135     Address m_base;
136     size_t m_size;
137 };
138 
139 // Representation of the memory used for a Blink heap page.
140 //
141 // The representation keeps track of two memory regions:
142 //
143 // 1. The virtual memory reserved from the sytem in order to be able
144 //    to free all the virtual memory reserved on destruction.
145 //
146 // 2. The writable memory (a sub-region of the reserved virtual
147 //    memory region) that is used for the actual heap page payload.
148 //
149 // Guard pages are create before and after the writable memory.
150 class PageMemory {
151 public:
~PageMemory()152     ~PageMemory() { m_reserved.release(); }
153 
commit()154     bool commit() WARN_UNUSED_RETURN { return m_writable.commit(); }
decommit()155     void decommit() { m_writable.decommit(); }
156 
writableStart()157     Address writableStart() { return m_writable.base(); }
158 
159     // Allocate a virtual address space for the blink page with the
160     // following layout:
161     //
162     //    [ guard os page | ... payload ... | guard os page ]
163     //    ^---{ aligned to blink page size }
164     //
allocate(size_t payloadSize)165     static PageMemory* allocate(size_t payloadSize)
166     {
167         ASSERT(payloadSize > 0);
168 
169         // Virtual memory allocation routines operate in OS page sizes.
170         // Round up the requested size to nearest os page size.
171         payloadSize = roundToOsPageSize(payloadSize);
172 
173         // Overallocate by blinkPageSize and 2 times OS page size to
174         // ensure a chunk of memory which is blinkPageSize aligned and
175         // has a system page before and after to use for guarding. We
176         // unmap the excess memory before returning.
177         size_t allocationSize = payloadSize + 2 * osPageSize() + blinkPageSize;
178 
179 #if OS(POSIX)
180         Address base = static_cast<Address>(mmap(0, allocationSize, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0));
181         RELEASE_ASSERT(base != MAP_FAILED);
182 
183         Address end = base + allocationSize;
184         Address alignedBase = roundToBlinkPageBoundary(base);
185         Address payloadBase = alignedBase + osPageSize();
186         Address payloadEnd = payloadBase + payloadSize;
187         Address blinkPageEnd = payloadEnd + osPageSize();
188 
189         // If the allocate memory was not blink page aligned release
190         // the memory before the aligned address.
191         if (alignedBase != base)
192             MemoryRegion(base, alignedBase - base).release();
193 
194         // Create guard pages by decommiting an OS page before and
195         // after the payload.
196         MemoryRegion(alignedBase, osPageSize()).decommit();
197         MemoryRegion(payloadEnd, osPageSize()).decommit();
198 
199         // Free the additional memory at the end of the page if any.
200         if (blinkPageEnd < end)
201             MemoryRegion(blinkPageEnd, end - blinkPageEnd).release();
202 
203         return new PageMemory(MemoryRegion(alignedBase, blinkPageEnd - alignedBase), MemoryRegion(payloadBase, payloadSize));
204 #else
205         Address base = 0;
206         Address alignedBase = 0;
207 
208         // On Windows it is impossible to partially release a region
209         // of memory allocated by VirtualAlloc. To avoid wasting
210         // virtual address space we attempt to release a large region
211         // of memory returned as a whole and then allocate an aligned
212         // region inside this larger region.
213         for (int attempt = 0; attempt < 3; attempt++) {
214             base = static_cast<Address>(VirtualAlloc(0, allocationSize, MEM_RESERVE, PAGE_NOACCESS));
215             RELEASE_ASSERT(base);
216             VirtualFree(base, 0, MEM_RELEASE);
217 
218             alignedBase = roundToBlinkPageBoundary(base);
219             base = static_cast<Address>(VirtualAlloc(alignedBase, payloadSize + 2 * osPageSize(), MEM_RESERVE, PAGE_NOACCESS));
220             if (base) {
221                 RELEASE_ASSERT(base == alignedBase);
222                 allocationSize = payloadSize + 2 * osPageSize();
223                 break;
224             }
225         }
226 
227         if (!base) {
228             // We failed to avoid wasting virtual address space after
229             // several attempts.
230             base = static_cast<Address>(VirtualAlloc(0, allocationSize, MEM_RESERVE, PAGE_NOACCESS));
231             RELEASE_ASSERT(base);
232 
233             // FIXME: If base is by accident blink page size aligned
234             // here then we can create two pages out of reserved
235             // space. Do this.
236             alignedBase = roundToBlinkPageBoundary(base);
237         }
238 
239         Address payloadBase = alignedBase + osPageSize();
240         PageMemory* storage = new PageMemory(MemoryRegion(base, allocationSize), MemoryRegion(payloadBase, payloadSize));
241         bool res = storage->commit();
242         RELEASE_ASSERT(res);
243         return storage;
244 #endif
245     }
246 
247 private:
PageMemory(const MemoryRegion & reserved,const MemoryRegion & writable)248     PageMemory(const MemoryRegion& reserved, const MemoryRegion& writable)
249         : m_reserved(reserved)
250         , m_writable(writable)
251     {
252         ASSERT(reserved.contains(writable));
253     }
254 
255     MemoryRegion m_reserved;
256     MemoryRegion m_writable;
257 };
258 
init(intptr_t * startOfStack)259 void Heap::init(intptr_t* startOfStack)
260 {
261     ThreadState::init(startOfStack);
262 }
263 
shutdown()264 void Heap::shutdown()
265 {
266     ThreadState::shutdown();
267 }
268 
269 }
270