CVE-2020-16009: Chrome Turbofan Type Confusion after Map Deprecation

Posted by Samuel Groß, Project Zero (2021-02-04)

Disclosure or Patch Date: 2 November 2020

Product: Google Chrome

Advisory: https://chromereleases.googleblog.com/2020/11/stable-channel-update-for-desktop.html 

Affected Versions: 86.0.4240.111 and previous

First Patched Version: 86.0.4240.183

Issue/Bug Report:

Patch CL: https://chromium.googlesource.com/v8/v8.git/+/3ba21a17ce2f26b015cc29adc473812247472776 

Bug-Introducing CL: N/A

Proof-of-Concept: https://bugs.chromium.org/p/project-zero/issues/detail?id=2106 
Exploit Sample:
N/A

Access to the exploit sample? Yes

Reporter(s): Clement Lecigne of Google's Threat Analysis Group and Samuel Groß of Google Project Zero

Bug Class: Type Confusion

Vulnerability Details: Incorrect Map deprecation in V8, leading to type confusions

When Turbofan compiles code that performs a Map transition, it usually installs a CodeDependency so that the resulting code is deoptimized should the target Map ever be deprecated (meaning that the code should now transition to a different Map). This is done through the TransitionDependencyOffTheRecord function. This function will only install the dependency if the target Map can be deprecated, which is determined by Map::CanBeDeprecated. As shown below, CanBeDeprecated assumes that a Map storing only fields of type Double or Tagged can not be deprecated if FLAG_unbox_double_fields is false, which is the case if pointer compression is enabled (the default on x64):

bool Map::CanBeDeprecated() const {

  for (InternalIndex i : IterateOwnDescriptors()) {

    PropertyDetails details = instance_descriptors(kRelaxedLoad).GetDetails(i);

    if (details.representation().IsNone()) return true;

    if (details.representation().IsSmi()) return true;

    if (details.representation().IsDouble() && FLAG_unbox_double_fields)    <---

      return true;

    if (details.representation().IsHeapObject()) return true;

    if (details.kind() == kData && details.location() == kDescriptor) {

      return true;

    }

  }

  return false;

}

However, in certain scenarios (refer to the PoC in the Project Zero issue tracker for details), V8 would accidentally deprecate a Map containing only tagged and double properties. This bug can then be exploited when combined with the in-place field generalization mechanism. In short, the idea is to:

1. JIT compile a function that performs a transition from map1{a:double} to map2{a:double,b:tagged}. Turbofan will assume that map2 can never be deprecated and will not install CodeDependencies to deoptimize the JIT code if it is.

2. Trigger the bug to deprecate map2. This does not deoptimize the JIT code.

3. In-place generalize map1.a to type tagged. This will not also generalize map2 since it is deprecated.

4. Execute the JIT code. This will effectively transition from map1{a:tagged} to map2{a:double,b:whatever}, which is incorrect and results in a type confusion

Exploit method: Still under analysis.

How do you think you would have found this bug? It is possible to find this bug through (targeted) fuzzing, although it is quite difficult to trigger it. As such, it is also (at least equally, IMO) possible that this bug would have been found through manual analysis, since the area of the code is known to be complex and security critical.

(Historical/present/future) context of bug: The Map transition/deprecation mechanism is fairly complex and various bugs have been found in it in the past, for example:

Although these were related to element kinds and not property types/representations.

Areas/approach for variant analysis: Fuzzing or auditing the Map transition/deprecation logic

Found variants: None

Structural improvements:

  • Build targeted fuzzers for the Map transition/deprecation logic, similar to this one
  • Add a custom “sanitizer” to v8 that detects invalid Map deprecations. This can help fuzzers detect these kinds of issues earlier
  • Simplifying Map operations, e.g. by removing Map deprecations

Potential detection methods for similar 0-days: N/A, likely hard to do generically. Triggers for JavaScript engine bugs are usually hard to distinguish from legitimate (and minified) JavaScript code. Especially for this type of bug, all the trigger code does is to create objects and load or store properties from/to them.

Other references:

No comments:

Post a Comment