DUMPLING: Fine-grained Differential JavaScript Engine Fuzzing
Liam Wachter
Network and Distributed System Security (NDSS) Symposium 2025 · Day 3 · JavaScript Security
Overview
Liam Wachter's presentation at the NDSS Symposium introduced DUMPLING, a novel approach to fine-grained differential JavaScript engine fuzzing. The talk addresses a critical security challenge within modern JavaScript engines like V8, which powers Chrome-based browsers. These engines employ multiple execution tiers—an interpreter and several Just-In-Time (JIT) compilers—all of which must adhere to the exact same JavaScript semantics. Any discrepancy in how these tiers execute the same code, no matter how subtle (e.g., returning -0.0 versus +0.0 for a floating-point value), can create a differential bug. Such bugs are not merely functional quirks; they represent exploitable vulnerabilities that can lead to remote code execution (RCE) in the browser's renderer process.
Key moments
- 0:00 Introduction: JavaScript engine tiers and RCE potential
- 1:00 Understanding the Virtual Machine (VM) state
- 2:10 DUMPLING's core idea: differential VM state comparison
- 3:20 The challenge of extracting state from JIT code
- 4:00 Solution: leveraging deoptimization points for state recovery
- 6:00 Implementing the DUMPLING hook for state capture
- 7:30 Fine-grained VM state serialization and comparison
- 8:10 Evaluation results: 8 new V8 bugs, competitive performance
DUMPLING: Fine-grained Differential JavaScript Engine Fuzzing
Speakers: Liam Wachter
Conference: NDSS Symposium
YouTube: https://www.youtube.com/watch?v=SXrjuJxi32I
Overview
Liam Wachter's presentation at the NDSS Symposium introduced DUMPLING, a novel approach to fine-grained differential JavaScript engine fuzzing. The talk addresses a critical security challenge within modern JavaScript engines like V8, which powers Chrome-based browsers. These engines employ multiple execution tiers—an interpreter and several Just-In-Time (JIT) compilers—all of which must adhere to the exact same JavaScript semantics. Any discrepancy in how these tiers execute the same code, no matter how subtle (e.g., returning -0.0 versus +0.0 for a floating-point value), can create a differential bug. Such bugs are not merely functional quirks; they represent exploitable vulnerabilities that can lead to remote code execution (RCE) in the browser's renderer process.
The core problem DUMPLING aims to solve is the automatic and precise detection of these "invisible" differential bugs. Traditional fuzzing techniques often rely on observable program outputs or crashes to identify issues, making them less effective at uncovering discrepancies that only manifest as internal state differences. DUMPLING overcomes this limitation by comparing the virtual machine (VM) state at a high frequency across different execution tiers, even when optimizations are enabled. This allows for the discovery of vulnerabilities before they cause visible effects, significantly enhancing the security posture of JavaScript engines.
The significance of DUMPLING lies in its ability to transparently extract and compare the intricate internal state of a JavaScript engine during execution. By leveraging existing deoptimization points, a mechanism already present in speculative JIT compilers, DUMPLING can obtain a detailed snapshot of the VM state without altering the program's semantics or the JIT compiler's optimizations. This innovative methodology has already proven its efficacy by identifying eight new bugs in the highly tested V8 engine, demonstrating its potential to uncover a class of vulnerabilities previously difficult to detect.
Background
▶ Watch: Introduction: JavaScript engine tiers and RCE potential (0:00)
Modern JavaScript engines are complex pieces of software designed for maximum performance. To achieve this, they utilize a multi-tiered execution architecture. When a snippet of JavaScript code is executed in an engine like V8, it typically passes through several stages. Initially, the code is interpreted by the Ignition interpreter. If a function is frequently called with consistent types and ranges, it may be escalated to one of several JIT compilers, such as Sparkplug, Mark, or Turbofan. These JIT compilers apply increasing levels of optimization and speculation, translating JavaScript into highly optimized machine code.
The fundamental security premise underlying this architecture is that all these execution tiers—the interpreter and every JIT compiler—must produce semantically identical results for any given JavaScript input. This strict requirement is paramount because even minute differences can be catastrophic. For instance, if Ignition interprets a calculation to yield -0.0 while a highly optimized JIT compiler like Turbofan produces +0.0 for the exact same calculation, this subtle floating-point discrepancy can be sufficient to trigger exploitable conditions, potentially leading to remote code execution. These differential bugs are particularly insidious because they often don't cause immediate crashes or observable output differences, making them exceedingly difficult for traditional fuzzing methods to detect.
Prior work in JavaScript engine fuzzing, such as tools like Fuzili, has been highly successful in finding many types of vulnerabilities. However, these fuzzers often rely on external observations—like program crashes, assertion failures, or differences in console.log output—to identify bugs. This reliance on observable side effects means that a differential bug that only manifests as an internal state inconsistency, without immediately impacting the program's visible behavior, can easily be missed. The challenge, therefore, is to develop a more granular and internal-state-aware fuzzing technique that can pinpoint these hidden discrepancies, bridging the gap left by existing methodologies. DUMPLING was conceived to address this specific gap, providing a mechanism to automatically detect these fine-grained internal state differentials.
Key Findings
▶ Watch: DUMPLING's core idea: differential VM state comparison (2:10)
DUMPLING has demonstrated significant success in uncovering previously undetected vulnerabilities within the V8 JavaScript engine. The most prominent finding is the discovery of eight new bugs in V8, a testament to the effectiveness of its fine-grained differential fuzzing approach. These findings are particularly noteworthy because V8 is one of the most widely used and thoroughly tested JavaScript engines, benefiting from continuous internal security auditing and state-of-the-art fuzzing techniques, including differential fuzzing. The fact that DUMPLING could still identify a substantial number of new bugs underscores the unique capabilities of its methodology.
The discovered bugs were not confined to a single execution tier; DUMPLING found vulnerabilities in both the JIT engine and the interpreter. This highlights the comprehensive nature of the tool, capable of identifying inconsistencies across the entire spectrum of V8's execution pipeline. Crucially, many of these bugs were "invisible" to other state-of-the-art fuzzers. This means they did not result in immediate crashes, assertion failures, or observable differences in program output that conventional fuzzing strategies, which often rely on console.log or similar external indicators, would detect. DUMPLING's ability to catch these subtle, internal state differentials before they become externally visible is a major contribution.
Despite the inherent overhead associated with frequent state extraction and comparison, DUMPLING proved to be competitive in terms of coverage over time and execution speed when compared to other differential and non-differential fuzzers. This efficiency is vital for practical application in large-scale vulnerability discovery. The recognition of DUMPLING's impact also came from Google, which awarded bug bounties for the discovered vulnerabilities, further validating the security relevance and practical value of this research. These findings collectively establish DUMPLING as a powerful and indispensable tool for enhancing the security of JavaScript engines.
Technical Deep Dive
▶ Watch: Solution: leveraging deoptimization points for state recovery (4:00)
DUMPLING's core innovation lies in its ability to perform fine-grained differential comparison of the internal VM state across different JavaScript execution tiers. The VM state is a critical data structure representing the complete execution context of a JavaScript program at any given point. It comprises all registers, the program counter, a copy of the global context, and other relevant information. Crucially, the VM state is sufficient to resume JavaScript execution correctly at any arbitrary point in time.
The DUMPLING approach operates by running the same JavaScript program twice: once with all optimizations disabled (forcing execution through the interpreter, e.g., Ignition), and once with all optimizing JIT compilers (e.g., Sparkplug, Mark, Turbofan) enabled. The objective is to compare the VM states generated by these two execution paths at a high frequency to detect any discrepancies.
A significant technical hurdle is extracting the VM state, especially from JIT-compiled code, where the state is distributed across machine registers and the stack. The challenge is to achieve this without influencing JavaScript execution semantics or altering JIT compiler optimizations, as doing so would defeat the purpose of finding bugs in those optimizations.
DUMPLING ingeniously leverages deoptimization points, a mechanism already present in all speculative JIT compilers, including those in modern JavaScript engines. JIT compilers make assumptions during optimization (e.g., a variable o1 is always an object). To maintain correctness, they insert deoptimization points that check these assumptions. If an assumption is violated (e.g., o1 turns out to be an integer), the JIT compiler has enough context to deoptimize back to an unoptimized execution tier (like the interpreter). This deoptimization process involves restoring the complete VM state from the optimized machine code to continue execution at a specific bytecode instruction.
DUMPLING exploits this by injecting a single assembly instruction—a call to a custom "dumpling hook"—immediately after each deoptimization point. This hook performs several critical actions:
- It saves the current machine state (registers, etc.).
- It then performs the same VM state restoration that the deoptimization mechanism would have done, "rematerializing" any escaped values.
- The restored VM state is then serialized using a custom function. This serialization ensures that the state representation is invariant across different engine contexts (e.g., no C++ heap addresses) and is fine-grained enough to capture subtle differences, such as distinguishing between
-infinityand+infinity, and deeply exploring object structures. - Finally, the hook jumps back to resume the original execution flow.
This process is entirely transparent to the JavaScript execution and the JIT compiler's optimizations, as DUMPLING merely piggybacks on an existing engine mechanism. For interpreter execution, VM state extraction is simpler, as the state is typically represented as a C++ object that can be directly serialized.
Comparing the two execution traces (one from optimized, one from unoptimized execution) presents another challenge: there isn't a direct one-to-one mapping of VM state dumps. The deoptimization mechanism might jump back one, two, or three bytecode instructions. DUMPLING addresses this by observing that the VM state is complete and that for any terminating program, there cannot be duplicate VM states within a single execution path. Therefore, the comparison algorithm checks if all VM states dumped during JIT-optimized execution are present within the set of VM states dumped during interpreter execution. Any VM state found in the JIT trace that does not have an equivalent in the interpreter trace indicates a differential bug.
Despite the overhead introduced by frequent partial deoptimizations and state dumping, DUMPLING maintains competitive performance in terms of execution speed and code coverage, making it a viable and powerful tool for continuous fuzzing.
Demo / Proof of Concept
▶ Watch: Implementing the DUMPLING hook for state capture (6:00)
While the talk did not feature a live, interactive demonstration in the traditional sense, Liam Wachter presented a concrete example of a bug found by DUMPLING to illustrate its capabilities. The speaker showed a JavaScript sample, automatically generated by the fuzzer, that exposed a differential bug within the V8 engine. The specific details of the JavaScript code itself were less important than the nature of the bug it uncovered.
The crucial aspect of this proof of concept was that DUMPLING was able to detect the internal inconsistency before it became visible to the user or to other conventional fuzzers. In the presented scenario, the engine performed a wrong optimization or the interpreter had an error, leading to a discrepancy in the internal VM state. However, at that point in the program's execution, there was no observable difference in console.log output, no program crash, and no immediate external symptom that would alert a standard fuzzer.
The speaker emphasized that state-of-the-art fuzzers typically rely on such observable phenomena (e.g., querying a broken state with console.log and comparing outputs between optimized and unoptimized runs). DUMPLING's ability to catch this bug internally, purely by comparing fine-grained VM states, highlights its unique advantage in identifying these subtle, "invisible" differential vulnerabilities. This example served as a powerful testament to DUMPLING's capability to identify a class of bugs that would otherwise elude detection until they potentially manifested as more severe, exploitable issues down the line.
Defensive Implications
▶ Watch: Evaluation results: 8 new V8 bugs, competitive performance (8:10)
The findings and methodology presented by DUMPLING have significant implications for the security of JavaScript engines and other software relying on complex, multi-tiered execution environments. The primary defensive implication is that developers of JavaScript engines, such as those behind V8, SpiderMonkey (Firefox), and JavaScriptCore (Safari), should strongly consider integrating fine-grained differential fuzzing techniques, similar to DUMPLING, into their continuous integration and security testing pipelines.
Relying solely on observable program behavior (crashes, console.log output differences, assertion failures) is demonstrably insufficient for catching all classes of vulnerabilities. The "invisible" differential bugs discovered by DUMPLING highlight a blind spot in current testing methodologies. By systematically comparing the internal VM state across different execution tiers, engine developers can preemptively identify and patch inconsistencies that could otherwise be exploited for remote code execution.
Furthermore, the technique of leveraging existing deoptimization points for transparent state extraction is not specific to V8; the speaker noted that this mechanism is present in other JavaScript engines as well. This suggests that the core principles of DUMPLING are broadly applicable, allowing similar tools to be developed for other engines. This provides a robust blueprint for enhancing the security of the entire JavaScript ecosystem.
Beyond JavaScript engines, the concept of fine-grained differential testing could be extended to other systems that employ multiple, semantically equivalent execution paths (e.g., different optimization levels in compilers, various interpreters for a given language, or different implementations of a specification). Defenders should recognize that discrepancies, no matter how subtle, between these paths can be a source of exploitable vulnerabilities and invest in tools that can detect such internal inconsistencies. While cross-engine differential testing (comparing V8 to SpiderMonkey) is a separate and more complex challenge due to differing VM state details, DUMPLING's within-engine approach is a crucial step for hardening individual engine implementations.
Key Takeaways
- JavaScript Engine Consistency is Paramount: Modern JavaScript engines rely on multiple execution tiers (interpreter and various JIT compilers) that must produce identical results. Even subtle differences (e.g., floating-point discrepancies) can lead to severe security vulnerabilities like Remote Code Execution.
- "Invisible" Bugs Pose a Threat: Many differential bugs manifest as internal VM state inconsistencies without causing immediate crashes or observable output differences, making them difficult to detect with traditional fuzzing techniques.
- DUMPLING's Novel Approach: DUMPLING performs fine-grained differential fuzzing by comparing the complete internal VM state between optimized (JIT) and unoptimized (interpreter) execution paths.
- Leveraging Deoptimization Points: DUMPLING transparently extracts VM state during JIT execution by cleverly injecting a hook after existing deoptimization points, without altering program semantics or JIT optimizations.
- Proven Effectiveness: DUMPLING successfully found eight new, previously undetected bugs in the highly tested V8 JavaScript engine, leading to bug bounties from Google. These bugs affected both the JIT compilers and the interpreter.
- Enhanced Security Posture: The methodology provides a powerful way to identify a critical class of vulnerabilities early, before they become externally visible, significantly improving the security and robustness of JavaScript engines.
About the Speaker(s)
Liam Wachter presented the paper "DUMPLING: Fine-grained Differential JavaScript Engine Fuzzing" at the NDSS Symposium. Based on the technical depth and academic context of the conference, Liam Wachter is a researcher specializing in system security, particularly in the domain of vulnerability discovery and fuzzing for complex software systems like JavaScript engines. His work, as demonstrated by DUMPLING, focuses on innovative techniques to uncover subtle yet critical security flaws that are often missed by conventional testing methods.
Reviews
Dr. Zero (Offensive Security Researcher) — STRONG ACCEPT
Solid academic security research with a genuinely clever core insight: hijack existing deoptimization machinery to transparently snapshot JIT VM state without perturbing the optimizer you're trying to test. Eight new V8 bugs, Google bounties, and competitive fuzzing throughput are concrete validation. Not a 5 because the within-engine constraint is a real limitation and the bug count, while meaningful, leaves open how many are exploitable versus theoretical state divergences.
Heather Calloway (CISO) — PASS
Technically credible vulnerability research with real findings — eight bugs in V8, Google bug bounties, a novel fuzzing methodology. Nothing here translates to governance, program decisions, or operator action. This is specialist fuzzing research, and I have no basis to penalize it for that.
→ Top-rated talks at Network and Distributed System Security (NDSS) Symposium 2025
All talks from Network and Distributed System Security (NDSS) Symposium 2025