tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit b23df1ad9a68e880cd2449c2e10f9f40e20156c5
parent cec17b64a17030a3a8726cfb4dea84f034be8263
Author: Tooru Fujisawa <arai_a@mac.com>
Date:   Wed, 19 Nov 2025 10:53:31 +0000

Bug 2000587 - Part 1: Check dead wrapper in JS::GetExecutionGlobalFromJSMicroTask. r=iain

Differential Revision: https://phabricator.services.mozilla.com/D272980

Diffstat:
Mjs/public/friend/MicroTask.h | 12++++++++----
Mjs/src/builtin/Promise.cpp | 14+++++++-------
Mjs/src/shell/js.cpp | 4++++
Mjs/src/vm/JSContext.cpp | 9++++++++-
Mxpcom/base/CycleCollectedJSContext.cpp | 3+++
5 files changed, 30 insertions(+), 12 deletions(-)

diff --git a/js/public/friend/MicroTask.h b/js/public/friend/MicroTask.h @@ -42,9 +42,12 @@ namespace JS { // run by calling RunJSMicroTask, while in the realm specified by the // global returned by GetExecutionGlobalFromJSMicroTask, e.g // -// AutoRealm ar(cx, JS::GetExecutionGlobalFromJSMicroTask(job)); -// if (!JS::RunJSMicroTask(cx, job)) { -// ... +// JSObject* global = JS::GetExecutionGlobalFromJSMicroTask(job); +// if (global) { +// AutoRealm ar(cx, global); +// if (!JS::RunJSMicroTask(cx, job)) { +// ... +// } // } // A MicroTask is a JS::Value. Using this MicroTask system allows @@ -126,7 +129,8 @@ JS_PUBLIC_API bool HasRegularMicroTasks(JSContext* cx); // Returns the length of the regular microtask queue. JS_PUBLIC_API size_t GetRegularMicroTaskCount(JSContext* cx); -// This is the global associated with the realm RunJSMicroTask expects to be in. +// This is the global associated with the realm RunJSMicroTask expects to be +// in. Returns nullptr if a dead wrapper is found. JS_PUBLIC_API JSObject* GetExecutionGlobalFromJSMicroTask(JSMicroTask* entry); // To handle cases where the queue needs to be set aside for some reason diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp @@ -7756,6 +7756,10 @@ JS_PUBLIC_API JSObject* JS::MaybeGetHostDefinedGlobalFromJSMicroTask( JS_PUBLIC_API JSObject* JS::GetExecutionGlobalFromJSMicroTask( JS::JSMicroTask* entry) { JSObject* unwrapped = UncheckedUnwrap(entry); + if (JS_IsDeadWrapper(unwrapped)) { + return nullptr; + } + if (unwrapped->is<PromiseReactionRecord>()) { // Use the stored equeue representative (which may need to be unwrapped) JSObject* enqueueGlobalRepresentative = @@ -7763,13 +7767,9 @@ JS_PUBLIC_API JSObject* JS::GetExecutionGlobalFromJSMicroTask( JSObject* unwrappedRepresentative = UncheckedUnwrap(enqueueGlobalRepresentative); - // We shouldn't lose the representative object as the global should remain - // alive while this job is pending; the global should be entrained because - // it will either be the global of the PromiseReactionRecord (which) - // should have been kept alive by being in the queue or rooted, or - // it should be the global of the handler function, which should - // be entrained by the PromiseReactionRecord. - MOZ_RELEASE_ASSERT(!JS_IsDeadWrapper(unwrappedRepresentative)); + if (JS_IsDeadWrapper(unwrappedRepresentative)) { + return nullptr; + } return &unwrappedRepresentative->nonCCWGlobal(); } diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp @@ -1478,6 +1478,10 @@ static bool GlobalOfFirstJobInQueue(JSContext* cx, unsigned argc, Value* vp) { JS::JSMicroTask* job = JS::ToUnwrappedJSMicroTask(genericJob); MOZ_ASSERT(job); RootedObject global(cx, JS::GetExecutionGlobalFromJSMicroTask(job)); + if (!global) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); + return false; + } MOZ_ASSERT(global); if (!cx->compartment()->wrap(cx, &global)) { return false; diff --git a/js/src/vm/JSContext.cpp b/js/src/vm/JSContext.cpp @@ -797,6 +797,11 @@ JSObject* InternalJobQueue::copyJobs(JSContext* cx) { if (task) { // All any test cares about is the global of the job so let's do it. RootedObject global(cx, JS::GetExecutionGlobalFromJSMicroTask(task)); + if (!global) { + JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, + JSMSG_DEAD_OBJECT); + return false; + } if (!cx->compartment()->wrap(cx, &global)) { return false; } @@ -919,7 +924,9 @@ void InternalJobQueue::runJobs(JSContext* cx) { JS::JobQueueIsEmpty(cx); } - MOZ_ASSERT(JS::GetExecutionGlobalFromJSMicroTask(job) != nullptr); + if (!JS::GetExecutionGlobalFromJSMicroTask(job)) { + continue; + } AutoRealm ar(cx, JS::GetExecutionGlobalFromJSMicroTask(job)); { if (!JS::RunJSMicroTask(cx, job)) { diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp @@ -1013,6 +1013,9 @@ static void MOZ_CAN_RUN_SCRIPT RunMicroTask( JS::RootedField<JSObject*, 0> callbackGlobal( roots, aMicroTask.get().GetExecutionGlobalFromJSMicroTask(aCx)); + if (!callbackGlobal) { + return; + } JS::RootedField<JSObject*, 1> hostDefinedData( roots, aMicroTask.get().MaybeGetHostDefinedDataFromJSMicroTask()); JS::RootedField<JSObject*, 2> allocStack(