
Root-cause analysis and working exploit for CVE-2026-78938, a V8 TurboFan type confusion leading to arbitrary read/write within the compressed heap. Includes crash PoC and runtime-addressed primitives.
Root-cause analysis and exploit for CVE-2026-78938, a type confusion in V8's TurboFan load elimination that was fixed in Chrome 152.0.7977.64 (V8 15.2.124.18). The bug was reported as exploited in the wild.
The exploit turns the bug into an out-of-bounds Float64Array and from there
into addrof / fakeobj and arbitrary read/write over the V8 compressed heap.
Everything past the initial write is located at runtime — there are no
hardcoded heap offsets in the R/W stage.
50597b0bb48a — "[compiler] Invalidate tracked fields on
CheckMaps with instance migration"Tested on macOS arm64 against a locally built d8 of both versions.
| file | what it is |
|---|
pocs/crash.js | Minimal trigger. SIGBUS on the vulnerable build, clean exit on the patched one. |
pocs/rw_final.js | Full exploit, stage 1: trigger + OOB array. Frozen — see "Reproducing" below. |
pocs/logic_final.js | Full exploit, stage 2: addrof/fakeobj/arbitrary R\W, loaded at runtime by stage 1. |
WRITEUP.md | Full root-cause analysis and exploitation notes. |
Build the two d8 versions (see WRITEUP.md for the exact commits and GN args),
then from the repo root:
# crash PoC: SIGBUS on vuln, clean on patched
out/vuln/d8 --allow-natives-syntax --homomorphic-ic --max-valid-polymorphic-map-count=4 pocs/crash.js
out/patched/d8 --allow-natives-syntax --homomorphic-ic --max-valid-polymorphic-map-count=4 pocs/crash.js
# full R/W: works on vuln, bails out cleanly on patched
out/vuln/d8 --allow-natives-syntax --homomorphic-ic --max-valid-polymorphic-map-count=4 pocs/rw_final.js
Expected output on the vulnerable build:
[+] OOB Float64Array len=0x7f000004
[+] after write: victim2.z = 2.5
[+] fakeobj(hn|1) typeof=number
[+] ALL PRIMITIVES VERIFIED
Two things matter:
--homomorphic-ic --max-valid-polymorphic-map-count=4 shape the polymorphic map feedback so
the migration path is taken in optimized code. Without them the trigger
quietly does nothing.pocs/rw_final.js is byte-frozen. The single corrupting write lands at
a heap-relative offset that depends on where the victim array is allocated,
which in turn depends on the script's exact bytes and path. Editing the file
(or renaming it) invalidates the baked offset; WRITEUP.md documents how to
re-calibrate. pocs/logic_final.js is loaded after the trigger fires, so
it can be edited freely.pocs/rw_final.js, for the reasons
above.This repository is for educational and defensive research purposes. The bug is patched in current Chrome; details are public in the V8 source tree.