Medium CVSS 8.8 webkit OOB 🔧 Commit mapped

Overview

Medium
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing maliciously crafted web content may lead to an unexpected process crash
ComponentJSC YARR
Bug ClassOOB
Tracker308707
Fix commite5368156542a (WebKit/WebKit) +89/-1
CWECWE-119, CWE-120 (Buffer bounds error, Buffer overflow)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedIdan Masas, DARKNAVY (@DarkNavyOrg), Anonymous working with TrendAI Zero Day Initiative, Daniel Rhea
Disclosed2026-05-11

Background

Offset vector (ovector)
The array Yarr fills with pairs of start/end string offsets, one pair for the whole match and one per capturing subpattern, that the caller reads back after a match.
m_numSubpatterns
The count of capturing subpatterns recorded for a compiled regular expression, historically used to size the offset vector as (count+1)*2.
m_offsetsSize
The authoritative number of offset slots the compiled Yarr bytecode expects, recorded on the bytecode object at compile time.
Duplicate named capture groups
An ECMAScript feature (with UnicodeSets/v mode) allowing the same group name to appear in mutually exclusive alternatives, which can change the offsets layout the compiler emits.
UnicodeSets flag
The Yarr Flags::UnicodeSets (v) mode enabling set-notation and, relevant here, duplicate named capture group semantics.

Root Cause Analysis

RegularExpression::match in Source/JavaScriptCore/yarr/RegularExpression.cpp allocates an offset vector to receive the start/end offsets of every capturing subpattern produced by a Yarr match. Before the patch it sized that vector as (d->m_numSubpatterns + 1) * 2, i.e. it assumed the number of offset slots the compiled bytecode writes is derived purely from the count of subpatterns plus one for the whole match, times two (start and end per group).

The patch replaces this with d->m_regExpByteCode->m_offsetsSize, the authoritative offsets count the bytecode compiler itself recorded. This means the old computation could under-size the offset vector relative to what the Yarr interpreter actually writes.

The added tests all use duplicate named capture groups under UnicodeSets/v flag (e.g. (?<a>x)|(?<a>y)), which strongly indicates the mismatch arises with duplicate named capture groups: when the same name is reused across alternatives, the compiler produces an offsets layout whose size is not simply (m_numSubpatterns + 1) * 2. The violated invariant is that the buffer handed to the Yarr interpreter must be at least as large as the offsets area the compiled bytecode assumes; when it is not, the interpreter writes match offsets past the end of nonReturnedOvector (the 32-element inline Vector<unsigned, 32>) or the returned vector, corrupting adjacent memory or reading/writing out of bounds.

The fix restores the invariant by sizing the offset vector from the single source of truth, m_offsetsSize, so the allocation always matches what the bytecode was compiled to use. (Inference: the exact divergence between (m_numSubpatterns + 1) * 2 and m_offsetsSize for duplicate named groups lives in the Yarr compiler, which is not part of this diff; the patch itself only shows the sizing correction in the consumer.)

Key insight
The offset vector was sized from a heuristic ((subpatterns+1)*2) that no longer matches how the Yarr compiler lays out offsets for duplicate named capture groups; the fix makes the allocation defer to the bytecode’s own recorded m_offsetsSize, the only value guaranteed to match the writer.

Attack Path

  1. Craft a duplicate-named-group regex From attacker-controlled web content (JavaScript), construct a regular expression that reuses the same capture-group name across alternatives under the UnicodeSets/v semantics, e.g. (?<a>x)|(?<a>y), or many such groups to enlarge the offsets area.
  2. Trigger the C++ RegularExpression path Cause WebKit to route the pattern through JSC::Yarr::RegularExpression::match (the WTF/WebCore-facing wrapper used internally), reaching the offset-vector sizing at line 112.
  3. Force offset-vector under-sizing Because the code computes (d->m_numSubpatterns + 1) * 2 rather than the bytecode’s real m_offsetsSize, the allocated vector is smaller than the number of offset slots the compiled bytecode writes for the duplicate-named layout.
  4. Drive a match that populates all offsets Supply an input string that matches, so the Yarr interpreter writes the full set of offset entries, spilling past the end of the under-sized inline/heap offset vector.
  5. Observe memory corruption / crash The out-of-bounds write of match offsets corrupts adjacent stack/heap memory (the inline Vector<unsigned, 32> nonReturnedOvector or its heap backing), producing an unexpected WebContent process crash; escalation beyond a crash is not demonstrated by the commit.

Impact Assessment

The primitive is an out-of-bounds write of Yarr match offsets into an under-allocated unsigned buffer; the number and values written are constrained by the compiled pattern and the matched input, so it is a comparatively weak, structured overflow rather than a freely controlled write. The commit and its Description establish only an unexpected process crash (LogicError, medium severity); no UAF or arbitrary-write primitive toward RCE is demonstrated by the diff. It is confined to the process that runs the JS/Yarr engine, i.e. the sandboxed WebContent process. Realistic escalation to code execution would require a separate, undemonstrated shaping of the adjacent memory and control over the overflowing values.

Changed Functions

FunctionChangeNotes
RegularExpression::match
Source/JavaScriptCore/yarr/RegularExpression.cpp
modified Offset-vector size now taken from d->m_regExpByteCode->m_offsetsSize instead of the derived `(d->m_numSubpatterns + 1) * 2`, fixing an under-sized buffer for patterns (notably duplicate named capture groups) whose real offsets count differs.
DuplicateNamedCaptureGroup* tests
Tools/TestWebKitAPI/Tests/JavaScriptCore/RegularExpression.cpp
added New API tests validating that duplicate named capture groups under UnicodeSets compile, match, fail-to-match, and reverse-search correctly with the corrected offset sizing.

Files Changed

  • Source/JavaScriptCore/yarr/RegularExpression.cpp
  • Tools/TestWebKitAPI/CMakeLists.txt
  • Tools/TestWebKitAPI/Tests/JavaScriptCore/RegularExpression.cpp

Audit Directions

  • Other ovector sizing in the same file/callers
    Grep RegularExpression.cpp and Yarr callers for m_numSubpatterns, (... + 1) * 2, and offsetVectorSize to find any remaining site that derives offset-vector size heuristically instead of from m_offsetsSize.
  • All consumers of Yarr::interpret / bytecode offsets
    Search for m_offsetsSize, nonReturnedOvector, and Vector<unsigned, 32>-style inline ovectors across Source/JavaScriptCore/yarr and runtime RegExp code to confirm every match entrypoint sizes buffers to the bytecode’s expectation.
  • Duplicate-named-group layout assumptions
    In the Yarr compiler (YarrPattern/YarrInterpreter/YarrJIT), audit how duplicate named capture groups affect subpattern-to-offset mapping; grep for hasNamedCaptures, duplicateNamed, m_numSubpatterns, and offset-slot allocation to ensure count-derived sizing is never reused elsewhere.

Original Bug Report

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