Released March 31, 2025

CVE-2025-24209

CVSS 7 severity high CWE-120 vector CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:H

Overview

Impact:

Description: Processing maliciously crafted web content may lead to an unexpected process crash

Researchers: Francisco Alonso (@revskills), and an anonymous researcher

Key insight. The transform fast path validated only the 9-character minimum for ‘rotate(0)’ but the translate branch reads index 11, so short ’translate’ inputs read out of bounds; a per-branch length check fixes it.

Patch Details

diff --git a/LayoutTests/fast/css/transform-translate-parsing-crash-expected.txt b/LayoutTests/fast/css/transform-translate-parsing-crash-expected.txt
new file mode 100644
index 000000000000..2985393944b6
--- /dev/null
+++ b/LayoutTests/fast/css/transform-translate-parsing-crash-expected.txt
@@ -0,0 +1 @@
+This test passes if it doesn't crash
diff --git a/LayoutTests/fast/css/transform-translate-parsing-crash.html b/LayoutTests/fast/css/transform-translate-parsing-crash.html
new file mode 100644
index 000000000000..aa3dd7fa63f2
--- /dev/null
+++ b/LayoutTests/fast/css/transform-translate-parsing-crash.html
@@ -0,0 +1,7 @@
+<div id=t>This test passes if it doesn't crash</div>
+<script>
+if (window.testRunner)
+    testRunner.dumpAsText();
+t.style.transform = "translatez";
+t.style.transform = "translate3d";
+</script>
diff --git a/Source/WebCore/css/parser/CSSParserFastPaths.cpp b/Source/WebCore/css/parser/CSSParserFastPaths.cpp
index c692b9543476..abcc2d5b5595 100644
--- a/Source/WebCore/css/parser/CSSParserFastPaths.cpp
+++ b/Source/WebCore/css/parser/CSSParserFastPaths.cpp
@@ -835,12 +835,12 @@ static bool parseTransformNumberArguments(CharType*& pos, CharType* end, unsigne
     return true;
 }
 
-static constexpr int kShortestValidTransformStringLength = 9; // "rotate(0)"
-
 template <typename CharType>
 static RefPtr<CSSFunctionValue> parseSimpleTransformValue(CharType*& pos, CharType* end)
 {
-    if (end - pos < kShortestValidTransformStringLength)
+    // Also guarantees indexes up to pos[8] are safe to access below.
+    constexpr auto shortestValidTransformStringLength = 9; // "rotate(0)"
+    if (end - pos < shortestValidTransformStringLength)
         return nullptr;
 
     bool isTranslate = toASCIILower(pos[0]) == 't'
@@ -854,6 +854,11 @@ static RefPtr<CSSFunctionValue> parseSimpleTransformValue(CharType*& pos, CharTy
         && toASCIILower(pos[8]) == 'e';
 
     if (isTranslate) {
+        // Also guarantees indexes up to pos[11] are safe to access below.
+        constexpr auto shortestValidTranslateStringLength = 12; // "translate(0)"
+        if (end - pos < shortestValidTranslateStringLength)
+            return nullptr;
+
         CSSValueID transformType;
         unsigned expectedArgumentCount = 1;
         unsigned argumentStart = 11;

Background

Advisory: Apple Security Advisory

Attribute Value
CVE CVE-2025-24209
Bugzilla 286462
Component WebCore
Bug Class OOB
Severity medium
Commit 575e5800d5bb2215…
Advisory Apple Advisory

Terminology

  • CSS transform fast path — An optimized parser for simple transform values (rotate/translate/scale) that indexes directly into the value string.
  • Length gate — A minimum-length check that makes a fixed set of indices safe; a longer branch needs its own, larger gate.
  • argumentStart = 11 — The translate branch reads the value at index 11, requiring the string to be at least 12 characters.

Root Cause Analysis

This fixes an out-of-bounds read in the CSS transform fast-path parser. parseSimpleTransformValue first checks that the input has at least 9 characters (shortestValidTransformStringLength, for ‘rotate(0)’), which guarantees pos[0]..pos[8] are safe to read. It then detects the ’translate’ family by inspecting those bytes, and in the isTranslate branch reads further into the buffer — up to pos[11] (argumentStart = 11) and beyond — without any additional length check. Inputs like ’translatez’ (10 chars) or ’translate3d’ (11 chars) satisfy the isTranslate detection but are shorter than the indices the branch accesses, so reading pos[9..11] runs past the end of the buffer — an out-of-bounds read that can crash (or leak adjacent memory). The fix adds, inside the isTranslate branch, a check end - pos < shortestValidTranslateStringLength (12, for ’translate(0)’) that returns nullptr before the higher-index reads. The restored invariant is that each transform sub-parser verifies enough remaining length for every index it will access. The regression test sets transform to ’translatez’ and ’translate3d’.

Attack Path

1. Set a short translate value

Assign a CSS transform like ’translatez’ or ’translate3d’ (10-11 chars) that passes the 9-char length gate and the isTranslate detection.

2. Access beyond the buffer

The translate branch reads pos[9..11] without checking the string is at least 12 chars.

3. Out-of-bounds read

Reading past the end of the value buffer crashes or reads adjacent heap memory in WebContent.

Impact Assessment

An out-of-bounds read in the WebContent process from a crafted CSS transform value — reachable from any styled page. The advisory rates it a process crash; OOB reads can also leak adjacent memory, though this fast-path read is a comparatively narrow primitive.

Audit Directions

Same function: per-branch length

Audit parseSimpleTransformValue’s scale/translate3d/matrix branches for index accesses beyond the 9-char gate lacking their own length checks.

Fixed-index string parsers

Grep CSSParserFastPaths for pos[N] accesses guarded only by an earlier, smaller minimum-length check.

Changed Functions

Function File Change Note
parseSimpleTransformValue Source/WebCore/css/parser/CSSParserFastPaths.cpp modified Adds a shortestValidTranslateStringLength (12) check inside the isTranslate branch before reading up to pos[11], preventing an OOB read on short ’translate’ inputs.

Files Changed

Source Files

  • Source/WebCore/css/parser/CSSParserFastPaths.cpp

Test Files

  • LayoutTests/fast/css/transform-translate-parsing-crash-expected.txt
  • LayoutTests/fast/css/transform-translate-parsing-crash.html

Regression Test

The fix ships this proof-of-concept / regression test:

diff --git a/LayoutTests/fast/css/transform-translate-parsing-crash-expected.txt b/LayoutTests/fast/css/transform-translate-parsing-crash-expected.txt
new file mode 100644
index 000000000000..2985393944b6
--- /dev/null
+++ b/LayoutTests/fast/css/transform-translate-parsing-crash-expected.txt
@@ -0,0 +1 @@
+This test passes if it doesn't crash
diff --git a/LayoutTests/fast/css/transform-translate-parsing-crash.html b/LayoutTests/fast/css/transform-translate-parsing-crash.html
new file mode 100644
index 000000000000..aa3dd7fa63f2
--- /dev/null
+++ b/LayoutTests/fast/css/transform-translate-parsing-crash.html
@@ -0,0 +1,7 @@
+<div id=t>This test passes if it doesn't crash</div>
+<script>
+if (window.testRunner)
+    testRunner.dumpAsText();
+t.style.transform = "translatez";
+t.style.transform = "translate3d";
+</script>