# CVE-2020-6418: Chrome incorrect side-effect modelling issue in Turbofan leading to type confusions
*Samuel Groß and Sergei Glazunov, Project Zero (Originally posted on [Project Zero blog](https://googleprojectzero.blogspot.com/p/rca.html) 2020-08-05)*

## The Basics

**Disclosure or Patch Date:** 24 February 2020

**Product:** Google Chrome

**Advisory:** https://chromereleases.googleblog.com/2020/02/stable-channel-update-for-desktop_24.html

**Affected Versions:** Google Chrome 60 - 80

**First Patched Version:** 80.0.3987.122

**Issue/Bug Report:** https://bugs.chromium.org/p/chromium/issues/detail?id=1053604

**Patch CL:** https://chromium.googlesource.com/v8/v8.git/+/fb0a60e15695466621cf65932f9152935d859447

**Bug-Introducing CL:** https://chromium.googlesource.com/v8/v8.git/+/0f716acadaed1d9e194593543dbe1340d600d6fc

**Reporter(s):** Clément Lecigne of Google's Threat Analysis Group

## The Code

**Proof-of-concept:**

```javascript
'use strict';
(function() {
  var popped;

  function trigger(new_target) {
    function inner(new_target) {
      function constructor() {
        popped = Array.prototype.pop.call(array);
      }
      var temp = array[0];
      return Reflect.construct(constructor, arguments, new_target);
    }

    inner(new_target);
  }

  var array = new Array(0, 0, 0, 0, 0);

  for (var i = 0; i < 20000; i++) {
    trigger(function() { });
    array.push(0);
  }

  var proxy = new Proxy(Object, {
    get: () => (array[4] = 1.1, Object.prototype)
  });
 
  trigger(proxy);
  print(popped);
}());
```

**Exploit sample:** N/A

**Did you have access to the exploit sample when doing the analysis?** Yes

## The Vulnerability

**Bug class:** Incorrect side-effect modelling in JIT Compiler

**Vulnerability details:**

The function [NodeProperties::InferReceiverMapsUnsafe](https://source.chromium.org/chromium/chromium/src/+/master:v8/src/compiler/node-properties.cc;drc=04ea8adc20fdf4e6dba9885781d415dcf851b153;bpv=1;bpt=1;l=337?originalUrl=https:%2F%2Fcs.chromium.org%2F) is responsible for inferring the Map of an object. From the source code documentation: "This information can be either 'reliable', meaning that the object is guaranteed to have one of these maps at runtime, or 'unreliable', meaning that the object is guaranteed to have HAD one of these maps". In the latter case, the caller has to ensure that the object has the correct type, either by using `CheckMap` nodes or `CodeDependencies`.


On a high level, the `InferReceiverMapsUnsafe` function traverses the effect chain until it finds the node creating the object in question and, at the same time, marks the result as unreliable if it encounters a node without the `kNoWrite` flag, indicating that executing the node could have side-effects such as changing the Maps of an object. There is a mistake in the handling of `kJSCreate`: if the object in question is not the output of `kJSCreate`, then the loop continues without marking the result as unreliable. This is incorrect because `kJSCreate` can have side-effects, for example by using a Proxy as third argument to `Reflect.construct`. The bug can then for example be triggered by inlining `Array.pop` and changing the elements' kind from `SMIs` to `Doubles` during the unexpected side effect.

Normally, this bug class occurs due to mis-modelling of an operation in the JIT compiler. However, this time the modelling was correct (the `JSCreate` function was correctly marked as having side effects), however, the specific piece of code relying on this data was incorrect as it had special-cased the `JSCreate` operation and then forgot to perform the side-effect modelling.

**Patch analysis:** N/A

**Thoughts on how this vuln might have been found _(fuzzing, code auditing, variant analysis, etc.)_:**

JIT side-effect related bugs are likely hard to discover through generic JavaScript engine fuzzing approaches, as, without [specific compiler instrumentation](https://github.com/mozilla/gecko-dev/commit/4ca7a9d3ee9c7fe0d432bd3d3e251238a6f71721), they don’t normally result in observable misbehaviour at runtime (i.e., a crash). Given that the vulnerable function is clearly security-critical, it is thus more likely that the bug was discovered through manual code analysis while looking for type inference related issues.

**(Historical/present/future) context of bug:** 

JIT side effect modelling issues have been found and exploited in all major engines in the past years.

This vulnerability was chained with both Windows 0-days ([CVE-2020-0938](CVE-2020-0938.md), [CVE-2020-1020](CVE-2020-1020.md), [CVE-2020-1027](CVE-2020-1027.md) and Android n-days as a part of a watering hole attack.

## The Exploit

**Is the exploit method known?** Yes

**Exploit method:**

The attacker abused the unexpected side-effect to cause type confusion between the floating-point and tagged pointer array types, which allowed them to implement the `fakeobj` primitive. `fakeobj` makes the JavaScript engine treat an arbitrary value as a pointer to a JavaScript object. The attacker constructed a “fake” typed array and used it to overwrite the machine code of a compiled WebAssembly function with their shellcode.

## The Next Steps

### Variant analysis

**Areas/approach for variant analysis (and why):**

Manual auditing for similar issues in the same function and related ones. A more generic approach would be to build compiler instrumentation to detect unexpected side effects at runtime and abort early.

**Found variants:** None

### Structural improvements

N/A. This bug seems to be somewhat unique (at least it’s quite different from all the other side-effect mis-modelling bugs we can think of) and thus probably not much can be done in terms of generic/structural improvements.

### 0-day detection methods

These types of exploits are likely hard to detect generically.

## Other References 

* January 2021: [“In The Wild: Chrome exploits”](https://googleprojectzero.blogspot.com/2021/01/in-wild-series-chrome-exploits.html) blogpost
* May 2019: ["Exploiting Logic Bugs in JavaScript JIT Engines"](http://www.phrack.org/issues/70/9.html#article) by @saelo
