Selaa lähdekoodia

[1.7>master] [MERGE #3729 @suwc] 17-09 ChakraCore servicing release

Merge pull request #3729 from suwc:build/suwc/1709B_1.7

[CVE-2017-8741]: Limit JSON Stringify Loop to Initialized Portion
[CVE-2017-8748] Fix UAF caused by GC during bailout
[CVE-2017-11767] Do not instantiate param scope if only the function expression symbol is captured
[CVE-2017-8756] JIT peephole optimization error
[CVE-2017-8753] Array Reverse OOM RCE
[CVE-2017-8729] incorrect object pattern.
[CVE-2017-8739] buffer overread IsMissingItem.
[CVE-2017-8751]Type confusion casting undefined with TypeOfPrototypeObjectDictionary type
[CVE-2017-8757]RCE on Windows Insider Preview
[CVE-2017-11764]Parser::ParseCatch doesn't handle "eval"
[CVE-2017-8660] Uninitialized local variables
[CVE-2017-8755] Fail fast if we can't reparse asm.js module after linking failure
[CVE-2017-8649] Bytecode tempering mitigation code accidently turned off - Internal
[CVE-2017-8740] Fix bad byte code gen for 'with'.
[CVE-2017-8752]fix missing bound check in asm.js in case of constant negative index
Suwei Chen 8 vuotta sitten
vanhempi
sitoutus
8bf433af85

+ 23 - 8
lib/Backend/BailOut.cpp

@@ -865,12 +865,12 @@ BailOutRecord::RestoreValue(IR::BailOutKind bailOutKind, Js::JavascriptCallStack
         {
             // Register save space (offset is the register number and index into the register save space)
             // Index is one based, so subtract one
-            Js::Var * registerSaveSpace = registerSaves ? registerSaves : (Js::Var *)scriptContext->GetThreadContext()->GetBailOutRegisterSaveSpace();
+            AssertOrFailFast(registerSaves);
 
             if (isFloat64)
             {
                 Assert(RegTypes[LinearScanMD::GetRegisterFromSaveIndex(offset)] == TyFloat64);
-                dblValue = *((double*)&(registerSaveSpace[offset - 1]));
+                dblValue = *((double*)&(registerSaves[offset - 1]));
 #ifdef _M_ARM
                 BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("Register %-4S  %4d"), RegNames[(offset - RegD0) / 2 + RegD0], offset);
 #else
@@ -885,17 +885,17 @@ BailOutRecord::RestoreValue(IR::BailOutKind bailOutKind, Js::JavascriptCallStack
                     isSimd128B8 || isSimd128B16
                    )
                 {
-                    simdValue = *((SIMDValue *)&(registerSaveSpace[offset - 1]));
+                    simdValue = *((SIMDValue *)&(registerSaves[offset - 1]));
                 }
                 else if (isInt32)
                 {
                     Assert(RegTypes[LinearScanMD::GetRegisterFromSaveIndex(offset)] != TyFloat64);
-                    int32Value = ::Math::PointerCastToIntegralTruncate<int32>(registerSaveSpace[offset - 1]);
+                    int32Value = ::Math::PointerCastToIntegralTruncate<int32>(registerSaves[offset - 1]);
                 }
                 else
                 {
                     Assert(RegTypes[LinearScanMD::GetRegisterFromSaveIndex(offset)] != TyFloat64);
-                    value = registerSaveSpace[offset - 1];
+                    value = registerSaves[offset - 1];
                 }
 
                 BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u("Register %-4S  %4d"), RegNames[LinearScanMD::GetRegisterFromSaveIndex(offset)], offset);
@@ -1171,11 +1171,19 @@ uint32 bailOutOffset, void * returnAddress, IR::BailOutKind bailOutKind, Js::Imp
     // Do not remove the following code.
     // Need to capture the int registers on stack as threadContext->bailOutRegisterSaveSpace is allocated from ThreadAlloc and is not scanned by recycler.
     // We don't want to save float (xmm) registers as they can be huge and they cannot contain a var.
-    Js::Var registerSaves[INT_REG_COUNT];
+    // However, we're somewhat stuck. We need to keep the references around until we restore values, but
+    // we don't have a use for them. The easiest solution is to simply pass this into the corresponding
+    // parameter for BailOutcommonNoCodeGen, but that requires us to save all of the vars, not just the
+    // int ones. This is ultimately significantly more predictable than attempting to manage the lifetimes
+    // in some other way though. We can't just do what we were doing previously, which is saving values
+    // here and not passing them into BailOutCommonNoCodeGen, because then the compiler will likely get
+    // rid of the memcpy and then the dead registerSaves array, since it can figure out that there's no
+    // side effect (due to the GC not being something that the optimizer can, or should, reason about).
+    Js::Var registerSaves[BailOutRegisterSaveSlotCount];
     js_memcpy_s(registerSaves, sizeof(registerSaves), (Js::Var *)layout->functionObject->GetScriptContext()->GetThreadContext()->GetBailOutRegisterSaveSpace(),
         sizeof(registerSaves));
 
-    Js::Var result = BailOutCommonNoCodeGen(layout, bailOutRecord, bailOutOffset, returnAddress, bailOutKind, branchValue, nullptr, bailOutReturnValue, argoutRestoreAddress);
+    Js::Var result = BailOutCommonNoCodeGen(layout, bailOutRecord, bailOutOffset, returnAddress, bailOutKind, branchValue, registerSaves, bailOutReturnValue, argoutRestoreAddress);
     ScheduleFunctionCodeGen(Js::ScriptFunction::FromVar(layout->functionObject), nullptr, bailOutRecord, bailOutKind, bailOutOffset, savedImplicitCallFlags, returnAddress);
     return result;
 }
@@ -1205,7 +1213,14 @@ uint32
 BailOutRecord::BailOutFromLoopBodyCommon(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord, uint32 bailOutOffset,
     IR::BailOutKind bailOutKind, Js::Var branchValue)
 {
-    uint32 result = BailOutFromLoopBodyHelper(layout, bailOutRecord, bailOutOffset, bailOutKind, branchValue);
+    // This isn't strictly necessary if there's no allocations on this path, but because such an
+    // issue would be hard to notice and introduce some significant issues, we can do this copy.
+    // The problem from not doing this and then doing an allocation before RestoreValues is that
+    // the GC doesn't check the BailOutRegisterSaveSpace.
+    Js::Var registerSaves[BailOutRegisterSaveSlotCount];
+    js_memcpy_s(registerSaves, sizeof(registerSaves), (Js::Var *)layout->functionObject->GetScriptContext()->GetThreadContext()->GetBailOutRegisterSaveSpace(),
+        sizeof(registerSaves));
+    uint32 result = BailOutFromLoopBodyHelper(layout, bailOutRecord, bailOutOffset, bailOutKind, branchValue, registerSaves);
     ScheduleLoopBodyCodeGen(Js::ScriptFunction::FromVar(layout->functionObject), nullptr, bailOutRecord, bailOutKind);
     return result;
 }

+ 2 - 2
lib/Backend/BailOut.h

@@ -234,7 +234,7 @@ protected:
         Js::RegSlot returnValueRegSlot;
     };
     static Js::Var BailOutCommonNoCodeGen(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord,
-        uint32 bailOutOffset, void * returnAddress, IR::BailOutKind bailOutKind, Js::Var branchValue = nullptr, Js::Var * registerSaves = nullptr,
+        uint32 bailOutOffset, void * returnAddress, IR::BailOutKind bailOutKind, Js::Var branchValue, Js::Var * registerSaves,
         BailOutReturnValue * returnValue = nullptr, void * argoutRestoreAddress = nullptr);
     static Js::Var BailOutCommon(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord,
         uint32 bailOutOffset, void * returnAddress, IR::BailOutKind bailOutKind, Js::ImplicitCallFlags savedImplicitCallFlags, Js::Var branchValue = nullptr, BailOutReturnValue * returnValue = nullptr, void * argoutRestoreAddress = nullptr);
@@ -251,7 +251,7 @@ protected:
     static void BailOutInlinedHelper(Js::JavascriptCallStackLayout * layout, BailOutRecord const *& bailOutRecord, uint32 bailOutOffset, void * returnAddress,
         IR::BailOutKind bailOutKind, Js::Var * registerSaves, BailOutReturnValue * returnValue, Js::ScriptFunction ** innerMostInlinee, bool isInLoopBody, Js::Var branchValue = nullptr);
     static uint32 BailOutFromLoopBodyHelper(Js::JavascriptCallStackLayout * layout, BailOutRecord const * bailOutRecord,
-        uint32 bailOutOffset, IR::BailOutKind bailOutKind, Js::Var branchValue = nullptr, Js::Var * registerSaves = nullptr, BailOutReturnValue * returnValue = nullptr);
+        uint32 bailOutOffset, IR::BailOutKind bailOutKind, Js::Var branchValue, Js::Var * registerSaves, BailOutReturnValue * returnValue = nullptr);
 
     static void UpdatePolymorphicFieldAccess(Js::JavascriptFunction *  function, BailOutRecord const * bailOutRecord);
 

+ 12 - 3
lib/Backend/IRBuilder.cpp

@@ -415,6 +415,11 @@ IRBuilder::Build()
     m_func->m_headInstr->InsertAfter(m_func->m_tailInstr);
     m_func->m_isLeaf = true;  // until proven otherwise
 
+    if (m_func->GetJITFunctionBody()->IsParamAndBodyScopeMerged())
+    {
+        this->SetParamScopeDone();
+    }
+
     if (m_func->GetJITFunctionBody()->GetLocalClosureReg() != Js::Constants::NoRegister)
     {
         m_func->InitLocalClosureSyms();
@@ -3465,9 +3470,10 @@ IRBuilder::BuildElementSlotI1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot r
     StackSym *   stackFuncPtrSym = nullptr;
     SymID        symID = m_func->GetJITFunctionBody()->GetLocalClosureReg();
     bool isLdSlotThatWasNotProfiled = false;
-    uint scopeSlotSize = m_func->GetJITFunctionBody()->GetScopeSlotArraySize();
     StackSym* closureSym = m_func->GetLocalClosureSym();
 
+    uint scopeSlotSize = this->IsParamScopeDone() ? m_func->GetJITFunctionBody()->GetScopeSlotArraySize() : m_func->GetJITFunctionBody()->GetParamScopeSlotArraySize();
+
     switch (newOpcode)
     {
         case Js::OpCode::LdParamSlot:
@@ -3477,7 +3483,7 @@ IRBuilder::BuildElementSlotI1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot r
             // Fall through
 
         case Js::OpCode::LdLocalSlot:
-            if (PHASE_ON(Js::ClosureRangeCheckPhase, m_func))
+            if (!PHASE_OFF(Js::ClosureRangeCheckPhase, m_func))
             {
                 if ((uint32)slotId >= scopeSlotSize + Js::ScopeSlots::FirstSlotIndex)
                 {
@@ -3583,7 +3589,7 @@ IRBuilder::BuildElementSlotI1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot r
 
         case Js::OpCode::StLocalSlot:
         case Js::OpCode::StLocalSlotChkUndecl:
-            if (PHASE_ON(Js::ClosureRangeCheckPhase, m_func))
+            if (!PHASE_OFF(Js::ClosureRangeCheckPhase, m_func))
             {
                 if ((uint32)slotId >= scopeSlotSize + Js::ScopeSlots::FirstSlotIndex)
                 {
@@ -6760,6 +6766,9 @@ IRBuilder::BuildEmpty(Js::OpCode newOpcode, uint32 offset)
         // This marks the end of a param socpe which is not merged with body scope.
         // So we have to first cache the closure so that we can use it to copy the initial values for
         // body syms from corresponding param syms (LdParamSlot). Body should get its own scope slot.
+        Assert(!this->IsParamScopeDone());
+        this->SetParamScopeDone();
+
         this->AddInstr(
             IR::Instr::New(
                 Js::OpCode::Ld_A,

+ 5 - 0
lib/Backend/IRBuilder.h

@@ -71,6 +71,7 @@ public:
         , m_switchBuilder(&m_switchAdapter)
         , m_stackFuncPtrSym(nullptr)
         , m_loopBodyForInEnumeratorArrayOpnd(nullptr)
+        , m_paramScopeDone(false)
 #if DBG
         , m_callsOnStack(0)
         , m_usedAsTemp(nullptr)
@@ -276,6 +277,9 @@ private:
         return reg > 0 && reg < m_func->GetJITFunctionBody()->GetConstCount();
     }
 
+    bool                IsParamScopeDone() const { return m_paramScopeDone; }
+    void                SetParamScopeDone(bool done = true) { m_paramScopeDone = done; }
+
     Js::RegSlot         InnerScopeIndexToRegSlot(uint32) const;
     Js::RegSlot         GetEnvReg() const;
     Js::RegSlot         GetEnvRegForEvalCode() const;
@@ -347,6 +351,7 @@ private:
     StackSym *          m_loopBodyRetIPSym;
     StackSym*           m_loopCounterSym;
     StackSym *          m_stackFuncPtrSym;
+    bool                m_paramScopeDone;
     bool                callTreeHasSomeProfileInfo;
     uint                finallyBlockLevel;
 

+ 31 - 2
lib/Backend/Lower.cpp

@@ -8858,6 +8858,7 @@ Lowerer::LowerLdArrViewElem(IR::Instr * instr)
     IR::Instr * instrPrev = instr->m_prev;
 
     IR::RegOpnd * indexOpnd = instr->GetSrc1()->AsIndirOpnd()->GetIndexOpnd();
+    int32 offset = instr->GetSrc1()->AsIndirOpnd()->GetOffset();
 
     IR::Opnd * dst = instr->GetDst();
     IR::Opnd * src1 = instr->GetSrc1();
@@ -8865,7 +8866,29 @@ Lowerer::LowerLdArrViewElem(IR::Instr * instr)
 
     IR::Instr * done;
 
-    if (indexOpnd || m_func->GetJITFunctionBody()->GetAsmJsInfo()->AccessNeedsBoundCheck((uint32)src1->AsIndirOpnd()->GetOffset()))
+    if (offset < 0)
+    {
+        IR::Opnd * oobValue = nullptr;
+        if(dst->IsFloat32())
+        {
+            oobValue = IR::MemRefOpnd::New(m_func->GetThreadContextInfo()->GetFloatNaNAddr(), TyFloat32, m_func);
+        }
+        else if(dst->IsFloat64())
+        {
+            oobValue = IR::MemRefOpnd::New(m_func->GetThreadContextInfo()->GetDoubleNaNAddr(), TyFloat64, m_func);
+        }
+        else
+        {
+            oobValue = IR::IntConstOpnd::New(0, dst->GetType(), m_func);
+        }
+        instr->ReplaceSrc1(oobValue);
+        if (src2)
+        {
+            instr->FreeSrc2();
+        }
+        return m_lowererMD.ChangeToAssign(instr);
+    }
+    if (indexOpnd || m_func->GetJITFunctionBody()->GetAsmJsInfo()->AccessNeedsBoundCheck((uint32)offset))
     {
         // CMP indexOpnd, src2(arrSize)
         // JA $helper
@@ -9143,6 +9166,7 @@ Lowerer::LowerStArrViewElem(IR::Instr * instr)
 
     // type of dst is the type of array
     IR::RegOpnd * indexOpnd = dst->AsIndirOpnd()->GetIndexOpnd();
+    int32 offset = dst->AsIndirOpnd()->GetOffset();
 
     Assert(!dst->IsFloat32() || src1->IsFloat32());
     Assert(!dst->IsFloat64() || src1->IsFloat64());
@@ -9154,7 +9178,12 @@ Lowerer::LowerStArrViewElem(IR::Instr * instr)
     {
         done = LowerWasmMemOp(instr, dst);
     }
-    else if (indexOpnd || m_func->GetJITFunctionBody()->GetAsmJsInfo()->AccessNeedsBoundCheck((uint32)dst->AsIndirOpnd()->GetOffset()))
+    else if (offset < 0)
+    {
+        instr->Remove();
+        return instrPrev;
+    }
+    else if (indexOpnd || m_func->GetJITFunctionBody()->GetAsmJsInfo()->AccessNeedsBoundCheck((uint32)offset))
     {
         // CMP indexOpnd, src2(arrSize)
         // JA $helper

+ 14 - 0
lib/Backend/amd64/PeepsMD.cpp

@@ -41,6 +41,20 @@ PeepsMD::ProcessImplicitRegs(IR::Instr *instr)
             this->peeps->ClearReg(RegRDX);
         }
     }
+    else if (instr->m_opcode == Js::OpCode::XCHG)
+    {
+        // At time of writing, I believe that src1 is always identical to dst, but clear both for robustness.
+
+        // Either of XCHG's operands (but not both) can be a memory address, so only clear registers.
+        if (instr->GetSrc1()->IsRegOpnd())
+        {
+            this->peeps->ClearReg(instr->GetSrc1()->AsRegOpnd()->GetReg());
+        }
+        if (instr->GetSrc2()->IsRegOpnd())
+        {
+            this->peeps->ClearReg(instr->GetSrc2()->AsRegOpnd()->GetReg());
+        }
+    }
 }
 
 void

+ 14 - 0
lib/Backend/i386/PeepsMD.cpp

@@ -47,6 +47,20 @@ PeepsMD::ProcessImplicitRegs(IR::Instr *instr)
             this->peeps->ClearReg(RegEDX);
         }
     }
+    else if (instr->m_opcode == Js::OpCode::XCHG)
+    {
+        // At time of writing, I believe that src1 is always identical to dst, but clear both for robustness.
+
+        // Either of XCHG's operands (but not both) can be a memory address, so only clear registers.
+        if (instr->GetSrc1()->IsRegOpnd())
+        {
+            this->peeps->ClearReg(instr->GetSrc1()->AsRegOpnd()->GetReg());
+        }
+        if (instr->GetSrc2()->IsRegOpnd())
+        {
+            this->peeps->ClearReg(instr->GetSrc2()->AsRegOpnd()->GetReg());
+        }
+    }
 }
 
 void

+ 0 - 12
lib/Parser/Hash.cpp

@@ -131,18 +131,6 @@ void HashTbl::Grow()
 #endif
 }
 
-void HashTbl::ClearPidRefStacks()
-{
-    // Clear pidrefstack pointers from all existing pid's.
-    for (uint i = 0; i < m_luMask; i++)
-    {
-        for (IdentPtr pid = m_prgpidName[i]; pid; pid = pid->m_pidNext)
-        {
-            pid->m_pidRefStack = nullptr;
-        }
-    }
-}
-
 #if DEBUG
 uint HashTbl::CountAndVerifyItems(IdentPtr *buckets, uint bucketCount, uint mask)
 {

+ 12 - 1
lib/Parser/Hash.h

@@ -379,7 +379,18 @@ public:
     NoReleaseAllocator* GetAllocator() {return &m_noReleaseAllocator;}
 
     bool Contains(_In_reads_(cch) LPCOLESTR prgch, int32 cch);
-    void ClearPidRefStacks();
+
+    template<typename Fn>
+    void VisitPids(Fn fn)
+    {
+        for (uint i = 0; i <= m_luMask; i++)
+        {
+            for (IdentPtr pid = m_prgpidName[i]; pid; pid = pid->m_pidNext)
+            {
+                fn(pid);
+            }
+        }
+    }
 
 private:
     NoReleaseAllocator m_noReleaseAllocator;            // to allocate identifiers

+ 21 - 13
lib/Parser/Parse.cpp

@@ -1715,6 +1715,10 @@ void Parser::BindPidRefsInScope(IdentPtr pid, Symbol *sym, int blockId, uint max
         {
             Assert(ref->GetFuncScopeId() > funcId);
             sym->SetHasNonLocalReference();
+            if (ref->IsDynamicBinding())
+            {
+                sym->SetNeedsScopeObject();
+            }
         }
 
         if (ref->IsFuncAssignment())
@@ -1844,15 +1848,13 @@ void Parser::PopDynamicBlock()
         return;
     }
     Assert(m_currentDynamicBlock);
-    for (BlockInfoStack *blockInfo = m_currentBlockInfo; blockInfo; blockInfo = blockInfo->pBlockInfoOuter)
-    {
-        for (ParseNodePtr pnodeDecl = blockInfo->pnodeBlock->sxBlock.pnodeLexVars;
-             pnodeDecl;
-             pnodeDecl = pnodeDecl->sxVar.pnodeNext)
+
+    m_phtbl->VisitPids([&](IdentPtr pid) {
+        for (PidRefStack *ref = pid->GetTopRef(); ref && ref->GetScopeId() >= blockId; ref = ref->prev)
         {
-            this->SetPidRefsInScopeDynamic(pnodeDecl->sxVar.pid, blockId);
-        }
+            ref->SetDynamicBinding();
     }
+    });
 
     m_currentDynamicBlock = m_currentDynamicBlock->prev;
 }
@@ -4491,6 +4493,9 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
 
                 bool couldBeObjectPattern = !isObjectPattern && m_token.tk == tkAsg;
 
+                // Saving the current state as we may change the isObjectPattern down below.
+                bool oldState = isObjectPattern;
+
                 if (couldBeObjectPattern)
                 {
                     declarationType = tkLCurly;
@@ -4531,6 +4536,8 @@ ParseNodePtr Parser::ParseMemberList(LPCOLESTR pNameHint, uint32* pNameHintLengt
                 {
                     pnodeArg = CreateBinNode(isObjectPattern && !couldBeObjectPattern ? knopObjectPatternMember : knopMemberShort, pnodeName, pnodeIdent);
                 }
+
+                isObjectPattern = oldState;
             }
             else
             {
@@ -9257,6 +9264,11 @@ ParseNodePtr Parser::ParseCatch()
             FinishParseBlock(pnodeCatchScope);
         }
 
+        if (pnodeCatchScope->sxBlock.GetCallsEval() || pnodeCatchScope->sxBlock.GetChildCallsEval())
+        {
+            GetCurrentBlock()->sxBlock.SetChildCallsEval(true);
+        }
+
         if (buildAST)
         {
             PopStmt(&stmt);
@@ -9938,10 +9950,6 @@ LEndSwitch:
         {
             GetCurrentFunctionNode()->sxFnc.SetHasWithStmt(); // Used by DeferNested
         }
-        for (Scope *scope = this->m_currentScope; scope; scope = scope->GetEnclosingScope())
-        {
-            scope->SetContainsWith();
-        }
 
         ichMin = m_pscan->IchMinTok();
         ChkNxtTok(tkLParen, ERRnoLparen);
@@ -10779,7 +10787,7 @@ void Parser::FinishDeferredFunction(ParseNodePtr pnodeScopeList)
             auto addArgsToScope = [&](ParseNodePtr pnodeArg) {
                 if (pnodeArg->IsVarLetOrConst())
                 {
-                    PidRefStack *ref = this->FindOrAddPidRef(pnodeArg->sxVar.pid, blockId, funcId);//this->PushPidRef(pnodeArg->sxVar.pid);
+                    PidRefStack *ref = this->FindOrAddPidRef(pnodeArg->sxVar.pid, blockId, funcId);
                     pnodeArg->sxVar.symRef = ref->GetSymRef();
                     if (ref->GetSym() != nullptr)
                     {
@@ -11200,7 +11208,7 @@ ParseNodePtr Parser::Parse(LPCUTF8 pszSrc, size_t offset, size_t length, charcou
         FinishParseBlock(pnodeGlobalBlock);
 
         // Clear out references to undeclared identifiers.
-        m_phtbl->ClearPidRefStacks();
+        m_phtbl->VisitPids([&](IdentPtr pid) { pid->SetTopRef(nullptr); });
 
         // Restore global scope and blockinfo stacks preparatory to reparsing deferred functions.
         PushScope(pnodeGlobalBlock->sxBlock.scope);

+ 16 - 3
lib/Runtime/ByteCode/ByteCodeEmitter.cpp

@@ -4221,7 +4221,20 @@ void ByteCodeGenerator::StartEmitFunction(ParseNode *pnodeFnc)
         {
             bodyScope->SetMustInstantiate(funcInfo->frameSlotsRegister != Js::Constants::NoRegister);
         }
-        paramScope->SetMustInstantiate(!pnodeFnc->sxFnc.IsBodyAndParamScopeMerged());
+
+        if (!pnodeFnc->sxFnc.IsBodyAndParamScopeMerged())
+        {
+            if (funcInfo->frameObjRegister != Js::Constants::NoRegister)
+            {
+                paramScope->SetMustInstantiate(true);
+            }
+            else
+            {
+                // In the case of function expression being captured in the param scope the hasownlocalinclosure will be false for param scope,
+                // as function expression symbol stays in the function expression scope. We don't have to set mustinstantiate for param scope in that case.
+                paramScope->SetMustInstantiate(paramScope->GetHasOwnLocalInClosure());
+            }
+        }
     }
     else
     {
@@ -8502,7 +8515,7 @@ void EmitMemberNode(ParseNode *memberNode, Js::RegSlot objectLocation, ByteCodeG
 
     if (nameNode->nop == knopComputedName)
     {
-        Assert(memberNode->nop == knopGetMember || memberNode->nop == knopSetMember || memberNode->nop == knopMember);
+        AssertOrFailFast(memberNode->nop == knopGetMember || memberNode->nop == knopSetMember || memberNode->nop == knopMember);
 
         Js::OpCode setOp = memberNode->nop == knopGetMember ?
             (isClassMember ? Js::OpCode::InitClassMemberGetComputedName : Js::OpCode::InitGetElemI) :
@@ -8574,7 +8587,7 @@ void EmitMemberNode(ParseNode *memberNode, Js::RegSlot objectLocation, ByteCodeG
     }
     else
     {
-        Assert(memberNode->nop == knopGetMember || memberNode->nop == knopSetMember);
+        AssertOrFailFast(memberNode->nop == knopGetMember || memberNode->nop == knopSetMember);
 
         Js::OpCode setOp = memberNode->nop == knopGetMember ?
             (isClassMember ? Js::OpCode::InitClassMemberGet : Js::OpCode::InitGetFld) :

+ 5 - 33
lib/Runtime/ByteCode/ByteCodeGenerator.cpp

@@ -1722,6 +1722,11 @@ Symbol * ByteCodeGenerator::AddSymbolToScope(Scope *scope, const char16 *key, in
 
     Assert(sym && sym->GetScope() && (sym->GetScope() == scope || sym->GetScope()->GetScopeType() == ScopeType_Parameter));
 
+    if (sym->NeedsScopeObject())
+    {
+        scope->SetIsObject();
+    }
+
     return sym;
 }
 
@@ -2736,34 +2741,6 @@ FuncInfo* PostVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerat
                     byteCodeGenerator->AssignParamSlotsRegister();
                 }
 
-                if (top->GetBodyScope()->ContainsWith() && 
-                    (top->GetBodyScope()->GetHasOwnLocalInClosure() ||
-                     (top->GetParamScope()->GetHasOwnLocalInClosure() &&
-                      top->IsBodyAndParamScopeMerged())))
-                {
-                    // Parent scopes may contain symbols called inside the with.
-                    // Current implementation needs the symScope isObject.
-
-                    top->GetBodyScope()->SetIsObject();
-                    if (top->byteCodeFunction->IsFunctionBody())
-                    {
-                        // Record this for future use in the no-refresh debugging.
-                        top->byteCodeFunction->GetFunctionBody()->SetHasSetIsObject(true);
-                    }
-                }
-
-                if (top->GetParamScope()->ContainsWith() &&
-                    (top->GetParamScope()->GetHasOwnLocalInClosure() &&
-                     !top->IsBodyAndParamScopeMerged()))
-                {
-                    top->GetParamScope()->SetIsObject();
-                    if (top->byteCodeFunction->IsFunctionBody())
-                    {
-                        // Record this for future use in the no-refresh debugging.
-                        top->byteCodeFunction->GetFunctionBody()->SetHasSetIsObject(true);
-                    }
-                }
-
                 if (byteCodeGenerator->NeedObjectAsFunctionScope(top, top->root)
                     || top->bodyScope->GetIsObject()
                     || top->paramScope->GetIsObject())
@@ -3094,11 +3071,6 @@ void ByteCodeGenerator::ProcessScopeWithCapturedSym(Scope *scope)
 {
     Assert(scope->GetHasOwnLocalInClosure());
 
-    if (scope->ContainsWith() && scope->GetScopeType() != ScopeType_Global)
-    {
-        scope->SetIsObject();
-    }
-
     // (Note: if any catch var is closure-captured, we won't merge the catch scope with the function scope.
     // So don't mark the function scope "has local in closure".)
     FuncInfo *func = scope->GetFunc();

+ 0 - 5
lib/Runtime/ByteCode/Scope.h

@@ -41,7 +41,6 @@ private:
     BYTE canMergeWithBodyScope : 1;
     BYTE hasLocalInClosure : 1;
     BYTE isBlockInLoop : 1;
-    BYTE containsWith : 1;
 public:
 #if DBG
     BYTE isRestored : 1;
@@ -61,7 +60,6 @@ public:
         canMergeWithBodyScope(true),
         hasLocalInClosure(false),
         isBlockInLoop(false),
-        containsWith(false),
         location(Js::Constants::NoRegister),
         m_symList(nullptr),
         m_count(0),
@@ -264,9 +262,6 @@ public:
     void SetIsBlockInLoop(bool is = true) { isBlockInLoop = is; }
     bool IsBlockInLoop() const { return isBlockInLoop; }
 
-    void SetContainsWith(bool does = true) { containsWith = does; }
-    bool ContainsWith() const { return containsWith; }
-
     bool HasInnerScopeIndex() const { return innerScopeIndex != (uint)-1; }
     uint GetInnerScopeIndex() const { return innerScopeIndex; }
     void SetInnerScopeIndex(uint index) { innerScopeIndex = index; }

+ 12 - 0
lib/Runtime/ByteCode/Symbol.h

@@ -46,6 +46,7 @@ private:
     BYTE isModuleExportStorage : 1; // If true, this symbol should be stored in the global scope export storage array.
     BYTE isModuleImport : 1; // If true, this symbol is the local name of a module import statement
     BYTE isUsedInLdElem : 1;
+    BYTE needsScopeObject : 1;
 
     // These are get and set a lot, don't put it in bit fields, we are exceeding the number of bits anyway
     bool hasFuncAssignment;
@@ -82,6 +83,7 @@ public:
         isModuleExportStorage(false),
         isModuleImport(false),
         isUsedInLdElem(false),
+        needsScopeObject(false),
         moduleIndex(Js::Constants::NoProperty)
     {
         SetSymbolType(symbolType);
@@ -192,6 +194,16 @@ public:
         return isUsedInLdElem;
     }
 
+    void SetNeedsScopeObject(bool does = true)
+    {
+        needsScopeObject = does;
+    }
+
+    bool NeedsScopeObject() const
+    {
+        return needsScopeObject;
+    }
+
     void SetModuleIndex(Js::PropertyId index)
     {
         moduleIndex = index;

+ 3 - 0
lib/Runtime/InternalPropertyList.h

@@ -6,6 +6,9 @@
 // They become nameless compile time known PropertyRecords, stored as static
 // fields on the InternalPropertyRecords class.
 
+// NOTE: When new property is added here, please evaluate if the property;s value needs to be restored to nullptr
+// when it gets reset to undefined inside DynamicObject::ResetObject()
+
 INTERNALPROPERTY(TypeOfPrototypeObjectInlined)     // Used to store the type of the prototype object in the prototype objects slots. Only DynamicTypes having TypeIds_Object are saved in this slot.
 // Used to store the type of the prototype object in the prototype objects slots. Everything else (except ExternalType) are stored in this slot as Dictionary.
 // Key in the Dictionary is combination of Type and TypeId and value is dynamicType object.

+ 3 - 2
lib/Runtime/Language/JavascriptOperators.cpp

@@ -4929,7 +4929,7 @@ CommonNumber:
         }
         else if (typeId == TypeIds_HostDispatch)
         {
-            TypeId remoteTypeId;
+            TypeId remoteTypeId = TypeIds_Limit;
             if (RecyclableObject::FromVar(thisVar)->GetRemoteTypeId(&remoteTypeId))
             {
                 if (remoteTypeId == TypeIds_Null || remoteTypeId == TypeIds_Undefined || remoteTypeId == TypeIds_ActivationObject)
@@ -5033,8 +5033,9 @@ CommonNumber:
         return thisVar;
     }
 
-    BOOL JavascriptOperators::GetRemoteTypeId(Var aValue, TypeId* typeId)
+    BOOL JavascriptOperators::GetRemoteTypeId(Var aValue, __out TypeId* typeId)
     {
+        *typeId = TypeIds_Limit;
         if (GetTypeId(aValue) != TypeIds_HostDispatch)
         {
             return FALSE;

+ 1 - 1
lib/Runtime/Language/JavascriptOperators.h

@@ -465,7 +465,7 @@ namespace Js
         static void OP_LoadUndefinedToElementScoped(FrameDisplay *pScope, PropertyId propertyId, Var defaultInstance, ScriptContext* scriptContext);
         static Var OP_IsInst(Var instance, Var aClass, ScriptContext* scriptContext, IsInstInlineCache *inlineCache);
         static Var IsIn(Var argProperty, Var instance, ScriptContext* scriptContext);
-        static BOOL GetRemoteTypeId(Var instance, TypeId* typeId);
+        static BOOL GetRemoteTypeId(Var instance, __out TypeId* typeId);
         static FunctionProxy* GetDeferredDeserializedFunctionProxy(JavascriptFunction* func);
 
         template <bool IsFromFullJit, class TInlineCache> static Var PatchGetValue(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId);

+ 2 - 1
lib/Runtime/Library/JSON.cpp

@@ -708,7 +708,8 @@ namespace JSON
                             }
 
                             // walk the property name list
-                            for (uint k = 0; k < precisePropertyCount; k++)
+                            // Note that we're only walking up to index, not precisePropertyCount, as we only know that we've filled the array up to index
+                            for (uint k = 0; k < index; k++)
                             {
                                 propertyName = Js::JavascriptString::FromVar(nameTable[k]);
                                 scriptContext->GetOrAddPropertyRecord(propertyName->GetString(), propertyName->GetLength(), &propRecord);

+ 12 - 2
lib/Runtime/Library/JavascriptArray.cpp

@@ -483,7 +483,7 @@ namespace Js
 
     bool JavascriptArray::IsMissingItem(uint32 index)
     {
-        if (this->length <= index)
+        if (!(this->head->left <= index && index < (this->head->left+ this->head->length)))
         {
             return false;
         }
@@ -5464,6 +5464,9 @@ Case0:
                 isFloatArray = true;
             }
 
+            // Code below has potential to throw due to OOM or SO. Just FailFast on those cases
+            AutoDisableInterrupt failFastOnError(scriptContext->GetThreadContext());
+
             // During the loop below we are going to reverse the segments list. The head segment will become the last segment.
             // We have to verify that the current head segment is not the inilined segement, otherwise due to shuffling below (of EnsureHeadStartsFromZero call below), the inlined segment will no longer
             // be the head and that can create issue down the line. Create new segment if it is an inilined segment.
@@ -5549,6 +5552,9 @@ Case0:
 #ifdef VALIDATE_ARRAY
             pArr->ValidateArray();
 #endif
+
+            failFastOnError.Completed();
+
         }
         else if (typedArrayBase)
         {
@@ -5980,8 +5986,12 @@ Case0:
         // Prototype lookup for missing elements
         if (!pArr->HasNoMissingValues())
         {
-            for (uint32 i = 0; i < newLen && (i + start) < pArr->length; i++)
+            for (uint32 i = 0; i < newLen; i++)
+            {
+                if (!(pArr->head->left <= (i + start) && (i + start) <  (pArr->head->left + pArr->head->length)))
             {
+                    break;
+                }
                 // array type might be changed in the below call to DirectGetItemAtFull
                 // need recheck array type before checking array item [i + start]
                 if (pArr->IsMissingItem(i + start))

+ 7 - 1
lib/Runtime/Library/JavascriptFunction.cpp

@@ -1687,8 +1687,9 @@ LABEL1:
     void JavascriptFunction::ReparseAsmJsModule(ScriptFunction** functionRef)
     {
         ParseableFunctionInfo* functionInfo = (*functionRef)->GetParseableFunctionInfo();
-
         Assert(functionInfo);
+        try
+        {
         functionInfo->GetFunctionBody()->AddDeferParseAttribute();
         functionInfo->GetFunctionBody()->ResetEntryPoint();
         functionInfo->GetFunctionBody()->ResetInParams();
@@ -1702,6 +1703,11 @@ LABEL1:
 
         (*functionRef)->UpdateUndeferredBody(funcBody);
     }
+        catch (JavascriptException&)
+        {
+                Js::Throw::FatalInternalError();
+        }
+    }
 
     // Thunk for handling calls to functions that have not had byte code generated for them.
 

+ 13 - 3
lib/Runtime/Library/JavascriptProxy.cpp

@@ -576,17 +576,27 @@ namespace Js
         return FALSE;
     }
   
-    BOOL JavascriptProxy::GetAccessors(PropertyId propertyId, Var* getter, Var* setter, ScriptContext * requestContext)
+    BOOL JavascriptProxy::GetAccessors(PropertyId propertyId, __out Var* getter, __out Var* setter, ScriptContext * requestContext)
     {
         PropertyDescriptor result;
+        if (getter != nullptr)
+        {
+            *getter = nullptr;
+        }
+
+        if (setter != nullptr)
+        {
+            *setter = nullptr;
+        }
+
         BOOL foundProperty = GetOwnPropertyDescriptor(this, propertyId, requestContext, &result);
         if (foundProperty && result.IsFromProxy())
         {
-            if (result.GetterSpecified())
+            if (result.GetterSpecified() && getter != nullptr)
             {
                 *getter = result.GetGetter();
             }
-            if (result.SetterSpecified())
+            if (result.SetterSpecified() && setter != nullptr)
             {
                 *setter = result.GetSetter();
             }

+ 1 - 1
lib/Runtime/Library/JavascriptProxy.h

@@ -81,7 +81,7 @@ namespace Js
         virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
         virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
         virtual BOOL GetInternalProperty(Var instance, PropertyId internalPropertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
-        virtual BOOL GetAccessors(PropertyId propertyId, Var* getter, Var* setter, ScriptContext * requestContext) override;
+        virtual BOOL GetAccessors(PropertyId propertyId, __out Var* getter, __out Var* setter, ScriptContext * requestContext) override;
         virtual PropertyQueryFlags GetPropertyReferenceQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
         virtual BOOL SetProperty(PropertyId propertyId, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) override;
         virtual BOOL SetProperty(JavascriptString* propertyNameString, Var value, PropertyOperationFlags flags, PropertyValueInfo* info) override;

+ 2 - 3
lib/Runtime/Library/JavascriptRegularExpression.cpp

@@ -184,10 +184,9 @@ namespace Js
 
         if (JavascriptOperators::GetTypeId(var) == TypeIds_HostDispatch)
         {
-            TypeId remoteTypeId;
+            TypeId remoteTypeId = TypeIds_Limit;
             RecyclableObject* reclObj = RecyclableObject::FromVar(var);
-            reclObj->GetRemoteTypeId(&remoteTypeId);
-            if (remoteTypeId == TypeIds_RegEx)
+            if (reclObj->GetRemoteTypeId(&remoteTypeId) && remoteTypeId == TypeIds_RegEx)
             {
                 return static_cast<JavascriptRegExp *>(reclObj->GetRemoteObject());
             }

+ 0 - 2
lib/Runtime/Library/JavascriptWeakMap.cpp

@@ -37,8 +37,6 @@ namespace Js
 
         if (key->GetScriptContext()->GetLibrary()->GetUndefined() == weakMapKeyData)
         {
-            // Assert to find out where this can happen.
-            Assert(false);
             return nullptr;
         }
 

+ 12 - 2
lib/Runtime/Types/DeferredTypeHandler.h

@@ -122,7 +122,7 @@ namespace Js
         virtual BOOL SetWritable(DynamicObject* instance, PropertyId propertyId, BOOL value) override;
         virtual BOOL SetConfigurable(DynamicObject* instance, PropertyId propertyId, BOOL value) override;
         virtual BOOL SetAccessors(DynamicObject* instance, PropertyId propertyId, Var getter, Var setter, PropertyOperationFlags flags = PropertyOperation_None) override;
-        virtual BOOL GetAccessors(DynamicObject* instance, PropertyId propertyId, Var *getter, Var *setter) override;
+        virtual BOOL GetAccessors(DynamicObject* instance, PropertyId propertyId, __out Var *getter, __out Var *setter) override;
         virtual BOOL PreventExtensions(DynamicObject *instance) override;
         virtual BOOL Seal(DynamicObject *instance) override;
         virtual BOOL SetPropertyWithAttributes(DynamicObject* instance, PropertyId propertyId, Var value, PropertyAttributes attributes, PropertyValueInfo* info, PropertyOperationFlags flags = PropertyOperation_None, SideEffects possibleSideEffects = SideEffects_Any) override;
@@ -553,8 +553,18 @@ namespace Js
     }
 
     template <DeferredTypeInitializer initializer, typename DeferredTypeFilter, bool isPrototypeTemplate, uint16 _inlineSlotCapacity, uint16 _offsetOfInlineSlots>
-    BOOL DeferredTypeHandler<initializer, DeferredTypeFilter, isPrototypeTemplate, _inlineSlotCapacity, _offsetOfInlineSlots>::GetAccessors(DynamicObject* instance, PropertyId propertyId, Var *getter, Var *setter)
+    BOOL DeferredTypeHandler<initializer, DeferredTypeFilter, isPrototypeTemplate, _inlineSlotCapacity, _offsetOfInlineSlots>::GetAccessors(DynamicObject* instance, PropertyId propertyId, __out Var *getter, __out Var *setter)
     {
+        if (getter != nullptr)
+        {
+            *getter = nullptr;
+        }
+
+        if (setter != nullptr)
+        {
+            *setter = nullptr;
+        }
+
         if (!EnsureObjectReady(instance, DeferredInitializeMode_Default))
         {
             return TRUE;

+ 7 - 0
lib/Runtime/Types/DynamicObject.cpp

@@ -717,6 +717,13 @@ namespace Js
             mutationBpValue = nullptr;
         }
 
+        // If value of TypeOfPrototypeObjectDictionary was set undefined above, reset it to nullptr so we don't type cast it wrongly to TypeTransitionMap* or we don't marshal the non-Var dictionary below
+        Var typeTransitionMap = nullptr;
+        if (this->GetInternalProperty(this, InternalPropertyIds::TypeOfPrototypeObjectDictionary, &typeTransitionMap, nullptr, this->GetScriptContext()))
+        {
+            this->SetInternalProperty(InternalPropertyIds::TypeOfPrototypeObjectDictionary, nullptr, PropertyOperation_Force, nullptr);
+        }
+
         if (keepProperties)
         {
             this->GetTypeHandler()->MarshalAllPropertiesToScriptContext(this, this->GetScriptContext(), false);

+ 2 - 2
lib/Runtime/Types/PathTypeHandler.cpp

@@ -1572,9 +1572,9 @@ namespace Js
         char16 reason[1024];
         swprintf_s(reason, 1024, _u("Cache not populated."));
 #endif
-        if (useCache && newPrototype->GetInternalProperty(newPrototype, Js::InternalPropertyIds::TypeOfPrototypeObjectDictionary, (Js::Var*)&oldTypeToPromotedTypeMap, nullptr, scriptContext))
+        if (useCache && newPrototype->GetInternalProperty(newPrototype, Js::InternalPropertyIds::TypeOfPrototypeObjectDictionary, (Js::Var*)&oldTypeToPromotedTypeMap, nullptr, scriptContext) && oldTypeToPromotedTypeMap != nullptr)
         {
-            Assert(oldTypeToPromotedTypeMap && (Js::Var)oldTypeToPromotedTypeMap != scriptContext->GetLibrary()->GetUndefined());
+            AssertOrFailFast((Js::Var)oldTypeToPromotedTypeMap != scriptContext->GetLibrary()->GetUndefined());
             oldTypeToPromotedTypeMap = reinterpret_cast<TypeTransitionMap*>(oldTypeToPromotedTypeMap);
 
             if (oldTypeToPromotedTypeMap->TryGetValue(oldType, &cachedDynamicType))

+ 27 - 0
test/Basics/With-defer-block-scope.js

@@ -0,0 +1,27 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+let h = function f(a0 = (function () {
+    a0;
+    a1;
+    a2;
+    a3;
+    a4;
+    a5;
+    a6;
+    a7 = 0x99999; // oob write
+
+    with ({});
+})(), a1, a2, a3, a4, a5, a6, a7) {
+    function g() {
+        f;
+    }
+};
+
+for (let i = 0; i < 0x10000; i++) {
+h();
+}
+
+WScript.Echo('pass');

+ 6 - 0
test/Basics/rlexe.xml

@@ -272,6 +272,12 @@
       <compile-flags>-force:deferparse</compile-flags>
     </default>
   </test>
+  <test>
+    <default>
+      <files>With-defer-block-scope.js</files>
+      <compile-flags>-force:deferparse</compile-flags>
+    </default>
+  </test>
   <test>
     <default>
       <files>withBug940841.js</files>

+ 32 - 0
test/Bugs/bug12628506.js

@@ -0,0 +1,32 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+function test0() {
+    var loopInvariant = 9;
+    var obj1 = {};
+    var arrObj0 = {};
+    var func0 = function () {
+    };
+    var func2 = function () {
+        protoObj0;
+    };
+    arrObj0.method0 = obj1;
+    var i8 = new Int8Array(256);
+    var VarArr0 = Array(protoObj0, -188);
+    var protoObj0 = Object.create(func0);
+    protoObj0.prop0 = -1;
+    arrObj0.prop0 = -1863021692;
+    var __loopvar0 = 3, __loopSecondaryVar0_0 = 9 - 9, __loopSecondaryVar0_1 = 9;
+    while ((VarArr0[i8[255] + (arrObj0.prop0 <= protoObj0.prop0)]) && __loopvar0 < 10) {
+        __loopvar0++;
+        __loopSecondaryVar0_1 += 3;
+        if (3 > loopInvariant) {
+            break;
+        }
+        __loopSecondaryVar0_0 += 3;
+        arrObj0 = protoObj0;
+    }
+}
+test0();
+print("passed");

+ 6 - 0
test/Bugs/rlexe.xml

@@ -362,6 +362,12 @@
       <tags>exclude_dynapogo</tags>
     </default>
   </test>
+  <test>
+    <default>
+      <files>bug12628506.js</files>
+      <compile-flags>-loopinterpretcount:1</compile-flags>
+    </default>
+  </test>
   <test>
     <default>
       <files>bug13172050.js</files>

+ 8 - 0
test/es6/default-splitscope.js

@@ -186,6 +186,14 @@ var tests = [
         };
         f13();
 
+        var f14 = function f15(a = (function() {
+                return f15(1);
+            })()) {
+                with({}) {
+                };
+                return a === 1 ? 10 : a;
+        };
+        assert.areEqual(10, f14(), "Function expresison is captured in the param scope when no other formals are captured");
     }
  },
  {