Reduce Gigacage sizes
[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 <mutex>
34
35 // FIXME: Ask dyld to put this in its own page, and mprotect the page after we ensure the gigacage.
36 // https://bugs.webkit.org/show_bug.cgi?id=174972
37 void* g_primitiveGigacageBasePtr;
38 void* g_jsValueGigacageBasePtr;
39
40 using namespace bmalloc;
41
42 namespace Gigacage {
43
44 static bool s_isDisablingPrimitiveGigacageDisabled;
45
46 struct Callback {
47     Callback() { }
48     
49     Callback(void (*function)(void*), void *argument)
50         : function(function)
51         , argument(argument)
52     {
53     }
54     
55     void (*function)(void*) { nullptr };
56     void* argument { nullptr };
57 };
58
59 struct PrimitiveDisableCallbacks {
60     PrimitiveDisableCallbacks(std::lock_guard<StaticMutex>&) { }
61     
62     Vector<Callback> callbacks;
63 };
64
65 void ensureGigacage()
66 {
67 #if GIGACAGE_ENABLED
68     static std::once_flag onceFlag;
69     std::call_once(
70         onceFlag,
71         [] {
72             if (!shouldBeEnabled())
73                 return;
74             
75             forEachKind(
76                 [&] (Kind kind) {
77                     // FIXME: Randomize where this goes.
78                     // https://bugs.webkit.org/show_bug.cgi?id=175245
79                     basePtr(kind) = tryVMAllocate(alignment(kind), totalSize(kind));
80                     if (!basePtr(kind)) {
81                         fprintf(stderr, "FATAL: Could not allocate %s gigacage.\n", name(kind));
82                         BCRASH();
83                     }
84                     
85                     vmDeallocatePhysicalPages(basePtr(kind), totalSize(kind));
86                 });
87         });
88 #endif // GIGACAGE_ENABLED
89 }
90
91 void disablePrimitiveGigacage()
92 {
93     ensureGigacage();
94     if (!g_primitiveGigacageBasePtr) {
95         // It was never enabled. That means that we never even saved any callbacks. Or, we had already disabled
96         // it before, and already called the callbacks.
97         return;
98     }
99     
100     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
101     std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
102     for (Callback& callback : callbacks.callbacks)
103         callback.function(callback.argument);
104     callbacks.callbacks.shrink(0);
105     g_primitiveGigacageBasePtr = nullptr;
106 }
107
108 void addPrimitiveDisableCallback(void (*function)(void*), void* argument)
109 {
110     ensureGigacage();
111     if (!g_primitiveGigacageBasePtr) {
112         // It was already disabled or we were never able to enable it.
113         function(argument);
114         return;
115     }
116     
117     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
118     std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
119     callbacks.callbacks.push(Callback(function, argument));
120 }
121
122 void removePrimitiveDisableCallback(void (*function)(void*), void* argument)
123 {
124     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
125     std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
126     for (size_t i = 0; i < callbacks.callbacks.size(); ++i) {
127         if (callbacks.callbacks[i].function == function
128             && callbacks.callbacks[i].argument == argument) {
129             callbacks.callbacks[i] = callbacks.callbacks.last();
130             callbacks.callbacks.pop();
131             return;
132         }
133     }
134 }
135
136 static void primitiveGigacageDisabled(void*)
137 {
138     static bool s_false;
139     fprintf(stderr, "FATAL: Primitive gigacage disabled, but we don't want that in this process.\n");
140     if (!s_false)
141         BCRASH();
142 }
143
144 void disableDisablingPrimitiveGigacageIfShouldBeEnabled()
145 {
146     if (shouldBeEnabled()) {
147         addPrimitiveDisableCallback(primitiveGigacageDisabled, nullptr);
148         s_isDisablingPrimitiveGigacageDisabled = true;
149     }
150 }
151
152 bool isDisablingPrimitiveGigacageDisabled()
153 {
154     return s_isDisablingPrimitiveGigacageDisabled;
155 }
156
157 bool shouldBeEnabled()
158 {
159     return GIGACAGE_ENABLED && !PerProcess<Environment>::get()->isDebugHeapEnabled();
160 }
161
162 } // namespace Gigacage
163
164
165