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