High firefox Logic Error 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionJIT miscompilation in the DOM: Core & HTML component
ComponentDOM
Bug ClassLogic Error
Tracker2043139
Fix commit72649967bc7b (firefox) +9/-12
CISA KEVNot listed
CreditedHyeonjun Ahn
Disclosed2026-06-16

Background

`[Pure]`
a Gecko WebIDL extended attribute asserting a getter/method is side-effect-free and returns consistent results, letting the JIT cache, hoist, reorder, or eliminate calls to it.
WebIDL bindings
generated C++ glue in dom/webidl/*.webidl that connects JavaScript-visible DOM APIs to their native implementations and carries JIT optimization hints.
JIT miscompilation
incorrect machine code produced when the JIT optimizes based on a false invariant, here treating impure DOM accessors as pure.
`innerText`/`editor` accessors
DOM members that respectively force layout reflow and lazily construct an nsIEditor, i.e. they mutate observable state on read.

Root Cause Analysis

Several JavaScript-visible DOM getters and methods across Document, EditContext, Element, HTMLElement, HTMLInputElement, and MozEditableElement were annotated [Pure], telling SpiderMonkey’s JIT they had no side effects and would return equivalent results on repeated calls. This invariant was violated: accessors like innerText/outerText flush layout, editor lazily creates and attaches an nsIEditor instance on first access, and sequence-returning methods such as getAttributeNames(), attachedElements(), and characterBounds() allocate a fresh object each call rather than being idempotent. Trusting the false [Pure] hint, the JIT could legally hoist, deduplicate, reorder, or eliminate these calls, producing code whose behavior diverged from the DOM’s actual semantics.

The fix removes [Pure] from exactly those members whose real implementations perform allocation, lazy construction, or layout side effects, so the JIT must treat each call as an ordinary effectful operation. Because the annotation was the sole source of the unsound assumption, dropping it restores correct sequencing and object identity for the affected APIs.

Key insight
The single core mistake was labeling effectful or freshly-allocating DOM accessors as [Pure], granting the JIT license to optimize away calls whose side effects and per-call object identity actually mattered; removing the annotation forces the JIT to emit a real, ordered call every time.

Attack Path

  1. Reach a JIT-hot path Attacker-controlled script repeatedly reads a mislabeled member such as innerText, editor, or getAttributeNames() until the method is JIT-compiled.
  2. Trigger unsound optimization The JIT, trusting [Pure], hoists or coalesces the repeated accesses into a single call or elides one entirely.
  3. Desynchronize state The elided/reordered call skips a layout flush or lazy nsIEditor creation, or reuses one allocation where distinct objects were expected, leaving JS-visible state inconsistent with the DOM’s true state.
  4. Exploit the mismatch Attacker crafts follow-on operations that assume the skipped side effect ran, driving the engine into an incorrect or unsafe state.

Impact Assessment

An attacker running untrusted JavaScript in a web content process gains a logic/JIT-miscompilation primitive where impure DOM reads are optimized away, desynchronizing engine state from DOM state. The precondition is only that the affected accessor be exercised enough to be JIT-compiled on a hot path in the content process. The metadata rates impact high; the divergence between assumed purity and real side effects (layout flushes, lazy editor allocation, per-call object creation) is the exploitable condition.

Files Changed

  • dom/webidl/Document.webidl
  • dom/webidl/EditContext.webidl
  • dom/webidl/Element.webidl
  • dom/webidl/HTMLElement.webidl
  • dom/webidl/HTMLInputElement.webidl

Audit Directions

  • Purity annotations vs. implementation
    For every [Pure] (and related JIT hint) in dom/webidl, confirm the backing C++ neither allocates fresh objects per call, lazily constructs state, nor flushes layout.
  • Layout-flushing getters
    Flag any DOM accessor that reflows or computes layout (innerText, sizing, grid/flex queries) as inherently impure and never eligible for [Pure].
  • Lazy-construction accessors
    Audit getters that create-on-first-access (editors, controllers, cached child objects) since their first call has side effects the JIT must not deduplicate or hoist.
diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl
index 03284d1f13c..944864e0b28 100644
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -669,7 +669,7 @@ partial interface Document {
    * tracking, fingerprinting, cryptomining and so on. This method is for
    * testing only.
    */
-  [ChromeOnly, Pure]
+  [ChromeOnly]
   readonly attribute NodeList blockedNodesByClassifier;
 };
 
diff --git a/dom/webidl/EditContext.webidl b/dom/webidl/EditContext.webidl
index 2f7df1c2c69..8d1c3808bde 100644
--- a/dom/webidl/EditContext.webidl
+++ b/dom/webidl/EditContext.webidl
@@ -25,7 +25,6 @@ interface EditContext : EventTarget {
     undefined updateSelectionBounds(DOMRect selectionBounds);
     undefined updateCharacterBounds(unsigned long rangeStart, sequence<DOMRect> characterBounds);
 
-    [Pure]
     sequence<HTMLElement> attachedElements();
 
     [Pure]
@@ -36,7 +35,6 @@ interface EditContext : EventTarget {
     readonly attribute unsigned long selectionEnd;
     [Pure]
     readonly attribute unsigned long characterBoundsRangeStart;
-    [Pure]
     sequence<DOMRect> characterBounds();
 
     attribute EventHandler ontextupdate;
diff --git a/dom/webidl/Element.webidl b/dom/webidl/Element.webidl
index 90c4a004958..aceb70aeebb 100644
--- a/dom/webidl/Element.webidl
+++ b/dom/webidl/Element.webidl
@@ -39,7 +39,6 @@ interface Element : Node {
 
   [SameObject]
   readonly attribute NamedNodeMap attributes;
-  [Pure]
   sequence<DOMString> getAttributeNames();
   [Pure]
   DOMString? getAttribute(DOMString name);
@@ -365,7 +364,7 @@ partial interface Element {
    * properties, as well as a property that exposes the flex lines
    * in this container.
    */
-  [ChromeOnly, Pure]
+  [ChromeOnly]
   Flex? getAsFlexContainer();
 
   // Support reporting of Grid properties
@@ -374,13 +373,13 @@ partial interface Element {
    * this property returns an object with computed values for grid
    * tracks and lines.
    */
-  [ChromeOnly, Pure]
+  [ChromeOnly]
   sequence<Grid> getGridFragments();
 
   /**
    * Returns whether there are any grid fragments on this element.
    */
-  [ChromeOnly, Pure]
+  [ChromeOnly]
   boolean hasGridFragments();
 
   /**
@@ -388,7 +387,7 @@ partial interface Element {
    * that have display:grid or display:inline-grid style and generate
    * a frame.
    */
-  [ChromeOnly, Pure]
+  [ChromeOnly]
   sequence<Element> getElementsWithGrid();
 
   /**
diff --git a/dom/webidl/HTMLElement.webidl b/dom/webidl/HTMLElement.webidl
index 034aa0de194..e5d4fea347e 100644
--- a/dom/webidl/HTMLElement.webidl
+++ b/dom/webidl/HTMLElement.webidl
@@ -25,9 +25,9 @@ interface HTMLElement : Element {
   [CEReactions, SetterThrows, Pure]
            attribute DOMString dir;
 
-  [CEReactions, GetterThrows, Pure]
+  [CEReactions, GetterThrows]
            attribute [LegacyNullToEmptyString] DOMString innerText;
-  [CEReactions, GetterThrows, SetterThrows, Pure]
+  [CEReactions, GetterThrows, SetterThrows]
            attribute [LegacyNullToEmptyString] DOMString outerText;
 
   // user interaction
diff --git a/dom/webidl/HTMLInputElement.webidl b/dom/webidl/HTMLInputElement.webidl
index be41ed49406..09964a99669 100644
--- a/dom/webidl/HTMLInputElement.webidl
+++ b/dom/webidl/HTMLInputElement.webidl
@@ -59,7 +59,7 @@ interface HTMLInputElement : HTMLElement {
            attribute boolean formNoValidate;
   [CEReactions, Pure, SetterThrows]
            attribute DOMString formTarget;
-  [CEReactions, Pure, SetterThrows]
+  [CEReactions, SetterThrows]
            attribute unsigned long height;
   [Pure]
            attribute boolean indeterminate;
@@ -215,7 +215,7 @@ interface mixin MozEditableElement {
   // Returns an nsIEditor instance which is associated with the element.
   // If the element can be associated with an editor but not yet created,
   // this creates new one automatically.
-  [Pure, ChromeOnly, BinaryName="editorForBindings"]
+  [ChromeOnly, BinaryName="editorForBindings"]
   readonly attribute nsIEditor? editor;
 
   // Returns true if an nsIEditor instance has already been associated with
Loading diff…