Released July 29, 2025

CVE-2025-31273

CVSS 8.8 severity high CWE-119 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. The add path cached storage capacity and the new-entry index and bumped the live count before normalizeMapKey, which can reallocate the table — so the write landed at a stale index; reordering the bookkeeping after the side effect fixes it.

Patch Details

diff --git a/Source/JavaScriptCore/runtime/OrderedHashTableHelper.h b/Source/JavaScriptCore/runtime/OrderedHashTableHelper.h
index 8c9190faebc2..ed3de117de16 100644
--- a/Source/JavaScriptCore/runtime/OrderedHashTableHelper.h
+++ b/Source/JavaScriptCore/runtime/OrderedHashTableHelper.h
@@ -439,12 +439,6 @@ public:
         Storage* candidate = expandIfNeeded(globalObject, owner, base);
         RETURN_IF_EXCEPTION(scope, void());
 
-        Storage& candidateRef = *candidate;
-        TableSize capacity = Helper::capacity(candidateRef);
-        Entry newEntry = usedCapacity(candidateRef);
-        TableIndex newEntryKeyIndex = entryDataStartIndex(dataTableStartIndex(capacity), newEntry);
-        incrementAliveEntryCount(candidateRef);
-
         bool firstAliveEntry = result.normalizedKey.isEmpty();
         if (firstAliveEntry) [[unlikely]] {
             result.normalizedKey = normalizeMapKey(key);
@@ -452,6 +446,12 @@ public:
             RETURN_IF_EXCEPTION(scope, void());
         }
 
+        Storage& candidateRef = *candidate;
+        TableSize capacity = Helper::capacity(candidateRef);
+        Entry newEntry = usedCapacity(candidateRef);
+        TableIndex newEntryKeyIndex = entryDataStartIndex(dataTableStartIndex(capacity), newEntry);
+        incrementAliveEntryCount(candidateRef);
+
         bool rehashed = &base != candidate;
         if (rehashed || firstAliveEntry) [[unlikely]]
             result.bucketIndex = bucketIndex(capacity, result.hash);

Background

Advisory: Apple Security Advisory

Attribute Value
CVE CVE-2025-31273
Bugzilla 293579
Component JSC
Bug Class OOB
Severity medium
Commit a05032c6961001c2…
Advisory Apple Advisory

Terminology

  • OrderedHashTable — The insertion-ordered hash table backing JavaScript Map and Set in JSC.
  • normalizeMapKey — Normalizes a key (e.g. -0 to 0); for object/proxy keys it can invoke user code with side effects.
  • Rehash / reallocation — Growing the table moves its storage; indices captured before a reallocation become stale.

Root Cause Analysis

This fixes a memory-corruption bug in JavaScriptCore’s OrderedHashTable (backing Map/Set) where storage-derived indices were computed before a JS-observable side effect that can reallocate the storage. In add(), after expandIfNeeded returns the candidate storage, the pre-patch code immediately captured capacity, newEntry = usedCapacity(candidate), the new entry’s key index, and called incrementAliveEntryCount(candidate) — and only THEN, for the first alive entry, called normalizeMapKey(key). normalizeMapKey can run arbitrary JS (via valueOf/toString or a proxy) which may re-enter the engine, allocate, trigger GC, or cause the table to rehash/reallocate, invalidating the previously captured candidate/capacity/newEntry index. The subsequent write then uses a stale index into freed or moved storage — memory corruption. The fix reorders the operations: it performs the firstAliveEntry normalizeMapKey step first, and only afterward computes capacity/newEntry/newEntryKeyIndex and increments the alive count from the current candidate state. The restored invariant is that storage-derived indices and counters are computed after any step that can reallocate the storage.

Attack Path

1. Insert a key with side effects

Add to a Map/Set a key whose normalization (valueOf/toString or proxy) runs attacker JS.

2. Reallocate during normalization

From that callback, cause the table to rehash/reallocate (or trigger GC), invalidating the pre-computed candidate storage and indices.

3. Write at a stale index

The add continues using the captured capacity/newEntry index into the now-freed/moved storage.

4. Corrupt memory

The stale-index write corrupts heap memory in the WebContent process.

Impact Assessment

A memory-corruption primitive in the WebContent process reachable from ordinary Map/Set usage with a side-effecting key. Stale-index writes into reallocated hash-table storage are controllable with grooming; the advisory rates it memory corruption.

Audit Directions

Same file: order of side effects

Audit OrderedHashTableHelper for other places that capture storage pointers/indices before a call that can rehash, GC, or run user code.

Side-effecting key normalization

Grep Map/Set/WeakMap operations for cached candidate/capacity values used across normalizeMapKey / hashValue calls that may re-enter.

Changed Functions

Function File Change Note
OrderedHashTable add (index bookkeeping) Source/JavaScriptCore/runtime/OrderedHashTableHelper.h modified Moves capacity/newEntry/newEntryKeyIndex computation and incrementAliveEntryCount to AFTER the normalizeMapKey side effect, so indices reflect the possibly-reallocated storage.

Files Changed

Source Files

  • Source/JavaScriptCore/runtime/OrderedHashTableHelper.h