Medium CVSS 4.3 webkit OOB 🔧 Commit mapped

Overview

Medium
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing maliciously crafted web content may lead to an unexpected Safari crash
ComponentJSC Runtime
Bug ClassOOB
Tracker316723
Fix commitd74d692503fc (WebKit/WebKit) +74/-13
CWECWE-119 (Buffer bounds error)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:L
CISA KEVNot listed
CreditedOpenAI Codex Security - Amy Burnett
Disclosed2026-08-17

Background

RegExp capture metadata
m_numSubpatterns and m_rareData record how many capture groups a RegExp has; match results index the capture array by them.
Soft-invalid RegExp
A pattern YARR fails to compile (e.g. excessive nesting); its cached metadata can become inconsistent with the compiled bytecode.
DFG NewRegExp constant folding
Strength reduction can bake a RegExp into optimized code; doing so for an invalid RegExp propagates the inconsistency.

Root Cause Analysis

This fixes stale/inconsistent RegExp capture metadata that could be used against a mismatched compiled pattern, and prevents the DFG from baking in an invalid RegExp. A RegExp caches capture metadata (m_numSubpatterns and, in m_rareData, capture-group names and named-group indices). finishCreation set this metadata, but the later compile paths (byteCodeCompileIfNecessary, compile, compileMatchOnly) re-parsed the pattern and only updated m_atom/m_specificPattern — with an ASSERT(m_numSubpatterns == pattern.m_numSubpatterns) that is compiled out in release — WITHOUT refreshing the capture metadata. For a pattern that is ‘soft invalid’ (e.g. too deeply nested so YARR fails), the RegExp can end up with capture metadata inconsistent with the bytecode actually compiled, so match results index captures using stale m_numSubpatterns / m_rareData against a differently-shaped compiled pattern — an out-of-bounds / stale-capture access. Compounding this, DFG strength reduction converted a NewRegExp to a frozen RegExp constant without checking validity, and RegExpCache cached even invalid RegExps.

The fix factors updateMetadataFromPattern() and calls it in ALL compile paths so metadata is consistently refreshed from the actually-compiled pattern (guarding m_rareData creation with if (!m_rareData)); adds if (!regExp->isValid()) guards so DFGStrengthReductionPhase does not convert to NewRegExp for an invalid RegExp and RegExpCache::lookupOrCreate does not cache an invalid RegExp (returns it uncached).

The restored invariant is that capture metadata always matches the compiled bytecode and invalid RegExps are neither cached nor constant-folded into optimized code. The regression test builds a ~34000-deep nested pattern that soft-fails, then drives the NewRegExp path to expose stale captures.

Key insight
RegExp compile paths refreshed only part of the metadata and asserted (compiled out in release) that the subpattern count was unchanged, so a soft-invalid pattern left stale capture metadata against a mismatched bytecode; consistently refreshing metadata and refusing to cache/constant-fold invalid RegExps fixes it.

Attack Path

  1. Create a soft-invalid RegExp Construct a pathologically nested pattern that YARR fails to compile (‘soft invalid’) so its cached capture metadata can diverge from the compiled bytecode.
  2. Constant-fold or reuse it Get the DFG to convert a NewRegExp to a frozen constant, or the RegExpCache to hand back the invalid RegExp.
  3. Match with stale captures Run the RegExp so match construction uses stale m_numSubpatterns / m_rareData against the mismatched compiled pattern.
  4. Out-of-bounds captures The stale capture count/indices drive an out-of-bounds or type-confused capture-array access, crashing the WebContent process.

Impact Assessment

A memory-safety bug in the WebContent process where stale RegExp capture metadata drives out-of-bounds/type-confused capture accesses, reachable from crafted regexes. The advisory rates it a crash; capture-array OOB is a controllable primitive that can be developed further.

Changed Functions

FunctionChangeNotes
RegExp::updateMetadataFromPattern (+ compile paths)
Source/JavaScriptCore/runtime/RegExp.cpp
modified Factors metadata refresh (m_atom, m_specificPattern, m_numSubpatterns, m_rareData capture names) and calls it in finishCreation, byteCodeCompileIfNecessary, compile and compileMatchOnly so capture metadata always matches the compiled pattern (guarding m_rareData with if (!m_rareData)).
strengthReduction NewRegExp handling
Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp
modified Skips convertToNewRegExp when !regExp->isValid(), so an invalid RegExp is not frozen into optimized code.
RegExpCache::lookupOrCreate
Source/JavaScriptCore/runtime/RegExpCache.cpp
modified Returns an invalid RegExp without caching it (if (!regExp->isValid()) return regExp).

Files Changed

  • JSTests/stress/new-regexp-untyped-soft-invalid-stale-captures.js
  • Source/JavaScriptCore/dfg/DFGStrengthReductionPhase.cpp
  • Source/JavaScriptCore/runtime/RegExp.cpp
  • Source/JavaScriptCore/runtime/RegExp.h
  • Source/JavaScriptCore/runtime/RegExpCache.cpp

Audit Directions

  • Metadata refresh completeness
    Audit RegExp compile/recompile paths to ensure all capture metadata (m_numSubpatterns, m_rareData) is refreshed together, not partially with an ASSERT.
  • Invalid-object propagation
    Grep for isValid() checks (or their absence) wherever a RegExp is cached, frozen, or constant-folded into DFG/FTL code.

Original Bug Report

The reporter's bug is still restricted on the tracker.