Put g_gigacageBasePtr into its own page and make it read-only
[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 <cstdio>
34 #include <mutex>
35
36 char g_gigacageBasePtrs[GIGACAGE_BASE_PTRS_SIZE] __attribute__((aligned(GIGACAGE_BASE_PTRS_SIZE)));
37
38 using namespace bmalloc;
39
40 namespace Gigacage {
41
42 namespace {
43
44 bool s_isDisablingPrimitiveGigacageDisabled;
45
46 void protectGigacageBasePtrs()
47 {
48     uintptr_t basePtrs = reinterpret_cast<uintptr_t>(g_gigacageBasePtrs);
49     // We might only get page size alignment, but that's also the minimum we need.
50     RELEASE_BASSERT(!(basePtrs & (vmPageSize() - 1)));
51     mprotect(g_gigacageBasePtrs, GIGACAGE_BASE_PTRS_SIZE, PROT_READ);
52 }
53
54 void unprotectGigacageBasePtrs()
55 {
56     mprotect(g_gigacageBasePtrs, GIGACAGE_BASE_PTRS_SIZE, PROT_READ | PROT_WRITE);
57 }
58
59 class UnprotectGigacageBasePtrsScope {
60 public:
61     UnprotectGigacageBasePtrsScope()
62     {
63         unprotectGigacageBasePtrs();
64     }
65     
66     ~UnprotectGigacageBasePtrsScope()
67     {
68         protectGigacageBasePtrs();
69     }
70 };
71
72 } // anonymous namespce
73
74 struct Callback {
75     Callback() { }
76     
77     Callback(void (*function)(void*), void *argument)
78         : function(function)
79         , argument(argument)
80     {
81     }
82     
83     void (*function)(void*) { nullptr };
84     void* argument { nullptr };
85 };
86
87 struct PrimitiveDisableCallbacks {
88     PrimitiveDisableCallbacks(std::lock_guard<StaticMutex>&) { }
89     
90     Vector<Callback> callbacks;
91 };
92
93 void ensureGigacage()
94 {
95 #if GIGACAGE_ENABLED
96     static std::once_flag onceFlag;
97     std::call_once(
98         onceFlag,
99         [] {
100             if (!shouldBeEnabled())
101                 return;
102             
103             forEachKind(
104                 [&] (Kind kind) {
105                     // FIXME: Randomize where this goes.
106                     // https://bugs.webkit.org/show_bug.cgi?id=175245
107                     basePtr(kind) = tryVMAllocate(alignment(kind), totalSize(kind));
108                     if (!basePtr(kind)) {
109                         fprintf(stderr, "FATAL: Could not allocate %s gigacage.\n", name(kind));
110                         BCRASH();
111                     }
112                     
113                     vmDeallocatePhysicalPages(basePtr(kind), totalSize(kind));
114                 });
115             
116             protectGigacageBasePtrs();
117         });
118 #endif // GIGACAGE_ENABLED
119 }
120
121 void disablePrimitiveGigacage()
122 {
123     ensureGigacage();
124     if (!basePtrs().primitive) {
125         // It was never enabled. That means that we never even saved any callbacks. Or, we had already disabled
126         // it before, and already called the callbacks.
127         return;
128     }
129     
130     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
131     std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
132     for (Callback& callback : callbacks.callbacks)
133         callback.function(callback.argument);
134     callbacks.callbacks.shrink(0);
135     UnprotectGigacageBasePtrsScope unprotectScope;
136     basePtrs().primitive = nullptr;
137 }
138
139 void addPrimitiveDisableCallback(void (*function)(void*), void* argument)
140 {
141     ensureGigacage();
142     if (!basePtrs().primitive) {
143         // It was already disabled or we were never able to enable it.
144         function(argument);
145         return;
146     }
147     
148     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
149     std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
150     callbacks.callbacks.push(Callback(function, argument));
151 }
152
153 void removePrimitiveDisableCallback(void (*function)(void*), void* argument)
154 {
155     PrimitiveDisableCallbacks& callbacks = *PerProcess<PrimitiveDisableCallbacks>::get();
156     std::unique_lock<StaticMutex> lock(PerProcess<PrimitiveDisableCallbacks>::mutex());
157     for (size_t i = 0; i < callbacks.callbacks.size(); ++i) {
158         if (callbacks.callbacks[i].function == function
159             && callbacks.callbacks[i].argument == argument) {
160             callbacks.callbacks[i] = callbacks.callbacks.last();
161             callbacks.callbacks.pop();
162             return;
163         }
164     }
165 }
166
167 static void primitiveGigacageDisabled(void*)
168 {
169     static bool s_false;
170     fprintf(stderr, "FATAL: Primitive gigacage disabled, but we don't want that in this process.\n");
171     if (!s_false)
172         BCRASH();
173 }
174
175 void disableDisablingPrimitiveGigacageIfShouldBeEnabled()
176 {
177     if (shouldBeEnabled()) {
178         addPrimitiveDisableCallback(primitiveGigacageDisabled, nullptr);
179         s_isDisablingPrimitiveGigacageDisabled = true;
180     }
181 }
182
183 bool isDisablingPrimitiveGigacageDisabled()
184 {
185     return s_isDisablingPrimitiveGigacageDisabled;
186 }
187
188 bool shouldBeEnabled()
189 {
190     return GIGACAGE_ENABLED && !PerProcess<Environment>::get()->isDebugHeapEnabled();
191 }
192
193 } // namespace Gigacage
194
195
196