Runtime disable gigacage on iOS because it broke WasmBench
[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 "CryptoRandom.h"
29 #include "Environment.h"
30 #include "PerProcess.h"
31 #include "VMAllocate.h"
32 #include "Vector.h"
33 #include "bmalloc.h"
34 #include <cstdio>
35 #include <mutex>
36
37 #if BCPU(ARM64)
38 // FIXME: There is no good reason for ARM64 to be special.
39 // https://bugs.webkit.org/show_bug.cgi?id=177605
40 #define GIGACAGE_RUNWAY 0
41 #else
42 // FIXME: Consider making this 32GB, in case unsigned 32-bit indices find their way into indexed accesses.
43 // https://bugs.webkit.org/show_bug.cgi?id=175062
44 #define GIGACAGE_RUNWAY (16llu * 1024 * 1024 * 1024)
45 #endif
46
47 char g_gigacageBasePtrs[GIGACAGE_BASE_PTRS_SIZE] __attribute__((aligned(GIGACAGE_BASE_PTRS_SIZE)));
48
49 using namespace bmalloc;
50
51 namespace Gigacage {
52
53 bool g_wasEnabled;
54
55 namespace {
56
57 bool s_isDisablingPrimitiveGigacageDisabled;
58
59 void protectGigacageBasePtrs()
60 {
61     uintptr_t basePtrs = reinterpret_cast<uintptr_t>(g_gigacageBasePtrs);
62     // We might only get page size alignment, but that's also the minimum we need.
63     RELEASE_BASSERT(!(basePtrs & (vmPageSize() - 1)));
64     mprotect(g_gigacageBasePtrs, GIGACAGE_BASE_PTRS_SIZE, PROT_READ);
65 }
66
67 void unprotectGigacageBasePtrs()
68 {
69     mprotect(g_gigacageBasePtrs, GIGACAGE_BASE_PTRS_SIZE, PROT_READ | PROT_WRITE);
70 }
71
72 class UnprotectGigacageBasePtrsScope {
73 public:
74     UnprotectGigacageBasePtrsScope()
75     {
76         unprotectGigacageBasePtrs();
77     }
78     
79     ~UnprotectGigacageBasePtrsScope()
80     {
81         protectGigacageBasePtrs();
82     }
83 };
84
85 struct Callback {
86     Callback() { }
87     
88     Callback(void (*function)(void*), void *argument)
89         : function(function)
90         , argument(argument)
91     {
92     }
93     
94     void (*function)(void*) { nullptr };
95     void* argument { nullptr };
96 };
97
98 struct PrimitiveDisableCallbacks {
99     PrimitiveDisableCallbacks(std::lock_guard<StaticMutex>&) { }
100     
101     Vector<Callback> callbacks;
102 };
103
104 } // anonymous namespace
105
106 void ensureGigacage()
107 {
108 #if GIGACAGE_ENABLED
109     static std::once_flag onceFlag;
110     std::call_once(
111         onceFlag,
112         [] {
113             if (!shouldBeEnabled())
114                 return;
115             
116             Kind shuffledKinds[numKinds];
117             for (unsigned i = 0; i < numKinds; ++i)
118                 shuffledKinds[i] = static_cast<Kind>(i);
119             
120             // We just go ahead and assume that 64 bits is enough randomness. That's trivially true right
121             // now, but would stop being true if we went crazy with gigacages. Based on my math, 21 is the
122             // largest value of n so that n! <= 2^64.
123             static_assert(numKinds <= 21, "too many kinds");
124             uint64_t random;
125             cryptoRandom(reinterpret_cast<unsigned char*>(&random), sizeof(random));
126             for (unsigned i = numKinds; i--;) {
127                 unsigned limit = i + 1;
128                 unsigned j = static_cast<unsigned>(random % limit);
129                 random /= limit;
130                 std::swap(shuffledKinds[i], shuffledKinds[j]);
131             }
132
133             auto alignTo = [] (Kind kind, size_t totalSize) -> size_t {
134                 return roundUpToMultipleOf(alignment(kind), totalSize);
135             };
136             auto bump = [] (Kind kind, size_t totalSize) -> size_t {
137                 return totalSize + size(kind);
138             };
139             
140             size_t totalSize = 0;
141             size_t maxAlignment = 0;
142             
143             for (Kind kind : shuffledKinds) {
144                 totalSize = bump(kind, alignTo(kind, totalSize));
145                 maxAlignment = std::max(maxAlignment, alignment(kind));
146             }
147             totalSize += GIGACAGE_RUNWAY;
148             
149             // FIXME: Randomize where this goes.
150             // https://bugs.webkit.org/show_bug.cgi?id=175245
151             void* base = tryVMAllocate(maxAlignment, totalSize);
152             if (!base) {
153                 if (GIGACAGE_ALLOCATION_CAN_FAIL)
154                     return;
155                 fprintf(stderr, "FATAL: Could not allocate gigacage memory with maxAlignment = %lu, totalSize = %lu.\n", maxAlignment, totalSize);
156                 BCRASH();
157             }
158             vmDeallocatePhysicalPages(base, totalSize);
159             
160             size_t nextCage = 0;
161             for (Kind kind : shuffledKinds) {
162                 nextCage = alignTo(kind, nextCage);
163                 basePtr(kind) = reinterpret_cast<char*>(base) + nextCage;
164                 nextCage = bump(kind, nextCage);
165             }
166             
167             protectGigacageBasePtrs();
168             g_wasEnabled = true;
169         });
170 #endif // GIGACAGE_ENABLED
171 }
172
173 void disablePrimitiveGigacage()
174 {
175     ensureGigacage();
176     if (!basePtrs().primitive) {
177         // It was never enabled. That means that we never even saved any callbacks. Or, we had already disabled
178         // it before, and already called the callbacks.
179         return;
180     }
181     
182     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
183     std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
184     for (Callback& callback : callbacks.callbacks)
185         callback.function(callback.argument);
186     callbacks.callbacks.shrink(0);
187     UnprotectGigacageBasePtrsScope unprotectScope;
188     basePtrs().primitive = nullptr;
189 }
190
191 void addPrimitiveDisableCallback(void (*function)(void*), void* argument)
192 {
193     ensureGigacage();
194     if (!basePtrs().primitive) {
195         // It was already disabled or we were never able to enable it.
196         function(argument);
197         return;
198     }
199     
200     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
201     std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
202     callbacks.callbacks.push(Callback(function, argument));
203 }
204
205 void removePrimitiveDisableCallback(void (*function)(void*), void* argument)
206 {
207     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
208     std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
209     for (size_t i = 0; i < callbacks.callbacks.size(); ++i) {
210         if (callbacks.callbacks[i].function == function
211             && callbacks.callbacks[i].argument == argument) {
212             callbacks.callbacks[i] = callbacks.callbacks.last();
213             callbacks.callbacks.pop();
214             return;
215         }
216     }
217 }
218
219 static void primitiveGigacageDisabled(void*)
220 {
221     static bool s_false;
222     fprintf(stderr, "FATAL: Primitive gigacage disabled, but we don't want that in this process.\n");
223     if (!s_false)
224         BCRASH();
225 }
226
227 void disableDisablingPrimitiveGigacageIfShouldBeEnabled()
228 {
229     if (shouldBeEnabled()) {
230         addPrimitiveDisableCallback(primitiveGigacageDisabled, nullptr);
231         s_isDisablingPrimitiveGigacageDisabled = true;
232     }
233 }
234
235 bool isDisablingPrimitiveGigacageDisabled()
236 {
237     return s_isDisablingPrimitiveGigacageDisabled;
238 }
239
240 bool shouldBeEnabled()
241 {
242     static std::once_flag onceFlag;
243     static bool cached = false;
244     std::call_once(
245         onceFlag,
246         [] {
247 #if BCPU(ARM64)
248             // FIXME: Make WasmBench run with gigacage on iOS and re-enable on ARM64:
249             // https://bugs.webkit.org/show_bug.cgi?id=178557
250             return;
251 #endif
252             bool result = GIGACAGE_ENABLED && !PerProcess<Environment>::get()->isDebugHeapEnabled();
253             if (!result)
254                 return;
255             
256             if (char* gigacageEnabled = getenv("GIGACAGE_ENABLED")) {
257                 if (!strcasecmp(gigacageEnabled, "no") || !strcasecmp(gigacageEnabled, "false") || !strcasecmp(gigacageEnabled, "0")) {
258                     fprintf(stderr, "Warning: disabling gigacage because GIGACAGE_ENABLED=%s!\n", gigacageEnabled);
259                     return;
260                 } else if (strcasecmp(gigacageEnabled, "yes") && strcasecmp(gigacageEnabled, "true") && strcasecmp(gigacageEnabled, "1"))
261                     fprintf(stderr, "Warning: invalid argument to GIGACAGE_ENABLED: %s\n", gigacageEnabled);
262             }
263             
264             cached = true;
265         });
266     
267     return cached;
268 }
269
270 } // namespace Gigacage
271
272
273