Skip to content

Commit

Permalink
fix typo
Browse files Browse the repository at this point in the history
  • Loading branch information
iamelli0t committed Apr 20, 2021
1 parent 2efbed4 commit 3d7f9fa
Showing 1 changed file with 16 additions and 13 deletions.
29 changes: 16 additions & 13 deletions _posts/2021-04-20-Chromium-Issue-1196683-1195777.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ author:

On April 12, a code commit[1] in Chromium get people's attention. This is a bugfix for some vulnerability in Chromium Javascript engine v8. At the same time, the regression test case regress-1196683.js for this bugfix was also submitted. Based on this regression test case, some security researcher published an exploit sample[2]. Due to Chrome release pipeline, the vulnerability wasn't been fixed in Chrome stable update until April 13[3]. <br>

Coincidentally, on April 15, another code commit[4] of some bugfix in v8 has also included one regression test case regress-1195777.js. Based on this test case, the exploit sample was exposed again[5]. Since the latest Chrome stable version does not pull this bugfix commit, the sample can still exploit in render process of latest Chrome. When the vulnerable Chormium browser accesses a malicious link without enalbing the sandbox (--no-sandbox), the vulnerability will be triggered and caused remote code execution.<br>
Coincidentally, on April 15, another code commit[4] of some bugfix in v8 has also included one regression test case regress-1195777.js. Based on this test case, the exploit sample was exposed again[5]. Since the latest Chrome stable version does not pull this bugfix commit, the sample can still exploit in render process of latest Chrome. When the vulnerable Chormium browser accesses a malicious link without enabling the sandbox (--no-sandbox), the vulnerability will be triggered and caused remote code execution.<br>

## RCA of Issue 1196683
The bugfix for this issue is shown as follows:<br>
Expand All @@ -32,7 +32,7 @@ First, let's analyze the root cause of this vulnerability via regress-1196683.js
});
```

The foo function that triggers JIT has only one code line. Let's focus on the optimization process of (arr[0] ^ 0) + 1 in the key stage of TurboFan:<br><br>
The foo function that triggers JIT has only one code line. Let's focus on the optimization process of (arr[0] ^ 0) + 1 in the key phases of TurboFan:<br><br>
**1) TyperPhase**<br>
![avatar](/images/Chromium-Issue-1196683-1195777/2.png)<br>

Expand All @@ -46,7 +46,7 @@ The original node 32: SpeculativeNumberBitwiseXor is optimized to Word32Xor, and
**3) EarlyOptimizationPhase**<br>
![avatar](/images/Chromium-Issue-1196683-1195777/4.png)<br>

We can see that the original node 32 (Word32Xor) has been deleted and replaced with node 80 as the input of the node 110 ChangeInt32ToInt64. Now the input node (LoadTypedElement) type of ChangeInt32ToInt64 is Unsigned32.<br>
We can see that the original node 32 (Word32Xor) has been deleted and replaced with node 80 as the input of the node 110 ChangeInt32ToInt64. Now the input node (LoadTypedElement) type of ChangeInt32ToInt64 is **Unsigned32**.<br>

The v8 code corresponding to the logic is as follows:<br>
```cpp
Expand Down Expand Up @@ -97,10 +97,10 @@ function foo() {
```

## RCA of Issue 1195777
The bugfix for this issue is as follows:<br>
The bugfix for this issue is shown as follows:<br>
![avatar](/images/Chromium-Issue-1196683-1195777/6.png)<br>

This commit fixes a integer conversion node generation error which used to convert a 64-bit integer to a 32-bit integer in SimplifiedLowering phase.
This commit fixes a integer conversion node generation error which used to convert a 64-bit integer to a 32-bit integer (truncation) in SimplifiedLowering phase.
Before the commit, if the output type of current node is Signed32 or Unsigned32, the TruncateInt64ToInt32 node is generated. After the commit, if the output type of current node is Unsigned32, the type of use_info is needed to be checked next. Only when use_info.type_check() == TypeCheckKind::kNone, the TruncateInt64ToInt32 node wiill be generated.<br>

First, let's analyze the root cause of this vulnerability via regress-1195777.js:<br>
Expand All @@ -109,7 +109,7 @@ First, let's analyze the root cause of this vulnerability via regress-1195777.js
(function() {
function foo(b) {
let x = -1;
if (b) x = 0xFFFF_FFFF;
if (b) x = 0xFFFFFFFF;
return -1 < Math.max(0, x, -1);
}
assertTrue(foo(true));
Expand All @@ -120,7 +120,7 @@ First, let's analyze the root cause of this vulnerability via regress-1195777.js
})();
```

The key code in foo function which triggers JIT is 'return -1 < Math.max(0, x, -1)'. Let's focus on the optimization process of Math.max(0, x, -1) in the key stages of TurboFan:<br><br>
The key code in foo function which triggers JIT is 'return -1 < Math.max(0, x, -1)'. Let's focus on the optimization process of Math.max(0, x, -1) in the key phases of TurboFan:<br><br>

**1) TyperPhase**<br>
![avatar](/images/Chromium-Issue-1196683-1195777/7.png)<br>
Expand All @@ -134,7 +134,9 @@ The two constant parameters 0, -1 (node 54 and node 55) in Math.max(0, x, -1) ar
**3) SimplifiedLoweringPhase**<br>
![avatar](/images/Chromium-Issue-1196683-1195777/9.png)<br>

The original NumberMax node 56 and node 58 are replaced by Int64LessThan + Select nodes. The original node 41: SpeculativeNumberLessThan is replaced with Int32LessThan. When processing the input node of SpeculativeNumberLessThan, because the output type of the input node (Select) is Unsigned32, the vulnerability is triggered and the node 76: TruncateInt64ToInt32 is generated incorrectly. The result of Math.max(0, x, -1) is truncated to Signed32. Therefore, when the x in Math.max(0, x, -1) is Unsigned32, it will be truncated to Signed32 by TruncateInt64ToInt32.<br>
The original NumberMax node 56 and node 58 are replaced by Int64LessThan + Select nodes. The original node 41: SpeculativeNumberLessThan is replaced with Int32LessThan. When processing the input node of SpeculativeNumberLessThan, because the output type of the input node (Select) is Unsigned32, the vulnerability is triggered and the node 76: TruncateInt64ToInt32 is generated incorrectly.<br>

The result of Math.max(0, x, -1) is truncated to Signed32. Therefore, when the x in Math.max(0, x, -1) is Unsigned32, it will be truncated to Signed32 by TruncateInt64ToInt32.<br>

Finally, using this vulnerability, a variable x with an unexpected value 1 in JIT can be obtained via the following code (the expected value should be 0):<br>

Expand All @@ -150,10 +152,11 @@ function foo(flag){
```

## Exploit analysis
According to the above root cause analysis, we can see that the two vulnerabilities are triggered when TurboFan performs integer data type conversion (expansion, truncation). Using the two vulnerabilities, a variable x with an unexpected value 1 in JIT can be obtained. Next, according to the samples exploited in the wild, the exploit are following the steps below:<br>
**1) Create an Array with length 1 with the help of variable x which has one error value of 1;**<br>
According to the above root cause analysis, we can see that the two vulnerabilities are triggered when TurboFan performs integer data type conversion (expansion, truncation). Using the two vulnerabilities, a variable x with an unexpected value 1 in JIT can be obtained.<br>
According to the samples exploited in the wild, the exploit is following the steps below:<br><br>
**1) Create an Array which length is 1 with the help of variable x which has the error value 1;**<br>
**2) Obtain an out-of-bounds array with length 0xFFFFFFFF through Array.prototype.shift();**<br><br>
The key code is as follows:<br>
The key code is as shown follows:<br>

```javascript
var arr = new Array(x); // wrong: x = 1
Expand All @@ -165,7 +168,7 @@ return [arr, cor];
The JIT code of var arr = new Array(x) is:<br>
![avatar](/images/Chromium-Issue-1196683-1195777/10.png)<br>

Rdi is the length of arr, which value is 1. It left shift one bit (rdi+rdi) by pointer compression and stored in JSArray.length property (+0xC).<br>
Rdi is the length of arr, which value is 1. It shift left one bit (rdi+rdi) by pointer compression and stored in JSArray.length property (+0xC).<br>

The JIT code of arr.shift() is:<br>
![avatar](/images/Chromium-Issue-1196683-1195777/11.png)<br>
Expand All @@ -178,7 +181,7 @@ The array length assignment operation is mainly composed of node 152 and node 15

(2)LoadEliminationPhase<br>
![avatar](/images/Chromium-Issue-1196683-1195777/13.png)<br>
Since the value of x which collected during the execution of Ignition is 0, constant folding (0-1=-1) happens here to get the constant 0xFFFFFFFF. After left shift one bit, it is 0xFFFFFFFE, which is stored in Array.length (+0xC). Thus, an out-of-bounds array with a length of 0xFFFFFFFF is obtained.<br>
Since the value of x which collected by Ignition is 0, constant folding (0-1=-1) happens here to get the constant 0xFFFFFFFF. After shift left one bit, it is 0xFFFFFFFE, which is stored in Array.length (+0xC). Thus, an out-of-bounds array with a length of 0xFFFFFFFF is obtained.<br>

After the out-of-bounds array is obtained, the next steps are common:<br>
**3) Realize addrof/fakeobj with the help of this out-of-bounds array;**<br>
Expand Down

0 comments on commit 3d7f9fa

Please sign in to comment.