ANT-2026-K6DHGAYS · webkit/webkit
heap-buffer-overflow critical
Severity Claude critical · Security research firm critical · Maintainer -
Discovered by Claude Mythos Preview
Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Trail of Bits.
ANT-2026-K6DHGAYS: Heap-buffer-overflow in FTLOperations.cpp:95
In JavaScriptCore's FTL tier, array allocation sinking (PhantomNewArrayWithButterfly) defers materialization until OSR exit. If Object.defineProperty installs an indexed accessor on Object.prototype and triggers haveABadTime() between operationMaterializeObjectInOSR and operationPopulateObjectInOSR, the Butterfly is created with a contiguous indexing shape but then populated via the ArrayStorage path. The mismatch lets putDirectIndex → setIndexQuicklyForArrayStorageIndexingType read and write past the end of the 16152-byte heap allocation. The bug is reachable from pure attacker-supplied JavaScript and reproduces deterministically (3/3 runs).
Target
Project: webkit
Location: FTLOperations.cpp:95
Technical Details
ASAN: heap-buffer-overflow, READ of size 8 at 0 bytes after a 16152-byte region. operationMaterializeObjectInOSR sizes and allocates the Butterfly assuming a contiguous layout, but a concurrent haveABadTime transition flips the global indexing mode so operationPopulateObjectInOSR treats the same buffer as ArrayStorage; the ArrayStorage header/vector layout differs, so index computations in setIndexQuicklyForArrayStorageIndexingType land outside the allocation, yielding OOB read and write.
Crash trace:
The PoC is a well-crafted JavaScript file (3582 bytes) that exploits a race condition in WebKit's JavaScriptCore FTL compiler during OSR exit object materialization. It triggers a heap-buffer-overflow by:
1. Getting a function FTL-compiled with array allocation sinking (PhantomNewArrayWithButterfly)
2. Triggering `haveABadTime()` via `Object.defineProperty` on `Object.prototype` with an indexed accessor
3. Causing an OSR exit where `operationMaterializeObjectInOSR` creates a Butterfly with contiguous layout but `operationPopulateObjectInOSR` uses it as ArrayStorage, resulting in an OOB read/write
All 3 reproduction attempts in a fresh Docker container crashed deterministically with:
- Error: `AddressSanitizer: heap-buffer-overflow`
- Location: `READ of size 8` at `0 bytes after 16152-byte region`
- Crash function: `operationPopulateObjectInOSR` at `FTLOperations.cpp:95`
- Call chain: `putDirectIndex → setIndexQuickly → setIndexQuicklyForArrayStorageIndexingType → WriteBarrierBase::get()`
- Exit code: 134 (SIGABRT from ASAN abort_on_error)
The crash is entirely in JavaScriptCore project code, is not OOM/timeout related, and reproduces with perfect determinism (3/3 runs).
Reproduction
- Warm a target function until it is FTL-compiled with array allocation sinking (PhantomNewArrayWithButterfly).
- Call Object.defineProperty on Object.prototype with an indexed accessor to trigger haveABadTime().
- Force an OSR exit so operationMaterializeObjectInOSR allocates a contiguous Butterfly but operationPopulateObjectInOSR populates it via the ArrayStorage path, causing OOB access.
[No reproducer or sanitizer output attached — request from security-cvd@anthropic.com if needed.]
Acknowledgement
This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research. Please direct questions to security-cvd@anthropic.com and reference ANT-2026-K6DHGAYS.
Reference: ANT-2026-K6DHGAYS
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure
Triage and disclosure were performed by Trail of Bits.
- Verdict
- true positive
- Severity
- critical
The change that resolved this finding.
diff --git a/JSTests/stress/ftl-osr-exit-phantom-new-array-with-butterfly-having-a-bad-time.js b/JSTests/stress/ftl-osr-exit-phantom-new-array-with-butterfly-having-a-bad-time.js
new file mode 100644
index 0000000000000..2611454cf3044
--- /dev/null
+++ b/JSTests/stress/ftl-osr-exit-phantom-new-array-with-butterfly-having-a-bad-time.js
@@ -0,0 +1,35 @@
+//@ runDefault("--jitPolicyScale=0.1")
+
+let trigger = false;
+
+function cb() {
+ if (trigger) {
+ Object.defineProperty(Array.prototype, 0, {
+ get() { return 42; }, configurable: true
+ });
+ }
+}
+noInline(cb);
+
+function collect() { gc(); }
+noInline(collect);
+
+function opt() {
+ let a = new Array(5);
+ a[0] = 1.1;
+ a[1] = 2.2;
+ a[2] = 3.3;
+ a[3] = 4.4;
+ a[4] = 5.5;
+ cb();
+ collect();
+ return a[0] + a[1] + a[2] + a[3] + a[4];
+}
+noInline(opt);
+
+for (let i = 0; i < 1000; i++)
+ opt();
+
+trigger = true;
+opt();
+gc();
diff --git a/Source/JavaScriptCore/ftl/FTLOperations.cpp b/Source/JavaScriptCore/ftl/FTLOperations.cpp
index d02c76c8578e4..4b4d59d411170 100644
--- a/Source/JavaScriptCore/ftl/FTLOperations.cpp
+++ b/Source/JavaScriptCore/ftl/FTLOperations.cpp
@@ -112,10 +112,17 @@ JSC_DEFINE_NOEXCEPT_JIT_OPERATION(operationPopulateObjectInOSR, void, (JSGlobalO
// empty JSValue is not an Int32. Contiguous is also handled here for the debug ASSERT
// in putDirectIndex that null-derefs on the empty JSValue. Write directly into the
// butterfly to preserve the indexing type.
+ //
+ // If the VM had a bad time between FTL compilation and this OSR exit, the Array was
+ // switched to SlowPutArrayStorage in operationMaterializeObjectInOSR. A hole (empty
+ // JSValue) must then be cleared in the ArrayStorage vector rather than the contiguous
+ // butterfly to match the rematerialized layout.
if (hasDouble(array->indexingType()) && value.isNumber() && std::isnan(value.asNumber())) [[unlikely]]
array->butterfly()->contiguousDouble().atUnsafe(index) = PNaN;
else if ((hasInt32(array->indexingType()) || hasContiguous(array->indexingType())) && !value) [[unlikely]]
array->butterfly()->contiguous().atUnsafe(index).setStartingValue(JSValue());
+ else if (hasAnyArrayStorage(array->indexingType()) && !value) [[unlikely]]
+ array->butterfly()->arrayStorage()->m_vector[index].clear();
else
array->putDirectIndex(globalObject, index, value);
@@ -308,7 +315,13 @@ JSC_DEFINE_NOEXCEPT_JIT_OPERATION(operationMaterializeObjectInOSR, HeapCell*, (J
}
case PhantomNewArrayWithButterfly: {
- Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(materialization->indexingType());
+ // Rematerialized butterflies are always non-ArrayStorage. However, isHavingABadTime could
+ // have become true between the FTL compilation and the rematerialization, which would have
+ // switched arrayStructureForIndexingTypeDuringAllocation to SlowPutArrayStorage for all
+ // indexing types. To avoid a layout mismatch, the original Array structure is used to
+ // rematerialize the Array initially. If we're having a bad time, the layout is switched to
+ // SlowPutArrayStorage below.
+ Structure* structure = globalObject->originalArrayStructureForIndexingType(materialization->indexingType());
Butterfly* butterfly = nullptr;
for (unsigned i = 0; i < materialization->properties().size(); ++i) {
@@ -349,6 +362,14 @@ JSC_DEFINE_NOEXCEPT_JIT_OPERATION(operationMaterializeObjectInOSR, HeapCell*, (J
butterfly->contiguous().atUnsafe(index).setStartingValue(jsNumber(sentinel));
}
+ if (globalObject->isHavingABadTime()) [[unlikely]] {
+#if ASSERT_ENABLED
+ Structure* originalStructure = globalObject->arrayStructureForIndexingTypeDuringAllocation(materialization->indexingType());
+ ASSERT(!originalStructure || hasSlowPutArrayStorage(originalStructure->indexingType()));
+#endif
+ result->switchToSlowPutArrayStorage(vm);
+ }
+
return result;
}
https://github.com/WebKit/WebKit/commit/eba64ef44de395091d66bdfdf1c2b1f0f4983c56
Dates from discovery through public reveal.
- 2026-03-24 Reported to tracker
- 2026-05-29 Patch released
- 2026-06-09 Sent to maintainer
- 2026-08-17 Publicly revealed
SHA-3-512 hash:
7dee0cf877692adbc6077f63c1523894b696da2817b42e1e84c5d5eafe515981fbabc5677aff61519a61903f6ac395009cf9d94cc2e4241a2fa38f07defbfcdb
Committed 2026-06-09 00:05 PT
Revealed 2026-08-17 13:03 PT
Verify (download preimage.json)
Show preimage JSON
{
"ant_id": "ANT-2026-K6DHGAYS",
"bug_class": "Heap-buffer-overflow",
"claude_severity": "critical",
"commit_sha": null,
"created_at": "2026-03-24T20:43:30+00:00",
"description": "In JavaScriptCore's FTL tier, array allocation sinking (PhantomNewArrayWithButterfly) defers materialization until OSR exit. If Object.defineProperty installs an indexed accessor on Object.prototype and triggers haveABadTime() between operationMaterializeObjectInOSR and operationPopulateObjectInOSR, the Butterfly is created with a contiguous indexing shape but then populated via the ArrayStorage path. The mismatch lets putDirectIndex → setIndexQuicklyForArrayStorageIndexingType read and write past the end of the 16152-byte heap allocation. The bug is reachable from pure attacker-supplied JavaScript and reproduces deterministically (3/3 runs).",
"discovered_at": null,
"location": "FTLOperations.cpp:95",
"poc_sha256": null,
"preimage_version": 1,
"project": "webkit",
"reproduction": [
"1. Warm a target function until it is FTL-compiled with array allocation sinking (PhantomNewArrayWithButterfly).",
"2. Call Object.defineProperty on Object.prototype with an indexed accessor to trigger haveABadTime().",
"3. Force an OSR exit so operationMaterializeObjectInOSR allocates a contiguous Butterfly but operationPopulateObjectInOSR populates it via the ArrayStorage path, causing OOB access."
],
"technical_details": "ASAN: heap-buffer-overflow, READ of size 8 at 0 bytes after a 16152-byte region. operationMaterializeObjectInOSR sizes and allocates the Butterfly assuming a contiguous layout, but a concurrent haveABadTime transition flips the global indexing mode so operationPopulateObjectInOSR treats the same buffer as ArrayStorage; the ArrayStorage header/vector layout differs, so index computations in setIndexQuicklyForArrayStorageIndexingType land outside the allocation, yielding OOB read and write.",
"title": "Heap-buffer-overflow in FTLOperations.cpp:95",
"vendor_severity": "critical"
}