Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* vim: set ts=8 sts=4 et sw=4 tw=99: */
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 : /* Per JSContext object */
8 :
9 : #include "mozilla/MemoryReporting.h"
10 : #include "mozilla/UniquePtr.h"
11 :
12 : #include "xpcprivate.h"
13 : #include "xpcpublic.h"
14 : #include "XPCWrapper.h"
15 : #include "XPCJSMemoryReporter.h"
16 : #include "WrapperFactory.h"
17 : #include "mozJSComponentLoader.h"
18 : #include "nsAutoPtr.h"
19 : #include "nsNetUtil.h"
20 : #include "nsThreadUtils.h"
21 :
22 : #include "nsIMemoryInfoDumper.h"
23 : #include "nsIMemoryReporter.h"
24 : #include "nsIObserverService.h"
25 : #include "nsIDebug2.h"
26 : #include "nsIDocShell.h"
27 : #include "nsIRunnable.h"
28 : #include "nsPIDOMWindow.h"
29 : #include "nsPrintfCString.h"
30 : #include "mozilla/Preferences.h"
31 : #include "mozilla/Telemetry.h"
32 : #include "mozilla/Services.h"
33 : #include "mozilla/dom/ScriptSettings.h"
34 :
35 : #include "nsContentUtils.h"
36 : #include "nsCCUncollectableMarker.h"
37 : #include "nsCycleCollectionNoteRootCallback.h"
38 : #include "nsCycleCollector.h"
39 : #include "jsapi.h"
40 : #include "js/MemoryMetrics.h"
41 : #include "mozilla/dom/BindingUtils.h"
42 : #include "mozilla/dom/Element.h"
43 : #include "mozilla/dom/ScriptLoader.h"
44 : #include "mozilla/dom/WindowBinding.h"
45 : #include "mozilla/extensions/WebExtensionPolicy.h"
46 : #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
47 : #include "mozilla/Atomics.h"
48 : #include "mozilla/Attributes.h"
49 : #include "mozilla/ProcessHangMonitor.h"
50 : #include "mozilla/Sprintf.h"
51 : #include "mozilla/ThreadLocal.h"
52 : #include "mozilla/UniquePtrExtensions.h"
53 : #include "mozilla/Unused.h"
54 : #include "AccessCheck.h"
55 : #include "nsGlobalWindow.h"
56 : #include "nsAboutProtocolUtils.h"
57 :
58 : #include "GeckoProfiler.h"
59 : #include "nsIInputStream.h"
60 : #include "nsIXULRuntime.h"
61 : #include "nsJSPrincipals.h"
62 : #include "ExpandedPrincipal.h"
63 : #include "SystemPrincipal.h"
64 :
65 : #if defined(XP_LINUX) && !defined(ANDROID)
66 : // For getrlimit and min/max.
67 : #include <algorithm>
68 : #include <sys/resource.h>
69 : #endif
70 :
71 : #ifdef XP_WIN
72 : #include <windows.h>
73 : #endif
74 :
75 : static MOZ_THREAD_LOCAL(XPCJSContext*) gTlsContext;
76 :
77 : using namespace mozilla;
78 : using namespace xpc;
79 : using namespace JS;
80 : using mozilla::dom::AutoEntryScript;
81 :
82 : static void WatchdogMain(void* arg);
83 : class Watchdog;
84 : class WatchdogManager;
85 : class MOZ_RAII AutoLockWatchdog final
86 : {
87 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
88 : Watchdog* const mWatchdog;
89 :
90 : public:
91 : explicit AutoLockWatchdog(Watchdog* aWatchdog MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
92 : ~AutoLockWatchdog();
93 : };
94 :
95 : class Watchdog
96 : {
97 : public:
98 1 : explicit Watchdog(WatchdogManager* aManager)
99 1 : : mManager(aManager)
100 : , mLock(nullptr)
101 : , mWakeup(nullptr)
102 : , mThread(nullptr)
103 : , mHibernating(false)
104 : , mInitialized(false)
105 : , mShuttingDown(false)
106 2 : , mMinScriptRunTimeSeconds(1)
107 1 : {}
108 0 : ~Watchdog() { MOZ_ASSERT(!Initialized()); }
109 :
110 : WatchdogManager* Manager() { return mManager; }
111 : bool Initialized() { return mInitialized; }
112 : bool ShuttingDown() { return mShuttingDown; }
113 : PRLock* GetLock() { return mLock; }
114 : bool Hibernating() { return mHibernating; }
115 0 : void WakeUp()
116 : {
117 0 : MOZ_ASSERT(Initialized());
118 0 : MOZ_ASSERT(Hibernating());
119 0 : mHibernating = false;
120 0 : PR_NotifyCondVar(mWakeup);
121 0 : }
122 :
123 : //
124 : // Invoked by the main thread only.
125 : //
126 :
127 1 : void Init()
128 : {
129 0 : MOZ_ASSERT(NS_IsMainThread());
130 1 : mLock = PR_NewLock();
131 0 : if (!mLock)
132 0 : MOZ_CRASH("PR_NewLock failed.");
133 :
134 1 : mWakeup = PR_NewCondVar(mLock);
135 1 : if (!mWakeup)
136 0 : MOZ_CRASH("PR_NewCondVar failed.");
137 :
138 : {
139 2 : AutoLockWatchdog lock(this);
140 :
141 : // Gecko uses thread private for accounting and has to clean up at thread exit.
142 : // Therefore, even though we don't have a return value from the watchdog, we need to
143 : // join it on shutdown.
144 1 : mThread = PR_CreateThread(PR_USER_THREAD, WatchdogMain, this,
145 : PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
146 : PR_JOINABLE_THREAD, 0);
147 1 : if (!mThread)
148 0 : MOZ_CRASH("PR_CreateThread failed!");
149 :
150 : // WatchdogMain acquires the lock and then asserts mInitialized. So
151 : // make sure to set mInitialized before releasing the lock here so
152 : // that it's atomic with the creation of the thread.
153 1 : mInitialized = true;
154 : }
155 0 : }
156 :
157 0 : void Shutdown()
158 : {
159 0 : MOZ_ASSERT(NS_IsMainThread());
160 0 : MOZ_ASSERT(Initialized());
161 : { // Scoped lock.
162 0 : AutoLockWatchdog lock(this);
163 :
164 : // Signal to the watchdog thread that it's time to shut down.
165 0 : mShuttingDown = true;
166 :
167 : // Wake up the watchdog, and wait for it to call us back.
168 0 : PR_NotifyCondVar(mWakeup);
169 : }
170 :
171 0 : PR_JoinThread(mThread);
172 :
173 : // The thread sets mShuttingDown to false as it exits.
174 0 : MOZ_ASSERT(!mShuttingDown);
175 :
176 : // Destroy state.
177 0 : mThread = nullptr;
178 0 : PR_DestroyCondVar(mWakeup);
179 0 : mWakeup = nullptr;
180 0 : PR_DestroyLock(mLock);
181 0 : mLock = nullptr;
182 :
183 : // All done.
184 0 : mInitialized = false;
185 0 : }
186 :
187 0 : void SetMinScriptRunTimeSeconds(int32_t seconds)
188 : {
189 : // This variable is atomic, and is set from the main thread without
190 : // locking.
191 3 : MOZ_ASSERT(seconds > 0);
192 6 : mMinScriptRunTimeSeconds = seconds;
193 0 : }
194 :
195 : //
196 : // Invoked by the watchdog thread only.
197 : //
198 :
199 0 : void Hibernate()
200 : {
201 0 : MOZ_ASSERT(!NS_IsMainThread());
202 0 : mHibernating = true;
203 0 : Sleep(PR_INTERVAL_NO_TIMEOUT);
204 0 : }
205 0 : void Sleep(PRIntervalTime timeout)
206 : {
207 0 : MOZ_ASSERT(!NS_IsMainThread());
208 13 : MOZ_ALWAYS_TRUE(PR_WaitCondVar(mWakeup, timeout) == PR_SUCCESS);
209 0 : }
210 0 : void Finished()
211 : {
212 0 : MOZ_ASSERT(!NS_IsMainThread());
213 0 : mShuttingDown = false;
214 0 : }
215 :
216 : int32_t MinScriptRunTimeSeconds()
217 : {
218 22 : return mMinScriptRunTimeSeconds;
219 : }
220 :
221 : private:
222 : WatchdogManager* mManager;
223 :
224 : PRLock* mLock;
225 : PRCondVar* mWakeup;
226 : PRThread* mThread;
227 : bool mHibernating;
228 : bool mInitialized;
229 : bool mShuttingDown;
230 : mozilla::Atomic<int32_t> mMinScriptRunTimeSeconds;
231 : };
232 :
233 : #define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
234 : #define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time"
235 : #define PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT "dom.max_ext_content_script_run_time"
236 :
237 : class WatchdogManager : public nsIObserver
238 : {
239 : public:
240 :
241 : NS_DECL_ISUPPORTS
242 1 : explicit WatchdogManager()
243 6 : {
244 : // All the timestamps start at zero.
245 0 : PodArrayZero(mTimestamps);
246 :
247 : // Register ourselves as an observer to get updates on the pref.
248 1 : mozilla::Preferences::AddStrongObserver(this, "dom.use_watchdog");
249 1 : mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT);
250 0 : mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME);
251 0 : mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT);
252 0 : }
253 :
254 : protected:
255 :
256 0 : virtual ~WatchdogManager()
257 0 : {
258 : // Shutting down the watchdog requires context-switching to the watchdog
259 : // thread, which isn't great to do in a destructor. So we require
260 : // consumers to shut it down manually before releasing it.
261 0 : MOZ_ASSERT(!mWatchdog);
262 0 : }
263 :
264 : public:
265 :
266 0 : void Shutdown()
267 : {
268 0 : mozilla::Preferences::RemoveObserver(this, "dom.use_watchdog");
269 0 : mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT);
270 0 : mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME);
271 0 : mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT);
272 0 : }
273 :
274 0 : NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
275 : const char16_t* aData) override
276 : {
277 2 : RefreshWatchdog();
278 2 : return NS_OK;
279 : }
280 :
281 : void
282 1 : RegisterContext(XPCJSContext* aContext)
283 : {
284 0 : MOZ_ASSERT(NS_IsMainThread());
285 3 : AutoLockWatchdog lock(mWatchdog);
286 :
287 0 : if (aContext->mActive == XPCJSContext::CONTEXT_ACTIVE) {
288 0 : mActiveContexts.insertBack(aContext);
289 : } else {
290 1 : mInactiveContexts.insertBack(aContext);
291 : }
292 :
293 : // Enable the watchdog, if appropriate.
294 1 : RefreshWatchdog();
295 1 : }
296 :
297 : void
298 0 : UnregisterContext(XPCJSContext* aContext)
299 : {
300 0 : MOZ_ASSERT(NS_IsMainThread());
301 0 : AutoLockWatchdog lock(mWatchdog);
302 :
303 : // aContext must be in one of our two lists, simply remove it.
304 0 : aContext->LinkedListElement<XPCJSContext>::remove();
305 :
306 : #ifdef DEBUG
307 : // If this was the last context, we should have already shut down
308 : // the watchdog.
309 0 : if (mActiveContexts.isEmpty() && mInactiveContexts.isEmpty()) {
310 0 : MOZ_ASSERT(!mWatchdog);
311 : }
312 : #endif
313 0 : }
314 :
315 : // Context statistics. These live on the watchdog manager, are written
316 : // from the main thread, and are read from the watchdog thread (holding
317 : // the lock in each case).
318 2284 : void RecordContextActivity(XPCJSContext* aContext, bool active)
319 : {
320 : // The watchdog reads this state, so acquire the lock before writing it.
321 2284 : MOZ_ASSERT(NS_IsMainThread());
322 6852 : AutoLockWatchdog lock(mWatchdog);
323 :
324 : // Write state.
325 2284 : aContext->mLastStateChange = PR_Now();
326 2284 : aContext->mActive = active ? XPCJSContext::CONTEXT_ACTIVE :
327 : XPCJSContext::CONTEXT_INACTIVE;
328 0 : UpdateContextLists(aContext);
329 :
330 : // The watchdog may be hibernating, waiting for the context to go
331 : // active. Wake it up if necessary.
332 3426 : if (active && mWatchdog && mWatchdog->Hibernating())
333 0 : mWatchdog->WakeUp();
334 0 : }
335 :
336 0 : bool IsAnyContextActive()
337 : {
338 0 : return !mActiveContexts.isEmpty();
339 : }
340 0 : PRTime TimeSinceLastActiveContext()
341 : {
342 : // Must be called on the watchdog thread with the lock held.
343 2 : MOZ_ASSERT(!NS_IsMainThread());
344 2 : PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mWatchdog->GetLock());
345 0 : MOZ_ASSERT(mActiveContexts.isEmpty());
346 0 : MOZ_ASSERT(!mInactiveContexts.isEmpty());
347 :
348 : // We store inactive contexts with the most recently added inactive
349 : // context at the end of the list.
350 2 : return PR_Now() - mInactiveContexts.getLast()->mLastStateChange;
351 : }
352 :
353 12 : void RecordTimestamp(WatchdogTimestampCategory aCategory)
354 : {
355 : // Must be called on the watchdog thread with the lock held.
356 12 : MOZ_ASSERT(!NS_IsMainThread());
357 12 : PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mWatchdog->GetLock());
358 0 : MOZ_ASSERT(aCategory != TimestampContextStateChange,
359 : "Use RecordContextActivity to update this");
360 :
361 12 : mTimestamps[aCategory] = PR_Now();
362 12 : }
363 :
364 : PRTime GetContextTimestamp(XPCJSContext* aContext,
365 : const AutoLockWatchdog& aProofOfLock)
366 : {
367 : return aContext->mLastStateChange;
368 : }
369 :
370 0 : PRTime GetTimestamp(WatchdogTimestampCategory aCategory,
371 : const AutoLockWatchdog& aProofOfLock)
372 : {
373 0 : MOZ_ASSERT(aCategory != TimestampContextStateChange,
374 : "Use GetContextTimestamp to retrieve this");
375 0 : return mTimestamps[aCategory];
376 : }
377 :
378 0 : Watchdog* GetWatchdog() { return mWatchdog; }
379 :
380 0 : void RefreshWatchdog()
381 : {
382 0 : bool wantWatchdog = Preferences::GetBool("dom.use_watchdog", true);
383 6 : if (wantWatchdog != !!mWatchdog) {
384 0 : if (wantWatchdog)
385 0 : StartWatchdog();
386 : else
387 0 : StopWatchdog();
388 : }
389 :
390 6 : if (mWatchdog) {
391 3 : int32_t contentTime = Preferences::GetInt(PREF_MAX_SCRIPT_RUN_TIME_CONTENT, 10);
392 0 : if (contentTime <= 0)
393 0 : contentTime = INT32_MAX;
394 0 : int32_t chromeTime = Preferences::GetInt(PREF_MAX_SCRIPT_RUN_TIME_CHROME, 20);
395 0 : if (chromeTime <= 0)
396 0 : chromeTime = INT32_MAX;
397 0 : int32_t extTime = Preferences::GetInt(PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT, 5);
398 0 : if (extTime <= 0)
399 0 : extTime = INT32_MAX;
400 0 : mWatchdog->SetMinScriptRunTimeSeconds(std::min({contentTime, chromeTime, extTime}));
401 : }
402 0 : }
403 :
404 0 : void StartWatchdog()
405 : {
406 0 : MOZ_ASSERT(!mWatchdog);
407 2 : mWatchdog = new Watchdog(this);
408 0 : mWatchdog->Init();
409 0 : }
410 :
411 0 : void StopWatchdog()
412 : {
413 0 : MOZ_ASSERT(mWatchdog);
414 0 : mWatchdog->Shutdown();
415 0 : mWatchdog = nullptr;
416 0 : }
417 :
418 : template<class Callback>
419 11 : void ForAllActiveContexts(Callback&& aCallback)
420 : {
421 : // This function must be called on the watchdog thread with the lock held.
422 11 : MOZ_ASSERT(!NS_IsMainThread());
423 11 : PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mWatchdog->GetLock());
424 :
425 0 : for (auto* context = mActiveContexts.getFirst(); context;
426 0 : context = context->LinkedListElement<XPCJSContext>::getNext()) {
427 0 : if (!aCallback(context)) {
428 : return;
429 : }
430 : }
431 : }
432 :
433 : private:
434 2284 : void UpdateContextLists(XPCJSContext* aContext)
435 : {
436 : // Given aContext whose activity state or timestamp has just changed,
437 : // put it back in the proper position in the proper list.
438 2284 : aContext->LinkedListElement<XPCJSContext>::remove();
439 2284 : auto& list = aContext->mActive == XPCJSContext::CONTEXT_ACTIVE ?
440 0 : mActiveContexts : mInactiveContexts;
441 :
442 : // Either the new list is empty or aContext must be more recent than
443 : // the existing last element.
444 2284 : MOZ_ASSERT_IF(!list.isEmpty(),
445 : list.getLast()->mLastStateChange < aContext->mLastStateChange);
446 0 : list.insertBack(aContext);
447 2284 : }
448 :
449 : LinkedList<XPCJSContext> mActiveContexts;
450 : LinkedList<XPCJSContext> mInactiveContexts;
451 : nsAutoPtr<Watchdog> mWatchdog;
452 :
453 : // We store ContextStateChange on the contexts themselves.
454 : PRTime mTimestamps[kWatchdogTimestampCategoryCount - 1];
455 : };
456 :
457 142 : NS_IMPL_ISUPPORTS(WatchdogManager, nsIObserver)
458 :
459 0 : AutoLockWatchdog::AutoLockWatchdog(Watchdog* aWatchdog MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
460 4574 : : mWatchdog(aWatchdog)
461 : {
462 0 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
463 2287 : if (mWatchdog) {
464 0 : PR_Lock(mWatchdog->GetLock());
465 : }
466 0 : }
467 :
468 0 : AutoLockWatchdog::~AutoLockWatchdog()
469 : {
470 0 : if (mWatchdog) {
471 2285 : PR_Unlock(mWatchdog->GetLock());
472 : }
473 0 : }
474 :
475 : static void
476 1 : WatchdogMain(void* arg)
477 : {
478 0 : AUTO_PROFILER_REGISTER_THREAD("JS Watchdog");
479 1 : NS_SetCurrentThreadName("JS Watchdog");
480 :
481 0 : Watchdog* self = static_cast<Watchdog*>(arg);
482 1 : WatchdogManager* manager = self->Manager();
483 :
484 : // Lock lasts until we return
485 1 : AutoLockWatchdog lock(self);
486 :
487 0 : MOZ_ASSERT(self->Initialized());
488 13 : while (!self->ShuttingDown()) {
489 : // Sleep only 1 second if recently (or currently) active; otherwise, hibernate
490 0 : if (manager->IsAnyContextActive() ||
491 2 : manager->TimeSinceLastActiveContext() <= PRTime(2*PR_USEC_PER_SEC))
492 : {
493 0 : self->Sleep(PR_TicksPerSecond());
494 : } else {
495 0 : manager->RecordTimestamp(TimestampWatchdogHibernateStart);
496 0 : self->Hibernate();
497 0 : manager->RecordTimestamp(TimestampWatchdogHibernateStop);
498 : }
499 :
500 : // Rise and shine.
501 12 : manager->RecordTimestamp(TimestampWatchdogWakeup);
502 :
503 : // Don't request an interrupt callback unless the current script has
504 : // been running long enough that we might show the slow script dialog.
505 : // Triggering the callback from off the main thread can be expensive.
506 :
507 : // We want to avoid showing the slow script dialog if the user's laptop
508 : // goes to sleep in the middle of running a script. To ensure this, we
509 : // invoke the interrupt callback after only half the timeout has
510 : // elapsed. The callback simply records the fact that it was called in
511 : // the mSlowScriptSecondHalf flag. Then we wait another (timeout/2)
512 : // seconds and invoke the callback again. This time around it sees
513 : // mSlowScriptSecondHalf is set and so it shows the slow script
514 : // dialog. If the computer is put to sleep during one of the (timeout/2)
515 : // periods, the script still has the other (timeout/2) seconds to
516 : // finish.
517 12 : if (!self->ShuttingDown() && manager->IsAnyContextActive()) {
518 11 : bool debuggerAttached = false;
519 0 : nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1");
520 0 : if (dbg)
521 0 : dbg->GetIsDebuggerAttached(&debuggerAttached);
522 0 : if (debuggerAttached) {
523 : // We won't be interrupting these scripts anyway.
524 0 : continue;
525 : }
526 :
527 11 : PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC / 2;
528 55 : manager->ForAllActiveContexts([usecs, manager, &lock](XPCJSContext* aContext) -> bool {
529 0 : auto timediff = PR_Now() - manager->GetContextTimestamp(aContext, lock);
530 0 : if (timediff > usecs) {
531 0 : JS_RequestInterruptCallback(aContext->Context());
532 : return true;
533 : }
534 : return false;
535 11 : });
536 : }
537 : }
538 :
539 : // Tell the manager that we've shut down.
540 0 : self->Finished();
541 0 : }
542 :
543 : PRTime
544 0 : XPCJSContext::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory)
545 : {
546 0 : AutoLockWatchdog lock(mWatchdogManager->GetWatchdog());
547 0 : return aCategory == TimestampContextStateChange ?
548 0 : mWatchdogManager->GetContextTimestamp(this, lock) :
549 0 : mWatchdogManager->GetTimestamp(aCategory, lock);
550 : }
551 :
552 : void
553 0 : xpc::SimulateActivityCallback(bool aActive)
554 : {
555 0 : XPCJSContext::ActivityCallback(XPCJSContext::Get(), aActive);
556 0 : }
557 :
558 : // static
559 : void
560 2284 : XPCJSContext::ActivityCallback(void* arg, bool active)
561 : {
562 0 : if (!active) {
563 1142 : ProcessHangMonitor::ClearHang();
564 : }
565 :
566 2284 : XPCJSContext* self = static_cast<XPCJSContext*>(arg);
567 2284 : self->mWatchdogManager->RecordContextActivity(self, active);
568 0 : }
569 :
570 : // static
571 : bool
572 0 : XPCJSContext::InterruptCallback(JSContext* cx)
573 : {
574 0 : XPCJSContext* self = XPCJSContext::Get();
575 :
576 : // Now is a good time to turn on profiling if it's pending.
577 0 : PROFILER_JS_INTERRUPT_CALLBACK();
578 :
579 : // Normally we record mSlowScriptCheckpoint when we start to process an
580 : // event. However, we can run JS outside of event handlers. This code takes
581 : // care of that case.
582 0 : if (self->mSlowScriptCheckpoint.IsNull()) {
583 0 : self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
584 0 : self->mSlowScriptSecondHalf = false;
585 0 : self->mSlowScriptActualWait = mozilla::TimeDuration();
586 0 : self->mTimeoutAccumulated = false;
587 0 : return true;
588 : }
589 :
590 : // Sometimes we get called back during XPConnect initialization, before Gecko
591 : // has finished bootstrapping. Avoid crashing in nsContentUtils below.
592 0 : if (!nsContentUtils::IsInitialized())
593 : return true;
594 :
595 : // This is at least the second interrupt callback we've received since
596 : // returning to the event loop. See how long it's been, and what the limit
597 : // is.
598 0 : TimeDuration duration = TimeStamp::NowLoRes() - self->mSlowScriptCheckpoint;
599 : int32_t limit;
600 :
601 0 : nsString addonId;
602 : const char* prefName;
603 :
604 0 : auto principal = BasePrincipal::Cast(nsContentUtils::SubjectPrincipal(cx));
605 0 : bool chrome = principal->Is<SystemPrincipal>();
606 0 : if (chrome) {
607 0 : prefName = PREF_MAX_SCRIPT_RUN_TIME_CHROME;
608 0 : limit = Preferences::GetInt(prefName, 20);
609 0 : } else if (auto policy = principal->ContentScriptAddonPolicy()) {
610 0 : policy->GetId(addonId);
611 0 : prefName = PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT;
612 0 : limit = Preferences::GetInt(prefName, 5);
613 : } else {
614 0 : prefName = PREF_MAX_SCRIPT_RUN_TIME_CONTENT;
615 0 : limit = Preferences::GetInt(prefName, 10);
616 : }
617 :
618 : // If there's no limit, or we're within the limit, let it go.
619 0 : if (limit == 0 || duration.ToSeconds() < limit / 2.0)
620 : return true;
621 :
622 0 : self->mSlowScriptActualWait += duration;
623 :
624 : // In order to guard against time changes or laptops going to sleep, we
625 : // don't trigger the slow script warning until (limit/2) seconds have
626 : // elapsed twice.
627 0 : if (!self->mSlowScriptSecondHalf) {
628 0 : self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
629 0 : self->mSlowScriptSecondHalf = true;
630 0 : return true;
631 : }
632 :
633 : //
634 : // This has gone on long enough! Time to take action. ;-)
635 : //
636 :
637 : // Get the DOM window associated with the running script. If the script is
638 : // running in a non-DOM scope, we have to just let it keep running.
639 0 : RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
640 0 : RefPtr<nsGlobalWindowInner> win = WindowOrNull(global);
641 0 : if (!win && IsSandbox(global)) {
642 : // If this is a sandbox associated with a DOMWindow via a
643 : // sandboxPrototype, use that DOMWindow. This supports GreaseMonkey
644 : // and JetPack content scripts.
645 0 : JS::Rooted<JSObject*> proto(cx);
646 0 : if (!JS_GetPrototype(cx, global, &proto))
647 0 : return false;
648 0 : if (proto && xpc::IsSandboxPrototypeProxy(proto) &&
649 0 : (proto = js::CheckedUnwrap(proto, /* stopAtWindowProxy = */ false)))
650 : {
651 0 : win = WindowGlobalOrNull(proto);
652 : }
653 : }
654 :
655 0 : if (!win) {
656 0 : NS_WARNING("No active window");
657 0 : return true;
658 : }
659 :
660 0 : if (win->IsDying()) {
661 : // The window is being torn down. When that happens we try to prevent
662 : // the dispatch of new runnables, so it also makes sense to kill any
663 : // long-running script. The user is primarily interested in this page
664 : // going away.
665 : return false;
666 : }
667 :
668 : // Accumulate slow script invokation delay.
669 0 : if (!chrome && !self->mTimeoutAccumulated) {
670 0 : uint32_t delay = uint32_t(self->mSlowScriptActualWait.ToMilliseconds() - (limit * 1000.0));
671 0 : Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_NOTIFY_DELAY, delay);
672 0 : self->mTimeoutAccumulated = true;
673 : }
674 :
675 : // Show the prompt to the user, and kill if requested.
676 0 : nsGlobalWindowInner::SlowScriptResponse response = win->ShowSlowScriptDialog(addonId);
677 0 : if (response == nsGlobalWindowInner::KillSlowScript) {
678 0 : if (Preferences::GetBool("dom.global_stop_script", true))
679 0 : xpc::Scriptability::Get(global).Block();
680 : return false;
681 : }
682 0 : if (response == nsGlobalWindowInner::KillScriptGlobal) {
683 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
684 :
685 0 : if (!IsSandbox(global) || !obs)
686 : return false;
687 :
688 : // Notify the extensions framework that the sandbox should be killed.
689 0 : nsIXPConnect* xpc = nsContentUtils::XPConnect();
690 0 : JS::RootedObject wrapper(cx, JS_NewPlainObject(cx));
691 0 : nsCOMPtr<nsISupports> supports;
692 :
693 : // Store the sandbox object on the wrappedJSObject property of the
694 : // subject so that JS recipients can access the JS value directly.
695 0 : if (!wrapper ||
696 0 : !JS_DefineProperty(cx, wrapper, "wrappedJSObject", global, JSPROP_ENUMERATE) ||
697 0 : NS_FAILED(xpc->WrapJS(cx, wrapper, NS_GET_IID(nsISupports), getter_AddRefs(supports)))) {
698 : return false;
699 : }
700 :
701 0 : obs->NotifyObservers(supports, "kill-content-script-sandbox", nullptr);
702 0 : return false;
703 : }
704 :
705 : // The user chose to continue the script. Reset the timer, and disable this
706 : // machinery with a pref of the user opted out of future slow-script dialogs.
707 0 : if (response != nsGlobalWindowInner::ContinueSlowScriptAndKeepNotifying)
708 0 : self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
709 :
710 0 : if (response == nsGlobalWindowInner::AlwaysContinueSlowScript)
711 0 : Preferences::SetInt(prefName, 0);
712 :
713 : return true;
714 : }
715 :
716 : #define JS_OPTIONS_DOT_STR "javascript.options."
717 :
718 : static mozilla::Atomic<bool> sDiscardSystemSource(false);
719 :
720 : bool
721 23 : xpc::ShouldDiscardSystemSource() { return sDiscardSystemSource; }
722 :
723 : #ifdef DEBUG
724 : static mozilla::Atomic<bool> sExtraWarningsForSystemJS(false);
725 22 : bool xpc::ExtraWarningsForSystemJS() { return sExtraWarningsForSystemJS; }
726 : #else
727 : bool xpc::ExtraWarningsForSystemJS() { return false; }
728 : #endif
729 :
730 : static mozilla::Atomic<bool> sSharedMemoryEnabled(false);
731 :
732 : bool
733 44 : xpc::SharedMemoryEnabled() { return sSharedMemoryEnabled; }
734 :
735 : static void
736 2 : ReloadPrefsCallback(const char* pref, void* data)
737 : {
738 0 : XPCJSContext* xpccx = static_cast<XPCJSContext*>(data);
739 2 : JSContext* cx = xpccx->Context();
740 :
741 0 : bool useBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit");
742 2 : bool useIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion");
743 0 : bool useAsmJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "asmjs");
744 0 : bool useWasm = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm");
745 0 : bool useWasmIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_ionjit");
746 0 : bool useWasmBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_baselinejit");
747 : #ifdef ENABLE_WASM_GC
748 0 : bool useWasmGc = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_gc");
749 : #endif
750 : bool throwOnAsmJSValidationFailure = Preferences::GetBool(JS_OPTIONS_DOT_STR
751 2 : "throw_on_asmjs_validation_failure");
752 2 : bool useNativeRegExp = Preferences::GetBool(JS_OPTIONS_DOT_STR "native_regexp");
753 :
754 0 : bool parallelParsing = Preferences::GetBool(JS_OPTIONS_DOT_STR "parallel_parsing");
755 : bool offthreadIonCompilation = Preferences::GetBool(JS_OPTIONS_DOT_STR
756 0 : "ion.offthread_compilation");
757 : bool useBaselineEager = Preferences::GetBool(JS_OPTIONS_DOT_STR
758 0 : "baselinejit.unsafe_eager_compilation");
759 2 : bool useIonEager = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation");
760 : #ifdef DEBUG
761 0 : bool fullJitDebugChecks = Preferences::GetBool(JS_OPTIONS_DOT_STR "jit.full_debug_checks");
762 : #endif
763 :
764 2 : int32_t baselineThreshold = Preferences::GetInt(JS_OPTIONS_DOT_STR "baselinejit.threshold", -1);
765 2 : int32_t ionThreshold = Preferences::GetInt(JS_OPTIONS_DOT_STR "ion.threshold", -1);
766 :
767 0 : sDiscardSystemSource = Preferences::GetBool(JS_OPTIONS_DOT_STR "discardSystemSource");
768 :
769 0 : bool useAsyncStack = Preferences::GetBool(JS_OPTIONS_DOT_STR "asyncstack");
770 :
771 : bool throwOnDebuggeeWouldRun = Preferences::GetBool(JS_OPTIONS_DOT_STR
772 2 : "throw_on_debuggee_would_run");
773 :
774 : bool dumpStackOnDebuggeeWouldRun = Preferences::GetBool(JS_OPTIONS_DOT_STR
775 2 : "dump_stack_on_debuggee_would_run");
776 :
777 0 : bool werror = Preferences::GetBool(JS_OPTIONS_DOT_STR "werror");
778 :
779 0 : bool extraWarnings = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict");
780 :
781 0 : bool streams = Preferences::GetBool(JS_OPTIONS_DOT_STR "streams");
782 :
783 0 : bool spectreIndexMasking = Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.index_masking");
784 : bool spectreObjectMitigationsBarriers =
785 0 : Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.object_mitigations.barriers");
786 : bool spectreObjectMitigationsMisc =
787 0 : Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.object_mitigations.misc");
788 : bool spectreStringMitigations =
789 0 : Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.string_mitigations");
790 2 : bool spectreValueMasking = Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.value_masking");
791 0 : bool spectreJitToCxxCalls = Preferences::GetBool(JS_OPTIONS_DOT_STR "spectre.jit_to_C++_calls");
792 :
793 0 : sSharedMemoryEnabled = Preferences::GetBool(JS_OPTIONS_DOT_STR "shared_memory");
794 :
795 : #ifdef DEBUG
796 4 : sExtraWarningsForSystemJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict.debug");
797 : #endif
798 :
799 : #ifdef JS_GC_ZEAL
800 2 : int32_t zeal = Preferences::GetInt(JS_OPTIONS_DOT_STR "gczeal", -1);
801 : int32_t zeal_frequency =
802 : Preferences::GetInt(JS_OPTIONS_DOT_STR "gczeal.frequency",
803 2 : JS_DEFAULT_ZEAL_FREQ);
804 2 : if (zeal >= 0) {
805 0 : JS_SetGCZeal(cx, (uint8_t)zeal, zeal_frequency);
806 : }
807 : #endif // JS_GC_ZEAL
808 :
809 : #ifdef FUZZING
810 : bool fuzzingEnabled = Preferences::GetBool("fuzzing.enabled");
811 : #endif
812 :
813 2 : bool arrayProtoValues = Preferences::GetBool(JS_OPTIONS_DOT_STR "array_prototype_values");
814 :
815 0 : JS::ContextOptionsRef(cx).setBaseline(useBaseline)
816 2 : .setIon(useIon)
817 0 : .setAsmJS(useAsmJS)
818 0 : .setWasm(useWasm)
819 0 : .setWasmIon(useWasmIon)
820 0 : .setWasmBaseline(useWasmBaseline)
821 : #ifdef ENABLE_WASM_GC
822 0 : .setWasmGc(useWasmGc)
823 : #endif
824 0 : .setThrowOnAsmJSValidationFailure(throwOnAsmJSValidationFailure)
825 2 : .setNativeRegExp(useNativeRegExp)
826 0 : .setAsyncStack(useAsyncStack)
827 0 : .setThrowOnDebuggeeWouldRun(throwOnDebuggeeWouldRun)
828 0 : .setDumpStackOnDebuggeeWouldRun(dumpStackOnDebuggeeWouldRun)
829 0 : .setWerror(werror)
830 : #ifdef FUZZING
831 : .setFuzzing(fuzzingEnabled)
832 : #endif
833 2 : .setStreams(streams)
834 2 : .setExtraWarnings(extraWarnings)
835 0 : .setArrayProtoValues(arrayProtoValues);
836 :
837 0 : nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
838 2 : if (xr) {
839 0 : bool safeMode = false;
840 0 : xr->GetInSafeMode(&safeMode);
841 0 : if (safeMode) {
842 0 : JS::ContextOptionsRef(cx).disableOptionsForSafeMode();
843 : }
844 : }
845 :
846 2 : JS_SetParallelParsingEnabled(cx, parallelParsing);
847 2 : JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation);
848 0 : JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
849 0 : useBaselineEager ? 0 : baselineThreshold);
850 0 : JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_ION_WARMUP_TRIGGER,
851 0 : useIonEager ? 0 : ionThreshold);
852 : #ifdef DEBUG
853 0 : JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_FULL_DEBUG_CHECKS, fullJitDebugChecks);
854 : #endif
855 :
856 2 : JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_INDEX_MASKING, spectreIndexMasking);
857 2 : JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_OBJECT_MITIGATIONS_BARRIERS,
858 0 : spectreObjectMitigationsBarriers);
859 0 : JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_OBJECT_MITIGATIONS_MISC,
860 0 : spectreObjectMitigationsMisc);
861 0 : JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_STRING_MITIGATIONS,
862 0 : spectreStringMitigations);
863 0 : JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_VALUE_MASKING, spectreValueMasking);
864 0 : JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_SPECTRE_JIT_TO_CXX_CALLS,
865 0 : spectreJitToCxxCalls);
866 0 : }
867 :
868 0 : XPCJSContext::~XPCJSContext()
869 : {
870 0 : MOZ_COUNT_DTOR_INHERITED(XPCJSContext, CycleCollectedJSContext);
871 : // Elsewhere we abort immediately if XPCJSContext initialization fails.
872 : // Therefore the context must be non-null.
873 0 : MOZ_ASSERT(MaybeContext());
874 :
875 : Preferences::UnregisterPrefixCallback(ReloadPrefsCallback,
876 : JS_OPTIONS_DOT_STR,
877 0 : this);
878 :
879 : #ifdef FUZZING
880 : Preferences::UnregisterCallback(ReloadPrefsCallback, "fuzzing.enabled", this);
881 : #endif
882 :
883 0 : js::SetActivityCallback(Context(), nullptr, nullptr);
884 :
885 : // Clear any pending exception. It might be an XPCWrappedJS, and if we try
886 : // to destroy it later we will crash.
887 0 : SetPendingException(nullptr);
888 :
889 : // If we're the last XPCJSContext around, clean up the watchdog manager.
890 0 : if (--sInstanceCount == 0) {
891 0 : if (mWatchdogManager->GetWatchdog()) {
892 0 : mWatchdogManager->StopWatchdog();
893 : }
894 :
895 0 : mWatchdogManager->UnregisterContext(this);
896 0 : mWatchdogManager->Shutdown();
897 : sWatchdogInstance = nullptr;
898 : } else {
899 : // Otherwise, simply remove ourselves from the list.
900 0 : mWatchdogManager->UnregisterContext(this);
901 : }
902 :
903 0 : if (mCallContext)
904 0 : mCallContext->SystemIsBeingShutDown();
905 :
906 0 : PROFILER_CLEAR_JS_CONTEXT();
907 :
908 0 : gTlsContext.set(nullptr);
909 0 : }
910 :
911 1 : XPCJSContext::XPCJSContext()
912 : : mCallContext(nullptr),
913 : mAutoRoots(nullptr),
914 : mResolveName(JSID_VOID),
915 : mResolvingWrapper(nullptr),
916 1 : mWatchdogManager(GetWatchdogManager()),
917 : mSlowScriptSecondHalf(false),
918 : mTimeoutAccumulated(false),
919 : mPendingResult(NS_OK),
920 : mActive(CONTEXT_INACTIVE),
921 5 : mLastStateChange(PR_Now())
922 : {
923 1 : MOZ_COUNT_CTOR_INHERITED(XPCJSContext, CycleCollectedJSContext);
924 1 : MOZ_RELEASE_ASSERT(!gTlsContext.get());
925 1 : MOZ_ASSERT(mWatchdogManager);
926 1 : ++sInstanceCount;
927 0 : mWatchdogManager->RegisterContext(this);
928 1 : gTlsContext.set(this);
929 0 : }
930 :
931 : /* static */ XPCJSContext*
932 0 : XPCJSContext::Get()
933 : {
934 0 : return gTlsContext.get();
935 : }
936 :
937 : #ifdef XP_WIN
938 : static size_t
939 : GetWindowsStackSize()
940 : {
941 : // First, get the stack base. Because the stack grows down, this is the top
942 : // of the stack.
943 : const uint8_t* stackTop;
944 : #ifdef _WIN64
945 : PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
946 : stackTop = reinterpret_cast<const uint8_t*>(pTib->StackBase);
947 : #else
948 : PNT_TIB pTib = reinterpret_cast<PNT_TIB>(NtCurrentTeb());
949 : stackTop = reinterpret_cast<const uint8_t*>(pTib->StackBase);
950 : #endif
951 :
952 : // Now determine the stack bottom. Note that we can't use tib->StackLimit,
953 : // because that's the size of the committed area and we're also interested
954 : // in the reserved pages below that.
955 : MEMORY_BASIC_INFORMATION mbi;
956 : if (!VirtualQuery(&mbi, &mbi, sizeof(mbi)))
957 : MOZ_CRASH("VirtualQuery failed");
958 :
959 : const uint8_t* stackBottom = reinterpret_cast<const uint8_t*>(mbi.AllocationBase);
960 :
961 : // Do some sanity checks.
962 : size_t stackSize = size_t(stackTop - stackBottom);
963 : MOZ_RELEASE_ASSERT(stackSize >= 1 * 1024 * 1024);
964 : MOZ_RELEASE_ASSERT(stackSize <= 32 * 1024 * 1024);
965 :
966 : // Subtract 40 KB (Win32) or 80 KB (Win64) to account for things like
967 : // the guard page and large PGO stack frames.
968 : return stackSize - 10 * sizeof(uintptr_t) * 1024;
969 : }
970 : #endif
971 :
972 : XPCJSRuntime*
973 10181 : XPCJSContext::Runtime() const
974 : {
975 10181 : return static_cast<XPCJSRuntime*>(CycleCollectedJSContext::Runtime());
976 : }
977 :
978 : CycleCollectedJSRuntime*
979 0 : XPCJSContext::CreateRuntime(JSContext* aCx)
980 : {
981 0 : return new XPCJSRuntime(aCx);
982 : }
983 :
984 : nsresult
985 0 : XPCJSContext::Initialize(XPCJSContext* aPrimaryContext)
986 : {
987 : nsresult rv;
988 1 : if (aPrimaryContext) {
989 0 : rv = CycleCollectedJSContext::InitializeNonPrimary(aPrimaryContext);
990 : } else {
991 0 : rv = CycleCollectedJSContext::Initialize(nullptr,
992 : JS::DefaultHeapMaxBytes,
993 1 : JS::DefaultNurseryBytes);
994 : }
995 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
996 : return rv;
997 : }
998 :
999 0 : MOZ_ASSERT(Context());
1000 1 : JSContext* cx = Context();
1001 :
1002 : // The JS engine permits us to set different stack limits for system code,
1003 : // trusted script, and untrusted script. We have tests that ensure that
1004 : // we can always execute 10 "heavy" (eval+with) stack frames deeper in
1005 : // privileged code. Our stack sizes vary greatly in different configurations,
1006 : // so satisfying those tests requires some care. Manual measurements of the
1007 : // number of heavy stack frames achievable gives us the following rough data,
1008 : // ordered by the effective categories in which they are grouped in the
1009 : // JS_SetNativeStackQuota call (which predates this analysis).
1010 : //
1011 : // The following "Stack Frames" numbers come from `chromeLimit` in
1012 : // js/xpconnect/tests/chrome/test_bug732665.xul
1013 : //
1014 : // Platform | Build | Stack Quota | Stack Frames | Stack Frame Size
1015 : // ------------+-------+-------------+--------------+------------------
1016 : // OSX 64 | Opt | 7MB | 1331 | ~5.4k
1017 : // OSX 64 | Debug | 7MB | 1202 | ~6.0k
1018 : // ------------+-------+-------------+--------------+------------------
1019 : // Linux 32 | Opt | 7.875MB | 2513 | ~3.2k
1020 : // Linux 32 | Debug | 7.875MB | 2146 | ~3.8k
1021 : // ------------+-------+-------------+--------------+------------------
1022 : // Linux 64 | Opt | 7.875MB | 1360 | ~5.9k
1023 : // Linux 64 | Debug | 7.875MB | 1180 | ~6.8k
1024 : // Linux 64 | ASan | 7.875MB | 473 | ~17.0k
1025 : // ------------+-------+-------------+--------------+------------------
1026 : // Windows 32 | Opt | 984k | 188 | ~5.2k
1027 : // Windows 32 | Debug | 984k | 208 | ~4.7k
1028 : // ------------+-------+-------------+--------------+------------------
1029 : // Windows 64 | Opt | 1.922MB | 189 | ~10.4k
1030 : // Windows 64 | Debug | 1.922MB | 175 | ~11.2k
1031 : //
1032 : // We tune the trusted/untrusted quotas for each configuration to achieve our
1033 : // invariants while attempting to minimize overhead. In contrast, our buffer
1034 : // between system code and trusted script is a very unscientific 10k.
1035 1 : const size_t kSystemCodeBuffer = 10 * 1024;
1036 :
1037 : // Our "default" stack is what we use in configurations where we don't have
1038 : // a compelling reason to do things differently. This is effectively 512KB
1039 : // on 32-bit platforms and 1MB on 64-bit platforms.
1040 1 : const size_t kDefaultStackQuota = 128 * sizeof(size_t) * 1024;
1041 :
1042 : // Set stack sizes for different configurations. It's probably not great for
1043 : // the web to base this decision primarily on the default stack size that the
1044 : // underlying platform makes available, but that seems to be what we do. :-(
1045 :
1046 : #if defined(XP_MACOSX) || defined(DARWIN)
1047 : // MacOS has a gargantuan default stack size of 8MB. Go wild with 7MB,
1048 : // and give trusted script 180k extra. The stack is huge on mac anyway.
1049 : const size_t kStackQuota = 7 * 1024 * 1024;
1050 : const size_t kTrustedScriptBuffer = 180 * 1024;
1051 : #elif defined(XP_LINUX) && !defined(ANDROID)
1052 : // Most Linux distributions set default stack size to 8MB. Use it as the
1053 : // maximum value.
1054 1 : const size_t kStackQuotaMax = 8 * 1024 * 1024;
1055 : # if defined(MOZ_ASAN) || defined(DEBUG)
1056 : // Bug 803182: account for the 4x difference in the size of js::Interpret
1057 : // between optimized and debug builds. We use 2x since the JIT part
1058 : // doesn't increase much.
1059 : // See the standalone MOZ_ASAN branch below for the ASan case.
1060 1 : const size_t kStackQuotaMin = 2 * kDefaultStackQuota;
1061 : # else
1062 : const size_t kStackQuotaMin = kDefaultStackQuota;
1063 : # endif
1064 : // Allocate 128kB margin for the safe space.
1065 1 : const size_t kStackSafeMargin = 128 * 1024;
1066 :
1067 : struct rlimit rlim;
1068 : const size_t kStackQuota =
1069 1 : getrlimit(RLIMIT_STACK, &rlim) == 0
1070 0 : ? std::max(std::min(size_t(rlim.rlim_cur - kStackSafeMargin),
1071 1 : kStackQuotaMax - kStackSafeMargin),
1072 2 : kStackQuotaMin)
1073 1 : : kStackQuotaMin;
1074 : # if defined(MOZ_ASAN)
1075 : // See the standalone MOZ_ASAN branch below for the ASan case.
1076 : const size_t kTrustedScriptBuffer = 450 * 1024;
1077 : # else
1078 1 : const size_t kTrustedScriptBuffer = 180 * 1024;
1079 : # endif
1080 : #elif defined(MOZ_ASAN)
1081 : // ASan requires more stack space due to red-zones, so give it double the
1082 : // default (1MB on 32-bit, 2MB on 64-bit). ASAN stack frame measurements
1083 : // were not taken at the time of this writing, so we hazard a guess that
1084 : // ASAN builds have roughly thrice the stack overhead as normal builds.
1085 : // On normal builds, the largest stack frame size we might encounter is
1086 : // 9.0k (see above), so let's use a buffer of 9.0 * 5 * 10 = 450k.
1087 : //
1088 : // FIXME: Does this branch make sense for Windows and Android?
1089 : // (See bug 1415195)
1090 : const size_t kStackQuota = 2 * kDefaultStackQuota;
1091 : const size_t kTrustedScriptBuffer = 450 * 1024;
1092 : #elif defined(XP_WIN)
1093 : // 1MB is the default stack size on Windows. We use the -STACK linker flag
1094 : // (see WIN32_EXE_LDFLAGS in config/config.mk) to request a larger stack,
1095 : // so we determine the stack size at runtime.
1096 : const size_t kStackQuota = GetWindowsStackSize();
1097 : const size_t kTrustedScriptBuffer = (sizeof(size_t) == 8) ? 180 * 1024 //win64
1098 : : 120 * 1024; //win32
1099 : #elif defined(ANDROID)
1100 : // Android appears to have 1MB stacks. Allow the use of 3/4 of that size
1101 : // (768KB on 32-bit), since otherwise we can crash with a stack overflow
1102 : // when nearing the 1MB limit.
1103 : const size_t kStackQuota = kDefaultStackQuota + kDefaultStackQuota / 2;
1104 : const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
1105 : #else
1106 : // Catch-all configuration for other environments.
1107 : # if defined(DEBUG)
1108 : const size_t kStackQuota = 2 * kDefaultStackQuota;
1109 : # else
1110 : const size_t kStackQuota = kDefaultStackQuota;
1111 : # endif
1112 : // Given the numbers above, we use 50k and 100k trusted buffers on 32-bit
1113 : // and 64-bit respectively.
1114 : const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
1115 : #endif
1116 :
1117 : // Avoid an unused variable warning on platforms where we don't use the
1118 : // default.
1119 : (void) kDefaultStackQuota;
1120 :
1121 1 : JS_SetNativeStackQuota(cx,
1122 : kStackQuota,
1123 : kStackQuota - kSystemCodeBuffer,
1124 1 : kStackQuota - kSystemCodeBuffer - kTrustedScriptBuffer);
1125 :
1126 1 : PROFILER_SET_JS_CONTEXT(cx);
1127 :
1128 1 : js::SetActivityCallback(cx, ActivityCallback, this);
1129 1 : JS_AddInterruptCallback(cx, InterruptCallback);
1130 :
1131 0 : if (!aPrimaryContext) {
1132 1 : Runtime()->Initialize(cx);
1133 : }
1134 :
1135 : // Watch for the JS boolean options.
1136 0 : ReloadPrefsCallback(nullptr, this);
1137 : Preferences::RegisterPrefixCallback(ReloadPrefsCallback,
1138 : JS_OPTIONS_DOT_STR,
1139 0 : this);
1140 :
1141 : #ifdef FUZZING
1142 : Preferences::RegisterCallback(ReloadPrefsCallback, "fuzzing.enabled", this);
1143 : #endif
1144 :
1145 1 : return NS_OK;
1146 : }
1147 :
1148 : // static
1149 : uint32_t
1150 : XPCJSContext::sInstanceCount;
1151 :
1152 : // static
1153 : StaticRefPtr<WatchdogManager>
1154 1 : XPCJSContext::sWatchdogInstance;
1155 :
1156 : // static
1157 : WatchdogManager*
1158 1 : XPCJSContext::GetWatchdogManager()
1159 : {
1160 1 : if (sWatchdogInstance) {
1161 : return sWatchdogInstance;
1162 : }
1163 :
1164 0 : MOZ_ASSERT(sInstanceCount == 0);
1165 2 : sWatchdogInstance = new WatchdogManager();
1166 1 : return sWatchdogInstance;
1167 : }
1168 :
1169 : // static
1170 : void
1171 1 : XPCJSContext::InitTLS()
1172 : {
1173 1 : MOZ_RELEASE_ASSERT(gTlsContext.init());
1174 0 : }
1175 :
1176 : // static
1177 : XPCJSContext*
1178 1 : XPCJSContext::NewXPCJSContext(XPCJSContext* aPrimaryContext)
1179 : {
1180 1 : XPCJSContext* self = new XPCJSContext();
1181 0 : nsresult rv = self->Initialize(aPrimaryContext);
1182 1 : if (NS_FAILED(rv)) {
1183 0 : MOZ_CRASH("new XPCJSContext failed to initialize.");
1184 : }
1185 :
1186 1 : if (self->Context())
1187 1 : return self;
1188 :
1189 0 : MOZ_CRASH("new XPCJSContext failed to initialize.");
1190 : }
1191 :
1192 : void
1193 1 : XPCJSContext::BeforeProcessTask(bool aMightBlock)
1194 : {
1195 1626 : MOZ_ASSERT(NS_IsMainThread());
1196 :
1197 : // Start the slow script timer.
1198 1626 : mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
1199 1 : mSlowScriptSecondHalf = false;
1200 1626 : mSlowScriptActualWait = mozilla::TimeDuration();
1201 1626 : mTimeoutAccumulated = false;
1202 :
1203 : // As we may be entering a nested event loop, we need to
1204 : // cancel any ongoing performance measurement.
1205 0 : js::ResetPerformanceMonitoring(Context());
1206 :
1207 1626 : CycleCollectedJSContext::BeforeProcessTask(aMightBlock);
1208 0 : }
1209 :
1210 : void
1211 0 : XPCJSContext::AfterProcessTask(uint32_t aNewRecursionDepth)
1212 : {
1213 : // Now that we're back to the event loop, reset the slow script checkpoint.
1214 1625 : mSlowScriptCheckpoint = mozilla::TimeStamp();
1215 0 : mSlowScriptSecondHalf = false;
1216 :
1217 : // Call cycle collector occasionally.
1218 0 : MOZ_ASSERT(NS_IsMainThread());
1219 1625 : nsJSContext::MaybePokeCC();
1220 :
1221 0 : CycleCollectedJSContext::AfterProcessTask(aNewRecursionDepth);
1222 :
1223 : // Now that we are certain that the event is complete,
1224 : // we can flush any ongoing performance measurement.
1225 0 : js::FlushPerformanceMonitoring(Context());
1226 :
1227 1625 : mozilla::jsipc::AfterProcessTask();
1228 0 : }
1229 :
1230 : bool
1231 0 : XPCJSContext::IsSystemCaller() const
1232 : {
1233 345 : return nsContentUtils::IsSystemCaller(Context());
1234 : }
|