CVE-2026-117984
Overview
Background
- `CanHaveOverflowMarkers`
- Decides whether a block frame can show overflow markers; has a special case for the internal editing root of text controls (
<input>/<textarea>). - `PseudoStyleType::MozTextControlEditingRoot`
- The anonymous pseudo-element that is the editing root inside a text control.
- `IsTextInputScrolledContent` / `ScrollContainerFrame`
- The editing root is normally wrapped by a scroll container;
CanHaveOverflowMarkerswants tostatic_castthe grandparent frame toScrollContainerFrame*and read its scroll position.
Root Cause Analysis
The old code keyed solely on the pseudo type: if (aBlockFrame->Style()->GetPseudoType() == PseudoStyleType::MozTextControlEditingRoot). Inside that branch it static_casted aBlockFrame->GetParent()->GetParent() to ScrollContainerFrame* and called GetScrollPosition(), protecting the assumption only with a MOZ_DIAGNOSTIC_ASSERT(IsTextInputScrolledContent(aBlockFrame->GetParent())) — a diagnostic/debug check that does nothing in release builds. The crash test shows the assumption is false in practice: a <textarea> with column-count: 2; writing-mode: vertical-lr and a text-indent produces an editing-root pseudo whose parent is not the expected scrolled content (multicol reframes the subtree). In that case the unchecked static_cast to ScrollContainerFrame* operates on a frame of the wrong type (or a wrong ancestor), and dereferencing it via GetScrollPosition() is an invalid-type access.
The patch makes the real check part of the branch condition: pseudoType == MozTextControlEditingRoot && MOZ_LIKELY(IsTextInputScrolledContent(aBlockFrame->GetParent())), and removes the now-redundant MOZ_DIAGNOSTIC_ASSERT. The branch — and therefore the ScrollContainerFrame* cast — only runs when the parent genuinely is the text-input scrolled content. The advisory’s “incorrect boundary conditions” generically describes this, but the diff substantiates a bad static downcast / type confusion: a frame was cast to ScrollContainerFrame* without verifying its actual type.
static_cast to ScrollContainerFrame* was gated only by a release-build no-op assertion on the frame’s shape; with multicol/vertical writing-mode text controls the assumption fails, so the code cast and dereferenced a frame of the wrong type. The fix promotes the shape check into the runtime branch condition.Attack Path
- Craft
Attacker page/XHTML contains a
<textarea>styled withcolumn-count: 2; writing-mode: vertical-lr; text-indent: ...(from the WPT crash test). - Layout
On load, forcing layout (
getBoundingClientRect()) and then settingtext-overflow: ellipsisdrivesCanHaveOverflowMarkerson the editing-root frame. - Mis-cast
The pseudo-type matches, but the parent is not text-input scrolled content; the old code
static_casts the grandparent toScrollContainerFrame*. - Invalid access
scc->GetScrollPosition()reads a frame of the wrong type (type confusion) → crash / out-of-type memory access. - Primitive Type-confused frame access in the layout engine (demonstrated as a crash; stronger exploitation inferred).
Impact Assessment
static_cast of a layout frame to ScrollContainerFrame* when the structural assumption fails, yielding at least a crash/DoS and an out-of-type pointer dereference. Whether the type confusion is massageable into a controlled read/write is not shown by the diff and is inferred. The advisory rates it moderate.Files Changed
layout/generic/TextOverflow.cpptesting/web-platform/tests/css/css-overflow/text-overflow-root-textarea-crash.xhtml
Audit Directions
- Diagnostic-asserted castsSearch
layout/forstatic_cast<...Frame*>guarded only byMOZ_DIAGNOSTIC_ASSERT/MOZ_ASSERT(no-ops in release); convert the invariant to a runtime check or usedo_QueryFrame. - Multicol/writing-mode reframesReview other special-case frame-ancestor assumptions (
GetParent()->GetParent()) that can be broken bycolumn-countor vertical writing modes. - Text-control editing-root assumptionsAudit other code matching
MozTextControlEditingRootthat assumes a specific scroll-container ancestry.
Patch
diff --git a/layout/generic/TextOverflow.cpp b/layout/generic/TextOverflow.cpp
index c7bb3e6edd5..c33297ca72f 100644
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -860,10 +860,10 @@ bool TextOverflow::CanHaveOverflowMarkers(nsBlockFrame* aBlockFrame,
// Make text-overflow: ellipsis work on the <input> editor.
if (aBlockFrame->Style()->GetPseudoType() ==
- PseudoStyleType::MozTextControlEditingRoot) {
+ PseudoStyleType::MozTextControlEditingRoot &&
+ MOZ_LIKELY(IsTextInputScrolledContent(aBlockFrame->GetParent()))) {
MOZ_ASSERT(IsInlineAxisOverflowVisible(*aBlockFrame),
"The control editing root must create overflow");
- MOZ_DIAGNOSTIC_ASSERT(IsTextInputScrolledContent(aBlockFrame->GetParent()));
auto* scc = static_cast<ScrollContainerFrame*>(
aBlockFrame->GetParent()->GetParent());
if (scc->GetScrollPosition() != nsPoint()) {
diff --git a/testing/web-platform/tests/css/css-overflow/text-overflow-root-textarea-crash.xhtml b/testing/web-platform/tests/css/css-overflow/text-overflow-root-textarea-crash.xhtml
new file mode 100644
index 00000000000..3e62fa32ca9
--- /dev/null
+++ b/testing/web-platform/tests/css/css-overflow/text-overflow-root-textarea-crash.xhtml
@@ -0,0 +1,6 @@
+<textarea xmlns="http://www.w3.org/1999/xhtml" style="column-count: 2; writing-mode: vertical-lr; text-indent: 81.98333px"><script>
+onload = () => {
+ document.documentElement.getBoundingClientRect();
+ document.documentElement.style.textOverflow = "ellipsis";
+};
+</script></textarea>
Regression Test / PoC
diff --git a/testing/web-platform/tests/css/css-overflow/text-overflow-root-textarea-crash.xhtml b/testing/web-platform/tests/css/css-overflow/text-overflow-root-textarea-crash.xhtml
new file mode 100644
index 00000000000..3e62fa32ca9
--- /dev/null
+++ b/testing/web-platform/tests/css/css-overflow/text-overflow-root-textarea-crash.xhtml
@@ -0,0 +1,6 @@
+<textarea xmlns="http://www.w3.org/1999/xhtml" style="column-count: 2; writing-mode: vertical-lr; text-indent: 81.98333px"><script>
+onload = () => {
+ document.documentElement.getBoundingClientRect();
+ document.documentElement.style.textOverflow = "ellipsis";
+};
+</script></textarea>