commit 9bd0056bb97aef3f6f44ec457417821f92a7eb4c
parent 9108500bb368dae51d67883f32c85dcbdc8406ba
Author: Jon Coppeard <jcoppeard@mozilla.com>
Date: Wed, 19 Nov 2025 13:24:57 +0000
Bug 1999464 - Don't nuke CCWs to the debugger object r=jandem
This is an alternative to the fix in bug 1995637 and removes that change.
The debugger mostly uses its own wrappers for cross compartment pointers
between debugger and debuggee globals. These are not in the CCW map and so are
not nuked by NukeCrossCompartmentWrappers. This pointer from a Breakpoint to
its owning debugger however is a normal CCW that is in the map and so can be
affected by that operation.
The change makes it so we skip nuking CCWs to the main debugger object. This
could affect other uses of this CCW but these are not accessible from content
JS so I don't think this is a problem.
It's important not to break the link between breakpoints and their debuggers
because they are present in two linked lists which are in different realms. To
clean up properly the link needs to be intact.
This change is also covered by the original test in bug 1995637.
Differential Revision: https://phabricator.services.mozilla.com/D273012
Diffstat:
3 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/js/src/debugger/Debugger.cpp b/js/src/debugger/Debugger.cpp
@@ -452,10 +452,8 @@ Breakpoint::Breakpoint(Debugger* debugger, HandleObject wrappedDebugger,
}
void Breakpoint::trace(JSTracer* trc) {
+ MOZ_ASSERT(!IsDeadProxyObject(wrappedDebugger));
TraceEdge(trc, &wrappedDebugger, "breakpoint owner");
- // Trace the debugger object too in case |wrappedDebugger| got nuked.
- TraceCrossCompartmentEdge(trc, wrappedDebugger, &debugger->object,
- "breakpoint debugger object");
TraceEdge(trc, &handler, "breakpoint handler");
}
diff --git a/js/src/jit-test/tests/debug/bug-1999464.js b/js/src/jit-test/tests/debug/bug-1999464.js
@@ -0,0 +1,6 @@
+fullcompartmentchecks(1);
+var x = newGlobal({ newCompartment: true });
+Debugger(x).onEnterFrame = function (y) {
+ y.script.setBreakpoint(0, {});
+};
+x.eval("(function(){})()");
diff --git a/js/src/proxy/CrossCompartmentWrapper.cpp b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "builtin/FinalizationRegistryObject.h"
+#include "debugger/Debugger.h"
#include "gc/GC.h"
#include "gc/PublicIterators.h"
#include "js/friend/WindowProxy.h" // js::IsWindow, js::IsWindowProxy
@@ -449,6 +450,12 @@ JS_PUBLIC_API bool js::NukeCrossCompartmentWrappers(
continue;
}
+ // Don't nuke wrappers for debugger objects. These are used in Breakpoints
+ // and nuking them breaks debugger invariants.
+ if (MOZ_UNLIKELY(wrapped->is<DebuggerInstanceObject>())) {
+ continue;
+ }
+
// We only skip nuking window references that point to a target
// compartment, not the ones that belong to it.
if (nukeReferencesToWindow == DontNukeWindowReferences &&