CVE-2026-64757
Overview
Background
- ScopedArgumentsTable
- JSC metadata mapping arguments indices to scope offsets, with a parallel watchpoint-set array; both must have the same length.
- Length vs buffer size
- A separate length field can drift from the real allocation size, so indexing by length can exceed the buffer.
- CachedTypes (bytecode cache)
- Serializes JSC structures like ScopedArgumentsTable; a length/buffer mismatch there decodes into an OOB.
Root Cause Analysis
This fixes an out-of-bounds access caused by a length field that could desynchronize from the backing buffer in JSC’s ScopedArgumentsTable (and its bytecode-cache serialization). ScopedArgumentsTable stored a separate m_length alongside m_arguments (the scope-offset array) and a parallel m_watchpointSets array; various operations (tryCreate, tryClone, trySetLength) and the CachedScopedArgumentsTable encode/decode used m_length to size and index m_arguments. If m_length and the actual m_arguments buffer size diverged — e.g. an allocation grew one but not the other, or the cached m_length did not match the real arguments array — code indexing m_arguments by m_length would read or write past the buffer, an out-of-bounds access (memory corruption / crash).
The fix removes the redundant m_length and derives the length from m_arguments.size(), makes m_arguments and m_watchpointSets growable together via checked tryGrow (both grown to the same new length, failing atomically), and uses spans for encode/decode (m_arguments.span().data() / mutableSpan().data()) so the serialized length always matches the buffer. tryClone now copies m_arguments/m_watchpointSets wholesale, and trySetLength grows both arrays consistently.
The restored invariant is that the arguments length is always exactly the buffer size and the two parallel arrays stay the same length, so no index derived from length can exceed the allocation. INFERENCE: the exact trigger that desynchronized length and buffer is not shown in this hunk; the patch removes the separate length field that made the desync possible.
Attack Path
- Use scoped arguments Run JS with functions whose arguments object is captured by a scope, creating a ScopedArgumentsTable.
- Desynchronize length and buffer Through resize/clone/serialization, get m_length to differ from the actual m_arguments buffer size.
- Index past the buffer An access using the stale length reads/writes beyond the m_arguments allocation.
- Out-of-bounds / corruption The OOB access corrupts or crashes the WebContent process.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
ScopedArgumentsTable::tryCreate / tryClone / trySetLengthSource/JavaScriptCore/runtime/ScopedArgumentsTable.cpp |
modified | Removes the separate m_length, derives length from m_arguments.size(), and grows m_arguments and m_watchpointSets together with checked tryGrow so the two parallel arrays and the length stay consistent. |
CachedScopedArgumentsTable::encode / decodeSource/JavaScriptCore/runtime/CachedTypes.cpp |
modified | Uses m_arguments.size() and span data for serialization so the encoded length matches the actual buffer, preventing an OOB on decode. |
Files Changed
Source/JavaScriptCore/runtime/CachedTypes.cppSource/JavaScriptCore/runtime/ScopedArgumentsTable.cppSource/JavaScriptCore/runtime/ScopedArgumentsTable.hSource/WTF/wtf/Vector.h
Audit Directions
- Separate length vs container sizeGrep JSC runtime structures for a stored m_length paralleling a resizable buffer; prefer deriving length from the container to avoid drift.
- Parallel-array growthAudit tryCreate/trySetLength/clone paths that grow one of several parallel arrays to confirm all are grown together with checked allocation.