Released July 29, 2025

CVE-2025-31277

CVSS 8.8 severity high CWE-119CWE-120 ⚠ CISA KEV vector CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Overview

Impact:

Description: Processing maliciously crafted web content may lead to memory corruption

Researchers: Yuhao Hu, Yan Kang, Chenggang Wu, and Xiaojie Wei

Key insight. Fast contiguous-array creation skipped the isHavingABadTime() check, so it produced fast-indexed arrays exactly when the prototype’s indexed accessors make that unsafe; adding the check restores the safe fallback.

Patch Details

diff --git a/JSTests/stress/regexp-matches-array-should-respect-have-a-bad-time.js b/JSTests/stress/regexp-matches-array-should-respect-have-a-bad-time.js
new file mode 100644
index 000000000000..0cf373454d20
--- /dev/null
+++ b/JSTests/stress/regexp-matches-array-should-respect-have-a-bad-time.js
@@ -0,0 +1,13 @@
+function F0(){
+    class C4{}
+    function f5(a6,a7,a8){
+        return {__proto__:a7}
+    }
+    f5(C4,C4)
+    Object.defineProperty(C4, 8, {configurable:true, get: f5, set: f5})
+}
+for(let v11=0;v11<5;v11++){
+    v14 = ("size").match(/e/g)
+    new F0()
+    v14[41530] = v14
+}
diff --git a/Source/JavaScriptCore/runtime/JSGlobalObjectInlines.h b/Source/JavaScriptCore/runtime/JSGlobalObjectInlines.h
index bfbf2bd6512a..13c7ce8ae40e 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalObjectInlines.h
+++ b/Source/JavaScriptCore/runtime/JSGlobalObjectInlines.h
@@ -332,6 +332,9 @@ ALWAYS_INLINE JSArray* tryCreateContiguousArrayWithPattern(JSGlobalObject* globa
     if (initialLength >= MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH)
         return nullptr;
 
+    if (globalObject->isHavingABadTime()) [[unlikely]]
+        return nullptr;
+
     VM& vm = globalObject->vm();
     Structure* structure = globalObject->originalArrayStructureForIndexingType(ArrayWithContiguous);
     if (!hasContiguous(structure->indexingType())) [[unlikely]]

Background

Advisory: Apple Security Advisory

Attribute Value
CVE CVE-2025-31277
Bugzilla 291745
Component JSC
Bug Class TypeConfusion
Severity medium
Commit 716536ce98d6f8d4…
Advisory Apple Advisory

Terminology

  • Having a bad time — JSC state entered when Array/Object.prototype gets indexed properties; fast indexed array storage becomes unsafe and arrays must use ArrayStorage.
  • ArrayWithContiguous — A fast array indexing type assuming dense storage and no interfering prototype accessors.
  • RegExp matches array — A consumer that uses the fast contiguous-array creation path, exercised by the exploit.

Root Cause Analysis

This fixes a type-confusion / memory-corruption bug where JavaScriptCore’s fast contiguous-array creation ignored the global object’s ‘having a bad time’ state. When Array.prototype or Object.prototype gains indexed properties (e.g. an indexed getter/setter), JSC enters isHavingABadTime(): fast contiguous/indexed array storage is no longer safe because indexed access must consult the prototype chain, so arrays must use the slower ArrayStorage path. tryCreateContiguousArrayWithPattern is a fast path (used for example when building RegExp matches arrays) that constructs an ArrayWithContiguous array directly. Pre-patch it checked length limits but did not check isHavingABadTime(), so during a bad time it still produced a contiguous-indexing array whose storage assumptions conflict with the prototype’s indexed accessors — later indexed reads/writes treat attacker-influenced state as fast contiguous storage, a type confusion leading to memory corruption. The fix adds if (globalObject->isHavingABadTime()) return nullptr; so the caller falls back to the safe array-creation path. The restored invariant is that fast contiguous-array creation is disabled whenever the global object is having a bad time. The regression test installs an indexed accessor (entering a bad time) and creates a RegExp matches array, then writes a large index.

Attack Path

1. Enter ‘having a bad time’

Define an indexed property (getter/setter) on Array.prototype or Object.prototype so the global object enters isHavingABadTime().

2. Trigger fast array creation

Cause a RegExp match (or other consumer) to call tryCreateContiguousArrayWithPattern, which pre-patch ignored the bad-time state.

3. Obtain an inconsistent array

Receive an ArrayWithContiguous array whose storage violates the bad-time invariant.

4. Corrupt memory

Indexed access on the array is type-confused against the prototype’s indexed accessors, yielding memory corruption in WebContent.

Impact Assessment

A type-confusion memory-corruption primitive in the WebContent process, triggerable from ordinary script by poisoning a prototype and forcing a fast array creation (e.g. via RegExp). Indexing-type confusions in JSC are historically strong routes to arbitrary read/write and code execution.

Audit Directions

Fast array-creation paths

Grep JSGlobalObjectInlines and array-allocation helpers for tryCreate*Array / originalArrayStructureForIndexingType uses that omit an isHavingABadTime() guard.

Consumers of fast patterns

Audit RegExp/spread/Array builtins that build arrays via fast patterns to confirm they honor the bad-time state.

Changed Functions

Function File Change Note
tryCreateContiguousArrayWithPattern Source/JavaScriptCore/runtime/JSGlobalObjectInlines.h modified Returns nullptr when globalObject->isHavingABadTime(), forcing the safe (ArrayStorage) path instead of building a contiguous-indexing array during a bad time.

Files Changed

Source Files

  • Source/JavaScriptCore/runtime/JSGlobalObjectInlines.h

Test Files

  • JSTests/stress/regexp-matches-array-should-respect-have-a-bad-time.js

Regression Test

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

diff --git a/JSTests/stress/regexp-matches-array-should-respect-have-a-bad-time.js b/JSTests/stress/regexp-matches-array-should-respect-have-a-bad-time.js
new file mode 100644
index 000000000000..0cf373454d20
--- /dev/null
+++ b/JSTests/stress/regexp-matches-array-should-respect-have-a-bad-time.js
@@ -0,0 +1,13 @@
+function F0(){
+    class C4{}
+    function f5(a6,a7,a8){
+        return {__proto__:a7}
+    }
+    f5(C4,C4)
+    Object.defineProperty(C4, 8, {configurable:true, get: f5, set: f5})
+}
+for(let v11=0;v11<5;v11++){
+    v14 = ("size").match(/e/g)
+    new F0()
+    v14[41530] = v14
+}