CVE-2020-16009: Chrome Turbofan Type Confusion after Map Deprecation
Samuel Groß, Project Zero (Originally posted on Project Zero blog 2021-02-04)
The Basics
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:
- Project Zero: https://bugs.chromium.org/p/project-zero/issues/detail?id=2106
- Chromium: https://bugs.chromium.org/p/chromium/issues/detail?id=1143772
Patch CL: https://chromium.googlesource.com/v8/v8.git/+/3ba21a17ce2f26b015cc29adc473812247472776
Bug-Introducing CL: N/A
Reporter(s): Clément Lecigne of Google's Threat Analysis Group and Samuel Groß of Google Project Zero
The Code
Proof-of-concept: https://bugs.chromium.org/p/project-zero/issues/detail?id=2106
Exploit sample: N/A
Did you have access to the exploit sample when doing the analysis? Yes
The Vulnerability
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:
- JIT compile a function that performs a transition from
map1{a:double}
tomap2{a:double,b:tagged}
. Turbofan will assume thatmap2
can never be deprecated and will not installCodeDependencies
to deoptimize the JIT code if it is. - Trigger the bug to deprecate
map2
. This does not deoptimize the JIT code. - In-place generalize
map1.a
to typetagged
. This will not also generalizemap2
since it is deprecated. - Execute the JIT code. This will effectively transition from
map1{a:tagged}
tomap2{a:double,b:whatever}
, which is incorrect and results in a type confusion
Patch analysis: N/A
Thoughts on how this vuln might have been found (fuzzing, code auditing, variant analysis, etc.):
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, in my opinion) 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:
- https://bugs.chromium.org/p/chromium/issues/detail?id=746946
- https://bugs.chromium.org/p/project-zero/issues/detail?id=1923
Although these were related to element kinds and not property types/representations.
The Exploit
(The terms exploit primitive, exploit strategy, exploit technique, and exploit flow are defined here.)
Exploit strategy (or strategies): Still under analysis
Exploit flow: Still under analysis
Known cases of the same exploit flow: Still under analysis
Part of an exploit chain? This vulnerability was used as a Chrome RCE towards Android, but we do not know what exploits would be used after this. This vulnerability was used by the same actors during the same operation as CVE-2020-15999, CVE-2020-17087, CVE-2020-16010, CVE-2020-27930, CVE-2020-27950, and CVE-2020-27932.
The Next Steps
Variant analysis
Areas/approach for variant analysis (and why): Fuzzing or auditing the Map transition/deprecation logic.
Found variants:
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
0-day detection methods
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.