tor-browser

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

commit 5b07337477c71fe4cb5d5d1cd67a1d36138d488e
parent 8b5066eeae2530e3653353cfbc2751d4162fd6ae
Author: Matthew Gaudet <mgaudet@mozilla.com>
Date:   Tue, 25 Nov 2025 18:24:38 +0000

Bug 2002134 - Disambiguate between unwrapping failure and missing data in JS Microtask Queue r=arai

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

Diffstat:
Mjs/public/friend/MicroTask.h | 15+++++++++------
Mjs/src/builtin/Promise.cpp | 41++++++++++++++++++++++++++---------------
Mxpcom/base/CycleCollectedJSContext.cpp | 17+++++++++++++----
Mxpcom/base/CycleCollectedJSContext.h | 13+++++++------
4 files changed, 55 insertions(+), 31 deletions(-)

diff --git a/js/public/friend/MicroTask.h b/js/public/friend/MicroTask.h @@ -157,12 +157,15 @@ JS_PUBLIC_API void RestoreMicroTaskQueue( // Via the following API functions various host defined data is exposed to the // embedder (see JobQueue::getHostDefinedData). // -// All of these may return null if there's no data, or if there's a -// security error. -JS_PUBLIC_API JSObject* MaybeGetHostDefinedDataFromJSMicroTask( - JSMicroTask* entry); -JS_PUBLIC_API JSObject* MaybeGetAllocationSiteFromJSMicroTask( - JSMicroTask* entry); +// These return true on success and false on failure. They return false if +// there are any unwrapping issues (e.g., dead wrappers), and true with nullptr +// if there just isn't any data. +// +// This disambiguates between no-data and the dead wrapper case +JS_PUBLIC_API bool MaybeGetHostDefinedDataFromJSMicroTask( + JSMicroTask* entry, MutableHandleObject out); +JS_PUBLIC_API bool MaybeGetAllocationSiteFromJSMicroTask( + JSMicroTask* entry, MutableHandleObject out); // In some circumstances an entry may not have host defined data but may // still have a host defined global; diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp @@ -7696,14 +7696,15 @@ inline bool JSObject::is<MicroTaskEntry>() const { return is<ThenableJob>() || is<PromiseReactionRecord>(); } -JS_PUBLIC_API JSObject* JS::MaybeGetHostDefinedDataFromJSMicroTask( - JS::JSMicroTask* entry) { +JS_PUBLIC_API bool JS::MaybeGetHostDefinedDataFromJSMicroTask( + JS::JSMicroTask* entry, MutableHandleObject out) { + out.set(nullptr); JSObject* task = CheckedUnwrapStatic(entry); if (!task) { - return nullptr; + return false; } if (JS_IsDeadWrapper(task)) { - return nullptr; + return false; } MOZ_ASSERT(task->is<MicroTaskEntry>()); @@ -7711,34 +7712,44 @@ JS_PUBLIC_API JSObject* JS::MaybeGetHostDefinedDataFromJSMicroTask( task->as<MicroTaskEntry>().getHostDefinedData().toObjectOrNull(); if (!maybeHostDefined) { - return nullptr; + return true; } if (JS_IsDeadWrapper(maybeHostDefined)) { - return nullptr; + return false; + } + + JSObject* unwrapped = CheckedUnwrapStatic(maybeHostDefined); + if (!unwrapped) { + return false; } - return CheckedUnwrapStatic(maybeHostDefined); + out.set(unwrapped); + return true; } -JS_PUBLIC_API JSObject* JS::MaybeGetAllocationSiteFromJSMicroTask( - JS::JSMicroTask* entry) { +JS_PUBLIC_API bool JS::MaybeGetAllocationSiteFromJSMicroTask( + JS::JSMicroTask* entry, MutableHandleObject out) { JSObject* task = UncheckedUnwrap(entry); if (JS_IsDeadWrapper(task)) { - return nullptr; + return false; }; MOZ_ASSERT(task->is<MicroTaskEntry>()); JSObject* maybeWrappedStack = task->as<MicroTaskEntry>().allocationStack(); - // If the stack is in a compartment which has gone away, best we - // can do is return nullptr. - if (!maybeWrappedStack || JS_IsDeadWrapper(maybeWrappedStack)) { - return nullptr; + if (!maybeWrappedStack) { + out.set(nullptr); + return true; + } + + if (JS_IsDeadWrapper(maybeWrappedStack)) { + return false; } JSObject* unwrapped = UncheckedUnwrap(maybeWrappedStack); MOZ_ASSERT(unwrapped->is<SavedFrame>()); - return unwrapped; + out.set(unwrapped); + return true; } JS_PUBLIC_API JSObject* JS::MaybeGetHostDefinedGlobalFromJSMicroTask( diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJSContext.cpp @@ -1016,10 +1016,19 @@ static void MOZ_CAN_RUN_SCRIPT RunMicroTask( if (!callbackGlobal) { return; } - JS::RootedField<JSObject*, 1> hostDefinedData( - roots, aMicroTask.get().MaybeGetHostDefinedDataFromJSMicroTask()); - JS::RootedField<JSObject*, 2> allocStack( - roots, aMicroTask.get().MaybeGetAllocationSiteFromJSMicroTask()); + JS::RootedField<JSObject*, 1> hostDefinedData(roots); + JS::RootedField<JSObject*, 2> allocStack(roots); + + // Don't run if we fail to unwrap the host defined data. + if (!aMicroTask.get().MaybeGetHostDefinedDataFromJSMicroTask( + &hostDefinedData)) { + return; + } + + // Don't run if we fail to unwrap the stack. + if (!aMicroTask.get().MaybeGetAllocationSiteFromJSMicroTask(&allocStack)) { + return; + } nsIGlobalObject* incumbentGlobal = nullptr; diff --git a/xpcom/base/CycleCollectedJSContext.h b/xpcom/base/CycleCollectedJSContext.h @@ -218,20 +218,21 @@ class MustConsumeMicroTask { return JS::MaybeGetPromiseFromJSMicroTask(task); } - JSObject* MaybeGetHostDefinedDataFromJSMicroTask() { + bool MaybeGetHostDefinedDataFromJSMicroTask( + JS::MutableHandle<JSObject*> out) { JS::JSMicroTask* task = JS::ToUnwrappedJSMicroTask(mMicroTask); if (!task) { - return nullptr; + return false; } - return JS::MaybeGetHostDefinedDataFromJSMicroTask(task); + return JS::MaybeGetHostDefinedDataFromJSMicroTask(task, out); } - JSObject* MaybeGetAllocationSiteFromJSMicroTask() { + bool MaybeGetAllocationSiteFromJSMicroTask(JS::MutableHandle<JSObject*> out) { JS::JSMicroTask* task = JS::ToUnwrappedJSMicroTask(mMicroTask); if (!task) { - return nullptr; + return false; } - return JS::MaybeGetAllocationSiteFromJSMicroTask(task); + return JS::MaybeGetAllocationSiteFromJSMicroTask(task, out); } JSObject* MaybeGetHostDefinedGlobalFromJSMicroTask() {