Released November 3, 2025

CVE-2025-43429

CVSS 4.3 severity medium CWE-119 vector CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L

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.cpp
  • Source/WTF/wtf/unicode/icu/ICUHelpers.h