Moderate firefox Logic Error 🔧 Commit mapped

Overview

Moderate
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionOther issue in the SVG component
ComponentDOM
Bug ClassLogic Error
Tracker2082311
Fix commitd3d64436e967 (firefox) +28/-1
CISA KEVNot listed
CreditedMozilla
Disclosed2026-10-27

Background

SVG `<use>` element
references another element by id and renders a clone of it; the clone lives in an anonymous use-element shadow tree attached to the <use> element, not in the main document tree.
`nsGenericHTMLFrameElement`
the base class (dom/html/nsGenericHTMLFrameElement.cpp) for frame-owning HTML elements such as <iframe>, <frame>, and <object>; it owns an nsFrameLoader that actually loads and runs the subframe’s content.
`EnsureFrameLoader()`
lazily creates the mFrameLoader so the element can load its referenced document; creating a frame loader is what causes the subframe to navigate/load and its content (including inline onload handlers) to run.
`IsInComposedDoc()` / `IsStaticDocument()` / `IsInSVGUseShadowTree()`
predicates gating frame-loader creation: composed-doc membership, print/snapshot static documents, and (newly) membership in an SVG <use> shadow tree.

Root Cause Analysis

EnsureFrameLoader() returned early (declining to create a frame loader) only when the element was not in a composed document, already had a frame loader, or lived in a static document. It did not check whether the element was inside an SVG <use> element’s shadow tree. So when a document contained a frame-owning element (e.g. an <iframe> inside a <foreignObject>) within a subtree referenced by <use>, the cloned copy of that element in the use-shadow-tree would reach EnsureFrameLoader(), create its own nsFrameLoader, and load/run as a real frame — the regression test shows the cloned <iframe>’s inline onload handler firing a second time (loads should be [false], i.e. only the original instance’s handler runs). Per the SVG spec and the linked svgwg issue #876, elements cloned into a <use> shadow tree are presentational and must not instantiate active frames; the old code let them do so.

The patch adds || IsInSVGUseShadowTree() to the early-return condition in EnsureFrameLoader(), so a frame-owning element that is part of a <use> shadow-tree clone no longer creates a frame loader and therefore does not load the referenced content or run its handlers. This is the whole fix plus a web-platform test. The advisory classes this as “Other issue in the SVG component,” which is deliberately vague; the diff substantiates a logic/spec-conformance error (unintended frame loading and script/handler execution for cloned frames), not a memory-safety sink. There is no freed-memory or corruption path visible in the diff — the security concern is the duplicate/unexpected frame instantiation and associated handler execution in a context that should be inert.

Key insight
EnsureFrameLoader() failed to exclude elements inside an SVG <use> shadow tree, so a frame-owning element cloned by <use> would spin up its own frame loader and load/run as a live frame instead of being an inert presentational clone.

Attack Path

  1. Web content author a page with an SVG whose referenced subtree contains a frame-owning element, e.g. <foreignObject><iframe onload=...></iframe></foreignObject> inside a <g id="g">.
  2. Instantiate clone add <use href="#g"/>, cloning the subtree (including the <iframe>) into the <use> shadow tree.
  3. Frame loader created the cloned <iframe>’s EnsureFrameLoader() runs without the use-shadow-tree guard and creates a frame loader for the clone.
  4. Unintended load/execution the cloned frame loads its content and runs script/handlers (the test observes the inline onload firing for the clone).
  5. Primitive unexpected instantiation and content/handler execution of a frame that should be an inert <use> clone (logic/spec violation; no memory-safety primitive shown by the diff).

Impact Assessment

The code runs in the content/renderer process and is reachable by ordinary web content with no special privilege. The demonstrated effect is a frame-owning element cloned into an SVG <use> shadow tree creating a real frame loader and loading/running as a live subframe — duplicate/unexpected frame loads and inline-handler execution contrary to the SVG spec. The diff shows a logic (spec-conformance) error, not a memory-safety bug; Mozilla’s generic “Other issue” label is consistent with that, and the security severity (moderate) stems from the unintended load/script-execution behavior rather than any corruption primitive, which is not present in this diff.

Changed Functions

FunctionChangeNotes
promise_test
testing/web-platform/tests/svg/struct/scripted/use-iframe-onload.html
modified

Files Changed

  • dom/html/nsGenericHTMLFrameElement.cpp
  • testing/web-platform/tests/svg/struct/scripted/use-iframe-onload.html

Audit Directions

  • Other active-content creation in use-shadow-trees
    audit sibling lazy-instantiation paths (plugin/object loaders, media element loading, script-bearing elements) for the same missing IsInSVGUseShadowTree() guard when cloned by <use>.
  • Shadow-tree clone invariants
    review how use-element (and other anonymous) shadow-tree clones are treated versus their originals for any case where the clone wrongly gains “live”/document-connected behavior.
  • `EnsureFrameLoader` callers
    check every caller/trigger of EnsureFrameLoader to confirm none bypasses the guard for an element already inside a <use> shadow tree.
diff --git a/dom/html/nsGenericHTMLFrameElement.cpp b/dom/html/nsGenericHTMLFrameElement.cpp
index f323d7b37d2..bd52333131e 100644
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -110,7 +110,8 @@ Nullable<WindowProxyHolder> nsGenericHTMLFrameElement::GetContentWindow() {
 }
 
 void nsGenericHTMLFrameElement::EnsureFrameLoader() {
-  if (!IsInComposedDoc() || mFrameLoader || OwnerDoc()->IsStaticDocument()) {
+  if (!IsInComposedDoc() || mFrameLoader || OwnerDoc()->IsStaticDocument() ||
+      IsInSVGUseShadowTree()) {
     // If frame loader is there, we just keep it around, cached
     return;
   }
diff --git a/testing/web-platform/tests/svg/struct/scripted/use-iframe-onload.html b/testing/web-platform/tests/svg/struct/scripted/use-iframe-onload.html
new file mode 100644
index 00000000000..735b2b0deb2
--- /dev/null
+++ b/testing/web-platform/tests/svg/struct/scripted/use-iframe-onload.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>An iframe cloned by an SVG use element doesn't run its inline onload handler</title>
+<link rel="help" href="https://svgwg.org/svg2-draft/struct.html#UseElement">
+<link rel="help" href="https://github.com/w3c/svgwg/issues/876">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  window.loads = [];
+</script>
+<svg>
+  <g id="g">
+    <foreignObject width="100" height="100">
+      <iframe onload="loads.push(this.getRootNode() instanceof ShadowRoot)"></iframe>
+    </foreignObject>
+  </g>
+  <use href="#g"/>
+</svg>
+<script>
+  promise_test(async function() {
+    await new Promise(resolve => window.addEventListener("load", resolve));
+    await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
+    await new Promise(resolve => step_timeout(resolve, 0));
+    assert_array_equals(loads, [false], "Only the original iframe's onload handler should run");
+  }, "Inline onload handler of an iframe in a use element shadow tree doesn't run");
+</script>
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/testing/web-platform/tests/svg/struct/scripted/use-iframe-onload.html b/testing/web-platform/tests/svg/struct/scripted/use-iframe-onload.html
new file mode 100644
index 00000000000..735b2b0deb2
--- /dev/null
+++ b/testing/web-platform/tests/svg/struct/scripted/use-iframe-onload.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>An iframe cloned by an SVG use element doesn't run its inline onload handler</title>
+<link rel="help" href="https://svgwg.org/svg2-draft/struct.html#UseElement">
+<link rel="help" href="https://github.com/w3c/svgwg/issues/876">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  window.loads = [];
+</script>
+<svg>
+  <g id="g">
+    <foreignObject width="100" height="100">
+      <iframe onload="loads.push(this.getRootNode() instanceof ShadowRoot)"></iframe>
+    </foreignObject>
+  </g>
+  <use href="#g"/>
+</svg>
+<script>
+  promise_test(async function() {
+    await new Promise(resolve => window.addEventListener("load", resolve));
+    await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
+    await new Promise(resolve => step_timeout(resolve, 0));
+    assert_array_equals(loads, [false], "Only the original iframe's onload handler should run");
+  }, "Inline onload handler of an iframe in a use element shadow tree doesn't run");
+</script>
Loading diff…