CVE-2025-31277
Overview
Background
- 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
- Enter 'having a bad time' Define an indexed property (getter/setter) on Array.prototype or Object.prototype so the global object enters isHavingABadTime().
- Trigger fast array creation Cause a RegExp match (or other consumer) to call tryCreateContiguousArrayWithPattern, which pre-patch ignored the bad-time state.
- Obtain an inconsistent array Receive an ArrayWithContiguous array whose storage violates the bad-time invariant.
- Corrupt memory Indexed access on the array is type-confused against the prototype’s indexed accessors, yielding memory corruption in WebContent.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
C4JSTests/stress/regexp-matches-array-should-respect-have-a-bad-time.js |
modified | |
forJSTests/stress/regexp-matches-array-should-respect-have-a-bad-time.js |
modified |
Files Changed
JSTests/stress/regexp-matches-array-should-respect-have-a-bad-time.jsSource/JavaScriptCore/runtime/JSGlobalObjectInlines.h
Audit Directions
- Fast array-creation pathsGrep JSGlobalObjectInlines and array-allocation helpers for tryCreate*Array / originalArrayStructureForIndexingType uses that omit an isHavingABadTime() guard.
- Consumers of fast patternsAudit RegExp/spread/Array builtins that build arrays via fast patterns to confirm they honor the bad-time state.
Patch
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]]
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
+}
Regression Test / PoC
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
+}