Bridging the Gap: Type Confusion and Boundary Vulnerabilities Between WebAssembly and JavaScript

Black Hat Asia 2025 · Day 2 · Briefings

Overview

This talk, presented by Nang (Sakura) and Jan Hansa, delves into a critical and evolving area of browser security: vulnerabilities arising from the interaction boundary between WebAssembly (Wasm) and JavaScript (JS) within the V8 engine. The speakers, both seasoned Chrome vulnerability researchers, highlight how the rapid development and introduction of new Wasm features, such as Garbage Collection (GC) and JavaScript Promise Integration (JSPI), are creating increasingly complex interaction layers—referred to as "wrappers"—that are ripe for exploitation.

Watch on YouTube

Visual summary for Bridging the Gap: Type Confusion and Boundary Vulnerabilities Between WebAssembly and JavaScript
Visual summary for Bridging the Gap: Type Confusion and Boundary Vulnerabilities Between WebAssembly and JavaScript

Key moments

  1. 3:00 Research focus: Wasm-JS interaction boundary vulnerabilities
  2. 3:30 Grammar-based JavaScript fuzzer architecture
  3. 5:00 WebAssembly GC proposal and new security risks
  4. 6:00 First type confusion: Wasm objects in JS prototype chain
  5. 7:40 Chrome's fix for prototype chain type confusion
  6. 8:00 Second type confusion: JavaScript 'instanceof' JIT optimization

Bridging the Gap: Type Confusion and Boundary Vulnerabilities Between WebAssembly and JavaScript

Speakers: Nang (Sakura), Chrome Vulnerability Researcher; Jan Hansa, Master Student at Tsinghua University & Intern Security Researcher at Innovate

Conference: Black Hat Asia

YouTube: https://www.youtube.com/watch?v=3mBm0LuliYo

Overview

This talk, presented by Nang (Sakura) and Jan Hansa, delves into a critical and evolving area of browser security: vulnerabilities arising from the interaction boundary between WebAssembly (Wasm) and JavaScript (JS) within the V8 engine. The speakers, both seasoned Chrome vulnerability researchers, highlight how the rapid development and introduction of new Wasm features, such as Garbage Collection (GC) and JavaScript Promise Integration (JSPI), are creating increasingly complex interaction layers—referred to as "wrappers"—that are ripe for exploitation.

The research presented focuses on identifying and analyzing several patterns of vulnerabilities, predominantly type confusion and memory safety issues like use-after-free (UAF), which occur when V8 incorrectly handles Wasm objects or functions during cross-language calls and optimizations. These vulnerabilities are not merely theoretical; they have been observed in high-profile security competitions like V8 CTF and Pwn2Own, and actively exploited in the wild, underscoring Wasm's growing appeal as a target for attackers. The talk provides a comprehensive technical deep dive into the root causes, exploitation potential, and subsequent fixes for six distinct vulnerabilities discovered through their enhanced fuzzing methodologies.

Background

▶ Watch: Research focus: Wasm-JS interaction boundary vulnerabilities (3:00)

The motivation for this research stems from the observation that Wasm-related vulnerabilities are becoming a significant concern in the browser security landscape. While traditional Wasm (the Minimum Viable Product - MVP models) primarily dealt with linear memory management and numeric types, the introduction of the GC proposal has fundamentally changed its interaction model. This proposal brings object modules, STR (Struct and Array types), RTT (Runtime Type Information), and automatic garbage collection to Wasm, simplifying development but simultaneously introducing new security risks when these complex Wasm objects interact with the JavaScript runtime.

At the heart of these risks lies the "bridging layer" between Wasm and JS, which consists of JS2Wasm and Wasm2JS wrappers. These wrappers are responsible for handling crucial cross-language interactions, including imports and exports of functions and objects. As Wasm continues to evolve with features like GC and JSPI, this interaction layer grows in complexity, making it a high-risk area for vulnerabilities. The speakers emphasize that the V8 engine, which powers Chrome, must be fully prepared to handle these new Wasm GC objects and asynchronous JS operations effectively and securely.

To systematically investigate these vulnerabilities, the researchers developed and utilized a sophisticated grammar-based JavaScript fuzzer. They recognized that purely random mutations in fuzzing often lead to syntactically or semantically incorrect code, making them inefficient for discovering deep vulnerabilities. To overcome this, their fuzzer incorporates three key analysis techniques: type analysis (tracking variable types), scope analysis (ensuring correct variable lifecycle and visibility), and context analysis (identifying appropriate syntax and API calls). These analyses guide the mutation engine to generate more meaningful and valid test cases, significantly increasing the fuzzer's effectiveness in uncovering subtle cross-language interaction flaws.

Key Findings

▶ Watch: WebAssembly GC proposal and new security risks (5:00)

The research uncovered a total of six high-risk vulnerabilities, predominantly focusing on type confusion and memory safety issues at the Wasm-JS boundary in V8:

  1. Type Confusion with Wasm GC Objects in JS Prototype Chains: Discovered when Wasm GC objects were inserted into JavaScript object prototype chains, leading to incorrect type assumptions and forceful casting by V8's internal functions like HasOnlySimpleElements.
  2. Type Confusion during JIT Optimization of instanceof: Identified when V8's Just-In-Time (JIT) compiler optimized instanceof checks, leading to incorrect assumptions about the type of objects in the prototype chain, specifically failing when Wasm struct or array objects were involved.
  3. Type Confusion in Store IC Handling: A critical vulnerability where V8's Store in Cache (Store IC) mechanism incorrectly cached the type of Wasm objects despite failed property assignments, leading to out-of-bounds (OOB) access during subsequent optimized operations.
  4. Use-After-Free (UAF) in Wasm Internal Function GC: A memory safety issue triggered when an imported JavaScript function caused garbage collection, leading to a dangling pointer in WasmInternalFunction::code_ due to V8's GC failing to properly track optimized Wasm-to-JS wrappers.
  5. JSPI-related Type Confusion (WasmApiFunctionRef vs. WasmTrustedInstanceData): A vulnerability stemming from JavaScript Promise Integration (JSPI), where V8 confused internal representations of imported JS functions (WasmApiFunctionRef) with native Wasm functions (WasmTrustedInstanceData), allowing an attacker to craft fake callable objects.
  6. JSPI-related Wrapper Optimization Type Confusion: Another JSPI-related flaw where V8's optimization of wrappers based on function signatures incorrectly replaced a JS2JS wrapper (for a JS function imported into Wasm and then re-exported) with an optimized native Wasm wrapper, causing a type error and crash.

Technical Deep Dive

▶ Watch: First type confusion: Wasm objects in JS prototype chain (6:00)

The vulnerabilities identified by the researchers demonstrate a consistent pattern: V8's internal mechanisms, designed for performance and efficiency, often make assumptions about object types that break down at the complex Wasm-JS boundary.

Wasm GC Type Confusion (Prototype Chain)

The first type confusion vulnerability involved the interaction of Wasm GC objects with JavaScript's prototype chain. When a Wasm object was inserted into the prototype chain of a JavaScript array, V8's internal HasOnlySimpleElements function, responsible for type checking array elements, would iterate through the prototype chain. Critically, this function only checked if an object was a JS proxy. If it wasn't a proxy, the function blindly assumed it was a standard JavaScript object and forcefully cast it. This assumption proved false when a Wasm object was encountered, leading to a type mismatch and memory layout confusion. The fix involved modifying HasOnlySimpleElements to explicitly check for JSObject types rather than merely avoiding JSProxy types.

Wasm GC Type Confusion (instanceof)

A second type confusion bug was found within V8's JIT compilation optimization process, specifically related to JavaScript's instanceof operator. During optimization, the compiler attempts to speed up instanceof checks by calling TryBuildFastInstanceOf, which in turn uses BuildHasInPrototypeChain to determine if a target prototype exists in an object's prototype chain. The BuildHasInPrototypeChain function contained a line, prototype as JSObject, which assumed that the prototype was always a JSObject. This assumption failed when the prototype was actually a Wasm struct or array (str), leading to an incorrect cast and subsequent type confusion. The fix mirrored the previous one, hardening type checks to account for non-JS object types.

Store IC Type Confusion

This vulnerability, internally discovered by Google's ClusterFuzz, highlighted a critical flaw in V8's Store in Cache (Store IC) mechanism. Store IC optimizes property assignments by caching object types and their corresponding handlers. It operates in three states: monomorphic (single type), polymorphic (multiple types), and megamorphic (too many types, generic path). Wasm objects are designed to be opaque, meaning property assignments should ideally trigger errors. However, the problem arose because V8's UpdateCache was called before Object.SetProperty. This meant that even if a property assignment to a Wasm object failed, the Store IC would still incorrectly record the Wasm object's map.

The exploit involved a three-stage process:

  1. Training: Repeatedly invoke a function setting a property on a normal JavaScript array, causing V8 to optimize the operation and transition the IC to a monomorphic state.
  2. Caching Malicious Type: Set a property on a Wasm array. This operation fails as expected, but crucially, the UpdateCache mechanism records the Wasm object's type in the IC.
  3. Exploitation: Perform more assignments on normal JavaScript arrays, transitioning the IC to a polymorphic state where the same handler is now incorrectly used for both JS arrays and Wasm arrays. Finally, setting a property on the Wasm array again triggers the optimized, faster path designed for JavaScript arrays, resulting in an out-of-bounds (OOB) access.

The impact of this OOB access was severe. In a Wasm array's memory layout, the third 4-byte field represents its length. In contrast, for a JS array, this field represents a pointer to its elements array. When the incorrect Store IC handler executed, it unexpectedly updated the Wasm array's length field with a pointer to actual data, changing its length to an extremely large value. This allowed for almost arbitrary OOB reading and writing, providing a straightforward path to remote code execution (RCE). The fix involved hardening type checks to prevent Wasm objects from incorrectly entering optimized execution paths meant for JavaScript objects.

Use-After-Free (UAF) in Wasm Internal Function GC

This memory safety vulnerability centered on V8's garbage collection mechanism for Wasm internal functions that call imported JavaScript functions. When a JavaScript function triggering garbage collection was imported into Wasm and called, V8 initially handled it via a generic WasmToJS built-in function. However, if JIT optimization flags were enabled, V8 might recompile and optimize this function, updating the WasmInternalFunction::code_ field to point to a newly optimized WasmToJS wrapper. The critical issue arose if garbage collection was triggered again during the execution of this JavaScript function. V8's GC would scan and update object references but failed to properly track and update the code_ field of WasmInternalFunction. If the memory location referenced by this field was moved or freed during GC, the pointer would become dangling. Subsequent attempts by Wasm to execute code at this invalid or recycled memory address would lead to a use-after-free (UAF). The fix involved instructing the garbage collector to track the code_ field in WasmInternalFunction as a strong reference.

JSPI-related Type Confusion (WasmApiFunctionRef vs. WasmTrustedInstanceData)

JavaScript Promise Integration (JSPI) is a significant new feature that bridges Wasm's synchronous execution model with JavaScript's asynchronous programming model. When Wasm calls an asynchronous JS function (e.g., fetch), JSPI suspends Wasm execution, transfers control to the JS environment, and resumes Wasm once the JS promise is resolved. This complex interaction relies on two key interfaces: WasmSuspending (for importing async JS functions into Wasm) and WasmPromising (for exporting Wasm functions that might internally call async JS functions).

The vulnerability arose because V8 internally differentiates between imported JS functions, represented by WasmApiFunctionRef, and native Wasm functions, represented by WasmTrustedInstanceData. In a specific scenario where a JavaScript function was imported into Wasm, then re-exported back to JavaScript, and finally used with WasmPromising, a type confusion occurred. The WasmPromising wrapper, when preparing call arguments, mistakenly pushed a WasmTrustedInstanceData object onto the stack, while the actual call target expected a WasmApiFunctionRef object.

The impact was profound: WasmApiFunctionRef has a callable field that points to objects within the V8 heap, while WasmTrustedInstanceData has a dispatch_table_for_imports field at the same offset, which points to a separate, isolated trusted memory region. By confusing WasmTrustedInstanceData with WasmApiFunctionRef, the pointer within the trusted data region was misinterpreted as pointing into the V8 heap. This allowed attackers to carefully manipulate the V8 heap layout, craft a fake callable object, and gain a powerful exploitation primitive to manipulate execution flow. The fix involved imposing restrictions on JS functions imported into Wasm, specifically disabling the generic JS2Wasm wrapper for them when JSPI is involved.

JSPI-related Wrapper Optimization Type Confusion

The final vulnerability also related to JSPI and V8's optimization of wrappers. V8 optimizes frequently called functions by generating "typed-up" wrappers to improve parameter conversion efficiency. This optimization mechanism can affect other functions with the same function signature (parameters and return types), assuming they can share the same optimized wrapper.

The critical issue arose in a scenario where a JavaScript function was first imported into Wasm and then re-exported back to JavaScript. For such a circular import-export, V8 correctly uses a JS2JS wrapper. Concurrently, a native Wasm function with the same function signature was defined. When the native Wasm function was frequently called, it triggered V8's optimization, causing V8 to replace all wrappers with matching signatures (including the JS2JS wrapper) with the optimized typed-up wrapper designed for native Wasm functions. This mismatch caused a crash because the typed-up wrapper, designed for converting JS types to Wasm types, was incorrectly used for a JS2JS call, where the underlying handling logic is entirely different, leading to a type error and memory access violation. The fix ensured that when replacing wrappers for Wasm exported functions, V8 would not replace a wrapper if the Wasm function originally originated from an imported JavaScript function.

Demo / Proof of Concept

▶ Watch: Chrome's fix for prototype chain type confusion (7:40)

While the talk did not feature live demonstrations, the speakers provided clear explanations and illustrative screenshots of the Proof of Concept (PoC) for some of the critical vulnerabilities.

For the Store IC Type Confusion vulnerability, the speakers described how an initial Wasm object with a length of zero could, through the exploit, have its length field overwritten with a pointer to a JS array's data. This manipulation effectively transformed the Wasm array into an arbitrary out-of-bounds (OOB) read/write primitive, making remote code execution (RCE) straightforward.

Regarding the JSPI Type Confusion between WasmApiFunctionRef and WasmTrustedInstanceData, the speakers presented screenshots demonstrating the exploitation process. This involved carefully shaping the memory layout in the V8 heap using an array filled with floating-point numbers. Upon triggering the type confusion, the crafted data was interpreted as a callable object. A captured output showed the RCX register containing parts of the binary representation of the floating-point number 1.1, confirming that the controlled data was indeed being treated as a function reference, thus providing a powerful exploitation primitive.

Finally, the JSPI Wrapper Optimization Type Confusion PoC was illustrated with a code snippet. This snippet defined JavaScript and Wasm functions with identical i32 parameters, imported the JS function into Wasm, re-exported it, and then defined a native Wasm function with the same signature. By repeatedly calling the native Wasm function, the optimization mechanism was triggered, leading to the incorrect replacement of the JS2JS wrapper and ultimately causing a type error and memory access violation. These detailed PoC descriptions and visuals effectively conveyed the mechanics and impact of the discovered vulnerabilities.

Defensive Implications

▶ Watch: Second type confusion: JavaScript 'instanceof' JIT optimization (8:00)

The vulnerabilities discussed in this talk underscore several crucial defensive implications for developers, security researchers, and browser vendors alike:

  • Prioritize Strict Type Checking at Boundaries: A recurring theme across multiple vulnerabilities, especially the type confusion bugs, is the inadequacy of V8's type checks at the Wasm-JS boundary. Defenders should advocate for and implement explicit, rigorous type validation whenever data or control flow crosses language runtimes. Relying on implicit assumptions or merely checking for known "bad" types (like JSProxy) is insufficient; every non-JavaScript object type must be explicitly accounted for.
  • Understand New Wasm Features and Their Attack Surface: Features like Wasm GC and JSPI, while offering significant developmental advantages, introduce considerable complexity. Security teams need to thoroughly understand the new interaction models, memory management paradigms, and asynchronous behaviors these features bring to identify potential new attack surfaces.
  • Invest Heavily in Cross-Language Fuzzing: The success of Nang and Jan Hansa's grammar-based JavaScript fuzzer, enhanced with type, scope, and context analysis, highlights the critical role of sophisticated fuzzing techniques. Fuzzing that specifically targets the nuances of Wasm-JS interactions, including randomized calls, mutations, and the integration of new wrapper functions (like WasmPromising and WasmSuspending), is essential for uncovering deep-seated vulnerabilities that traditional JS-only or Wasm-only fuzzing would miss.
  • Strengthen Memory Management Across Language Runtimes: The use-after-free (UAF) vulnerability demonstrates that garbage collection mechanisms must correctly track all references, including optimized internal function pointers, across language boundaries. Inconsistent or incomplete tracking can lead to dangling pointers and memory corruption.
  • Scrutinize JIT Optimization Heuristics: V8's JIT compiler optimizations, while vital for performance, can introduce security flaws if their underlying assumptions are violated. The vulnerabilities related to instanceof and wrapper sharing based on function signatures show that optimization heuristics must be carefully designed to avoid unintended type confusions. Code generation and optimization pipelines require robust type validation at every stage.
  • Maintain Strong Isolation Boundaries: The revelation that WasmTrustedInstanceData points to a trusted memory region separate from the V8 heap underscores the importance of strong isolation. However, the type confusion vulnerability showed how misinterpreting object types could effectively bypass this isolation, making a pointer within the trusted region appear to point into the exploitable V8 heap. This highlights the need for continuous vigilance in protecting these critical isolation boundaries.

Key Takeaways

  • The interaction boundary between WebAssembly (Wasm) and JavaScript (JS) in engines like V8 is a rapidly evolving and high-risk attack surface.
  • New Wasm features like Garbage Collection (GC) and JavaScript Promise Integration (JSPI) significantly increase the complexity of this boundary, introducing novel opportunities for exploitation.
  • Type confusion is a prevalent and dangerous vulnerability pattern at the Wasm-JS interface, often leading to powerful primitives like out-of-bounds (OOB) access and remote code execution (RCE).
  • V8's JIT compilation optimization and Store in Cache (Store IC) mechanisms, while performance-enhancing, can introduce security flaws if their underlying type assumptions are violated by cross-language interactions.
  • Sophisticated grammar-based fuzzing with targeted cross-language mutation strategies is highly effective for discovering these complex boundary vulnerabilities.
  • Defenders must prioritize explicit and robust type checking, careful memory management, and scrutiny of optimization heuristics at the Wasm-JS interface to mitigate these growing security risks.

About the Speaker(s)

Nang (Sakura) is a highly regarded security researcher specializing in Chrome vulnerability research. She has been recognized as a top researcher in the Kroom program for the past four years and achieved the second rank in Facebook's bounty program in 2023. Her expertise is frequently shared at major security conferences, including Black Hat USA and Asia, and Zer0Con.

Jan Hansa is a Master's student at Tsinghua University and an intern security researcher at Innovate. His research focuses on browser security and fuzzing techniques, and he is also recognized as a top ChromeP researcher. Jan Hansa has presented his findings at prominent security conferences such as Black Hat USA and Zer0Con, representing Innovate.

Their company, Innovate, is a cybersecurity firm dedicated to integrated offensive and defensive security solutions. They specialize in attack behavior monitoring, threat intelligence, cyber security risk management services, and security assurance through text defense simulations. Innovate aims to enhance organizational security by combining AI-driven technology with advanced analysis to provide intelligent and comprehensive security solutions for enterprises.

Reviews

Dr. Zero (Offensive Security Researcher) — MUST SEE

This talk is a critical deep dive into the most dangerous new attack surface in modern browsers: the WebAssembly-JavaScript boundary. Nang and Hansa don't just point out problems; they dissect six high-impact vulnerabilities, detailing the V8 internals, the type confusion, and the memory corruption that leads to potent RCE primitives. Their grammar-based fuzzer, tailored for cross-language interaction, is a testament to real research. This isn't just a collection of bugs; it's a foundational understanding of where browser security is headed, and how attackers are already exploiting it.

Heather Calloway (CISO) — STRONG ACCEPT

This talk delivers a critical and timely analysis of the evolving attack surface at the WebAssembly-JavaScript boundary in browser engines. While deeply technical, the speakers effectively translate complex vulnerabilities like type confusion and use-after-free into clear defensive implications, highlighting systemic risks introduced by rapid feature development. The insights are highly actionable for security leaders, emphasizing the need for rigorous type checking, advanced fuzzing, and scrutiny of optimization heuristics to mitigate significant business exposure from browser-based remote code execution.

→ Top-rated talks at Black Hat Asia 2025

All talks from Black Hat Asia 2025