Attacking Client-Side JIT Compilers.Key
Total Page:16
File Type:pdf, Size:1020Kb
Attacking Client-Side JIT Compilers Samuel Groß (@5aelo) !1 A JavaScript Engine Parser JIT Compiler Interpreter Runtime Garbage Collector !2 A JavaScript Engine • Parser: entrypoint for script execution, usually emits custom Parser bytecode JIT Compiler • Bytecode then consumed by interpreter or JIT compiler • Executing code interacts with the Interpreter runtime which defines the Runtime representation of various data structures, provides builtin functions and objects, etc. Garbage • Garbage collector required to Collector deallocate memory !3 A JavaScript Engine • Parser: entrypoint for script execution, usually emits custom Parser bytecode JIT Compiler • Bytecode then consumed by interpreter or JIT compiler • Executing code interacts with the Interpreter runtime which defines the Runtime representation of various data structures, provides builtin functions and objects, etc. Garbage • Garbage collector required to Collector deallocate memory !4 A JavaScript Engine • Parser: entrypoint for script execution, usually emits custom Parser bytecode JIT Compiler • Bytecode then consumed by interpreter or JIT compiler • Executing code interacts with the Interpreter runtime which defines the Runtime representation of various data structures, provides builtin functions and objects, etc. Garbage • Garbage collector required to Collector deallocate memory !5 A JavaScript Engine • Parser: entrypoint for script execution, usually emits custom Parser bytecode JIT Compiler • Bytecode then consumed by interpreter or JIT compiler • Executing code interacts with the Interpreter runtime which defines the Runtime representation of various data structures, provides builtin functions and objects, etc. Garbage • Garbage collector required to Collector deallocate memory !6 Agenda 1. Background: Runtime Parser • Object representation and Builtins JIT Compiler 2. JIT Compiler Internals • Problem: missing type information • Solution: "speculative" JIT Interpreter 3. JIT Compiler Attack Surface Runtime • Different vulnerability categories 4. CVE-2018-4233 (Pwn2Own) Garbage Collector • Typical JIT Bug in JavaScriptCore !7 The Runtime !8 Builtins A "builtin": a function exposed to script which is implemented by the engine itself* var a = [ 1, 2, 3 ]; a.slice(1, 2); // [ 2 ] * definition for this talk !9 Builtins A "builtin": a function exposed to script which is implemented by the engine itself* var a = [ 1, 2, 3 ]; a.slice(1, 2); // [ 2 ] Engine can implement builtins in various ways: in C++, in JavaScript, in assembly, in its JIT compiler IL (v8 turbofan builtins), ... * definition for this talk !10 Builtins var a = [ 1, 2, 3 ]; a.slice(1, 2); // [ 2 ] EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec) { // https://tc39.github.io/ecma262/#sec-array.prototype.slice VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); ...; !11 Builtins Builtins historically the source of many bugs • Unexpected callbacks var a = [ 1, 2, 3 ]; • Integer related issues a.slice(1, 2); • Use-after-frees (missing GC rooting) // [ 2 ] • ... EncodedJSValue JSC_HOST_CALL arrayProtoFuncSlice(ExecState* exec) { // https://tc39.github.io/ecma262/#sec-array.prototype.slice VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); ...; !12 var a = 42; JSValues a = "foo"; var o = {}; a = {}; o.a = 42; o.a = "foo"; o.a = {}; • JavaScript is dynamically typed => Type information stored in runtime values, not compile time variables • Challenge: efficiently store type information and value information together • Solution: clever hacks to fit both into 8 bytes (a single CPU register) !13 JSValues • Common approaches: NaN-boxing and pointer tagging • For this talk we'll use the pointer tagging scheme from v8: • 1-bit cleared: it's a "SMI", a SMall Integer (32 bits) • 1-bit set: it's a pointer to some object, can be dereferenced 0x0000004200000000 0x00000e0359b8e611 1-bit cleared => a SMI 1-bit set => a pointer to an object located Payload in the upper 32 bits (0x42) at address 0x00000e0359b8e610 !14 JSObjects var p1 = { x: 0x41, y: 0x42 }; !15 JSObjects var p1 = { x: 0x41, y: 0x42 }; Object 1 map<String, JSValue> or similar - properties: "x" -> 0x41 "y" -> 0x42 ??? !16 JSObjects var p1 = { x: 0x41, y: 0x42 }; Object 1 map<String, JSValue> or similar - properties: "x" -> 0x41 "y" -> 0x42 ??? !17 JSObjects Idea: separate property names from property values Shape* object stores property names and their location in the object var o = { x: 0x41, y: 0x42 }; * Abstract name used for this talk, does not refer to a specific implementation !18 JSObjects Idea: separate property names from property values Shape* object stores property names and their location in the object var o = { x: 0x41, Object 1 y: 0x42 }; - properties: "x" -> 0x41 "y" -> 0x42 * Abstract name used for this talk, does not refer to a specific implementation !19 JSObjects Idea: separate property names from property values Shape* object stores property names and their location in the object var o = { Shape 1 x: 0x41, Object 1 y: 0x42 - properties: }; "x" -> slot 0 "y" -> slot 1 - properties: Object 1 "x" -> 0x41 "y" -> 0x42 - shape - slots: 0: 0x41 * Abstract name used for this talk, does 1: 0x42 not refer to a specific implementation !20 Benefit: Shape Sharing Shape 1 - properties: var o1 = { "x" -> slot 0 o1 x: 0x41, "y" -> slot 1 y: 0x42 - shape }; - slots: 0: 0x41 1: 0x42 !21 Benefit: Shape Sharing Shape 1 - properties: var o1 = { "x" -> slot 0 o1 x: 0x41, "y" -> slot 1 y: 0x42 - shape }; - slots: var o2 = { 0: 0x41 x: 0x1337, 1: 0x42 o2 y: 0x1338 }; - shape - slots: 0: 0x1337 1: 0x1338 !22 Benefit: Shape Sharing Shape 1 Shape is shared between similar objects! - properties: var o1 = { "x" -> slot 0 o1 x: 0x41, "y" -> slot 1 y: 0x42 - shape }; - slots: var o2 = { 0: 0x41 x: 0x1337, 1: 0x42 o2 y: 0x1338 }; - shape - slots: 0: 0x1337 1: 0x1338 !23 Benefit: Shape Sharing var o1 = { o1 ??? x: 0x41, y: 0x42 - shape }; - slots: var o2 = { 0: 0x41 x: 0x1337, 1: 0x42 o2 y: 0x1338 2: 0x43 }; - shape o1.z = 0x43; - slots: 0: 0x1337 1: 0x1338 !24 Benefit: Shape Sharing Shapes are immutable so Shape 2 a new Shape is created! - properties: "x" -> slot 0 var o1 = { "y" -> slot 1 o1 x: 0x41, "z" -> slot 2 Shape 1 y: 0x42 - shape }; - slots: - properties: var o2 = { 0: 0x41 "x" -> slot 0 "y" -> slot 1 x: 0x1337, 1: 0x42 o2 y: 0x1338 2: 0x43 }; - shape o1.z = 0x43; - slots: 0: 0x1337 1: 0x1338 !25 Benefit: Shape Sharing Shape 2 - properties: "x" -> slot 0 var o1 = { "y" -> slot 1 o1 x: 0x41, "z" -> slot 2 y: 0x42 - shape }; - slots: var o2 = { 0: 0x41 o2 x: 0x1337, 1: 0x42 y: 0x1338 2: 0x43 - shape }; - slots: o1.z = 0x43; 0: 0x1337 o2.z = 0x1339; 1: 0x1338 2: 0x1339 !26 Object Example: v8 var o = { x: 0x41, y: 0x42 }; o.z = 0x43; o[0] = 0x1337; o[1] = 0x1338; Underlined: v8::Map pointer Green: Inline properties Red: Out-of-line Properties Blue: Elements !27 Object Example: v8 Shape (called "Map" in v8) (lldb) x/5gx 0xe0359b8e610 var o = { 0xe0359b8e610: 0x00000e034a80d309 0x00000e0359b90601 x: 0x41, 0xe0359b8e620: 0x00000e0359b90699 0x0000004100000000 y: 0x42 0xe0359b8e630: 0x0000004200000000 }; o.z = 0x43; o[0] = 0x1337; o[1] = 0x1338; Underlined: v8::Map pointer Green: Inline properties Red: Out-of-line Properties Blue: Elements !28 Object Example: v8 Shape (called "Map" in v8) (lldb) x/5gx 0xe0359b8e610 var o = { 0xe0359b8e610: 0x00000e034a80d309 0x00000e0359b90601 x: 0x41, 0xe0359b8e620: 0x00000e0359b90699 0x0000004100000000 y: 0x42 0xe0359b8e630: 0x0000004200000000 }; o.z = 0x43; (lldb) x/3gx 0x00000e0359b90600 o[0] = 0x1337; 0xe0359b90600: 0x00000e034ee836f9 0x0000000300000000 o[1] = 0x1338; 0xe0359b90610: 0x0000004300000000 Underlined: v8::Map pointer Green: Inline properties Red: Out-of-line Properties Blue: Elements !29 Object Example: v8 Shape (called "Map" in v8) (lldb) x/5gx 0xe0359b8e610 var o = { 0xe0359b8e610: 0x00000e034a80d309 0x00000e0359b90601 x: 0x41, 0xe0359b8e620: 0x00000e0359b90699 0x0000004100000000 y: 0x42 0xe0359b8e630: 0x0000004200000000 }; o.z = 0x43; (lldb) x/3gx 0x00000e0359b90600 o[0] = 0x1337; 0xe0359b90600: 0x00000e034ee836f9 0x0000000300000000 o[1] = 0x1338; 0xe0359b90610: 0x0000004300000000 (lldb) x/4gx 0x00000e0359b90698 Underlined: v8::Map pointer 0xe0359b90698: 0x00000e034ee82361 0x0000001100000000 Green: Inline properties 0xe0359b906a8: 0x0000133700000000 0x0000133800000000 Red: Out-of-line Properties Blue: Elements !30 Summary Objects In all major engines, a JavaScript object roughly consists of: • A reference to a Shape and Group/Map/Structure/Type instance • Immutable and shared between similar objects • Stores name and location of properties, element kind, prototype, ... => "describes" the object • Inline property slots • Out-of-line property slots • Out-of-line buffer for elements • Possibly additional, type-specific fields (e.g. data pointer in TypedArrays) !31 (Speculative) JIT Compilers !32 Interpreter vs. JIT Compiler • Usually execution starts in Interpreter JIT Compiler the interpreter • After a certain number of Code Speed - + invocations a function becomes "hot" and is compiled to machine code Startup Time + - • Afterwards execution switches to the machine Memory code instead of the Footprint + - interpreter !33 Introduction How to compile this code? int add(int a, int b) { return a + b; } !34 Introduction How to compile this code? ; add(int, int): lea eax, [rdi+rsi] ret int add(int a, int b) { return a + b; Easy: } • Know parameter types • Know ABI Try this at home: https://godbolt.org/ !35 Introduction How to compile this code? function add(a, b) { return a + b; } !36 Introduction How to