1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* SpiderMonkey initialization and shutdown code. */
8 
9 #include "js/Initialization.h"
10 
11 #include "mozilla/Assertions.h"
12 #include "mozilla/TextUtils.h"
13 
14 #include "jstypes.h"
15 
16 #include "builtin/AtomicsObject.h"
17 #include "builtin/TestingFunctions.h"
18 #include "ds/MemoryProtectionExceptionHandler.h"
19 #include "gc/Statistics.h"
20 #include "jit/AtomicOperations.h"
21 #include "jit/ExecutableAllocator.h"
22 #include "jit/Ion.h"
23 #include "jit/JitCommon.h"
24 #include "js/Utility.h"
25 
26 #if JS_HAS_INTL_API
27 #  include "unicode/putil.h"
28 #  include "unicode/uclean.h"
29 #  include "unicode/utypes.h"
30 #endif  // JS_HAS_INTL_API
31 
32 #include "threading/ProtectedData.h"  // js::AutoNoteSingleThreadedRegion
33 #include "util/Poison.h"
34 #include "vm/ArrayBufferObject.h"
35 #include "vm/BigIntType.h"
36 #include "vm/DateTime.h"
37 #include "vm/HelperThreads.h"
38 #include "vm/Runtime.h"
39 #include "vm/Time.h"
40 #include "vm/TraceLogging.h"
41 #ifdef MOZ_VTUNE
42 #  include "vtune/VTuneWrapper.h"
43 #endif
44 #include "wasm/WasmProcess.h"
45 
46 using js::FutexThread;
47 using JS::detail::InitState;
48 using JS::detail::libraryInitState;
49 
50 InitState JS::detail::libraryInitState;
51 
52 #ifdef DEBUG
MessageParameterCount(const char * format)53 static unsigned MessageParameterCount(const char* format) {
54   unsigned numfmtspecs = 0;
55   for (const char* fmt = format; *fmt != '\0'; fmt++) {
56     if (*fmt == '{' && mozilla::IsAsciiDigit(fmt[1])) {
57       ++numfmtspecs;
58     }
59   }
60   return numfmtspecs;
61 }
62 
CheckMessageParameterCounts()63 static void CheckMessageParameterCounts() {
64   // Assert that each message format has the correct number of braced
65   // parameters.
66 #  define MSG_DEF(name, count, exception, format) \
67     MOZ_ASSERT(MessageParameterCount(format) == count);
68 #  include "js/friend/ErrorNumbers.msg"
69 #  undef MSG_DEF
70 }
71 #endif /* DEBUG */
72 
73 #if defined(JS_RUNTIME_CANONICAL_NAN)
74 namespace JS::detail {
75 uint64_t CanonicalizedNaNBits;
76 }  // namespace JS::detail
77 #endif
78 
SetupCanonicalNaN()79 static void SetupCanonicalNaN() {
80   // Compute the standard NaN value that the hardware generates.
81   volatile double infinity = mozilla::PositiveInfinity<double>();
82   volatile double hardwareNaN = infinity - infinity;
83   uint64_t hardwareNaNBits = mozilla::BitwiseCast<uint64_t>(hardwareNaN);
84   hardwareNaNBits &= ~mozilla::FloatingPoint<double>::kSignBit;
85 
86 #if defined(JS_NONCANONICAL_HARDWARE_NAN)
87   // If the NaN generated by hardware operations is not compatible
88   // with our canonical NaN, we must canonicalize every double. This
89   // is implemented for C++ code in Value::bitsFromDouble, but is not
90   // implemented for JIT code.
91 #  if !defined(JS_CODEGEN_NONE)
92 #    error "No JIT support for non-canonical hardware NaN"
93 #  endif
94 
95   (void)hardwareNaNBits;
96 #elif defined(JS_RUNTIME_CANONICAL_NAN)
97   // Determine canonical NaN at startup. It must still match the ValueIsDouble
98   // requirements.
99   MOZ_RELEASE_ASSERT(JS::detail::ValueIsDouble(hardwareNaNBits));
100   JS::detail::CanonicalizedNaNBits = hardwareNaNBits;
101 #else
102   // Assert that the NaN generated by hardware operations is
103   // compatible with the canonical NaN we use for JS::Value. This is
104   // true for all of our supported platforms, but not for SPARC.
105   MOZ_RELEASE_ASSERT(hardwareNaNBits == JS::detail::CanonicalizedNaNBits,
106                      "Unexpected default hardware NaN value");
107 #endif
108 }
109 
110 #define RETURN_IF_FAIL(code)           \
111   do {                                 \
112     if (!code) return #code " failed"; \
113   } while (0)
114 
115 extern "C" void install_rust_panic_hook();
116 
InitWithFailureDiagnostic(bool isDebugBuild)117 JS_PUBLIC_API const char* JS::detail::InitWithFailureDiagnostic(
118     bool isDebugBuild) {
119   // Verify that our DEBUG setting matches the caller's.
120 #ifdef DEBUG
121   MOZ_RELEASE_ASSERT(isDebugBuild);
122 #else
123   MOZ_RELEASE_ASSERT(!isDebugBuild);
124 #endif
125 
126   MOZ_ASSERT(libraryInitState == InitState::Uninitialized,
127              "must call JS_Init once before any JSAPI operation except "
128              "JS_SetICUMemoryFunctions");
129   MOZ_ASSERT(!JSRuntime::hasLiveRuntimes(),
130              "how do we have live runtimes before JS_Init?");
131 
132   libraryInitState = InitState::Initializing;
133 
134 #ifndef NO_RUST_PANIC_HOOK
135   install_rust_panic_hook();
136 #endif
137 
138   PRMJ_NowInit();
139 
140   // The first invocation of `ProcessCreation` creates a temporary thread
141   // and crashes if that fails, i.e. because we're out of memory. To prevent
142   // that from happening at some later time, get it out of the way during
143   // startup.
144   mozilla::TimeStamp::ProcessCreation();
145 
146 #ifdef DEBUG
147   CheckMessageParameterCounts();
148 #endif
149 
150   SetupCanonicalNaN();
151 
152   RETURN_IF_FAIL(js::TlsContext.init());
153 
154 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
155   RETURN_IF_FAIL(js::oom::InitThreadType());
156 #endif
157 
158 #if defined(FUZZING)
159   js::oom::InitLargeAllocLimit();
160 #endif
161 
162 #if defined(JS_GC_ALLOW_EXTRA_POISONING)
163   if (getenv("JSGC_EXTRA_POISONING")) {
164     js::gExtraPoisoningEnabled = true;
165   }
166 #endif
167 
168   js::InitMallocAllocator();
169 
170   RETURN_IF_FAIL(js::Mutex::Init());
171 
172   js::gc::InitMemorySubsystem();  // Ensure gc::SystemPageSize() works.
173 
174   RETURN_IF_FAIL(js::wasm::Init());
175 
176   js::coverage::InitLCov();
177 
178   RETURN_IF_FAIL(js::jit::InitProcessExecutableMemory());
179 
180   RETURN_IF_FAIL(js::MemoryProtectionExceptionHandler::install());
181 
182   RETURN_IF_FAIL(js::jit::InitializeJit());
183 
184   RETURN_IF_FAIL(js::InitDateTimeState());
185 
186 #ifdef MOZ_VTUNE
187   RETURN_IF_FAIL(js::vtune::Initialize());
188 #endif
189 
190   RETURN_IF_FAIL(js::jit::AtomicOperations::Initialize());
191 
192 #if JS_HAS_INTL_API
193 #  if !MOZ_SYSTEM_ICU
194   // Explicitly set the data directory to its default value, but only when we're
195   // sure that we use our in-tree ICU copy. See bug 1527879 and ICU bug
196   // report <https://unicode-org.atlassian.net/browse/ICU-20491>.
197   u_setDataDirectory("");
198 #  endif
199 
200   UErrorCode err = U_ZERO_ERROR;
201   u_init(&err);
202   if (U_FAILURE(err)) {
203     return "u_init() failed";
204   }
205 #endif  // JS_HAS_INTL_API
206 
207   RETURN_IF_FAIL(js::CreateHelperThreadsState());
208   RETURN_IF_FAIL(FutexThread::initialize());
209   RETURN_IF_FAIL(js::gcstats::Statistics::initialize());
210   RETURN_IF_FAIL(js::InitTestingFunctions());
211 
212 #ifdef JS_SIMULATOR
213   RETURN_IF_FAIL(js::jit::SimulatorProcess::initialize());
214 #endif
215 
216 #ifdef JS_TRACE_LOGGING
217   RETURN_IF_FAIL(JS::InitTraceLogger());
218 #endif
219 
220   libraryInitState = InitState::Running;
221   return nullptr;
222 }
223 
224 #undef RETURN_IF_FAIL
225 
InitSelfHostedCode(JSContext * cx,SelfHostedCache cache,SelfHostedWriter writer)226 JS_PUBLIC_API bool JS::InitSelfHostedCode(JSContext* cx, SelfHostedCache cache,
227                                           SelfHostedWriter writer) {
228   MOZ_RELEASE_ASSERT(!cx->runtime()->hasInitializedSelfHosting(),
229                      "JS::InitSelfHostedCode() called more than once");
230 
231   js::AutoNoteSingleThreadedRegion anstr;
232 
233   JSRuntime* rt = cx->runtime();
234 
235   if (!rt->initializeAtoms(cx)) {
236     return false;
237   }
238 
239   if (!rt->initializeParserAtoms(cx)) {
240     return false;
241   }
242 
243 #ifndef JS_CODEGEN_NONE
244   if (!rt->createJitRuntime(cx)) {
245     return false;
246   }
247 #endif
248 
249   if (!rt->initSelfHosting(cx, cache, writer)) {
250     return false;
251   }
252 
253   if (!rt->parentRuntime && !rt->initMainAtomsTables(cx)) {
254     return false;
255   }
256 
257   return true;
258 }
259 
JS_ShutDown(void)260 JS_PUBLIC_API void JS_ShutDown(void) {
261   MOZ_ASSERT(
262       libraryInitState == InitState::Running,
263       "JS_ShutDown must only be called after JS_Init and can't race with it");
264 #ifdef DEBUG
265   if (JSRuntime::hasLiveRuntimes()) {
266     // Gecko is too buggy to assert this just yet.
267     fprintf(stderr,
268             "WARNING: YOU ARE LEAKING THE WORLD (at least one JSRuntime "
269             "and everything alive inside it, that is) AT JS_ShutDown "
270             "TIME.  FIX THIS!\n");
271   }
272 #endif
273 
274   FutexThread::destroy();
275 
276   js::DestroyHelperThreadsState();
277 
278 #ifdef JS_SIMULATOR
279   js::jit::SimulatorProcess::destroy();
280 #endif
281 
282   js::jit::AtomicOperations::ShutDown();
283 
284 #ifdef JS_TRACE_LOGGING
285   js::DestroyTraceLoggerThreadState();
286   js::DestroyTraceLoggerGraphState();
287 #endif
288 
289   js::MemoryProtectionExceptionHandler::uninstall();
290 
291   js::wasm::ShutDown();
292 
293   // The only difficult-to-address reason for the restriction that you can't
294   // call JS_Init/stuff/JS_ShutDown multiple times is the Windows PRMJ
295   // NowInit initialization code, which uses PR_CallOnce to initialize the
296   // PRMJ_Now subsystem.  (For reinitialization to be permitted, we'd need to
297   // "reset" the called-once status -- doable, but more trouble than it's
298   // worth now.)  Initializing that subsystem from JS_Init eliminates the
299   // problem, but initialization can take a comparatively long time (15ms or
300   // so), so we really don't want to do it in JS_Init, and we really do want
301   // to do it only when PRMJ_Now is eventually called.
302   PRMJ_NowShutdown();
303 
304 #if JS_HAS_INTL_API
305   u_cleanup();
306 #endif  // JS_HAS_INTL_API
307 
308 #ifdef MOZ_VTUNE
309   js::vtune::Shutdown();
310 #endif  // MOZ_VTUNE
311 
312   js::FinishDateTimeState();
313 
314   if (!JSRuntime::hasLiveRuntimes()) {
315     js::jit::ReleaseProcessExecutableMemory();
316     MOZ_ASSERT(!js::LiveMappedBufferCount());
317   }
318 
319   js::ShutDownMallocAllocator();
320 
321   libraryInitState = InitState::ShutDown;
322 }
323 
JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn,JS_ICUReallocFn reallocFn,JS_ICUFreeFn freeFn)324 JS_PUBLIC_API bool JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn,
325                                             JS_ICUReallocFn reallocFn,
326                                             JS_ICUFreeFn freeFn) {
327   MOZ_ASSERT(libraryInitState == InitState::Uninitialized,
328              "must call JS_SetICUMemoryFunctions before any other JSAPI "
329              "operation (including JS_Init)");
330 
331 #if JS_HAS_INTL_API
332   UErrorCode status = U_ZERO_ERROR;
333   u_setMemoryFunctions(/* context = */ nullptr, allocFn, reallocFn, freeFn,
334                        &status);
335   return U_SUCCESS(status);
336 #else
337   return true;
338 #endif
339 }
340