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
45namespace Gigacage {
46
47enum Kind {
48 Primitive,
49 JSValue,
50 NumberOfKinds
51};
52
53BINLINE 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 BCPU(ARM64)
70constexpr size_t primitiveGigacageSize = 2 * bmalloc::Sizes::GB;
71constexpr size_t jsValueGigacageSize = 2 * bmalloc::Sizes::GB;
72constexpr size_t maximumCageSizeReductionForSlide = bmalloc::Sizes::GB / 4;
73constexpr size_t configSizeToProtect = 16 * bmalloc::Sizes::kB;
74#else
75constexpr size_t primitiveGigacageSize = 32 * bmalloc::Sizes::GB;
76constexpr size_t jsValueGigacageSize = 16 * bmalloc::Sizes::GB;
77constexpr size_t maximumCageSizeReductionForSlide = 4 * bmalloc::Sizes::GB;
78constexpr 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
90static_assert(bmalloc::isPowerOfTwo(primitiveGigacageSize), "");
91static_assert(bmalloc::isPowerOfTwo(jsValueGigacageSize), "");
92static_assert(primitiveGigacageSize > maximumCageSizeReductionForSlide, "");
93static_assert(jsValueGigacageSize > maximumCageSizeReductionForSlide, "");
94
95constexpr size_t gigacageSizeToMask(size_t size) { return size - 1; }
96
97constexpr size_t primitiveGigacageMask = gigacageSizeToMask(primitiveGigacageSize);
98constexpr size_t jsValueGigacageMask = gigacageSizeToMask(jsValueGigacageSize);
99
100struct 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};
137static_assert(sizeof(Config) == configSizeToProtect, "Gigacage Config must fit in configSizeToProtect");
138
139extern "C" alignas(configSizeToProtect) BEXPORT Config g_gigacageConfig;
140
141// These constants are needed by the LLInt.
142constexpr ptrdiff_t offsetOfPrimitiveGigacageBasePtr = Kind::Primitive * sizeof(void*);
143constexpr ptrdiff_t offsetOfJSValueGigacageBasePtr = Kind::JSValue * sizeof(void*);
144
145
146BINLINE bool isEnabled() { return g_gigacageConfig.isEnabled; }
147
148BEXPORT void ensureGigacage();
149
150BEXPORT void disablePrimitiveGigacage();
151
152// This will call the disable callback immediately if the Primitive Gigacage is currently disabled.
153BEXPORT void addPrimitiveDisableCallback(void (*)(void*), void*);
154BEXPORT void removePrimitiveDisableCallback(void (*)(void*), void*);
155
156BEXPORT void forbidDisablingPrimitiveGigacage();
157
158BEXPORT bool isDisablingPrimitiveGigacageForbidden();
159inline bool isPrimitiveGigacagePermanentlyEnabled() { return isDisablingPrimitiveGigacageForbidden(); }
160inline bool canPrimitiveGigacageBeDisabled() { return !isDisablingPrimitiveGigacageForbidden(); }
161
162BINLINE void* basePtr(Kind kind)
163{
164 return g_gigacageConfig.basePtr(kind);
165}
166
167BINLINE void* addressOfBasePtr(Kind kind)
168{
169 RELEASE_BASSERT(kind < NumberOfKinds);
170 return &g_gigacageConfig.basePtrs[kind];
171}
172
173BINLINE bool isEnabled(Kind kind)
174{
175 return !!g_gigacageConfig.basePtr(kind);
176}
177
178BINLINE 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
192BINLINE size_t alignment(Kind kind)
193{
194 return size(kind);
195}
196
197BINLINE size_t mask(Kind kind)
198{
199 return gigacageSizeToMask(size(kind));
200}
201
202template<typename Func>
203void forEachKind(const Func& func)
204{
205 func(Primitive);
206 func(JSValue);
207}
208
209template<typename T>
210BINLINE 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
221template<typename T>
222BINLINE T* cagedMayBeNull(Kind kind, T* ptr)
223{
224 if (!ptr)
225 return ptr;
226 return caged(kind, ptr);
227}
228
229BINLINE bool isCaged(Kind kind, const void* ptr)
230{
231 return caged(kind, ptr) == ptr;
232}
233
234BINLINE 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
241BEXPORT bool shouldBeEnabled();
242
243#else // GIGACAGE_ENABLED
244
245BINLINE void* basePtr(Kind)
246{
247 BCRASH();
248 static void* unreachable;
249 return unreachable;
250}
251BINLINE size_t size(Kind) { BCRASH(); return 0; }
252BINLINE void ensureGigacage() { }
253BINLINE bool contains(const void*) { return false; }
254BINLINE bool isEnabled() { return false; }
255BINLINE bool isCaged(Kind, const void*) { return true; }
256BINLINE bool isEnabled(Kind) { return false; }
257template<typename T> BINLINE T* caged(Kind, T* ptr) { return ptr; }
258template<typename T> BINLINE T* cagedMayBeNull(Kind, T* ptr) { return ptr; }
259BINLINE void forbidDisablingPrimitiveGigacage() { }
260BINLINE bool canPrimitiveGigacageBeDisabled() { return false; }
261BINLINE void disablePrimitiveGigacage() { }
262BINLINE void addPrimitiveDisableCallback(void (*)(void*), void*) { }
263BINLINE void removePrimitiveDisableCallback(void (*)(void*), void*) { }
264
265#endif // GIGACAGE_ENABLED
266
267} // namespace Gigacage
268
269
270
271