DUMPLING: Fine-grained Differential JavaScript Engine Fuzzing

Liam Wachter
Julian Gremminger
Christian Wressnegger
Mathias Payer
Flavio Toffalini
Asymmetric Research
KIT
EPFL
RUB

V8 Execution Tiers

Even confusing -0.0 with +0.0 is enough for RCE [Röt18]

VM State


 0 : 01 0d e8 ff ff 1f LdaSmi.ExtraWide [536870888]
 6 : c5                Star0
 7 : 13 00             LdaConstant [0]
 9 : c2                Star3
10 : 2d f6 01 00       GetNamedProperty r3, [1], [0]
14 : c3                Star2
15 : 5e f7 f6 f9 02    CallProperty1 r2, r3, r0, [2]
20 : c4                Star1
21 : 2d f8 02 04       GetNamedProperty r1, [2], [4]
25 : c3                Star2
26 : 13 03             LdaConstant [3]
28 : c1                Star4
29 : 5f f7 f8 f5 f9 06 CallProperty2 r2, r1, r4, r0, [6]
35 : aa                Return
          

JIT Compilation

JIT Compilation

Compare VM states from unoptimized execution (left) to optimized execution (right).

Overview

Traces

Execution traces even during JIT

Matching

Matching algorithm to compare traces

Dumpling

Differential Fuzzer using our bug oracle

V8 Bugs

Evaluation and 8 new V8 bugs

State Extraction

State Extraction: JIT

  • State is spread accross machine registers and stack
  • How do we get back to state comparable to interpreter execution?
  • Where is state extraction possible?
  • No influence on JS execution semantics and JIT compiler optimizations

Deoptimization Points

function f(o1, o2) {
      return o1.a * o2.a;
  }
  • Deopt points guard usage of specualtive assumption
  • JIT tracks context to restore VM state at deopt points
  • → Deopt points as natural probing positions for interesting state

Dumping during speculative JIT execution

  1. Save state
  2. Build VM state
  3. Rematerialize escaped values
  4. "Dump" VM state
  5. Restore state and continue JIT execution

→ partial use of existing deopt mechanism

State Extraction: Dumpling mode - Interpreter

  • Optimized run reports dump locations to the fuzzer
  • Hook bytecode execution and extract state at those dump locations

State Serialization

-------TurboFan frame dump-------
pc: 7
acc: 13.37
a0: <Object>{
__proto__: <Class C7>{<String[1]: f>[enumerable]<JSArray>[]},
<String[1]: a>[configurable][enumerable]42(enum cache: 2),
<String[1]: f>[configurable][enumerable]13.37(enum cache: 0)
}
r0: -INFINITY
context: <ScriptContext[4]>
receiver: <JSGlobalProxy>
closure: <JSFunction f0>
Function ID: 27
  • Invariant across execution tiers
  • Fine-grained and in-depth
  • Concise to minimize transmission overhead

Differential Oracle

  • No 1:1 mapping of dumps
  • Any JIT dump must have an interpreter equivalent in the same function invocation

Evaluation: Overhead

Fuzzer Fuzzilli JIT-Picker FuzzJIT Dumpling
Executions 63,775,062 99,240,042 61,434,736 51,535,553

Bugs

Found 8 new V8 bugs 🎉

Bug Id Kind Status Changes By Description
CR41488094 Diff fixed +28/-23 D, J Enumerating properties eagerly, has incorrect side effect
CR335310000 Diff fixed +15/0 D Property not marked as enumerable by Maglev and TurboFan
CR332745405 Diff fixed +5/0 D DefineOwnProperty called the setter of an existing accessor property
CR329330868 assert dup N/A D, J array.shift does not update pointers in spill slots
CR41484971 Diff fixed +52/-40 D Store inline cache handlers are incorrectly used for defining properties
V8:14605 Diff fixed +1/-1 D The JumpLoop bytecode does not clobber the accumulator in all cases
CR345960102 Diff fixed +6/-4 D TurboFan incorrectly optimizes 64 bit BigInt shifts
CR346086168 Diff fixed +109/-107 D Overflow in BigInt64 shift optimization
V8:14556 Diff available N/A D The arguments array is handled differently in optimizing compilers
CR40945996 assert dup N/A D The profiler in Maglev interferes with deoptimization

Case Study


function A() {
    Object.defineProperty(this, "x", { writable: true, configurable: true, value: undefined });
}

class B extends A {
    x = {};
}

for (let i = 0; i < 100; i++) {
    new B();
}
        
        

Here not "visible", but already detectable by Dumpling

Other fuzzers need generate something like


let b = new B();
console.log(b.propertyIsEnumerable("x"));
        
        

optimizations enabled: "true", optimizations disabled: "false"

Conclusion

Key Problem

Find differentials between JS engine execution tiers automatically

Dumpling

Extract VM states during runtime and compare between JIT and interpreter

Leveraging deoptimization points, a mechanism already implemented in JS engines

Result

Find bugs before they become "visible"

Questions?

Find our artifact here: github.com/two-heart/dumpling-artifact-evaluation

@95p@mastodon.cloud

@NearBeteigeuze

liam@seine.email

Bibliography

[GSV22] Jakob Gruber, Leszek Swirski, and Toon Verwaest. Maglev. 2022. url: https:// docs.google.com/document/d/13CwgSL4yawxuYg3iNlM-4ZPCB8RgJya6b8H_ E2F-Aek/ (visited on 11/28/2023).
[Röt18] Stephen Röttger. Chrome: V8: incorrect type information on Math.expm1. 2018. url: https://crbug.com/project-zero/1710 (visited on 03/18/2024).
[Flü16] Olvier Flückiger. Ignition: V8 Interpreter. 2016. url: https://docs.google.com/document/d/11T2CRex9hXxoJwbYqVQ32yIPMh0uouUZLdyrtmMoL44 (visited on 11/20/2023).