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:
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(