Enable gigacage on iOS
[WebKit-https.git] / Source / bmalloc / bmalloc / Gigacage.cpp
1 /*
2  * Copyright (C) 2017 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "Gigacage.h"
27
28 #include "Environment.h"
29 #include "PerProcess.h"
30 #include "VMAllocate.h"
31 #include "Vector.h"
32 #include "bmalloc.h"
33 #include <cstdio>
34 #include <mutex>
35
36 #if BCPU(ARM64)
37 // FIXME: There is no good reason for ARM64 to be special.
38 // https://bugs.webkit.org/show_bug.cgi?id=177605
39 #define PRIMITIVE_GIGACAGE_RUNWAY 0
40 #else
41 // FIXME: Consider making this 32GB, in case unsigned 32-bit indices find their way into indexed accesses.
42 // https://bugs.webkit.org/show_bug.cgi?id=175062
43 #define PRIMITIVE_GIGACAGE_RUNWAY (16llu * 1024 * 1024 * 1024)
44 #endif
45
46 // FIXME: Reconsider this.
47 // https://bugs.webkit.org/show_bug.cgi?id=175921
48 #define JSVALUE_GIGACAGE_RUNWAY 0
49 #define STRING_GIGACAGE_RUNWAY 0
50
51 char g_gigacageBasePtrs[GIGACAGE_BASE_PTRS_SIZE] __attribute__((aligned(GIGACAGE_BASE_PTRS_SIZE)));
52
53 using namespace bmalloc;
54
55 namespace Gigacage {
56
57 bool g_wasEnabled;
58
59 namespace {
60
61 bool s_isDisablingPrimitiveGigacageDisabled;
62
63 void protectGigacageBasePtrs()
64 {
65     uintptr_t basePtrs = reinterpret_cast<uintptr_t>(g_gigacageBasePtrs);
66     // We might only get page size alignment, but that's also the minimum we need.
67     RELEASE_BASSERT(!(basePtrs & (vmPageSize() - 1)));
68     mprotect(g_gigacageBasePtrs, GIGACAGE_BASE_PTRS_SIZE, PROT_READ);
69 }
70
71 void unprotectGigacageBasePtrs()
72 {
73     mprotect(g_gigacageBasePtrs, GIGACAGE_BASE_PTRS_SIZE, PROT_READ | PROT_WRITE);
74 }
75
76 class UnprotectGigacageBasePtrsScope {
77 public:
78     UnprotectGigacageBasePtrsScope()
79     {
80         unprotectGigacageBasePtrs();
81     }
82     
83     ~UnprotectGigacageBasePtrsScope()
84     {
85         protectGigacageBasePtrs();
86     }
87 };
88
89 struct Callback {
90     Callback() { }
91     
92     Callback(void (*function)(void*), void *argument)
93         : function(function)
94         , argument(argument)
95     {
96     }
97     
98     void (*function)(void*) { nullptr };
99     void* argument { nullptr };
100 };
101
102 struct PrimitiveDisableCallbacks {
103     PrimitiveDisableCallbacks(std::lock_guard<StaticMutex>&) { }
104     
105     Vector<Callback> callbacks;
106 };
107
108 } // anonymous namespace
109
110 void ensureGigacage()
111 {
112 #if GIGACAGE_ENABLED
113     static std::once_flag onceFlag;
114     std::call_once(
115         onceFlag,
116         [] {
117             if (!shouldBeEnabled())
118                 return;
119             
120             bool ok = true;
121             
122             forEachKind(
123                 [&] (Kind kind) {
124                     if (!ok)
125                         return;
126                     // FIXME: Randomize where this goes.
127                     // https://bugs.webkit.org/show_bug.cgi?id=175245
128                     basePtr(kind) = tryVMAllocate(alignment(kind), totalSize(kind));
129                     if (!basePtr(kind)) {
130                         if (GIGACAGE_ALLOCATION_CAN_FAIL) {
131                             ok = false;
132                             return;
133                         }
134                         fprintf(stderr, "FATAL: Could not allocate %s gigacage.\n", name(kind));
135                         BCRASH();
136                     }
137                     
138                     vmDeallocatePhysicalPages(basePtr(kind), totalSize(kind));
139                 });
140             
141             if (!ok) {
142                 forEachKind(
143                     [&] (Kind kind) {
144                         if (!basePtr(kind))
145                             return;
146                         
147                         vmDeallocate(basePtr(kind), totalSize(kind));
148                         
149                         basePtr(kind) = nullptr;
150                     });
151                 return;
152             }
153             
154             protectGigacageBasePtrs();
155             g_wasEnabled = true;
156         });
157 #endif // GIGACAGE_ENABLED
158 }
159
160 size_t runway(Kind kind)
161 {
162     switch (kind) {
163     case Primitive:
164         return static_cast<size_t>(PRIMITIVE_GIGACAGE_RUNWAY);
165     case JSValue:
166         return static_cast<size_t>(JSVALUE_GIGACAGE_RUNWAY);
167     case String:
168         return static_cast<size_t>(STRING_GIGACAGE_RUNWAY);
169     }
170     BCRASH();
171     return 0;
172 }
173
174 size_t totalSize(Kind kind)
175 {
176     return size(kind) + runway(kind);
177 }
178
179 void disablePrimitiveGigacage()
180 {
181     ensureGigacage();
182     if (!basePtrs().primitive) {
183         // It was never enabled. That means that we never even saved any callbacks. Or, we had already disabled
184         // it before, and already called the callbacks.
185         return;
186     }
187     
188     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
189     std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
190     for (Callback& callback : callbacks.callbacks)
191         callback.function(callback.argument);
192     callbacks.callbacks.shrink(0);
193     UnprotectGigacageBasePtrsScope unprotectScope;
194     basePtrs().primitive = nullptr;
195 }
196
197 void addPrimitiveDisableCallback(void (*function)(void*), void* argument)
198 {
199     ensureGigacage();
200     if (!basePtrs().primitive) {
201         // It was already disabled or we were never able to enable it.
202         function(argument);
203         return;
204     }
205     
206     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
207     std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
208     callbacks.callbacks.push(Callback(function, argument));
209 }
210
211 void removePrimitiveDisableCallback(void (*function)(void*), void* argument)
212 {
213     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
214     std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
215     for (size_t i = 0; i < callbacks.callbacks.size(); ++i) {
216         if (callbacks.callbacks[i].function == function
217             && callbacks.callbacks[i].argument == argument) {
218             callbacks.callbacks[i] = callbacks.callbacks.last();
219             callbacks.callbacks.pop();
220             return;
221         }
222     }
223 }
224
225 static void primitiveGigacageDisabled(void*)
226 {
227     static bool s_false;
228     fprintf(stderr, "FATAL: Primitive gigacage disabled, but we don't want that in this process.\n");
229     if (!s_false)
230         BCRASH();
231 }
232
233 void disableDisablingPrimitiveGigacageIfShouldBeEnabled()
234 {
235     if (shouldBeEnabled()) {
236         addPrimitiveDisableCallback(primitiveGigacageDisabled, nullptr);
237         s_isDisablingPrimitiveGigacageDisabled = true;
238     }
239 }
240
241 bool isDisablingPrimitiveGigacageDisabled()
242 {
243     return s_isDisablingPrimitiveGigacageDisabled;
244 }
245
246 bool shouldBeEnabled()
247 {
248     static std::once_flag onceFlag;
249     static bool cached;
250     std::call_once(
251         onceFlag,
252         [] {
253             bool result = GIGACAGE_ENABLED && !PerProcess<Environment>::get()->isDebugHeapEnabled();
254             if (!result)
255                 return;
256             
257             if (char* gigacageEnabled = getenv("GIGACAGE_ENABLED")) {
258                 if (!strcasecmp(gigacageEnabled, "no") || !strcasecmp(gigacageEnabled, "false") || !strcasecmp(gigacageEnabled, "0")) {
259                     fprintf(stderr, "Warning: disabling gigacage because GIGACAGE_ENABLED=%s!\n", gigacageEnabled);
260                     return;
261                 } else if (strcasecmp(gigacageEnabled, "yes") && strcasecmp(gigacageEnabled, "true") && strcasecmp(gigacageEnabled, "1"))
262                     fprintf(stderr, "Warning: invalid argument to GIGACAGE_ENABLED: %s\n", gigacageEnabled);
263             }
264             
265             cached = true;
266         });
267     
268     return cached;
269 }
270
271 } // namespace Gigacage
272
273
274