Firefox · DOM
CVE-2026-2770
UAF in DOM
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifdom/bindings/BindingUtils.cpp |
modified |
Files Changed
dom/bindings/BindingUtils.cpp
Patch
diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp
index d2f09bc7a07..61758ec21c5 100644
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -33,6 +33,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Encoding.h"
+#include "mozilla/Maybe.h"
#include "mozilla/Preferences.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Sprintf.h"
@@ -3808,7 +3809,7 @@ bool HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
// Technically, per spec, a window always has a document. In Gecko, a
// sufficiently torn-down window might not, so check for that case. We're
// going to need a document to create an element.
- Document* doc = window->GetExtantDoc();
+ RefPtr<Document> doc = window->GetExtantDoc();
if (!doc) {
rv.Throw(NS_ERROR_UNEXPECTED);
return false;
@@ -3851,7 +3852,7 @@ bool HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
}
// Step 3.
- CustomElementDefinition* definition =
+ RefPtr<CustomElementDefinition> definition =
registry->LookupCustomElementDefinition(aCx, newTarget);
if (!definition) {
rv.ThrowTypeError<MSG_ILLEGAL_CONSTRUCTOR>();
@@ -3951,6 +3952,21 @@ bool HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
// Steps 7 and 8.
JS::Rooted<JSObject*> desiredProto(aCx);
+
+ // Check which construction path we're taking before running any JS.
+ // This determines whether we need AutoConstructionDepth protection.
+ nsTArray<RefPtr<Element>>& constructionStack = definition->mConstructionStack;
+ const bool isDirectConstruction = constructionStack.IsEmpty();
+
+ // For direct construction (not upgrade), create AutoConstructionDepth before
+ // GetDesiredProto. This ensures mConstructionDepth is incremented before any
+ // re-entrant JS can run via Proxy traps, preventing desynchronization with
+ // mPrefixStack which may be pushed by nsContentUtils::NewXULOrHTMLElement.
+ mozilla::Maybe<AutoConstructionDepth> autoDepth;
+ if (isDirectConstruction) {
+ autoDepth.emplace(definition);
+ }
+
if (!GetDesiredProto(aCx, args, aProtoId, aCreator, &desiredProto)) {
return false;
}
@@ -3961,14 +3977,12 @@ bool HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
// one branch and steps 9-12 on another branch, then common up the "return
// element" work.
RefPtr<Element> element;
- nsTArray<RefPtr<Element>>& constructionStack = definition->mConstructionStack;
- if (constructionStack.IsEmpty()) {
+ if (isDirectConstruction) {
// Step 8.
// Now we go to construct an element. We want to do this in global's
// realm, not caller realm (the normal constructor behavior),
// just in case those elements create JS things.
JSAutoRealm ar(aCx, global.Get());
- AutoConstructionDepth acd(definition);
RefPtr<NodeInfo> nodeInfo = doc->NodeInfoManager()->GetNodeInfo(
definition->mLocalName, definition->mPrefixStack.LastElement(), ns,
Loading diff…
References
On This Page