CVE-2017-0028: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: A remote code execution vulnerability exists when Microsoft scripting engine improperly accesses objects in memory. The vulnerability could corrupt memory in a way that enables an attacker to execute arbitrary code in the context of the current user. An attacker who successfully exploited the vulnerability could gain the same user rights as the current user, aka "Scripting Engine Memory Corruption Vulnerability."
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
CVSS v3 base score: 9.8
Published: 2017-07-17T13:18Z
Repository: https://github.com/Microsoft/ChakraCore
Language: C++
Files changed: lib/Parser/Parse.cpp, test/es6/lambda-params-shadow.js
Commit date: 2017-03-16 15:40:15 -0700
Commit message: [CVE-2017-0028] Fix binding of 'async' identifier in the presence of async arrow function.

Diff: diff --git a/lib/Parser/Parse.cpp b/lib/Parser/Parse.cpp
index 5db98640..683a8fdf 100644
--- a/lib/Parser/Parse.cpp
+++ b/lib/Parser/Parse.cpp
@@ -2861,10 +2861,11 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
     _Inout_opt_ BOOL* pfLikelyPattern /*= nullptr*/,
     _Out_opt_ bool* pfIsDotOrIndex /*= nullptr*/,
     _Inout_opt_ charcount_t *plastRParen /*= nullptr*/)
 {
     ParseNodePtr pnode = nullptr;
+    PidRefStack *savedTopAsyncRef = nullptr;
     charcount_t ichMin = 0;
     size_t iecpMin = 0;
     size_t iuMin;
     IdentToken term;
     BOOL fInNew = FALSE;
@@ -2913,10 +2914,17 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
             else if (m_token.tk == tkID || m_token.tk == tkAWAIT)
             {
                 isLambdaExpr = true;
                 goto LFunction;
             }
+            else if (m_token.tk == tkLParen)
+            {
+                // This is potentially an async arrow function. Save the state of the async references
+                // in case it needs to be restored. (Note that the case of a single parameter with no ()'s
+                // is detected upstream and need not be handled here.)
+                savedTopAsyncRef = pid->GetTopRef();
+            }
         }
 
         // Don't push a reference if this is a single lambda parameter, because we'll reparse with
         // a correct function ID.
         if (m_token.tk != tkDArrow)
@@ -3267,10 +3275,22 @@ LFunction :
         break;
     }
 
     pnode = ParsePostfixOperators<buildAST>(pnode, fAllowCall, fInNew, isAsyncExpr, &fCanAssign, &term, pfIsDotOrIndex);
 
+    if (savedTopAsyncRef != nullptr &&
+        this->m_token.tk == tkDArrow)
+    {
+        // This is an async arrow function; we're going to back up and reparse it.
+        // Make sure we don't leave behind a bogus reference to the 'async' identifier.
+        for (IdentPtr pid = wellKnownPropertyPids.async; pid->GetTopRef() != savedTopAsyncRef;)
+        {
+            Assert(pid->GetTopRef() != nullptr);
+            pid->RemovePrevPidRef(nullptr);
+        }
+    }
+
     // Pass back identifier if requested
     if (pToken && term.tk == tkID)
     {
         *pToken = term;
     }
diff --git a/test/es6/lambda-params-shadow.js b/test/es6/lambda-params-shadow.js
index 1e45ab40..69c07a58 100644
--- a/test/es6/lambda-params-shadow.js
+++ b/test/es6/lambda-params-shadow.js
@@ -16,10 +16,17 @@ class B extends A {
         let C = async (B) => { B };
         let D = async A => { A };
     }
 }
 let b = new B();
-if (count !== 3) {
+class async extends A {
+    constructor() {
+        super();
+        let Q = async A => { A };
+    }
+}
+let a = new async();
+if (count !== 4) {
     WScript.Echo('fail');
 }
 
 WScript.Echo('pass');

Vulnerable code: if (count !== 3) {
Fixed code:     PidRefStack *savedTopAsyncRef = nullptr;
            else if (m_token.tk == tkLParen)
            {
                // This is potentially an async arrow function. Save the state of the async references
                // in case it needs to be restored. (Note that the case of a single parameter with no ()'s
                // is detected upstream and need not be handled here.)
                savedTopAsyncRef = pid->GetTopRef();
            }
    if (savedTopAsyncRef != nullptr &&
        this->m_token.tk == tkDArrow)
    {
        // This is an async arrow function; we're going to back up and reparse it.
        // Make sure we don't leave behind a bogus reference to the 'async' identifier.
        for (IdentPtr pid = wellKnownPropertyPids.async; pid->GetTopRef() != savedTopAsyncRef;)
        {
            Assert(pid->GetTopRef() != nullptr);
            pid->RemovePrevPidRef(nullptr);
        }
    }

class async extends A {
    constructor() {
        super();
        let Q = async A => { A };
    }
}
let a = new async();
if (count !== 4) {