CVE-2025-31223
Overview
Background
- FastStringifier
- JSC’s optimized JSON.stringify path that writes into a preallocated buffer, guarded by capacity checks.
- Worst-case escaped length
- Each character can expand to up to 6 output chars when escaped; the buffer must be checked against that bound.
- CheckedUint32
- An overflow-trapping type; without it, stringLength*6 wraps and defeats the capacity check.
Root Cause Analysis
This fixes a heap buffer overflow in JavaScriptCore’s JSON fast stringifier caused by an overflowing worst-case-length capacity check. FastStringifier::append, before emitting an escaped string, must ensure the buffer has room for the worst case where each character escapes to up to 6 output characters (e.g. \u00XX) plus surrounding quotes: 1 + stringLength*6 + 1. Pre-patch this was computed as 1 + static_cast<size_t>(stringLength) * 6 + 1 and passed to hasRemainingCapacity, but the value is used in a 32-bit capacity domain, so for a very long string (the test uses 715,827,883 characters, whose *6 exceeds 2^32) the computation overflows and wraps to a small number. hasRemainingCapacity then returns true and the stringifier writes the escaped string past the end of the buffer — a heap buffer overflow.
The fix computes auto escapedLength = 1 + CheckedUint32 { stringLength } * 6 + 1 and, if escapedLength.hasOverflowed(), calls recordBufferFull() and returns (aborting the fast path); otherwise it checks hasRemainingCapacity(escapedLength.value()).
The restored invariant is that the worst-case escaped-length capacity computation cannot overflow undetected.
Attack Path
- Build a huge string Create a string long enough that stringLength*6 overflows 32 bits (~715M+ characters).
- Call JSON.stringify Stringify a value containing that string, entering the fast stringifier’s append path.
- Overflow the capacity check The 1 + stringLength*6 + 1 computation wraps to a small value, so the capacity check wrongly reports room.
- Overflow the buffer The escaped output is written past the buffer end — a heap buffer overflow in WebContent.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
FastStringifier::appendSource/JavaScriptCore/runtime/JSONObject.cpp |
modified | Computes the worst-case escaped length with CheckedUint32 and aborts (recordBufferFull) on overflow before the hasRemainingCapacity check, preventing an under-sized capacity check. |
Files Changed
JSTests/stress/fast-stringifier-check-string-length.jsSource/JavaScriptCore/runtime/JSONObject.cpp
Audit Directions
- Same file: capacity mathAudit FastStringifier hasRemainingCapacity call sites and other *6 / worst-case length computations in JSONObject.cpp for overflow-safe arithmetic.
- Preallocated-buffer writersGrep for buffer-capacity checks multiplying a length by a constant expansion factor without Checked arithmetic.