09bf3b7238a49a3939ad60cc38b0cf64d3636362
[WebKit-https.git] / Source / bmalloc / bmalloc / Gigacage.h
1 /*
2  * Copyright (C) 2017-2019 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 #pragma once
27
28 #include "Algorithm.h"
29 #include "BAssert.h"
30 #include "BExport.h"
31 #include "BInline.h"
32 #include "BPlatform.h"
33 #include "Sizes.h"
34 #include <cstddef>
35 #include <inttypes.h>
36
37 #if ((BOS(DARWIN) || BOS(LINUX)) && \
38     (BCPU(X86_64) || (BCPU(ARM64) && !defined(__ILP32__) && (!BPLATFORM(IOS_FAMILY) || BPLATFORM(IOS)))))
39 #define GIGACAGE_ENABLED 1
40 #else
41 #define GIGACAGE_ENABLED 0
42 #endif
43
44
45 namespace Gigacage {
46
47 enum Kind {
48     Primitive,
49     JSValue,
50     NumberOfKinds
51 };
52
53 BINLINE const char* name(Kind kind)
54 {
55     switch (kind) {
56     case Primitive:
57         return "Primitive";
58     case JSValue:
59         return "JSValue";
60     case NumberOfKinds:
61         break;
62     }
63     BCRASH();
64     return nullptr;
65 }
66
67 #if GIGACAGE_ENABLED
68
69 #if BOS_EFFECTIVE_ADDRESS_WIDTH < 48
70 constexpr size_t primitiveGigacageSize = 2 * bmalloc::Sizes::GB;
71 constexpr size_t jsValueGigacageSize = 2 * bmalloc::Sizes::GB;
72 constexpr size_t maximumCageSizeReductionForSlide = bmalloc::Sizes::GB / 4;
73 constexpr size_t configSizeToProtect = 16 * bmalloc::Sizes::kB;
74 #else
75 constexpr size_t primitiveGigacageSize = 32 * bmalloc::Sizes::GB;
76 constexpr size_t jsValueGigacageSize = 16 * bmalloc::Sizes::GB;
77 constexpr size_t maximumCageSizeReductionForSlide = 4 * bmalloc::Sizes::GB;
78 constexpr size_t configSizeToProtect = 4 * bmalloc::Sizes::kB;
79 #endif
80
81 // In Linux, if `vm.overcommit_memory = 2` is specified, mmap with large size can fail if it exceeds the size of RAM.
82 // So we specify GIGACAGE_ALLOCATION_CAN_FAIL = 1.
83 #if BOS(LINUX)
84 #define GIGACAGE_ALLOCATION_CAN_FAIL 1
85 #else
86 #define GIGACAGE_ALLOCATION_CAN_FAIL 0
87 #endif
88
89
90 static_assert(bmalloc::isPowerOfTwo(primitiveGigacageSize), "");
91 static_assert(bmalloc::isPowerOfTwo(jsValueGigacageSize), "");
92 static_assert(primitiveGigacageSize > maximumCageSizeReductionForSlide, "");
93 static_assert(jsValueGigacageSize > maximumCageSizeReductionForSlide, "");
94
95 constexpr size_t gigacageSizeToMask(size_t size) { return size - 1; }
96
97 constexpr size_t primitiveGigacageMask = gigacageSizeToMask(primitiveGigacageSize);
98 constexpr size_t jsValueGigacageMask = gigacageSizeToMask(jsValueGigacageSize);
99
100 struct Config {
101     void* basePtr(Kind kind) const
102     {
103         RELEASE_BASSERT(kind < NumberOfKinds);
104         return basePtrs[kind];
105     }
106
107     void setBasePtr(Kind kind, void* ptr)
108     {
109         RELEASE_BASSERT(kind < NumberOfKinds);
110         basePtrs[kind] = ptr;
111     }
112
113     union {
114         struct {
115             // All the fields in this struct should be chosen such that their
116             // initial value is 0 / null / falsy because Config is instantiated
117             // as a global singleton.
118
119             bool isEnabled;
120             bool isPermanentlyFrozen;
121             bool disablingPrimitiveGigacageIsForbidden;
122             bool shouldBeEnabled;
123
124             // We would like to just put the std::once_flag for these functions
125             // here, but we can't because std::once_flag has a implicitly-deleted
126             // default constructor. So, we use a boolean instead.
127             bool shouldBeEnabledHasBeenCalled;
128             bool ensureGigacageHasBeenCalled;
129
130             void* start;
131             size_t totalSize;
132             void* basePtrs[NumberOfKinds];
133         };
134         char ensureSize[configSizeToProtect];
135     };
136 };
137 static_assert(sizeof(Config) == configSizeToProtect, "Gigacage Config must fit in configSizeToProtect");
138
139 extern "C" alignas(configSizeToProtect) BEXPORT Config g_gigacageConfig;
140
141 // These constants are needed by the LLInt.
142 constexpr ptrdiff_t offsetOfPrimitiveGigacageBasePtr = Kind::Primitive * sizeof(void*);
143 constexpr ptrdiff_t offsetOfJSValueGigacageBasePtr = Kind::JSValue * sizeof(void*);
144
145
146 BINLINE bool isEnabled() { return g_gigacageConfig.isEnabled; }
147
148 BEXPORT void ensureGigacage();
149
150 BEXPORT void disablePrimitiveGigacage();
151
152 // This will call the disable callback immediately if the Primitive Gigacage is currently disabled.
153 BEXPORT void addPrimitiveDisableCallback(void (*)(void*), void*);
154 BEXPORT void removePrimitiveDisableCallback(void (*)(void*), void*);
155
156 BEXPORT void forbidDisablingPrimitiveGigacage();
157
158 BEXPORT bool isDisablingPrimitiveGigacageForbidden();
159 inline bool isPrimitiveGigacagePermanentlyEnabled() { return isDisablingPrimitiveGigacageForbidden(); }
160 inline bool canPrimitiveGigacageBeDisabled() { return !isDisablingPrimitiveGigacageForbidden(); }
161
162 BINLINE void* basePtr(Kind kind)
163 {
164     return g_gigacageConfig.basePtr(kind);
165 }
166
167 BINLINE void* addressOfBasePtr(Kind kind)
168 {
169     RELEASE_BASSERT(kind < NumberOfKinds);
170     return &g_gigacageConfig.basePtrs[kind];
171 }
172
173 BINLINE bool isEnabled(Kind kind)
174 {
175     return !!g_gigacageConfig.basePtr(kind);
176 }
177
178 BINLINE size_t size(Kind kind)
179 {
180     switch (kind) {
181     case Primitive:
182         return static_cast<size_t>(primitiveGigacageSize);
183     case JSValue:
184         return static_cast<size_t>(jsValueGigacageSize);
185     case NumberOfKinds:
186         break;
187     }
188     BCRASH();
189     return 0;
190 }
191
192 BINLINE size_t alignment(Kind kind)
193 {
194     return size(kind);
195 }
196
197 BINLINE size_t mask(Kind kind)
198 {
199     return gigacageSizeToMask(size(kind));
200 }
201
202 template<typename Func>
203 void forEachKind(const Func& func)
204 {
205     func(Primitive);
206     func(JSValue);
207 }
208
209 template<typename T>
210 BINLINE T* caged(Kind kind, T* ptr)
211 {
212     BASSERT(ptr);
213     void* gigacageBasePtr = g_gigacageConfig.basePtr(kind);
214     if (!gigacageBasePtr)
215         return ptr;
216     return reinterpret_cast<T*>(
217         reinterpret_cast<uintptr_t>(gigacageBasePtr) + (
218             reinterpret_cast<uintptr_t>(ptr) & mask(kind)));
219 }
220
221 template<typename T>
222 BINLINE T* cagedMayBeNull(Kind kind, T* ptr)
223 {
224     if (!ptr)
225         return ptr;
226     return caged(kind, ptr);
227 }
228
229 BINLINE bool isCaged(Kind kind, const void* ptr)
230 {
231     return caged(kind, ptr) == ptr;
232 }
233
234 BINLINE bool contains(const void* ptr)
235 {
236     auto* start = reinterpret_cast<const uint8_t*>(g_gigacageConfig.start);
237     auto* p = reinterpret_cast<const uint8_t*>(ptr);
238     return static_cast<size_t>(p - start) < g_gigacageConfig.totalSize;
239 }
240
241 BEXPORT bool shouldBeEnabled();
242
243 #else // GIGACAGE_ENABLED
244
245 BINLINE void* basePtr(Kind)
246 {
247     BCRASH();
248     static void* unreachable;
249     return unreachable;
250 }
251 BINLINE size_t size(Kind) { BCRASH(); return 0; }
252 BINLINE void ensureGigacage() { }
253 BINLINE bool contains(const void*) { return false; }
254 BINLINE bool isEnabled() { return false; }
255 BINLINE bool isCaged(Kind, const void*) { return true; }
256 BINLINE bool isEnabled(Kind) { return false; }
257 template<typename T> BINLINE T* caged(Kind, T* ptr) { return ptr; }
258 template<typename T> BINLINE T* cagedMayBeNull(Kind, T* ptr) { return ptr; }
259 BINLINE void forbidDisablingPrimitiveGigacage() { }
260 BINLINE bool canPrimitiveGigacageBeDisabled() { return false; }
261 BINLINE void disablePrimitiveGigacage() { }
262 BINLINE void addPrimitiveDisableCallback(void (*)(void*), void*) { }
263 BINLINE void removePrimitiveDisableCallback(void (*)(void*), void*) { }
264
265 #endif // GIGACAGE_ENABLED
266
267 } // namespace Gigacage
268
269
270