CVE-2017-0152 (published 2017-07-17T13:18Z)

A remote code execution vulnerability exists in the way affected Microsoft scripting engine render when handling objects in memory in Microsoft browsers. The vulnerability could corrupt memory in such a way that an attacker could 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."

Severity: HIGH
CVSS v2 base score: 9.3 (AV:N/AC:M/Au:N/C:C/I:C/A:C)
CVSS v3 base score: 8.1, HIGH (CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-119
cwe_name: Improper Restriction of Operations within the Bounds of a Memory Buffer
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.
extended_description: 
            Certain languages allow direct addressing of memory locations and do not automatically ensure that these locations are valid for the memory buffer that is being referenced. This can cause read or write operations to be performed on memory locations that may be associated with other variables, data structures, or internal program data.
         
         

Fix information:
repo_url: https://github.com/Microsoft/ChakraCore
commit_details: repo_url: https://github.com/Microsoft/ChakraCore
author: Aneesh Divakarakurup
author_date: 2017-03-08 16:58:06-08:00
msg: [CVE-2017-0152] MSFT: 10592731 : Issue with Function name capturing in param scope

In a function expression with name, where the name is captured in one
of the param scope functions, if there is a function or var declaration
with the same name as the function expression name we were marking the
function expression name as shadowed. In non-eval case this causes
issue because the name symbol won't get added to the body. This change is to
fix it in such a way if the name is captured in the param scope then we
split the param and body scope such that the name symbol is added to the
param scope not body scope.
repository: repo_url: https://github.com/Microsoft/ChakraCore
repo_name: chakra-core/ChakraCore
description: ChakraCore is an open source Javascript engine with a C API.
repo_language: JavaScript
owner: Microsoft
file_changes: filename: Parse.cpp
change_type: ModificationType.MODIFY
diff: @@ -5198,6 +5198,19 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, usho
                         paramScope->SetCannotMergeWithBodyScope();
                     }
                 }
+                if (paramScope->GetCanMergeWithBodyScope() && !fDeclaration && pnodeFnc->sxFnc.pnodeName != nullptr)
+                {
+                    Symbol* funcSym = pnodeFnc->sxFnc.pnodeName->sxVar.sym;
+                    if (funcSym->GetPid()->GetTopRef()->GetFuncScopeId() > pnodeFnc->sxFnc.functionId)
+                    {
+                        // This is a function expression with name captured in the param scope. In non-eval, non-split cases the function
+                        // name symbol is added to the body scope to make it accessible in the body. But if there is a function or var
+                        // declaration with the same name in the body then adding to the body will fail. So in this case we have to add
+                        // the name symbol to the param scope by splitting it.
+                        paramScope->SetCannotMergeWithBodyScope();
+                    }
+                }
+
             }
         }
 

programming_language: C++
filename: ByteCodeEmitter.cpp
change_type: ModificationType.MODIFY
diff: @@ -3319,6 +3319,7 @@ void ByteCodeGenerator::EmitOneFunction(ParseNode *pnode)
         {
             // Emit bytecode to copy the initial values from param names to their corresponding body bindings.
             // We have to do this after the rest param is marked as false for need declaration.
+            Symbol* funcSym = funcInfo->root->sxFnc.GetFuncSymbol();
             paramScope->ForEachSymbol([&](Symbol* param) {
                 Symbol* varSym = funcInfo->GetBodyScope()->FindLocalSymbol(param->GetName());
                 Assert(varSym || pnode->sxFnc.pnodeName->sxVar.sym == param);
@@ -3327,7 +3328,9 @@ void ByteCodeGenerator::EmitOneFunction(ParseNode *pnode)
                 {
                     // Do not copy the arguments to the body if it is not used
                 }
-                else if (varSym && varSym->GetSymbolType() == STVariable && (varSym->IsInSlot(funcInfo) || varSym->GetLocation() != Js::Constants::NoRegister))
+                else if ((funcSym == nullptr || funcSym != param)    // Do not copy the symbol over to body as the function expression symbol
+                                                                     // is expected to stay inside the function expression scope
+                    && (varSym && varSym->GetSymbolType() == STVariable && (varSym->IsInSlot(funcInfo) || varSym->GetLocation() != Js::Constants::NoRegister)))
                 {
                     // Simulating EmitPropLoad here. We can't directly call the method as we have to use the param scope specifically.
                     // Walking the scope chain is not possible at this time.

programming_language: C++
filename: default-splitscope.js
change_type: ModificationType.MODIFY
diff: @@ -159,6 +159,22 @@ var tests = [
             return a;
         }
         assert.areEqual(10, f11()(), "Recursive call to the function from the body scope returns the right value when eval is there in the body");
+
+        function f13() {
+            var a = function jnvgfg(sfgnmj = function ccunlk() { jnvgfg(undefined, 1); }, b) {
+                if (b) {
+                    assert.areEqual(undefined, jnvgfg, "This refers to the instance in the body and the value of the function expression is not copied over");
+                }
+                var jnvgfg = 10;
+                if (!b) {
+                    sfgnmj();
+                    return 100;
+                }
+            };
+            assert.areEqual(100, a(), "After the recursion the right value is returned by the split scoped function");
+        };
+        f13();
+
     } 
  }, 
  { 

programming_language: JavaScript