CVE-2025-43429
Overview
Impact:
Description: Processing maliciously crafted web content may lead to an unexpected process crash
Researchers: Google Big Sleep
Key insight. JSC passed unbounded string lengths into ICU whose buffer-length math is 32-bit; the fix rejects lengths that can overflow it and honors ICU’s allocation-error status instead of only asserting grow-to-fit.
Patch Details
diff --git a/Source/JavaScriptCore/runtime/StringPrototype.cpp b/Source/JavaScriptCore/runtime/StringPrototype.cpp
index 0def0bc52472..b9b4072e0409 100644
--- a/Source/JavaScriptCore/runtime/StringPrototype.cpp
+++ b/Source/JavaScriptCore/runtime/StringPrototype.cpp
@@ -1438,6 +1438,12 @@ static JSValue normalize(JSGlobalObject* globalObject, JSString* string, Normali
if (view->is8Bit() && (form == NormalizationForm::NFC || view->containsOnlyASCII()))
RELEASE_AND_RETURN(scope, string);
+ // rdar://160634825
+ // ICU isn't able to handle large strings due to buffer length calculations potentially overflowing.
+ // We'll add a length check here to catch those cases ahead of time.
+ if (view->length() >= (1 << 30))
+ return throwOutOfMemoryError(globalObject, scope);
+
const UNormalizer2* normalizer = JSC::normalizer(form);
// Since ICU does not offer functions that can perform normalization or check for
@@ -1451,6 +1457,8 @@ static JSValue normalize(JSGlobalObject* globalObject, JSString* string, Normali
RELEASE_AND_RETURN(scope, string);
int32_t normalizedStringLength = unorm2_normalize(normalizer, characters, view->length(), nullptr, 0, &status);
+ if (isICUMemoryAllocationError(status))
+ return throwOutOfMemoryError(globalObject, scope);
ASSERT(needsToGrowToProduceBuffer(status));
std::span<char16_t> buffer;
diff --git a/Source/WTF/wtf/unicode/icu/ICUHelpers.h b/Source/WTF/wtf/unicode/icu/ICUHelpers.h
index 6a415e586a33..66525e2ef44b 100644
--- a/Source/WTF/wtf/unicode/icu/ICUHelpers.h
+++ b/Source/WTF/wtf/unicode/icu/ICUHelpers.h
@@ -61,6 +61,11 @@ constexpr bool needsToGrowToProduceCString(UErrorCode errorCode)
return needsToGrowToProduceBuffer(errorCode) || errorCode == U_STRING_NOT_TERMINATED_WARNING;
}
+constexpr bool isICUMemoryAllocationError(UErrorCode errorCode)
+{
+ return errorCode == U_MEMORY_ALLOCATION_ERROR;
+}
+
namespace CallBufferProducingFunction {
template<typename CharacterType, size_t inlineCapacity, typename ...ArgumentTypes> auto& findVector(Vector<CharacterType, inlineCapacity>& buffer, ArgumentTypes&&...)
@@ -128,4 +133,5 @@ WTF_EXPORT_PRIVATE unsigned minorVersion();
using WTF::callBufferProducingFunction;
using WTF::needsToGrowToProduceCString;
using WTF::needsToGrowToProduceBuffer;
+using WTF::isICUMemoryAllocationError;
using WTF::ICUDeleter;
Background
Advisory: Apple Security Advisory
| Attribute | Value |
|---|---|
| CVE | CVE-2025-43429 |
| Bugzilla | 298232 |
| Component | JSC |
| Bug Class | IntegerOverflow |
| Severity | medium |
| Commit | 7a45348e0e20683e… |
| Advisory | Apple Advisory |
Terminology
- String.prototype.normalize — JS API that Unicode-normalizes a string (NFC/NFD/NFKC/NFKD) via ICU.
- ICU preflight pattern — Call a converter with a zero-length buffer to learn the needed size, then allocate and call again; relies on correct length math.
- U_MEMORY_ALLOCATION_ERROR — ICU status code signalling an internal allocation failure that callers must check rather than assume grow-to-fit.
Root Cause Analysis
This fixes an integer overflow reachable through String.prototype.normalize. In StringPrototype.cpp’s normalize(), JSC calls ICU’s unorm2_normalize in a preflight/allocate/normalize pattern: it first calls with a zero-length output buffer to learn the required length (normalizedStringLength), then allocates and normalizes for real. ICU computes internal buffer lengths with 32-bit arithmetic, and for a sufficiently large input string those length calculations can overflow, so ICU either mis-sizes buffers or reports a memory-allocation error that the caller ignored (the code only ASSERTed needsToGrowToProduceBuffer(status)). The fix adds two guards: a pre-check that rejects inputs of length >= 2^30 with an OutOfMemoryError before calling ICU at all (the comment cites rdar://160634825 and the ICU overflow), and a check of the ICU status via the new isICUMemoryAllocationError(status) helper (U_MEMORY_ALLOCATION_ERROR) after the preflight call, again throwing OutOfMemoryError instead of proceeding. The restored invariant is that JSC never continues normalization when the input is large enough to overflow ICU’s length math or when ICU signals an allocation failure; it converts those conditions into a clean JS exception. The vulnerable overflow itself is inside ICU (not shown), so the precise corrupted computation is inferred, but the patch establishes the trigger (huge strings) and the missing status handling.
Attack Path
1. Build a huge string
Construct a JS string near or above 2^30 code units.
2. Call normalize()
Invoke str.normalize(form) so JSC hands the oversized length to ICU’s unorm2_normalize preflight.
3. Overflow ICU length math
ICU’s 32-bit buffer-length calculation overflows / signals allocation failure, previously unhandled beyond an ASSERT.
4. Crash the process
The mis-sized buffer or ignored error path leads to a process crash in WebContent.
Impact Assessment
A controlled process crash / denial of service in the WebContent process from a single normalize() call on a very large string; the overflow lives inside ICU’s length computation. This diff hardens the boundary rather than showing a corrupt write, so the realistic outcome established here is a crash/OOM abort rather than a demonstrated write primitive.
Audit Directions
Other ICU call sites
Grep for unorm2_, ucol_, u_str* and other ICU calls that use the preflight/grow pattern and check whether they handle U_MEMORY_ALLOCATION_ERROR (needsToGrowToProduceBuffer only).
Length preconditions
Look for JSC string operations that forward view->length() to a 32-bit C API without a size cap, especially near normalization, collation, and case mapping.
Changed Functions
| Function | File | Change | Note |
|---|---|---|---|
normalize |
Source/JavaScriptCore/runtime/StringPrototype.cpp |
modified | Adds an early length>=2^30 OutOfMemoryError guard and checks isICUMemoryAllocationError(status) after the preflight unorm2_normalize call, throwing instead of proceeding. |
isICUMemoryAllocationError |
Source/WTF/wtf/unicode/icu/ICUHelpers.h |
added | New helper returning true for U_MEMORY_ALLOCATION_ERROR, exported and used to detect ICU allocation failures. |
Files Changed
Source Files
Source/JavaScriptCore/runtime/StringPrototype.cppSource/WTF/wtf/unicode/icu/ICUHelpers.h