# CVE-2021-30551: Chrome Type Confusion in V8

HTMLEmbedElement property interceptor runs JS mid-assignment, letting attacker add a same-named property so V8 adds a duplicate, corrupting field maps and enabling JIT type confusion.

- Payout: Real-world hack
- Severity: high
- Program: Google Chrome
- Credited: Sergei Glazunov, Project Zero
- Disclosed: 2021-06-09
- CVE: CVE-2021-30551
- Write-up quality: 9/10
- Original: https://github.com/googleprojectzero/0days-in-the-wild/blob/main/0day-RCAs/2021/CVE-2021-30551.md
- Library page: https://bugbountylibrary.com/r/p0%3ACVE-2021-30551
- Tags: domain:browser impact:rce kind:incident platform:project-zero sev:high tech:chromium technique:chained technique:source-review vuln:memory-corruption

**Target:** V8 JS engine / HTMLEmbedElement property interceptor in Chrome
**Root cause:** SetPropertyInternal runs the embed interceptor to check for read-only props, but if the interceptor defines the property on the receiver, the lookup still reports ABSENT and AddDataProperty adds a duplicate property, corrupting the object's map/descriptor state.
**Steps:**
1. Create an `embed` element with `type='text/html'` and set it as prototype of a global object.
2. Assign a property on an object whose prototype chain hits the embed interceptor; the interceptor synchronously loads a subframe executing JS.
3. Inside the interceptor, define the same-named property on the receiver and deprecate its map.
4. SetProperty then calls AddDataProperty creating a new descriptor while raw value of existing property is modified without descriptor update.
5. Field map no longer matches value, giving type confusion in TurboFan JIT.
6. Use relative read/write to corrupt a TypedArray for absolute R/W, then overwrite WASM function body in RWX region and call it.
**How found:** Likely started from crbug.com/619166 where unexpected JS execution inside interceptors during property assignment bypassed same-origin policy; audited interceptor call paths during SetProperty for state changes missed by the lookup.
**Exploitation:** Attacker triggers interceptor reentrancy to create an object whose field value mismatches its field map, defeating TurboFan load-elimination map checks. JIT-compiled code then treats a JS object as a JS array, yielding OOB read/write; escalate to absolute R/W via TypedArray corruption, overwrite WASM code with shellcode, and invoke the function.
**Escalation:** Relative R/W -> absolute R/W via TypedArray corruption -> RWX WASM body overwrite -> code execution; part of a larger exploit chain.
**Insight:** Property interceptors can reenter JS mid-assignment; look for engine code that queries a prototype interceptor then trusts the earlier ABSENT result to add a property, especially where map deprecation can desync descriptor from raw value.
