CVE-2025-24209
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.txtLayoutTests/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>