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