Critical severity9.8NVD Advisory· Published Aug 11, 2017· Updated May 13, 2026
CVE-2017-8658
CVE-2017-8658
Description
A remote code execution vulnerability exists in the way that the Chakra JavaScript engine renders when handling objects in memory, aka "Scripting Engine Memory Corruption Vulnerability".
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
Microsoft.ChakraCoreNuGet | < 1.6.1 | 1.6.1 |
Affected products
2- Microsoft Corporation/ChakraCorev5Range: ChakraCore V1.7.1
Patches
12500e1cdc12c17-08 ChakraCore servicing release
78 files changed · +1385 −344
Build/Chakra.Build.props+1 −0 modified@@ -4,6 +4,7 @@ <PropertyGroup> <Win32_WinNTVersion Condition="'$(NtTargetVersion)'=='$(NtTargetVersion_Win7)'">0x0601</Win32_WinNTVersion> <Win32_WinNTVersion Condition="'$(NtTargetVersion)'=='$(NtTargetVersion_Win8)'">0x0602</Win32_WinNTVersion> + <Win32_WinNTVersion Condition="'$(NtTargetVersion)'=='$(NtTargetVersion_WinBlue)'">0x0603</Win32_WinNTVersion> <Win32_WinNTVersion Condition="'$(NtTargetVersion)'=='$(NtTargetVersion_Win10)'">0x0A00</Win32_WinNTVersion> </PropertyGroup> <PropertyGroup>
Build/Common.Build.Default.props+1 −0 modified@@ -4,6 +4,7 @@ <PropertyGroup> <NtTargetVersion_Win7 >0x601</NtTargetVersion_Win7> <NtTargetVersion_Win8 >0x602</NtTargetVersion_Win8> + <NtTargetVersion_WinBlue>0x603</NtTargetVersion_WinBlue> <NtTargetVersion_Win10>0xA00</NtTargetVersion_Win10> </PropertyGroup>
Build/Common.Build.props+3 −0 modified@@ -29,6 +29,8 @@ <!-- ======== sources.inc ======== --> <!-- generates SAL annotations for our interface --> <AdditionalOptions>%(AdditionalOptions) -sal_local</AdditionalOptions> + + <PreprocessorDefinitions>%(PreprocessorDefinitions);WINVER=$(Win32_WinNTVersion)</PreprocessorDefinitions> </Midl> <ClCompile> <PreprocessorDefinitions>%(PreprocessorDefinitions);NOMINMAX;USE_EDGEMODE_JSRT</PreprocessorDefinitions> @@ -105,6 +107,7 @@ <MinimumRequiredVersion Condition="'$(NtTargetVersion)'=='$(NtTargetVersion_Win7)'" >6.1</MinimumRequiredVersion> <MinimumRequiredVersion Condition="'$(NtTargetVersion)'=='$(NtTargetVersion_Win8)'" >6.2</MinimumRequiredVersion> + <MinimumRequiredVersion Condition="'$(NtTargetVersion)'=='$(NtTargetVersion_WinBlue)'" >6.3</MinimumRequiredVersion> <MinimumRequiredVersion Condition="'$(NtTargetVersion)'=='$(NtTargetVersion_Win10)'" >10.00</MinimumRequiredVersion> <!-- Always set the checksum -->
lib/Backend/arm/LowerMD.cpp+1 −1 modified@@ -8559,7 +8559,7 @@ LowererMD::LoadFloatValue(IR::Opnd * opndDst, double value, IR::Instr * instrIns #if DBG NativeCodeData::GetDataDescription(pValue, instrInsert->m_func->m_alloc), #endif - instrInsert->m_func); + instrInsert->m_func, true); } else {
lib/Backend/AsmJsJITInfo.cpp+1 −1 modified@@ -60,7 +60,7 @@ AsmJsJITInfo::GetArgTypeArray() const Js::AsmJsVarType::Which AsmJsJITInfo::GetArgType(Js::ArgSlot argNum) const { - Assert(argNum < GetArgCount()); + AssertOrFailFast(argNum < GetArgCount()); return GetArgTypeArray()[argNum]; }
lib/Backend/AsmJsJITInfo.h+1 −1 modified@@ -28,7 +28,6 @@ class AsmJsJITInfo Js::ArgSlot GetArgCount() const; Js::ArgSlot GetArgByteSize() const; Js::AsmJsRetType::Which GetRetType() const; - Js::AsmJsVarType::Which * GetArgTypeArray() const; Js::AsmJsVarType::Which GetArgType(Js::ArgSlot argNum) const; #ifdef ENABLE_WASM @@ -40,6 +39,7 @@ class AsmJsJITInfo bool AccessNeedsBoundCheck(uint offset) const; private: + Js::AsmJsVarType::Which * GetArgTypeArray() const; AsmJsDataIDL m_data; #endif };
lib/Backend/CodeGenWorkItem.h+7 −0 modified@@ -58,6 +58,13 @@ struct CodeGenWorkItem : public JsUtil::Job return functionBody->GetScriptContext(); } + uint GetByteCodeLength() const + { + return this->functionBody->IsInDebugMode() + ? this->functionBody->GetOriginalByteCode()->GetLength() + : this->functionBody->GetByteCode()->GetLength(); + } + Js::FunctionBody* GetFunctionBody() const { return functionBody;
lib/Backend/Func.cpp+8 −0 modified@@ -153,6 +153,14 @@ Func::Func(JitArenaAllocator *alloc, JITTimeWorkItem * workItem, Assert(this->IsInlined() == !!runtimeInfo); + AssertOrFailFast(!HasProfileInfo() || GetReadOnlyProfileInfo()->GetLoopCount() == GetJITFunctionBody()->GetLoopCount()); + Js::RegSlot tmpResult; + AssertOrFailFast(!UInt32Math::Add(GetJITFunctionBody()->GetConstCount(), GetJITFunctionBody()->GetVarCount(), &tmpResult)); + AssertOrFailFast(GetJITFunctionBody()->IsAsmJsMode() || GetJITFunctionBody()->GetFirstTmpReg() <= GetJITFunctionBody()->GetLocalsCount()); + AssertOrFailFast(!IsLoopBody() || m_workItem->GetLoopNumber() < GetJITFunctionBody()->GetLoopCount()); + AssertOrFailFast(CONFIG_FLAG(Prejit) || CONFIG_ISENABLED(Js::ForceNativeFlag) || GetJITFunctionBody()->GetByteCodeLength() < (uint)CONFIG_FLAG(MaxJITFunctionBytecodeByteLength)); + GetJITFunctionBody()->EnsureConsistentConstCount(); + if (this->IsTopFunc()) { outputData->hasJittedStackClosure = false;
lib/Backend/IRBuilderAsmJs.cpp+22 −11 modified@@ -27,7 +27,7 @@ IRBuilderAsmJs::Build() m_switchBuilder.Init(m_func, m_tempAlloc, true); m_firstVarConst = 0; - Js::RegSlot tempCount = 0; + m_tempCount = 0; m_firstsType[0] = m_firstVarConst + AsmJsRegSlots::RegCount; for (int i = 0, j = 1; i < WAsmJs::LIMIT; ++i, ++j) { @@ -36,7 +36,7 @@ IRBuilderAsmJs::Build() m_firstsType[j] = typedInfo.constCount; m_firstsType[j + WAsmJs::LIMIT] = typedInfo.varCount; m_firstsType[j + 2 * WAsmJs::LIMIT] = typedInfo.tmpCount; - tempCount += typedInfo.tmpCount; + m_tempCount += typedInfo.tmpCount; } // Fixup the firsts by looking at the previous value for (int i = 1; i < m_firstsTypeCount; ++i) @@ -66,10 +66,10 @@ IRBuilderAsmJs::Build() // we will be using lower space for type specialized syms, so bump up where new temp syms can be created m_func->m_symTable->IncreaseStartingID(m_firstIRTemp - m_func->m_symTable->GetMaxSymID()); - if (tempCount > 0) + if (m_tempCount > 0) { - m_tempMap = (SymID*)m_tempAlloc->AllocZero(sizeof(SymID) * tempCount); - m_fbvTempUsed = BVFixed::New<JitArenaAllocator>(tempCount, m_tempAlloc); + m_tempMap = AnewArrayZ(m_tempAlloc, SymID, m_tempCount); + m_fbvTempUsed = BVFixed::New<JitArenaAllocator>(m_tempCount, m_tempAlloc); } else { @@ -359,9 +359,10 @@ IRBuilderAsmJs::BuildIntConstOpnd(Js::RegSlot regSlot) Js::Var * constTable = (Js::Var*)m_func->GetJITFunctionBody()->GetConstTable(); const WAsmJs::TypedSlotInfo& info = m_func->GetJITFunctionBody()->GetAsmJsInfo()->GetTypedSlotInfo(WAsmJs::INT32); Assert(info.constSrcByteOffset != Js::Constants::InvalidOffset); + AssertOrFailFast(info.constSrcByteOffset < UInt32Math::Mul<sizeof(Js::Var)>(m_func->GetJITFunctionBody()->GetConstCount())); int* intConstTable = reinterpret_cast<int*>(((byte*)constTable) + info.constSrcByteOffset); Js::RegSlot srcReg = GetTypedRegFromRegSlot(regSlot, WAsmJs::INT32); - Assert(srcReg >= Js::FunctionBody::FirstRegSlot && srcReg < info.constCount); + AssertOrFailFast(srcReg >= Js::FunctionBody::FirstRegSlot && srcReg < info.constCount); const int32 value = intConstTable[srcReg]; IR::IntConstOpnd *opnd = IR::IntConstOpnd::New(value, TyInt32, m_func); @@ -536,7 +537,9 @@ IRBuilderAsmJs::GetMappedTemp(Js::RegSlot reg) AssertMsg(RegIsTemp(reg), "Processing non-temp reg as a temp?"); AssertMsg(m_tempMap, "Processing non-temp reg without a temp map?"); - return m_tempMap[reg - GetFirstTmp(WAsmJs::FirstType)]; + Js::RegSlot tempIndex = reg - GetFirstTmp(WAsmJs::FirstType); + AssertOrFailFast(tempIndex < m_tempCount); + return m_tempMap[tempIndex]; } void @@ -545,7 +548,9 @@ IRBuilderAsmJs::SetMappedTemp(Js::RegSlot reg, SymID tempId) AssertMsg(RegIsTemp(reg), "Processing non-temp reg as a temp?"); AssertMsg(m_tempMap, "Processing non-temp reg without a temp map?"); - m_tempMap[reg - GetFirstTmp(WAsmJs::FirstType)] = tempId; + Js::RegSlot tempIndex = reg - GetFirstTmp(WAsmJs::FirstType); + AssertOrFailFast(tempIndex < m_tempCount); + m_tempMap[tempIndex] = tempId; } BOOL @@ -554,7 +559,9 @@ IRBuilderAsmJs::GetTempUsed(Js::RegSlot reg) AssertMsg(RegIsTemp(reg), "Processing non-temp reg as a temp?"); AssertMsg(m_fbvTempUsed, "Processing non-temp reg without a used BV?"); - return m_fbvTempUsed->Test(reg - GetFirstTmp(WAsmJs::FirstType)); + Js::RegSlot tempIndex = reg - GetFirstTmp(WAsmJs::FirstType); + AssertOrFailFast(tempIndex < m_tempCount); + return m_fbvTempUsed->Test(tempIndex); } void @@ -563,13 +570,15 @@ IRBuilderAsmJs::SetTempUsed(Js::RegSlot reg, BOOL used) AssertMsg(RegIsTemp(reg), "Processing non-temp reg as a temp?"); AssertMsg(m_fbvTempUsed, "Processing non-temp reg without a used BV?"); + Js::RegSlot tempIndex = reg - GetFirstTmp(WAsmJs::FirstType); + AssertOrFailFast(tempIndex < m_tempCount); if (used) { - m_fbvTempUsed->Set(reg - GetFirstTmp(WAsmJs::FirstType)); + m_fbvTempUsed->Set(tempIndex); } else { - m_fbvTempUsed->Clear(reg - GetFirstTmp(WAsmJs::FirstType)); + m_fbvTempUsed->Clear(tempIndex); } } @@ -697,6 +706,8 @@ void IRBuilderAsmJs::CreateLoadConstInstrForType( ) { T* typedTable = (T*)(table + byteOffset); + AssertOrFailFast(byteOffset < UInt32Math::Mul<sizeof(Js::Var)>(m_func->GetJITFunctionBody()->GetConstCount())); + AssertOrFailFast(AllocSizeMath::Add((size_t)typedTable, UInt32Math::Mul<sizeof(T)>(constCount)) <= (size_t)((Js::Var*)m_func->GetJITFunctionBody()->GetConstTable() + m_func->GetJITFunctionBody()->GetConstCount())); // 1 for return register ++regAllocated; ++typedTable;
lib/Backend/IRBuilderAsmJs.h+2 −0 modified@@ -172,6 +172,8 @@ class IRBuilderAsmJs Js::RegSlot m_firstsType[m_firstsTypeCount]; Js::RegSlot m_firstVarConst; Js::RegSlot m_firstIRTemp; + Js::RegSlot m_tempCount; + Js::OpCode * m_simdOpcodesMap; Js::RegSlot GetFirstConst(WAsmJs::Types type) { return m_firstsType[type]; }
lib/Backend/IRBuilder.cpp+4 −1 modified@@ -400,7 +400,7 @@ IRBuilder::Build() Js::RegSlot tempCount = m_func->GetJITFunctionBody()->GetTempCount(); if (tempCount > 0) { - this->tempMap = (SymID*)m_tempAlloc->AllocZero(sizeof(SymID) * tempCount); + this->tempMap = AnewArrayZ(m_tempAlloc, SymID, tempCount); this->fbvTempUsed = BVFixed::New<JitArenaAllocator>(tempCount, m_tempAlloc); } else @@ -446,6 +446,7 @@ IRBuilder::Build() #endif lastOffset = m_func->m_workItem->GetLoopHeader()->endOffset; + AssertOrFailFast(lastOffset < m_func->GetJITFunctionBody()->GetByteCodeLength()); // Ret is created at lastOffset + 1, so we need lastOffset + 2 entries offsetToInstructionCount = lastOffset + 2; @@ -2721,6 +2722,7 @@ IRBuilder::BuildUnsigned1(Js::OpCode newOpcode, uint32 offset, uint32 num) case Js::OpCode::ProfiledLoopStart: { + AssertOrFailFast(num < m_func->GetJITFunctionBody()->GetLoopCount()); // If we're in profiling SimpleJit and jitting loop bodies, we need to keep this until lowering. if (m_func->DoSimpleJitDynamicProfile() && m_func->GetJITFunctionBody()->DoJITLoopBody()) { @@ -2771,6 +2773,7 @@ IRBuilder::BuildUnsigned1(Js::OpCode newOpcode, uint32 offset, uint32 num) case Js::OpCode::ProfiledLoopEnd: { + AssertOrFailFast(num < m_func->GetJITFunctionBody()->GetLoopCount()); // TODO: Decide whether we want the implicit loop call flags to be recorded in simplejitted loop bodies if (m_func->DoSimpleJitDynamicProfile() && m_func->GetJITFunctionBody()->DoJITLoopBody()) {
lib/Backend/IRBuilder.h+13 −5 modified@@ -225,37 +225,45 @@ class IRBuilder AssertMsg(this->RegIsTemp(reg), "Processing non-temp reg as a temp?"); AssertMsg(this->tempMap, "Processing non-temp reg without a temp map?"); - return this->tempMap[reg - this->firstTemp]; + Js::RegSlot tempIndex = reg - this->firstTemp; + AssertOrFailFast(tempIndex < m_func->GetJITFunctionBody()->GetTempCount()); + return this->tempMap[tempIndex]; } void SetMappedTemp(Js::RegSlot reg, SymID tempId) { AssertMsg(this->RegIsTemp(reg), "Processing non-temp reg as a temp?"); AssertMsg(this->tempMap, "Processing non-temp reg without a temp map?"); - this->tempMap[reg - this->firstTemp] = tempId; + Js::RegSlot tempIndex = reg - this->firstTemp; + AssertOrFailFast(tempIndex < m_func->GetJITFunctionBody()->GetTempCount()); + this->tempMap[tempIndex] = tempId; } BOOL GetTempUsed(Js::RegSlot reg) { AssertMsg(this->RegIsTemp(reg), "Processing non-temp reg as a temp?"); AssertMsg(this->fbvTempUsed, "Processing non-temp reg without a used BV?"); - return this->fbvTempUsed->Test(reg - this->firstTemp); + Js::RegSlot tempIndex = reg - this->firstTemp; + AssertOrFailFast(tempIndex < m_func->GetJITFunctionBody()->GetTempCount()); + return this->fbvTempUsed->Test(tempIndex); } void SetTempUsed(Js::RegSlot reg, BOOL used) { AssertMsg(this->RegIsTemp(reg), "Processing non-temp reg as a temp?"); AssertMsg(this->fbvTempUsed, "Processing non-temp reg without a used BV?"); + Js::RegSlot tempIndex = reg - this->firstTemp; + AssertOrFailFast(tempIndex < m_func->GetJITFunctionBody()->GetTempCount()); if (used) { - this->fbvTempUsed->Set(reg - this->firstTemp); + this->fbvTempUsed->Set(tempIndex); } else { - this->fbvTempUsed->Clear(reg - this->firstTemp); + this->fbvTempUsed->Clear(tempIndex); } }
lib/Backend/IR.cpp+6 −0 modified@@ -84,6 +84,12 @@ Instr::HasEquivalentTypeCheckBailOut() const return this->HasBailOutInfo() && IR::IsEquivalentTypeCheckBailOutKind(this->GetBailOutKind()); } +bool +Instr::HasBailOnNoProfile() const +{ + return this->HasBailOutInfo() && this->GetBailOutKind() == IR::BailOutOnNoProfile; +} + void Instr::ChangeEquivalentToMonoTypeCheckBailOut() {
lib/Backend/IR.h+1 −0 modified@@ -203,6 +203,7 @@ class Instr bool HasAuxBailOut() const { return hasAuxBailOut; } bool HasTypeCheckBailOut() const; bool HasEquivalentTypeCheckBailOut() const; + bool HasBailOnNoProfile() const; void ClearBailOutInfo(); bool IsDstNotAlwaysConvertedToInt32() const; bool IsDstNotAlwaysConvertedToNumber() const;
lib/Backend/JITTimeFunctionBody.cpp+16 −1 modified@@ -870,6 +870,19 @@ JITTimeFunctionBody::GetConstantContent(Js::RegSlot location) const return obj; } +void +JITTimeFunctionBody::EnsureConsistentConstCount() const +{ + if (GetConstCount() == 0 || IsAsmJsMode()) + { + AssertOrFailFast(m_bodyData.constTableContent == nullptr); + } + else + { + AssertOrFailFast(m_bodyData.constTableContent != nullptr && GetConstCount() == m_bodyData.constTableContent->count); + } +} + intptr_t JITTimeFunctionBody::GetInlineCache(uint index) const { @@ -1059,20 +1072,22 @@ JITTimeFunctionBody::GetAuxDataAddr(uint offset) const void * JITTimeFunctionBody::ReadFromAuxData(uint offset) const { + AssertOrFailFast(offset < m_bodyData.auxDataCount); return (void *)(m_bodyData.auxData + offset); } void * JITTimeFunctionBody::ReadFromAuxContextData(uint offset) const { + AssertOrFailFast(offset < m_bodyData.auxContextDataCount); return (void *)(m_bodyData.auxContextData + offset); } const Js::PropertyIdArray * JITTimeFunctionBody::ReadPropertyIdArrayFromAuxData(uint offset) const { Js::PropertyIdArray * auxArray = (Js::PropertyIdArray *)(m_bodyData.auxData + offset); - Assert(offset + auxArray->GetDataSize() <= m_bodyData.auxDataCount); + AssertOrFailFast(AllocSizeMath::Add(offset, auxArray->GetDataSize()) <= m_bodyData.auxDataCount); return auxArray; }
lib/Backend/JITTimeFunctionBody.h+1 −0 modified@@ -98,6 +98,7 @@ class JITTimeFunctionBody bool CanInlineRecursively(uint depth, bool tryAggressive = true) const; bool NeedScopeObjectForArguments(bool hasNonSimpleParams) const; bool GetDoScopeObjectCreation() const; + void EnsureConsistentConstCount() const; const byte * GetByteCodeBuffer() const; StatementMapIDL * GetFullStatementMap() const;
lib/Backend/JITTimeProfileInfo.h+2 −1 modified@@ -34,6 +34,8 @@ class JITTimeProfileInfo Js::ImplicitCallFlags GetImplicitCallFlags() const; Js::LoopFlags GetLoopFlags(uint loopNum) const; + uint GetLoopCount() const; + uint16 GetConstantArgInfo(Js::ProfileId callSiteId) const; bool IsModulusOpByPowerOf2(Js::ProfileId profileId) const; @@ -116,7 +118,6 @@ class JITTimeProfileInfo Js::ProfileId GetProfiledSlotCount() const; Js::ArgSlot GetProfiledInParamsCount() const; uint GetProfiledFldCount() const; - uint GetLoopCount() const; BVFixed * GetLoopFlags() const; bool TestFlag(ProfileDataFlags flag) const;
lib/Backend/LinearScan.cpp+1 −1 modified@@ -3632,7 +3632,7 @@ void LinearScan::TrackInlineeArgLifetimes(IR::Instr* instr) Assert(this->currentBlock->inlineeStack.Count() == 0); } } - else if (instr->m_opcode == Js::OpCode::InlineeEnd) + else if (instr->m_opcode == Js::OpCode::InlineeEnd || instr->HasBailOnNoProfile()) { if (instr->m_func->m_hasInlineArgsOpt) {
lib/Backend/Lower.cpp+4 −4 modified@@ -7138,7 +7138,7 @@ Lowerer::GenerateCachedTypeCheck(IR::Instr *instrChk, IR::PropertySymOpnd *prope #if DBG NativeCodeData::GetDataDescription(typeCheckGuard, func->m_alloc), #endif - func); + func, true); this->addToLiveOnBackEdgeSyms->Set(func->GetTopFunc()->GetNativeCodeDataSym()->m_id); } else @@ -7311,7 +7311,7 @@ Lowerer::GenerateCachedTypeWithoutPropertyCheck(IR::Instr *instrInsert, IR::Prop #if DBG NativeCodeData::GetDataDescription(typePropertyGuard, this->m_func->m_alloc), #endif - this->m_func); + this->m_func, true); this->addToLiveOnBackEdgeSyms->Set(m_func->GetTopFunc()->GetNativeCodeDataSym()->m_id); } @@ -9471,7 +9471,7 @@ IR::Instr* Lowerer::LowerMultiBr(IR::Instr * instr, IR::JnHelperMethod helperMet #if DBG NativeCodeData::GetDataDescription(dictionary, this->m_func->m_alloc), #endif - this->m_func), instr); + this->m_func, true), instr); this->addToLiveOnBackEdgeSyms->Set(m_func->GetTopFunc()->GetNativeCodeDataSym()->m_id); @@ -13145,7 +13145,7 @@ Lowerer::GenerateBailOut(IR::Instr * instr, IR::BranchInstr * branchInstr, IR::L #if DBG NativeCodeData::GetDataDescription(bailOutInfo->bailOutRecord, this->m_func->m_alloc), #endif - m_func); + m_func, true); this->addToLiveOnBackEdgeSyms->Set(m_func->GetTopFunc()->GetNativeCodeDataSym()->m_id); }
lib/Backend/LowerMDShared.cpp+2 −2 modified@@ -6201,7 +6201,7 @@ LowererMD::EmitLoadFloatCommon(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertIn #if DBG NativeCodeData::GetDataDescription(pDouble, m_func->m_alloc), #endif - m_func); + m_func, true); GetLowerer()->addToLiveOnBackEdgeSyms->Set(m_func->GetTopFunc()->GetNativeCodeDataSym()->m_id); } @@ -7037,7 +7037,7 @@ LowererMD::LoadFloatValue(IR::Opnd * opndDst, double value, IR::Instr * instrIns #if DBG NativeCodeData::GetDataDescription(pValue, instrInsert->m_func->m_alloc), #endif - instrInsert->m_func); + instrInsert->m_func, true); } // movsd xmm, [reg+offset]
lib/Backend/LowerMDSharedSimd128.cpp+1 −1 modified@@ -390,7 +390,7 @@ IR::Instr* LowererMD::Simd128LoadConst(IR::Instr* instr) #if DBG NativeCodeData::GetDataDescription(pValue, m_func->m_alloc), #endif - m_func); + m_func, true); GetLowerer()->addToLiveOnBackEdgeSyms->Set(m_func->GetTopFunc()->GetNativeCodeDataSym()->m_id); }
lib/Backend/NativeCodeGenerator.cpp+2 −1 modified@@ -1783,7 +1783,8 @@ NativeCodeGenerator::WorkItemExceedsJITLimits(CodeGenWorkItem *const codeGenWork return (codeGenWork->GetScriptContext()->GetThreadContext()->GetCodeSize() >= Js::Constants::MaxThreadJITCodeHeapSize) || (ThreadContext::GetProcessCodeSize() >= Js::Constants::MaxProcessJITCodeHeapSize) || - (codeGenWork->GetByteCodeCount() >= (uint)CONFIG_FLAG(MaxJITFunctionBytecodeSize)); + (codeGenWork->GetByteCodeLength() >= (uint)CONFIG_FLAG(MaxJITFunctionBytecodeByteLength)) || + (codeGenWork->GetByteCodeCount() >= (uint)CONFIG_FLAG(MaxJITFunctionBytecodeCount)); } bool NativeCodeGenerator::Process(JsUtil::Job *const job, JsUtil::ParallelThreadData *threadData)
lib/Backend/ObjTypeSpecFldInfo.cpp+11 −2 modified@@ -114,6 +114,7 @@ Js::TypeId ObjTypeSpecFldInfo::GetTypeId(uint i) const { Assert(IsPoly()); + AssertOrFailFast(i < m_data.fixedFieldInfoArraySize); return (Js::TypeId)GetFixedFieldInfoArray()[i].GetType()->GetTypeId(); } @@ -151,6 +152,7 @@ intptr_t ObjTypeSpecFldInfo::GetFieldValue(uint i) const { Assert(IsPoly()); + AssertOrFailFast(i < m_data.fixedFieldInfoArraySize); return GetFixedFieldInfoArray()[i].GetFieldValue(); } @@ -164,6 +166,7 @@ intptr_t ObjTypeSpecFldInfo::GetFieldValueAsFixedDataIfAvailable() const { Assert(HasFixedValue() && GetFixedFieldCount() == 1); + AssertOrFailFast(m_data.fixedFieldInfoArraySize > 0); return GetFixedFieldInfoArray()[0].GetFieldValue(); } @@ -191,6 +194,7 @@ JITTypeHolder ObjTypeSpecFldInfo::GetType(uint i) const { Assert(i == 0 || IsPoly()); + AssertOrFailFast(i < m_data.fixedFieldInfoArraySize); JITType * type = GetFixedFieldInfoArray()[i].GetType(); if (!type) { @@ -223,6 +227,7 @@ ObjTypeSpecFldInfo::GetFixedFieldIfAvailableAsFixedFunction() { Assert(HasFixedValue()); Assert(IsMono() || (IsPoly() && !DoesntHaveEquivalence())); + AssertOrFailFast(m_data.fixedFieldInfoArraySize > 0); Assert(GetFixedFieldInfoArray()); if (GetFixedFieldInfoArray()[0].GetFuncInfoAddr() != 0) { @@ -236,9 +241,13 @@ ObjTypeSpecFldInfo::GetFixedFieldIfAvailableAsFixedFunction(uint i) { Assert(HasFixedValue()); Assert(IsPoly()); - if (m_data.fixedFieldCount > 0 && GetFixedFieldInfoArray()[i].GetFuncInfoAddr() != 0) + if (m_data.fixedFieldCount > 0) { - return &GetFixedFieldInfoArray()[i]; + AssertOrFailFast(i < m_data.fixedFieldInfoArraySize); + if (GetFixedFieldInfoArray()[i].GetFuncInfoAddr() != 0) + { + return &GetFixedFieldInfoArray()[i]; + } } return nullptr; }
lib/Backend/SccLiveness.cpp+1 −1 modified@@ -490,7 +490,7 @@ SCCLiveness::ProcessBailOutUses(IR::Instr * instr) // lifetimes wouldn't have been extended beyond the bailout point (InlineeEnd extends the lifetimes) // Extend argument lifetimes up to the bail out point to allow LinearScan::SpillInlineeArgs to spill // inlinee args. - if ((instr->GetBailOutKind() == IR::BailOutOnNoProfile) && !instr->m_func->IsTopFunc()) + if (instr->HasBailOnNoProfile() && !instr->m_func->IsTopFunc()) { Func * inlinee = instr->m_func; while (!inlinee->IsTopFunc())
lib/Backend/Security.cpp+243 −104 modified@@ -13,29 +13,108 @@ Security::EncodeLargeConstants() return; } - FOREACH_REAL_INSTR_IN_FUNC_EDITING(instr, instrNext, this->func) + uint prevInstrConstSize = 0; + FOREACH_INSTR_IN_FUNC_EDITING(instr, instrNext, this->func) { if (!instr->IsRealInstr()) { + if (instr->IsLabelInstr()) + { + IR::LabelInstr * label = instr->AsLabelInstr(); + + if (label->labelRefs.Count() > 1 || (label->labelRefs.Count() == 1 && label->labelRefs.Head() != label->m_prev)) + { + if (this->cookieOpnd != nullptr) + { + this->cookieOpnd->Free(this->func); + } + this->baseOpnd = nullptr; + this->cookieOpnd = nullptr; + this->basePlusCookieOpnd = nullptr; + } + } continue; } + + IR::Opnd *src1 = instr->GetSrc1(); + IR::Opnd *src2 = instr->GetSrc2(); IR::Opnd *dst = instr->GetDst(); - if (dst) + + if (dst && this->baseOpnd && dst->IsEqual(this->baseOpnd)) { - this->EncodeOpnd(instr, dst); + if (this->cookieOpnd != nullptr) + { + this->cookieOpnd->Free(this->func); + } + this->baseOpnd = nullptr; + this->cookieOpnd = nullptr; + this->basePlusCookieOpnd = nullptr; } - IR::Opnd *src1 = instr->GetSrc1(); + + uint currInstrConstSize = 0; + uint dstSize = dst ? CalculateConstSize(dst) : 0; + uint src1Size = 0; + uint src2Size = 0; if (src1) { - this->EncodeOpnd(instr, src1); - - IR::Opnd *src2 = instr->GetSrc2(); + src1Size = CalculateConstSize(src1); if (src2) { - this->EncodeOpnd(instr, src2); + src2Size = CalculateConstSize(src2); + } + } + + prevInstrConstSize = currInstrConstSize; + currInstrConstSize = dstSize + src1Size + src2Size; + + // we don't need to blind constants if user controlled byte size < 3 + if (currInstrConstSize + prevInstrConstSize <= 2 && !PHASE_FORCE1(Js::EncodeConstantsPhase)) + { + continue; + } + + bool isSrc1EqualDst = false; + if (dstSize >= 2) + { + // don't count instrs where dst == src1 against size + if (src1 && dstSize == src1Size && src1->IsEqual(dst)) + { + currInstrConstSize -= dstSize; + isSrc1EqualDst = true; + + if (currInstrConstSize + prevInstrConstSize <= 2 && !PHASE_FORCE1(Js::EncodeConstantsPhase)) + { + continue; + } + } + + this->EncodeOpnd(instr, dst); + if (isSrc1EqualDst) + { + instr->ReplaceSrc1(dst); + } + currInstrConstSize -= dstSize; + if (currInstrConstSize + prevInstrConstSize <= 2 && !PHASE_FORCE1(Js::EncodeConstantsPhase)) + { + continue; + } + } + if (src1Size >= 2 && !isSrc1EqualDst) + { + this->EncodeOpnd(instr, src1); + currInstrConstSize -= src1Size; + if (currInstrConstSize + prevInstrConstSize <= 2 && !PHASE_FORCE1(Js::EncodeConstantsPhase)) + { + continue; } } - } NEXT_REAL_INSTR_IN_FUNC_EDITING; + if (src2Size >= 2) + { + this->EncodeOpnd(instr, src2); + currInstrConstSize -= src2Size; + } + } NEXT_INSTR_IN_FUNC_EDITING; + } int @@ -91,10 +170,10 @@ Security::InsertNOPs() int count = 0; IR::Instr *instr = this->func->m_headInstr; - while(true) + while (true) { count = this->GetNextNOPInsertPoint(); - while(instr && count--) + while (instr && count--) { instr = instr->GetNextRealInstr(); } @@ -174,7 +253,7 @@ Security::InsertSmallNOP(IR::Instr * instr, DWORD nopSize) IR::Instr *nopInstr = nullptr; - switch(nopSize) + switch (nopSize) { case 1: case 2: @@ -210,29 +289,54 @@ Security::DontEncode(IR::Opnd *opnd) { IR::AddrOpnd *addrOpnd = opnd->AsAddrOpnd(); return (addrOpnd->m_dontEncode || - !addrOpnd->IsVar() || - addrOpnd->m_address == nullptr || - !Js::TaggedNumber::Is(addrOpnd->m_address)); + !addrOpnd->IsVar() || + addrOpnd->m_address == nullptr || + !Js::TaggedNumber::Is(addrOpnd->m_address)); } - - case IR::OpndKindHelperCall: - // Never encode helper call addresses, as these are always internal constants. + case IR::OpndKindIndir: + { + IR::IndirOpnd *indirOpnd = opnd->AsIndirOpnd(); + return indirOpnd->m_dontEncode || indirOpnd->GetOffset() == 0; + } + default: return true; } - - return false; } -void -Security::EncodeOpnd(IR::Instr *instr, IR::Opnd *opnd) +uint +Security::CalculateConstSize(IR::Opnd *opnd) { - IR::RegOpnd *newOpnd; - bool isSrc2 = false; - - if (Security::DontEncode(opnd)) + if (DontEncode(opnd)) { - return; + return 0; } + switch (opnd->GetKind()) + { + case IR::OpndKindIntConst: + { + IR::IntConstOpnd *intConstOpnd = opnd->AsIntConstOpnd(); + return GetByteCount(intConstOpnd->GetValue()); + } + case IR::OpndKindAddr: + { + IR::AddrOpnd *addrOpnd = opnd->AsAddrOpnd(); + return Js::TaggedInt::Is(addrOpnd->m_address) ? GetByteCount(Js::TaggedInt::ToInt32(addrOpnd->m_address)) : GetByteCount((intptr_t)addrOpnd->m_address); + } + case IR::OpndKindIndir: + { + IR::IndirOpnd * indirOpnd = opnd->AsIndirOpnd(); + return GetByteCount(indirOpnd->GetOffset()); + } + default: + Assume(UNREACHED); + } + return 0; +} +bool +Security::EncodeOpnd(IR::Instr * instr, IR::Opnd *opnd) +{ + IR::RegOpnd *newOpnd; + bool isSrc2 = false; const auto unlinkSrc = [&]() { if (opnd != instr->GetSrc1()) @@ -247,38 +351,12 @@ Security::EncodeOpnd(IR::Instr *instr, IR::Opnd *opnd) } }; - switch(opnd->GetKind()) - { - case IR::OpndKindInt64Const: + switch (opnd->GetKind()) { -#if TARGET_64 - IR::Int64ConstOpnd *intConstOpnd = opnd->AsInt64ConstOpnd(); - if (!this->IsLargeConstant(intConstOpnd->GetValue())) - { - return; - } - unlinkSrc(); - int64 encodedValue = EncodeValue(instr, intConstOpnd, intConstOpnd->GetValue(), &newOpnd); - intConstOpnd->SetEncodedValue(encodedValue); -#else - Assert(UNREACHED); - return; -#endif - } - break; - case IR::OpndKindIntConst: { IR::IntConstOpnd *intConstOpnd = opnd->AsIntConstOpnd(); - if ( -#if TARGET_64 - IRType_IsInt64(intConstOpnd->GetType()) ? !this->IsLargeConstant(intConstOpnd->GetValue()) : -#endif - !this->IsLargeConstant(intConstOpnd->AsInt32())) - { - return; - } unlinkSrc(); intConstOpnd->SetEncodedValue(EncodeValue(instr, intConstOpnd, intConstOpnd->GetValue(), &newOpnd)); @@ -289,12 +367,6 @@ Security::EncodeOpnd(IR::Instr *instr, IR::Opnd *opnd) { IR::AddrOpnd *addrOpnd = opnd->AsAddrOpnd(); - // Only encode large constants. Small ones don't allow control of enough bits - if (Js::TaggedInt::Is(addrOpnd->m_address) && !this->IsLargeConstant(Js::TaggedInt::ToInt32(addrOpnd->m_address))) - { - return; - } - unlinkSrc(); addrOpnd->SetEncodedValue((Js::Var)this->EncodeValue(instr, addrOpnd, (IntConstType)addrOpnd->m_address, &newOpnd), addrOpnd->GetAddrOpndKind()); @@ -305,29 +377,63 @@ Security::EncodeOpnd(IR::Instr *instr, IR::Opnd *opnd) { IR::IndirOpnd *indirOpnd = opnd->AsIndirOpnd(); - if (!this->IsLargeConstant(indirOpnd->GetOffset()) || indirOpnd->m_dontEncode) + // Using 32 bit cookie causes major perf loss on the subsequent indirs, so only support this path for 16 bit offset + // It's relatively rare for base to be null or to have index + offset, so fall back to the more generic xor method for these + if (indirOpnd->GetBaseOpnd() && indirOpnd->GetIndexOpnd() == nullptr && Math::FitsInWord(indirOpnd->GetOffset())) { - return; + if (!this->baseOpnd || !this->baseOpnd->IsEqual(indirOpnd->GetBaseOpnd())) + { + if (this->cookieOpnd != nullptr) + { + this->cookieOpnd->Free(this->func); + } + this->cookieOpnd = BuildCookieOpnd(TyInt16, instr->m_func); + this->basePlusCookieOpnd = IR::RegOpnd::New(TyMachReg, instr->m_func); + this->baseOpnd = indirOpnd->GetBaseOpnd(); + IR::IndirOpnd * indir = IR::IndirOpnd::New(this->baseOpnd, this->cookieOpnd->AsInt32(), TyMachReg, instr->m_func); + Lowerer::InsertLea(this->basePlusCookieOpnd, indir, instr); + } + int32 diff = indirOpnd->GetOffset() - this->cookieOpnd->AsInt32(); + indirOpnd->SetOffset((int32)diff); + indirOpnd->SetBaseOpnd(this->basePlusCookieOpnd); + return true; } - AssertMsg(indirOpnd->GetIndexOpnd() == nullptr, "Code currently doesn't support indir with offset and indexOpnd"); - IR::IntConstOpnd *indexOpnd = IR::IntConstOpnd::New(indirOpnd->GetOffset(), TyInt32, instr->m_func); + IR::IntConstOpnd *indexOpnd = IR::IntConstOpnd::New(indirOpnd->GetOffset(), TyMachReg, instr->m_func); + + indexOpnd->SetValue(EncodeValue(instr, indexOpnd, indexOpnd->GetValue(), &newOpnd)); - indexOpnd->SetEncodedValue(EncodeValue(instr, indexOpnd, indexOpnd->GetValue(), &newOpnd)); indirOpnd->SetOffset(0); - indirOpnd->SetIndexOpnd(newOpnd); + if (indirOpnd->GetIndexOpnd() != nullptr) + { + // update the base rather than the index, because index might have scale + if (indirOpnd->GetBaseOpnd() != nullptr) + { + IR::RegOpnd * newBaseOpnd = IR::RegOpnd::New(TyMachReg, instr->m_func); + Lowerer::InsertAdd(false, newBaseOpnd, newOpnd, indirOpnd->GetBaseOpnd(), instr); + indirOpnd->SetBaseOpnd(newBaseOpnd); + } + else + { + indirOpnd->SetBaseOpnd(newOpnd); + } + } + else + { + indirOpnd->SetIndexOpnd(newOpnd); + } } - return; + return true; default: - return; + return false; } IR::Opnd *dst = instr->GetDst(); if (dst) { -#if _M_X64 +#if TARGET_64 // Ensure the left and right operand has the same type (that might not be true for constants on x64) newOpnd = (IR::RegOpnd *)newOpnd->UseWithNewType(dst->GetType(), instr->m_func); #endif @@ -347,32 +453,70 @@ Security::EncodeOpnd(IR::Instr *instr, IR::Opnd *opnd) } } - LowererMD::ImmedSrcToReg(instr, newOpnd, isSrc2 ? 2 : 1); + LowererMD::ImmedSrcToReg(instr, newOpnd, isSrc2 ? 2 : 1); + return true; +} + +IR::IntConstOpnd * +Security::BuildCookieOpnd(IRType type, Func * func) +{ + IntConstType cookie = 0; + switch (type) + { + case TyInt8: + cookie = (int8)Math::Rand(); + break; + case TyUint8: + cookie = (uint8)Math::Rand(); + break; + case TyInt16: + cookie = (int16)Math::Rand(); + break; + case TyUint16: + cookie = (uint16)Math::Rand(); + break; +#if TARGET_32 + case TyVar: +#endif + case TyInt32: + cookie = (int32)Math::Rand(); + break; + case TyUint32: + cookie = (uint32)Math::Rand(); + break; +#if TARGET_64 + case TyVar: + case TyInt64: + case TyUint64: + cookie = Math::Rand(); + break; +#endif + default: + Assume(UNREACHED); + } + IR::IntConstOpnd * cookieOpnd = IR::IntConstOpnd::New(cookie, type, func); + +#if DBG_DUMP + cookieOpnd->SetName(_u("cookie")); +#endif + return cookieOpnd; } IntConstType -Security::EncodeValue(IR::Instr *instr, IR::Opnd *opnd, IntConstType constValue, IR::RegOpnd **pNewOpnd) +Security::EncodeValue(IR::Instr * instr, IR::Opnd *opnd, IntConstType constValue, _Out_ IR::RegOpnd **pNewOpnd) { if (opnd->GetType() == TyInt32 || opnd->GetType() == TyInt16 || opnd->GetType() == TyInt8 -#if _M_IX86 +#if TARGET_32 || opnd->GetType() == TyVar #endif ) { - int32 cookie = (int32)Math::Rand(); - IR::RegOpnd *regOpnd = IR::RegOpnd::New(StackSym::New(TyInt32, instr->m_func), TyInt32, instr->m_func); + IR::RegOpnd *regOpnd = IR::RegOpnd::New(StackSym::New(opnd->GetType(), instr->m_func), opnd->GetType(), instr->m_func); IR::Instr * instrNew = LowererMD::CreateAssign(regOpnd, opnd, instr); - - IR::IntConstOpnd * cookieOpnd = IR::IntConstOpnd::New(cookie, TyInt32, instr->m_func); - -#if DBG_DUMP - cookieOpnd->SetName(_u("cookie")); -#endif - - instrNew = IR::Instr::New(Js::OpCode::Xor_I4, regOpnd, regOpnd, cookieOpnd, instr->m_func); + IR::IntConstOpnd * cookieOpnd = BuildCookieOpnd(opnd->GetType(), instr->m_func); + instrNew = IR::Instr::New(LowererMD::MDXorOpcode, regOpnd, regOpnd, cookieOpnd, instr->m_func); instr->InsertBefore(instrNew); - - LowererMD::EmitInt4Instr(instrNew); + LowererMD::Legalize(instrNew); StackSym * stackSym = regOpnd->m_sym; Assert(!stackSym->m_isSingleDef); @@ -383,25 +527,19 @@ Security::EncodeValue(IR::Instr *instr, IR::Opnd *opnd, IntConstType constValue, *pNewOpnd = regOpnd; int32 value = (int32)constValue; - value = value ^ cookie; + value = value ^ cookieOpnd->AsInt32(); return value; } else if (opnd->GetType() == TyUint32 || opnd->GetType() == TyUint16 || opnd->GetType() == TyUint8) { - uint32 cookie = (uint32)Math::Rand(); - IR::RegOpnd *regOpnd = IR::RegOpnd::New(StackSym::New(TyUint32, instr->m_func), TyUint32, instr->m_func); + IR::RegOpnd *regOpnd = IR::RegOpnd::New(StackSym::New(opnd->GetType(), instr->m_func), opnd->GetType(), instr->m_func); IR::Instr * instrNew = LowererMD::CreateAssign(regOpnd, opnd, instr); - IR::IntConstOpnd * cookieOpnd = IR::IntConstOpnd::New(cookie, TyUint32, instr->m_func); - -#if DBG_DUMP - cookieOpnd->SetName(_u("cookie")); -#endif + IR::IntConstOpnd * cookieOpnd = BuildCookieOpnd(opnd->GetType(), instr->m_func); - instrNew = IR::Instr::New(Js::OpCode::Xor_I4, regOpnd, regOpnd, cookieOpnd, instr->m_func); + instrNew = IR::Instr::New(LowererMD::MDXorOpcode, regOpnd, regOpnd, cookieOpnd, instr->m_func); instr->InsertBefore(instrNew); - - LowererMD::EmitInt4Instr(instrNew); + LowererMD::Legalize(instrNew); StackSym * stackSym = regOpnd->m_sym; Assert(!stackSym->m_isSingleDef); @@ -412,32 +550,33 @@ Security::EncodeValue(IR::Instr *instr, IR::Opnd *opnd, IntConstType constValue, *pNewOpnd = regOpnd; uint32 value = (uint32)constValue; - value = value ^ cookie; + value = value ^ cookieOpnd->AsUint32(); return (IntConstType)value; } else { -#ifdef _M_X64 +#if TARGET_64 return this->EncodeAddress(instr, opnd, constValue, pNewOpnd); #else Assert(false); + // (Prefast warning on failure to assign *pNewOpnd.) + *pNewOpnd = nullptr; return 0; #endif } } -#ifdef _M_X64 +#if TARGET_64 size_t -Security::EncodeAddress(IR::Instr *instr, IR::Opnd *opnd, size_t value, IR::RegOpnd **pNewOpnd) +Security::EncodeAddress(IR::Instr * instr, IR::Opnd *opnd, size_t value, _Out_ IR::RegOpnd **pNewOpnd) { IR::Instr *instrNew = nullptr; - IR::RegOpnd *regOpnd = IR::RegOpnd::New(TyMachReg, instr->m_func); + IR::RegOpnd *regOpnd = IR::RegOpnd::New(TyMachReg, instr->m_func); instrNew = LowererMD::CreateAssign(regOpnd, opnd, instr); - size_t cookie = (size_t)Math::Rand(); - IR::IntConstOpnd *cookieOpnd = IR::IntConstOpnd::New(cookie, TyMachReg, instr->m_func); - instrNew = IR::Instr::New(Js::OpCode::XOR, regOpnd, regOpnd, cookieOpnd, instr->m_func); + IR::IntConstOpnd *cookieOpnd = BuildCookieOpnd(TyMachReg, instr->m_func); + instrNew = IR::Instr::New(LowererMD::MDXorOpcode, regOpnd, regOpnd, cookieOpnd, instr->m_func); instr->InsertBefore(instrNew); LowererMD::Legalize(instrNew); @@ -448,6 +587,6 @@ Security::EncodeAddress(IR::Instr *instr, IR::Opnd *opnd, size_t value, IR::RegO stackSym->constantValue = value; *pNewOpnd = regOpnd; - return value ^ cookie; + return value ^ cookieOpnd->GetValue(); } #endif
lib/Backend/Security.h+28 −9 modified@@ -8,26 +8,45 @@ class Security { private: Func *func; + IR::RegOpnd * basePlusCookieOpnd; + IR::IntConstOpnd * cookieOpnd; + IR::RegOpnd * baseOpnd; public: - Security(Func * func) : func(func) {} + Security(Func * func) : func(func), basePlusCookieOpnd(nullptr), cookieOpnd(nullptr), baseOpnd(nullptr) { } void EncodeLargeConstants(); void InsertNOPs(); static bool DontEncode(IR::Opnd *opnd); static void InsertRandomFunctionPad(IR::Instr * instrBeforeInstr); private: - void EncodeOpnd(IR::Instr *instr, IR::Opnd *opnd); - IntConstType EncodeValue(IR::Instr *instr, IR::Opnd *opnd, IntConstType constValue, IR::RegOpnd ** pNewOpnd); -#ifdef _M_X64 - size_t EncodeAddress(IR::Instr *instr, IR::Opnd *opnd, size_t value, IR::RegOpnd **pNewOpnd); + bool EncodeOpnd(IR::Instr *instr, IR::Opnd *opnd); + uint CalculateConstSize(IR::Opnd *opnd); + IntConstType EncodeValue(IR::Instr *instr, IR::Opnd *opnd, IntConstType constValue, _Out_ IR::RegOpnd ** pNewOpnd); +#if TARGET_64 + size_t EncodeAddress(IR::Instr *instr, IR::Opnd *opnd, size_t value, _Out_ IR::RegOpnd **pNewOpnd); #endif + static IR::IntConstOpnd * BuildCookieOpnd(IRType type, Func * func); - // Large constants have more than 16 significant bits. - // Constants except these are considered large: 0x0000????, 0xffff????, 0x????0000, 0x????ffff - static bool IsLargeConstant(int32 value) { return static_cast<int16>(value) != 0 && static_cast<int16>(value) != -1 && (value >> 16) != 0 && (value >> 16) != -1; } - static bool IsLargeConstant(int64 value) { return IsLargeConstant((int32)value) || IsLargeConstant((int32)(value >> 16)) || IsLargeConstant((int32)(value >> 32)); } + template<typename T> static uint GetByteCount(T value) + { + uint byteCount = 0; + for (uint i = 0; i < sizeof(T); ++i) + { + if (IsByteSet(value, i)) + { + ++byteCount; + } + } + return byteCount; + } + + template<typename T> static bool IsByteSet(T value, uint32 index) + { + const byte byteValue = (byte)(value >> (index * MachBits)); + return byteValue != 0 && byteValue != 0xFF; + } void InsertNOPBefore(IR::Instr *instr); int GetNextNOPInsertPoint();
lib/Backend/ServerThreadContext.cpp+13 −13 modified@@ -8,27 +8,26 @@ #if ENABLE_OOP_NATIVE_CODEGEN #include "JITServer/JITServer.h" -ServerThreadContext::ServerThreadContext(ThreadContextDataIDL * data) : - m_autoProcessHandle((HANDLE)data->processHandle), +ServerThreadContext::ServerThreadContext(ThreadContextDataIDL * data, HANDLE processHandle) : + m_autoProcessHandle(processHandle), m_threadContextData(*data), m_refCount(0), m_numericPropertyBV(nullptr), - m_preReservedSectionAllocator((HANDLE)data->processHandle), - m_sectionAllocator((HANDLE)data->processHandle), - m_thunkPageAllocators(nullptr, /* allocXData */ false, &m_sectionAllocator, nullptr, (HANDLE)data->processHandle), - m_codePageAllocators(nullptr, ALLOC_XDATA, &m_sectionAllocator, &m_preReservedSectionAllocator, (HANDLE)data->processHandle), - m_codeGenAlloc(nullptr, nullptr, &m_codePageAllocators, (HANDLE)data->processHandle), + m_preReservedSectionAllocator(processHandle), + m_sectionAllocator(processHandle), + m_thunkPageAllocators(nullptr, /* allocXData */ false, &m_sectionAllocator, nullptr, processHandle), + m_codePageAllocators(nullptr, ALLOC_XDATA, &m_sectionAllocator, &m_preReservedSectionAllocator, processHandle), + m_codeGenAlloc(nullptr, nullptr, &m_codePageAllocators, processHandle), #if defined(_CONTROL_FLOW_GUARD) && (_M_IX86 || _M_X64) - m_jitThunkEmitter(this, &m_sectionAllocator, (HANDLE)data->processHandle), + m_jitThunkEmitter(this, &m_sectionAllocator, processHandle), #endif m_pageAlloc(nullptr, Js::Configuration::Global.flags, PageAllocatorType_BGJIT, AutoSystemInfo::Data.IsLowMemoryProcess() ? PageAllocator::DefaultLowMaxFreePageCount : PageAllocator::DefaultMaxFreePageCount ) { - ucrtC99MathApis.Ensure(); - m_pid = GetProcessId((HANDLE)data->processHandle); + m_pid = GetProcessId(processHandle); #if !_M_X64_OR_ARM64 && _CONTROL_FLOW_GUARD m_codeGenAlloc.canCreatePreReservedSegment = data->allowPrereserveAlloc != FALSE; @@ -112,7 +111,7 @@ ServerThreadContext::IsThreadBound() const HANDLE ServerThreadContext::GetProcessHandle() const { - return reinterpret_cast<HANDLE>(m_threadContextData.processHandle); + return m_autoProcessHandle.GetHandle(); } CustomHeap::OOPCodePageAllocators * @@ -159,10 +158,11 @@ ServerThreadContext::GetRuntimeCRTBaseAddress() const return static_cast<intptr_t>(m_threadContextData.crtBaseAddress); } +/* static */ intptr_t -ServerThreadContext::GetJITCRTBaseAddress() const +ServerThreadContext::GetJITCRTBaseAddress() { - return (intptr_t)ucrtC99MathApis.GetHandle(); + return (intptr_t)AutoSystemInfo::GetCRTHandle(); } PageAllocator *
lib/Backend/ServerThreadContext.h+3 −11 modified@@ -11,7 +11,7 @@ class ServerThreadContext : public ThreadContextInfo public: typedef BVSparseNode<JitArenaAllocator> BVSparseNode; - ServerThreadContext(ThreadContextDataIDL * data); + ServerThreadContext(ThreadContextDataIDL * data, HANDLE processHandle); ~ServerThreadContext(); virtual HANDLE GetProcessHandle() const override; @@ -56,18 +56,10 @@ class ServerThreadContext : public ThreadContextInfo intptr_t GetRuntimeChakraBaseAddress() const; intptr_t GetRuntimeCRTBaseAddress() const; - intptr_t GetJITCRTBaseAddress() const; -private: + static intptr_t GetJITCRTBaseAddress(); - class AutoCloseHandle - { - public: - AutoCloseHandle(HANDLE handle) : handle(handle) { Assert(handle != GetCurrentProcess()); } - ~AutoCloseHandle() { CloseHandle(this->handle); } - private: - HANDLE handle; - }; +private: AutoCloseHandle m_autoProcessHandle;
lib/Common/Common/MathUtil.h+2 −0 modified@@ -47,6 +47,8 @@ class Math static bool FitsInDWord(size_t value) { return ((size_t)(signed int)(value & 0xFFFFFFFF) == value); } static bool FitsInDWord(int64 value) { return ((int64)(signed int)(value & 0xFFFFFFFF) == value); } + static bool FitsInWord(int32 value) { return ((int32)(int16)(value & 0xFFFF) == value); } + static UINT_PTR Rand(); static bool IsPow2(int32 val) { return (val > 0 && ((val-1) & val) == 0); } static uint32 NextPowerOf2(uint32 n);
lib/Common/ConfigFlagsList.h+4 −2 modified@@ -609,7 +609,8 @@ PHASE(All) #define DEFAULT_CONFIG_EnumerateSpecialPropertiesInDebugger (true) #endif -#define DEFAULT_CONFIG_MaxJITFunctionBytecodeSize (120000) +#define DEFAULT_CONFIG_MaxJITFunctionBytecodeByteLength (4800000) +#define DEFAULT_CONFIG_MaxJITFunctionBytecodeCount (120000) #define DEFAULT_CONFIG_JitQueueThreshold (6) @@ -1243,7 +1244,8 @@ FLAGR(Number, MinDeferredFuncTokenCount, "Minimum length in tokens of defer-pa #if DBG FLAGNR(Number, SkipFuncCountForBailOnNoProfile, "Initial Number of functions in a func body to be skipped from forcibly inserting BailOnNoProfile.", DEFAULT_CONFIG_SkipFuncCountForBailOnNoProfile) #endif -FLAGNR(Number, MaxJITFunctionBytecodeSize, "The biggest function we'll JIT (bytecode size)", DEFAULT_CONFIG_MaxJITFunctionBytecodeSize) +FLAGNR(Number, MaxJITFunctionBytecodeByteLength, "The biggest function we'll JIT (bytecode bytelength)", DEFAULT_CONFIG_MaxJITFunctionBytecodeByteLength) +FLAGNR(Number, MaxJITFunctionBytecodeCount, "The biggest function we'll JIT (bytecode count)", DEFAULT_CONFIG_MaxJITFunctionBytecodeCount) FLAGNR(Number, MaxLoopsPerFunction , "Maximum number of loops in any function in the script", DEFAULT_CONFIG_MaxLoopsPerFunction) FLAGNR(Number, FuncObjectInlineCacheThreshold , "Maximum number of inline caches a function body may have to allow for inline caches to be allocated on the function object", DEFAULT_CONFIG_FuncObjectInlineCacheThreshold) FLAGNR(Boolean, NoDeferParse , "Disable deferred parsing", false)
lib/Common/Core/SysInfo.cpp+5 −0 modified@@ -397,6 +397,11 @@ LPCWSTR AutoSystemInfo::GetJscriptDllFileName() return (LPCWSTR)Data.binaryName; } +HMODULE AutoSystemInfo::GetCRTHandle() +{ + return GetModuleHandle(_u("msvcrt.dll")); +} + bool AutoSystemInfo::IsLowMemoryProcess() { ULONG64 commit = ULONG64(-1);
lib/Common/Core/SysInfo.h+1 −0 modified@@ -49,6 +49,7 @@ class AutoSystemInfo : public SYSTEM_INFO static DWORD SaveModuleFileName(HANDLE hMod); static LPCWSTR GetJscriptDllFileName(); static HRESULT GetJscriptFileVersion(DWORD* majorVersion, DWORD* minorVersion, DWORD *buildDateHash = nullptr, DWORD *buildTimeHash = nullptr); + static HMODULE GetCRTHandle(); #if DBG static bool IsInitialized(); #endif
lib/JITClient/JITManager.cpp+12 −24 modified@@ -31,7 +31,6 @@ JITManager::JITManager() : m_oopJitEnabled(false), m_isJITServer(false), m_failingHRESULT(S_OK), - m_serverHandle(nullptr), m_jitConnectionId() { } @@ -42,10 +41,6 @@ JITManager::~JITManager() { RpcBindingFree(&m_rpcBindingHandle); } - if (m_serverHandle) - { - CloseHandle(m_serverHandle); - } } /* static */ @@ -187,12 +182,6 @@ JITManager::IsConnected() const return m_rpcBindingHandle != nullptr && !HasJITFailed(); } -HANDLE -JITManager::GetServerHandle() const -{ - return m_serverHandle; -} - void JITManager::EnableOOPJIT() { @@ -230,7 +219,6 @@ JITManager::ConnectRpcServer(__in HANDLE jitProcessHandle, __in_opt void* server { Assert(IsOOPJITEnabled()); Assert(m_rpcBindingHandle == nullptr); - Assert(m_serverHandle == nullptr); HRESULT hr = E_FAIL; @@ -240,12 +228,6 @@ JITManager::ConnectRpcServer(__in HANDLE jitProcessHandle, __in_opt void* server return E_FAIL; } - if (!DuplicateHandle(GetCurrentProcess(), jitProcessHandle, GetCurrentProcess(), &m_serverHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto FailureCleanup; - } - hr = CreateBinding(jitProcessHandle, serverSecurityDescriptor, &connectionUuid, &m_rpcBindingHandle); if (FAILED(hr)) { @@ -257,11 +239,6 @@ JITManager::ConnectRpcServer(__in HANDLE jitProcessHandle, __in_opt void* server return hr; FailureCleanup: - if (m_serverHandle) - { - CloseHandle(m_serverHandle); - m_serverHandle = nullptr; - } if (m_rpcBindingHandle) { RpcBindingFree(&m_rpcBindingHandle); @@ -298,6 +275,9 @@ JITManager::Shutdown() HRESULT JITManager::InitializeThreadContext( __in ThreadContextDataIDL * data, +#ifdef USE_RPC_HANDLE_MARSHALLING + __in HANDLE processHandle, +#endif __out PPTHREADCONTEXT_HANDLE threadContextInfoAddress, __out intptr_t * prereservedRegionAddr, __out intptr_t * jitThunkAddr) @@ -307,7 +287,15 @@ JITManager::InitializeThreadContext( HRESULT hr = E_FAIL; RpcTryExcept { - hr = ClientInitializeThreadContext(m_rpcBindingHandle, data, threadContextInfoAddress, prereservedRegionAddr, jitThunkAddr); + hr = ClientInitializeThreadContext( + m_rpcBindingHandle, + data, +#ifdef USE_RPC_HANDLE_MARSHALLING + processHandle, +#endif + threadContextInfoAddress, + prereservedRegionAddr, + jitThunkAddr); } RpcExcept(RpcExceptionFilter(RpcExceptionCode())) {
lib/JITClient/JITManager.h+6 −8 modified@@ -31,10 +31,11 @@ class JITManager void SetJITFailed(HRESULT hr); bool HasJITFailed() const; - HANDLE GetServerHandle() const; - HRESULT InitializeThreadContext( __in ThreadContextDataIDL * data, +#ifdef USE_RPC_HANDLE_MARSHALLING + __in HANDLE processHandle, +#endif __out PPTHREADCONTEXT_HANDLE threadContextInfoAddress, __out intptr_t * prereservedRegionAddr, __out intptr_t * jitThunkAddr); @@ -123,7 +124,6 @@ class JITManager __out RPC_BINDING_HANDLE* bindingHandle); RPC_BINDING_HANDLE m_rpcBindingHandle; - HANDLE m_serverHandle; UUID m_jitConnectionId; bool m_oopJitEnabled; bool m_isJITServer; @@ -147,13 +147,11 @@ class JITManager void EnableOOPJIT() { Assert(false); } void SetJITFailed(HRESULT hr) { Assert(false); } - HANDLE GetServerHandle() const - { - Assert(false); return HANDLE(); - } - HRESULT InitializeThreadContext( __in ThreadContextDataIDL * data, +#ifdef USE_RPC_HANDLE_MARSHALLING + __in HANDLE processHandle, +#endif __out PPTHREADCONTEXT_HANDLE threadContextInfoAddress, __out intptr_t *prereservedRegionAddr, __out intptr_t * jitThunkAddr)
lib/JITIDL/ChakraJIT.idl+10 −0 modified@@ -8,6 +8,13 @@ cpp_quote("#define __JITTypes_h__") #include "JITTypes.h" +// we can't include Windows.h, so for simplicity, let's define these values ourself +// from: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684880.aspx +#define PROCESS_VM_OPERATION 0x0008 +#define PROCESS_VM_READ 0x0010 +#define PROCESS_VM_WRITE 0x0020 +#define PROCESS_QUERY_LIMITED_INFORMATION 0x1000 + cpp_quote("#endif //__JITTypes_h__") [ @@ -21,6 +28,9 @@ interface IChakraJIT HRESULT InitializeThreadContext( [in] handle_t binding, [in] ThreadContextDataIDL * threadData, +#ifdef USE_RPC_HANDLE_MARSHALLING + [in, system_handle(sh_process, PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_QUERY_LIMITED_INFORMATION)] HANDLE processHandle, +#endif [out] PPTHREADCONTEXT_HANDLE threadContextInfoAddress, [out] CHAKRA_PTR * prereservedRegionAddr, [out] CHAKRA_PTR * jitThunkAddr);
lib/JITIDL/JITTypes.h+8 −1 modified@@ -7,6 +7,14 @@ #ifdef __midl import "wtypes.idl"; +#include "sdkddkver.h" +#endif + +#if defined(WINVER) && WINVER >= _WIN32_WINNT_WINBLUE // on 8.1+, RPC can marshal process handle for us +#ifdef __midl +cpp_quote("#define USE_RPC_HANDLE_MARSHALLING 1") +#endif +#define USE_RPC_HANDLE_MARSHALLING 1 #endif #if defined(_M_IX86) || defined(_M_ARM) @@ -302,7 +310,6 @@ typedef struct ThreadContextDataIDL IDL_PAD2(0) X64_PAD4(1) - CHAKRA_PTR processHandle; CHAKRA_PTR chakraBaseAddress; CHAKRA_PTR crtBaseAddress; CHAKRA_PTR threadStackLimitAddr;
lib/JITServer/JITServer.cpp+30 −19 modified@@ -193,6 +193,9 @@ HRESULT ServerInitializeThreadContext( /* [in] */ handle_t binding, /* [in] */ __RPC__in ThreadContextDataIDL * threadContextData, +#ifdef USE_RPC_HANDLE_MARSHALLING + /* [in] */ __RPC__in HANDLE processHandle, +#endif /* [out] */ __RPC__deref_out_opt PPTHREADCONTEXT_HANDLE threadContextInfoAddress, /* [out] */ __RPC__out intptr_t *prereservedRegionAddr, /* [out] */ __RPC__out intptr_t *jitThunkAddr) @@ -208,45 +211,53 @@ ServerInitializeThreadContext( *jitThunkAddr = 0; ServerThreadContext * contextInfo = nullptr; + + DWORD clientPid; + HRESULT hr = HRESULT_FROM_WIN32(I_RpcBindingInqLocalClientPID(binding, &clientPid)); + if (FAILED(hr)) + { + return hr; + } +#ifdef USE_RPC_HANDLE_MARSHALLING + HANDLE targetHandle; + if (!DuplicateHandle(GetCurrentProcess(), processHandle, GetCurrentProcess(), &targetHandle, 0, false, DUPLICATE_SAME_ACCESS)) + { + return E_ACCESSDENIED; + } +#else + HANDLE targetHandle = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_QUERY_LIMITED_INFORMATION, false, clientPid); + if (!targetHandle) + { + return E_ACCESSDENIED; + } +#endif try { AUTO_NESTED_HANDLED_EXCEPTION_TYPE(static_cast<ExceptionType>(ExceptionType_OutOfMemory)); - contextInfo = HeapNew(ServerThreadContext, threadContextData); + contextInfo = HeapNew(ServerThreadContext, threadContextData, targetHandle); ServerContextManager::RegisterThreadContext(contextInfo); } catch (Js::OutOfMemoryException) { - CloseHandle((HANDLE)threadContextData->processHandle); + CloseHandle(targetHandle); return E_OUTOFMEMORY; } return ServerCallWrapper(contextInfo, [&]()->HRESULT { - RPC_CALL_ATTRIBUTES CallAttributes = { 0 }; - - CallAttributes.Version = RPC_CALL_ATTRIBUTES_VERSION; - CallAttributes.Flags = RPC_QUERY_CLIENT_PID; - HRESULT hr = HRESULT_FROM_WIN32(RpcServerInqCallAttributes(binding, &CallAttributes)); - if (FAILED(hr)) - { - return hr; - } - if (CallAttributes.ClientPID != (HANDLE)contextInfo->GetRuntimePid()) + if (clientPid != contextInfo->GetRuntimePid()) { return E_ACCESSDENIED; } - hr = CheckModuleAddress(contextInfo->GetProcessHandle(), (LPCVOID)contextInfo->GetRuntimeChakraBaseAddress(), (LPCVOID)AutoSystemInfo::Data.dllLoadAddress); + hr = CheckModuleAddress(targetHandle, (LPCVOID)contextInfo->GetRuntimeChakraBaseAddress(), (LPCVOID)AutoSystemInfo::Data.dllLoadAddress); if (FAILED(hr)) { return hr; } - if (contextInfo->GetUCrtC99MathApis()->IsAvailable()) + hr = CheckModuleAddress(targetHandle, (LPCVOID)contextInfo->GetRuntimeCRTBaseAddress(), (LPCVOID)contextInfo->GetJITCRTBaseAddress()); + if (FAILED(hr)) { - hr = CheckModuleAddress(contextInfo->GetProcessHandle(), (LPCVOID)contextInfo->GetRuntimeCRTBaseAddress(), (LPCVOID)contextInfo->GetJITCRTBaseAddress()); - if (FAILED(hr)) - { - return hr; - } + return hr; } *threadContextInfoAddress = (PTHREADCONTEXT_HANDLE)EncodePointer(contextInfo);
lib/Parser/Parse.cpp+20 −8 modified@@ -3802,9 +3802,10 @@ ParseNodePtr Parser::ParseArgList( bool *pCallOfConstants, uint16 *pSpreadArgCou int count=0; while (true) { - // the count of arguments has to fit in an unsigned short - if (count > 0xffffU) + if (count >= Js::Constants::MaxAllowedArgs) + { Error(ERRnoMemory); + } // Allow spread in argument lists. IdentToken token; pnodeArg = ParseExpr<buildAST>(koplCma, nullptr, TRUE, /* fAllowEllipsis */TRUE, NULL, nullptr, nullptr, &token); @@ -5164,7 +5165,18 @@ bool Parser::ParseFncDeclHelper(ParseNodePtr pnodeFnc, LPCOLESTR pNameHint, usho { m_reparsingLambdaParams = true; } + DeferredFunctionStub *saveDeferredStub = nullptr; + if (buildAST) + { + // Don't try to make use of stubs while parsing formals. Issues with arrow functions, nested functions. + saveDeferredStub = m_currDeferredStub; + m_currDeferredStub = nullptr; + } this->ParseFncFormals<buildAST>(pnodeFnc, pnodeFncParent, flags); + if (buildAST) + { + m_currDeferredStub = saveDeferredStub; + } m_reparsingLambdaParams = fLambdaParamsSave; } @@ -6300,9 +6312,9 @@ void Parser::ParseFncFormals(ParseNodePtr pnodeFnc, ParseNodePtr pnodeParentFnc, Assert(lexNode->IsVarLetOrConst()); UpdateOrCheckForDuplicateInFormals(lexNode->sxVar.pid, &formals); lexNode->sxVar.sym->SetSymbolType(STFormal); - if (m_currentNodeFunc != nullptr && lexNode->sxVar.pid == wellKnownPropertyPids.arguments) + if (lexNode->sxVar.pid == wellKnownPropertyPids.arguments) { - m_currentNodeFunc->grfpn |= PNodeFlags::fpnArguments_overriddenInParam; + GetCurrentFunctionNode()->grfpn |= PNodeFlags::fpnArguments_overriddenInParam; } } @@ -7577,7 +7589,7 @@ ParseNodePtr Parser::ParseStringTemplateDecl(ParseNodePtr pnodeTagFnc) // We are not able to pass more than a ushort worth of arguments to the tag // so use that as a logical limit on the number of string constant pieces. - if (stringConstantCount >= USHRT_MAX) + if (stringConstantCount >= Js::Constants::MaxAllowedArgs) { Error(ERRnoMemory); } @@ -8842,19 +8854,19 @@ ParseNodePtr Parser::ParseVariableDeclaration( CHAKRATEL_LANGSTATS_INC_LANGFEATURECOUNT(Let, m_scriptContext); } - if (pid == wellKnownPropertyPids.arguments && m_currentNodeFunc) + if (pid == wellKnownPropertyPids.arguments) { // This var declaration may change the way an 'arguments' identifier in the function is resolved if (declarationType == tkVAR) { - m_currentNodeFunc->grfpn |= PNodeFlags::fpnArguments_varDeclaration; + GetCurrentFunctionNode()->grfpn |= PNodeFlags::fpnArguments_varDeclaration; } else { if (GetCurrentBlockInfo()->pnodeBlock->sxBlock.blockType == Function) { // Only override arguments if we are at the function block level. - m_currentNodeFunc->grfpn |= PNodeFlags::fpnArguments_overriddenByDecl; + GetCurrentFunctionNode()->grfpn |= PNodeFlags::fpnArguments_overriddenByDecl; } } }
lib/Parser/ptree.h+12 −0 modified@@ -368,6 +368,18 @@ struct PnFnc bool CanBeDeferred() const { return canBeDeferred; } bool IsBodyAndParamScopeMerged() { return isBodyAndParamScopeMerged; } bool FIBPreventsDeferral() const { return fibPreventsDeferral; } + // Only allow the normal functions to be asm.js + bool IsAsmJsAllowed() const { return (fncFlags & ~( + kFunctionAsmjsMode | + kFunctionNested | + kFunctionDeclaration | + kFunctionStrictMode | + kFunctionNameIsHidden | + kFunctionHasReferenceableBuiltInArguments | + kFunctionHasNonThisStmt | + // todo:: we shouldn't accept kFunctionHasAnyWriteToFormals on the asm module, but it looks like a bug is setting that flag incorrectly + kFunctionHasAnyWriteToFormals + )) == 0; } size_t LengthInBytes() {
lib/Runtime/Base/Constants.h+5 −2 modified@@ -45,8 +45,11 @@ namespace Js static const InlineCacheIndex NoInlineCacheIndex = (InlineCacheIndex)-1; static const uint UninitializedValue = (uint)-1; static const ArgSlot InvalidArgSlot = (ArgSlot)-1; - static const uint32 InvalidSymID = (uint32)-1; - static const size_t InvalidSignature = (size_t)-1; + static const uint32 InvalidSymID = (uint32)-1; + static const size_t InvalidSignature = (size_t)-1; + + // We add extra args during bytecode phase, so account for those and few extra slots for padding. + static const uint16 MaxAllowedArgs = UShortMaxValue - 6; static const uint64 ExponentMask; static const uint64 MantissaMask;
lib/Runtime/Base/FunctionBody.cpp+17 −21 modified@@ -948,6 +948,23 @@ namespace Js Js::ParseableFunctionInfo::NewDeferredFunctionFromFunctionBody(this); FunctionInfo * functionInfo = this->GetFunctionInfo(); + this->RedeferFunctionObjectTypes(); + + this->Cleanup(false); + if (GetIsFuncRegistered()) + { + this->GetUtf8SourceInfo()->RemoveFunctionBody(this); + } + + // New allocation is done at this point, so update existing structures + // Adjust functionInfo attributes, point to new proxy + functionInfo->SetAttributes((FunctionInfo::Attributes)(functionInfo->GetAttributes() | FunctionInfo::Attributes::DeferredParse)); + functionInfo->SetFunctionProxy(parseableFunctionInfo); + functionInfo->SetOriginalEntryPoint(DefaultEntryThunk); + } + + void FunctionBody::RedeferFunctionObjectTypes() + { this->MapFunctionObjectTypes([&](ScriptFunctionType* functionType) { Assert(functionType->GetTypeId() == TypeIds_Function); @@ -961,18 +978,6 @@ namespace Js functionType->GetEntryPointInfo()->jsMethod = GetScriptContext()->DeferredParsingThunk; } }); - - this->Cleanup(false); - if (GetIsFuncRegistered()) - { - this->GetUtf8SourceInfo()->RemoveFunctionBody(this); - } - - // New allocation is done at this point, so update existing structures - // Adjust functionInfo attributes, point to new proxy - functionInfo->SetAttributes((FunctionInfo::Attributes)(functionInfo->GetAttributes() | FunctionInfo::Attributes::DeferredParse)); - functionInfo->SetFunctionProxy(parseableFunctionInfo); - functionInfo->SetOriginalEntryPoint(DefaultEntryThunk); } void FunctionBody::SetDefaultFunctionEntryPointInfo(FunctionEntryPointInfo* entryPointInfo, const JavascriptMethod originalEntryPoint) @@ -1923,15 +1928,6 @@ namespace Js FunctionInfoArray nested = this->GetNestedFuncArray(); nested[index] = nestedFunc; - - if (nestedFunc) - { - if (!this->GetSourceContextInfo()->IsDynamic() && nestedFunc->IsDeferredParseFunction() && nestedFunc->GetParseableFunctionInfo()->GetIsDeclaration() && this->GetIsTopLevel() && !(flags & fscrEvalCode)) - { - this->GetUtf8SourceInfo()->TrackDeferredFunction(nestedFunc->GetLocalFunctionId(), nestedFunc->GetParseableFunctionInfo()); - } - } - } FunctionInfo* ParseableFunctionInfo::GetNestedFunc(uint index)
lib/Runtime/Base/FunctionBody.h+3 −0 modified@@ -2641,6 +2641,7 @@ namespace Js bool DoRedeferFunction(uint inactiveThreshold) const; void RedeferFunction(); + void RedeferFunctionObjectTypes(); bool IsActiveFunction(ActiveFunctionSet * pActiveFuncs) const; bool TestAndUpdateActiveFunctions(ActiveFunctionSet * pActiveFuncs) const; void UpdateActiveFunctionSet(ActiveFunctionSet * pActiveFuncs, FunctionCodeGenRuntimeData *callSiteData) const; @@ -3729,6 +3730,8 @@ namespace Js if (this->pfi != nullptr && this->pfi->GetFunctionInfo()->GetFunctionProxy() != this->pfi) { FunctionInfo *functionInfo = this->pfi->GetFunctionInfo(); + FunctionBody *functionBody = functionInfo->GetFunctionProxy()->GetFunctionBody(); + functionBody->RedeferFunctionObjectTypes(); functionInfo->SetAttributes( (FunctionInfo::Attributes)(functionInfo->GetAttributes() | FunctionInfo::Attributes::DeferredParse)); functionInfo->SetFunctionProxy(this->pfi);
lib/Runtime/Base/ThreadContext.cpp+15 −10 modified@@ -1976,20 +1976,18 @@ ThreadContext::EnsureJITThreadContext(bool allowPrereserveAlloc) return true; } - ThreadContextDataIDL contextData; - HANDLE serverHandle = JITManager::GetJITManager()->GetServerHandle(); - - HANDLE jitTargetHandle = nullptr; - if (!DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), serverHandle, &jitTargetHandle, 0, FALSE, DUPLICATE_SAME_ACCESS)) +#ifdef USE_RPC_HANDLE_MARSHALLING + HANDLE processHandle; + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &processHandle, 0, false, DUPLICATE_SAME_ACCESS)) { return false; } + AutoCloseHandle autoClose(processHandle); +#endif - contextData.processHandle = (intptr_t)jitTargetHandle; - + ThreadContextDataIDL contextData; contextData.chakraBaseAddress = (intptr_t)AutoSystemInfo::Data.GetChakraBaseAddr(); - ucrtC99MathApis.Ensure(); - contextData.crtBaseAddress = (intptr_t)ucrtC99MathApis.GetHandle(); + contextData.crtBaseAddress = (intptr_t)AutoSystemInfo::GetCRTHandle(); contextData.threadStackLimitAddr = reinterpret_cast<intptr_t>(GetAddressOfStackLimitForCurrentThread()); contextData.bailOutRegisterSaveSpaceAddr = (intptr_t)bailOutRegisterSaveSpace; contextData.disableImplicitFlagsAddr = (intptr_t)GetAddressOfDisableImplicitFlags(); @@ -2012,7 +2010,14 @@ ThreadContext::EnsureJITThreadContext(bool allowPrereserveAlloc) } } - HRESULT hr = JITManager::GetJITManager()->InitializeThreadContext(&contextData, &m_remoteThreadContextInfo, &m_prereservedRegionAddr, &m_jitThunkStartAddr); + HRESULT hr = JITManager::GetJITManager()->InitializeThreadContext( + &contextData, +#ifdef USE_RPC_HANDLE_MARSHALLING + processHandle, +#endif + &m_remoteThreadContextInfo, + &m_prereservedRegionAddr, + &m_jitThunkStartAddr); JITManager::HandleServerCallResult(hr, RemoteCallType::StateUpdate); return m_remoteThreadContextInfo != nullptr;
lib/Runtime/Base/ThreadContext.h+4 −0 modified@@ -822,6 +822,8 @@ class ThreadContext sealed : NativeLibraryEntryRecord nativeLibraryEntry; + UCrtC99MathApis ucrtC99MathApis; + // Indicates the current loop depth as observed by the interpreter. The interpreter causes this value to be updated upon // entering and leaving a loop. uint8 loopDepth; @@ -859,6 +861,8 @@ class ThreadContext sealed : CriticalSection* GetFunctionBodyLock() { return &csFunctionBody; } + UCrtC99MathApis* GetUCrtC99MathApis() { return &ucrtC99MathApis; } + Js::IsConcatSpreadableCache* GetIsConcatSpreadableCache() { return &isConcatSpreadableCache; } #ifdef ENABLE_GLOBALIZATION
lib/Runtime/Base/ThreadContextInfo.cpp+1 −1 modified@@ -395,7 +395,7 @@ ThreadContextInfo::IsCFGEnabled() PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY CfgPolicy; m_delayLoadWinCoreProcessThreads.EnsureFromSystemDirOnly(); BOOL isGetMitigationPolicySucceeded = m_delayLoadWinCoreProcessThreads.GetMitigationPolicyForProcess( - this->GetProcessHandle(), + GetCurrentProcess(), ProcessControlFlowGuardPolicy, &CfgPolicy, sizeof(CfgPolicy));
lib/Runtime/Base/ThreadContextInfo.h+9 −3 modified@@ -121,10 +121,16 @@ class ThreadContextInfo Js::DelayLoadWinCoreProcessThreads m_delayLoadWinCoreProcessThreads; #endif - UCrtC99MathApis* GetUCrtC99MathApis() { return &ucrtC99MathApis; } protected: - - UCrtC99MathApis ucrtC99MathApis; + class AutoCloseHandle + { + public: + AutoCloseHandle(HANDLE handle) : handle(handle) { Assert(this->handle != GetCurrentProcess()); } + ~AutoCloseHandle() { CloseHandle(this->handle); } + HANDLE GetHandle() const { return this->handle; } + private: + HANDLE handle; + }; Js::TypeId wellKnownHostTypeIds[WellKnownHostType_Last + 1];
lib/Runtime/ByteCode/ByteCodeEmitter.cpp+19 −14 modified@@ -3595,6 +3595,19 @@ void ByteCodeGenerator::EmitOneFunction(ParseNode *pnode) } #endif + if (!byteCodeFunction->GetSourceContextInfo()->IsDynamic() && byteCodeFunction->GetIsTopLevel() && !(this->flags & fscrEvalCode)) + { + // Add the top level of nested functions to the tracking dictionary. Wait until this point so that all nested functions have gone + // through the Emit API so source info, etc., is initialized, and these are not orphaned functions left behind by an unfinished pass. + byteCodeFunction->ForEachNestedFunc([&](Js::FunctionProxy * nestedFunc, uint32 i) + { + if (nestedFunc && nestedFunc->IsDeferredParseFunction() && nestedFunc->GetParseableFunctionInfo()->GetIsDeclaration()) + { + byteCodeFunction->GetUtf8SourceInfo()->TrackDeferredFunction(nestedFunc->GetLocalFunctionId(), nestedFunc->GetParseableFunctionInfo()); + } + return true; + }); + } byteCodeFunction->SetInitialDefaultEntryPoint(); byteCodeFunction->SetCompileCount(UInt32Math::Add(byteCodeFunction->GetCompileCount(), 1)); @@ -6132,6 +6145,8 @@ unsigned int CountArguments(ParseNode *pnode, BOOL *pSideEffect = nullptr) } } + AssertOrFailFastMsg(argCount < Js::Constants::UShortMaxValue, "Number of allowed arguments are already capped at parser level"); + return argCount; } @@ -7353,15 +7368,10 @@ Js::ArgSlot EmitArgListEnd( BOOL fIsEval = (evalLocation != Js::Constants::NoRegister); BOOL fHasNewTarget = (newTargetLocation != Js::Constants::NoRegister); - Js::ArgSlot argSlotIndex = (Js::ArgSlot) argIndex; - static const Js::ArgSlot maxExtraArgSlot = 4; // max(extraEvalArg, extraArg), where extraEvalArg==2 (moduleRoot,env), extraArg==4 (this, eval, evalInModule, newTarget) - - // check for integer overflow with margin for increments below to calculate argument count - if ((size_t)argSlotIndex != argIndex || argSlotIndex + maxExtraArgSlot < argSlotIndex) - { - Js::Throw::OutOfMemory(); - } + static const size_t maxExtraArgSlot = 4; // max(extraEvalArg, extraArg), where extraEvalArg==2 (moduleRoot,env), extraArg==4 (this, eval, evalInModule, newTarget) + AssertOrFailFastMsg(argIndex < Js::Constants::UShortMaxValue - maxExtraArgSlot, "Number of allowed arguments are already capped at parser level"); + Js::ArgSlot argSlotIndex = (Js::ArgSlot) argIndex; Js::ArgSlot evalIndex; if (fIsPut) @@ -8208,12 +8218,7 @@ void EmitNew(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* f BOOL fSideEffectArgs = FALSE; unsigned int tmpCount = CountArguments(pnode->sxCall.pnodeArgs, &fSideEffectArgs); - Assert(argCount == tmpCount); - - if (argCount != (Js::ArgSlot)argCount) - { - Js::Throw::OutOfMemory(); - } + AssertOrFailFastMsg(argCount == tmpCount, "argCount cannot overflow as max args capped at parser level"); byteCodeGenerator->StartStatement(pnode);
lib/Runtime/ByteCode/ByteCodeGenerator.cpp+10 −0 modified@@ -3688,6 +3688,8 @@ void PreVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator) item->sxVar.pid->Psz(), sym->GetSymbolTypeName()); } #endif + sym->SetIsCatch(true); + sym->SetIsBlockVar(true); }); } else @@ -5258,6 +5260,14 @@ void AssignRegisters(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator) #endif auto symName = sym->GetName(); sym = funcInfo->bodyScope->FindLocalSymbol(symName); + + if (sym == nullptr && nop == knopLetDecl && pnode->sxVar.sym->GetIsCatch()) + { + // This should be a scenario like try {} catch([x]) {} with no duplicate definition inside the catch block. + // In non-destructured catch block param case, the created node will be a name node, not a var node. + break; + } + if (sym == nullptr) { sym = funcInfo->paramScope->FindLocalSymbol(symName);
lib/Runtime/Language/AsmJs.cpp+7 −2 modified@@ -62,7 +62,7 @@ namespace Js if (fnc.HasNonSimpleParameterList()) { - return m.Fail(fn, _u("default & rest args not allowed")); + return m.Fail(fn, _u("default, rest & destructuring args not allowed")); } if (fnc.IsStaticMember()) @@ -90,6 +90,11 @@ namespace Js return m.Fail(fn, _u("closure functions are not allowed")); } + if (!fnc.IsAsmJsAllowed()) + { + return m.Fail(fn, _u("invalid function flags detected")); + } + return true; } @@ -765,7 +770,7 @@ namespace Js } else if (decl->nop != knopConstDecl && decl->nop != knopVarDecl) { - break; + goto varDeclEnd; } if (decl->sxVar.pnodeInit && decl->sxVar.pnodeInit->nop == knopArray)
lib/Runtime/Language/InterpreterStackFrame.cpp+2 −1 modified@@ -2936,7 +2936,8 @@ namespace Js newInstance->m_reader.Create(funcObj->GetFunctionBody()); // now that we have set up the new frame, let's interpret it! funcObj->GetFunctionBody()->BeginExecution(); - PushPopFrameHelper(newInstance, _ReturnAddress(), _AddressOfReturnAddress()); + + PushPopFrameHelper pushPopFrameHelper(newInstance, this->returnAddress, this->addressOfReturnAddress); Var retVal = newInstance->ProcessUnprofiled(); if (doProfile)
lib/Runtime/Language/JavascriptExceptionOperators.cpp+1 −1 modified@@ -970,7 +970,7 @@ namespace Js // We might be trying to raise a stack overflow exception from the interpreter before // we've executed code in the current script stack frame. In that case the current byte // code offset is 0. In such cases walk to the caller's caller. - BOOL JavascriptExceptionOperators::GetCaller(JavascriptStackWalker& walker, JavascriptFunction*& jsFunc) + BOOL JavascriptExceptionOperators::GetCaller(JavascriptStackWalker& walker, _Out_opt_ JavascriptFunction*& jsFunc) { if (! walker.GetCaller(&jsFunc)) {
lib/Runtime/Language/JavascriptExceptionOperators.h+1 −1 modified@@ -97,7 +97,7 @@ namespace Js uint64 stackCrawlLimit, PVOID returnAddress, bool isThrownException, bool resetStack = false); static void ThrowExceptionObjectInternal(Js::JavascriptExceptionObject * exceptionObject, ScriptContext* scriptContext, bool fillExceptionContext, bool considerPassingToDebugger, PVOID returnAddress, bool resetStack); - static BOOL GetCaller(JavascriptStackWalker& walker, JavascriptFunction*& jsFunc); + static BOOL GetCaller(JavascriptStackWalker& walker, _Out_opt_ JavascriptFunction*& jsFunc); static void DumpStackTrace(JavascriptExceptionContext& exceptionContext, bool isThrownException = true); static JavascriptExceptionContext::StackTrace* TrimStackTraceForThrownObject(JavascriptExceptionContext::StackTrace* stackTraceOriginal, Var thrownObject, ScriptContext& scriptContext); static void AppendExternalFrameToStackTrace(CompoundString* bs, LPCWSTR functionName, LPCWSTR fileName, ULONG lineNumber, LONG characterPosition);
lib/Runtime/Language/JavascriptStackWalker.cpp+22 −9 modified@@ -747,31 +747,39 @@ namespace Js return true; } - BOOL JavascriptStackWalker::GetCallerWithoutInlinedFrames(JavascriptFunction ** ppFunc) + BOOL JavascriptStackWalker::GetCallerWithoutInlinedFrames(_Out_opt_ JavascriptFunction ** ppFunc) { return GetCaller(ppFunc, /*includeInlineFrames*/ false); } - BOOL JavascriptStackWalker::GetCaller(JavascriptFunction ** ppFunc, bool includeInlineFrames) + BOOL JavascriptStackWalker::GetCaller(_Out_opt_ JavascriptFunction ** ppFunc, bool includeInlineFrames) { while (this->Walk(includeInlineFrames)) { if (this->IsJavascriptFrame()) { Assert(entryExitRecord != NULL); - *ppFunc = this->GetCurrentFunction(); + if (ppFunc) + { + *ppFunc = this->GetCurrentFunction(); + } AssertMsg(!this->shouldDetectPartiallyInitializedInterpreterFrame, "must have skipped first frame if needed"); return true; } } - *ppFunc = (JavascriptFunction*)this->scriptContext->GetLibrary()->GetNull(); + if (ppFunc) + { + *ppFunc = nullptr; + } return false; } - BOOL JavascriptStackWalker::GetNonLibraryCodeCaller(JavascriptFunction ** ppFunc) + BOOL JavascriptStackWalker::GetNonLibraryCodeCaller(_Out_opt_ JavascriptFunction ** ppFunc) { while (this->GetCaller(ppFunc)) { + Assert(ppFunc != nullptr); + __analysis_assume(ppFunc != nullptr); if (!(*ppFunc)->IsLibraryCode()) { return true; @@ -801,10 +809,12 @@ namespace Js } } - bool JavascriptStackWalker::GetDisplayCaller(JavascriptFunction ** ppFunc) + bool JavascriptStackWalker::GetDisplayCaller(_Out_opt_ JavascriptFunction ** ppFunc) { while (this->GetCaller(ppFunc)) { + Assert(ppFunc != nullptr); + __analysis_assume(ppFunc != nullptr); if (IsDisplayCaller(*ppFunc)) { return true; @@ -1104,19 +1114,22 @@ namespace Js return (threadContext->GetScriptEntryExit() != NULL); } - BOOL JavascriptStackWalker::GetCaller(JavascriptFunction** ppFunc, ScriptContext* scriptContext) + BOOL JavascriptStackWalker::GetCaller(_Out_opt_ JavascriptFunction** ppFunc, ScriptContext* scriptContext) { if (!IsWalkable(scriptContext)) { - *ppFunc = nullptr; + if (ppFunc) + { + *ppFunc = nullptr; + } return FALSE; } JavascriptStackWalker walker(scriptContext); return walker.GetCaller(ppFunc); } - BOOL JavascriptStackWalker::GetCaller(JavascriptFunction** ppFunc, uint32* byteCodeOffset, ScriptContext* scriptContext) + BOOL JavascriptStackWalker::GetCaller(_Out_opt_ JavascriptFunction** ppFunc, uint32* byteCodeOffset, ScriptContext* scriptContext) { JavascriptStackWalker walker(scriptContext); if (walker.GetCaller(ppFunc))
lib/Runtime/Language/JavascriptStackWalker.h+6 −6 modified@@ -184,9 +184,9 @@ namespace Js ~JavascriptStackWalker() { inlinedFrameWalker.Close(); } #endif BOOL Walk(bool includeInlineFrames = true); - BOOL GetCaller(JavascriptFunction ** ppFunc, bool includeInlineFrames = true); - BOOL GetCallerWithoutInlinedFrames(JavascriptFunction ** ppFunc); - BOOL GetNonLibraryCodeCaller(JavascriptFunction ** ppFunc); + BOOL GetCaller(_Out_opt_ JavascriptFunction ** ppFunc, bool includeInlineFrames = true); + BOOL GetCallerWithoutInlinedFrames(_Out_opt_ JavascriptFunction ** ppFunc); + BOOL GetNonLibraryCodeCaller(_Out_opt_ JavascriptFunction ** ppFunc); BOOL WalkToTarget(JavascriptFunction * funcTarget); BOOL WalkToArgumentsFrame(ArgumentsObject *argsObj); @@ -237,13 +237,13 @@ namespace Js bool IsCurrentPhysicalFrameForLoopBody() const; // noinline, we want to use own stack frame. - static _NOINLINE BOOL GetCaller(JavascriptFunction** ppFunc, ScriptContext* scriptContext); - static _NOINLINE BOOL GetCaller(JavascriptFunction** ppFunc, uint32* byteCodeOffset, ScriptContext* scriptContext); + static _NOINLINE BOOL GetCaller(_Out_opt_ JavascriptFunction** ppFunc, ScriptContext* scriptContext); + static _NOINLINE BOOL GetCaller(_Out_opt_ JavascriptFunction** ppFunc, uint32* byteCodeOffset, ScriptContext* scriptContext); static _NOINLINE bool GetThis(Var* pThis, int moduleId, ScriptContext* scriptContext); static _NOINLINE bool GetThis(Var* pThis, int moduleId, JavascriptFunction* func, ScriptContext* scriptContext); static bool IsDisplayCaller(JavascriptFunction* func); - bool GetDisplayCaller(JavascriptFunction ** ppFunc); + bool GetDisplayCaller(_Out_opt_ JavascriptFunction ** ppFunc); PCWSTR GetCurrentNativeLibraryEntryName() const; static bool IsLibraryStackFrameEnabled(Js::ScriptContext * scriptContext); static bool IsWalkable(ScriptContext *scriptContext);
lib/Runtime/Library/ArgumentsObject.cpp+1 −1 modified@@ -60,7 +60,7 @@ namespace Js break; } - if (funcCaller == nullptr || JavascriptOperators::GetTypeId(funcCaller) == TypeIds_Null) + if (funcCaller == nullptr) { return scriptContext->GetLibrary()->GetNull(); }
lib/Runtime/Library/GlobalObject.cpp+15 −10 modified@@ -685,17 +685,22 @@ namespace Js if (args.Info.Flags & CallFlags_ExtraArg) { JavascriptFunction* pfuncCaller = nullptr; - JavascriptStackWalker::GetCaller(&pfuncCaller, scriptContext); // If we are non-hidden call to eval then look for the "this" object in the frame display if the caller is a lambda else get "this" from the caller's frame. - FunctionInfo* functionInfo = pfuncCaller->GetFunctionInfo(); - if (functionInfo != nullptr && (functionInfo->IsLambda() || functionInfo->IsClassConstructor())) + bool successful = false; + if (JavascriptStackWalker::GetCaller(&pfuncCaller, scriptContext)) { - Var defaultInstance = (moduleID == kmodGlobal) ? JavascriptOperators::OP_LdRoot(scriptContext)->ToThis() : (Var)JavascriptOperators::GetModuleRoot(moduleID, scriptContext); - varThis = JavascriptOperators::OP_GetThisScoped(environment, defaultInstance, scriptContext); - UpdateThisForEval(varThis, moduleID, scriptContext, strictMode); + FunctionInfo* functionInfo = pfuncCaller->GetFunctionInfo(); + if (functionInfo != nullptr && (functionInfo->IsLambda() || functionInfo->IsClassConstructor())) + { + Var defaultInstance = (moduleID == kmodGlobal) ? JavascriptOperators::OP_LdRoot(scriptContext)->ToThis() : (Var)JavascriptOperators::GetModuleRoot(moduleID, scriptContext); + varThis = JavascriptOperators::OP_GetThisScoped(environment, defaultInstance, scriptContext); + UpdateThisForEval(varThis, moduleID, scriptContext, strictMode); + successful = true; + } } - else + + if (!successful) { JavascriptStackWalker::GetThis(&varThis, moduleID, scriptContext); UpdateThisForEval(varThis, moduleID, scriptContext, strictMode); @@ -792,7 +797,7 @@ namespace Js ArenaAllocator tempAlloc(_u("ValidateSyntaxArena"), scriptContext->GetThreadContext()->GetPageAllocator(), Throw::OutOfMemory); size_t cchSource = sourceLength; - size_t cbUtf8Buffer = (cchSource + 1) * 3; + size_t cbUtf8Buffer = UInt32Math::AddMul<1, 3>(sourceLength); LPUTF8 utf8Source = AnewArray(&tempAlloc, utf8char_t, cbUtf8Buffer); Assert(cchSource < MAXLONG); size_t cbSource = utf8::EncodeIntoAndNullTerminate(utf8Source, source, static_cast< charcount_t >(cchSource)); @@ -864,7 +869,7 @@ namespace Js BEGIN_TRANSLATE_EXCEPTION_TO_HRESULT { uint cchSource = sourceLength; - size_t cbUtf8Buffer = (cchSource + 1) * 3; + size_t cbUtf8Buffer = UInt32Math::AddMul<1, 3>(cchSource); ArenaAllocator tempArena(_u("EvalHelperArena"), scriptContext->GetThreadContext()->GetPageAllocator(), Js::Throw::OutOfMemory); LPUTF8 utf8Source = AnewArray(&tempArena, utf8char_t, cbUtf8Buffer); @@ -1024,7 +1029,7 @@ namespace Js BEGIN_TRANSLATE_EXCEPTION_TO_HRESULT { size_t cchSource = sourceLength; - size_t cbUtf8Buffer = (cchSource + 1) * 3; + size_t cbUtf8Buffer = UInt32Math::AddMul<1, 3>(sourceLength); ArenaAllocator tempArena(_u("EvalHelperArena"), scriptContext->GetThreadContext()->GetPageAllocator(), Js::Throw::OutOfMemory); LPUTF8 utf8Source = AnewArray(&tempArena, utf8char_t, cbUtf8Buffer);
lib/Runtime/Library/JavascriptArray.cpp+16 −7 modified@@ -3088,7 +3088,8 @@ namespace Js if (JavascriptArray::Is(pDestObj)) { - pDestArray = JavascriptArray::FromVar(pDestObj); + // ConcatArgs function expects to work on the Var array so we are ensuring it. + pDestArray = EnsureNonNativeArray(JavascriptArray::FromVar(pDestObj)); } T idxDest = startIdxDest; @@ -3217,11 +3218,13 @@ namespace Js ++idxDest; } } + + firstPromotedItemIsSpreadable = false; } if (!pDestArray) { JS_REENTRANT(jsReentLock, pDestObj->SetProperty(PropertyIds::length, ConvertToIndex<T, Var>(idxDest, scriptContext), Js::PropertyOperation_None, nullptr)); - } + } else if (pDestArray->GetLength() != ConvertToIndex<T, uint32>(idxDest, scriptContext)) { pDestArray->SetLength(ConvertToIndex<T, uint32>(idxDest, scriptContext)); @@ -5462,19 +5465,25 @@ namespace Js } // 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 inlined segement, otherwise due to shuffling 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 inlined segment. - if (pArr->head && pArr->head->next) + // 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. + if (pArr->head && (pArr->head->next || (pArr->head->left + pArr->head->length) < length)) { if (isIntArray) { CopyHeadIfInlinedHeadSegment<int32>(pArr, recycler); - ReallocateNonLeafLastSegmentIfLeaf<int32>(pArr, recycler); + if (pArr->head->next) + { + ReallocateNonLeafLastSegmentIfLeaf<int32>(pArr, recycler); + } } else if (isFloatArray) { CopyHeadIfInlinedHeadSegment<double>(pArr, recycler); - ReallocateNonLeafLastSegmentIfLeaf<double>(pArr, recycler); + if (pArr->head->next) + { + ReallocateNonLeafLastSegmentIfLeaf<double>(pArr, recycler); + } } else {
lib/Runtime/Library/JavascriptFunction.cpp+14 −5 modified@@ -621,13 +621,20 @@ namespace Js /// Check Argument[0] has internal [[Call]] property /// If not, throw TypeError /// - if (args.Info.Count == 0 || !JavascriptConversion::IsCallable(args[0])) + uint argCount = args.Info.Count; + if (callInfo.Flags & CallFlags_ExtraArg) + { + // The last argument is the "extra". Don't consider it in the logic below. + // It will either remain in place (argCount == 1) or be copied. + argCount--; + } + if (argCount == 0 || !JavascriptConversion::IsCallable(args[0])) { JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedFunction, _u("Function.prototype.call")); } RecyclableObject *pFunc = RecyclableObject::FromVar(args[0]); - if (args.Info.Count == 1) + if (argCount == 1) { args.Values[0] = scriptContext->GetLibrary()->GetUndefined(); } @@ -2719,12 +2726,14 @@ void __cdecl _alloca_probe_16() break; } - if (funcCaller->GetScriptContext() != requestContext && funcCaller->GetTypeId() == TypeIds_Null) + if (funcCaller == nullptr) { - // There are cases where StackWalker might return null value from different scriptContext - // Caller of this function expects nullValue from the requestContext. + // We no longer return Null objects as JavascriptFunctions, so we don't have to worry about + // cross-context null objects. We do want to clean up null pointers though, since some call + // later in this function may depend on non-nullptr calls. funcCaller = nullValue; } + if (ScriptFunction::Is(funcCaller)) { // If this is the internal function of a generator function then return the original generator function
lib/Runtime/Library/ScriptFunction.cpp+18 −3 modified@@ -706,14 +706,29 @@ namespace Js InlineCache * ScriptFunctionWithInlineCache::GetInlineCache(uint index) { - Assert(this->m_inlineCaches != nullptr); - Assert(index < this->GetInlineCacheCount()); + void** inlineCaches = this->GetInlineCaches(); + Assert(inlineCaches != nullptr); + AssertOrFailFast(index < this->GetInlineCacheCount()); #if DBG Assert(this->m_inlineCacheTypes[index] == InlineCacheTypeNone || this->m_inlineCacheTypes[index] == InlineCacheTypeInlineCache); this->m_inlineCacheTypes[index] = InlineCacheTypeInlineCache; #endif - return reinterpret_cast<InlineCache *>(PointerValue(this->m_inlineCaches[index])); + return reinterpret_cast<InlineCache *>(PointerValue(inlineCaches[index])); + } + + Field(void**) ScriptFunctionWithInlineCache::GetInlineCaches() + { + // If script function have inline caches pointing to function body and function body got reparsed we need to reset cache + if (this->GetHasInlineCaches() && + !this->GetHasOwnInlineCaches() && + this->m_inlineCaches != this->GetFunctionBody()->GetInlineCaches()) + { + Assert(this->GetFunctionBody()->GetCompileCount() > 1); + this->SetInlineCachesFromFunctionBody(); + } + + return this->m_inlineCaches; } void ScriptFunctionWithInlineCache::SetInlineCachesFromFunctionBody()
lib/Runtime/Library/ScriptFunction.h+1 −1 modified@@ -180,7 +180,7 @@ namespace Js void ClearBorrowedInlineCacheOnFunctionObject(); InlineCache * GetInlineCache(uint index); uint GetInlineCacheCount() { return inlineCacheCount; } - Field(void**) GetInlineCaches() { return m_inlineCaches; } + Field(void**) GetInlineCaches(); bool GetHasOwnInlineCaches() { return hasOwnInlineCaches; } void SetInlineCachesFromFunctionBody(); static uint32 GetOffsetOfInlineCaches() { return offsetof(ScriptFunctionWithInlineCache, m_inlineCaches); };
lib/Runtime/Types/ActivationObject.h+4 −0 modified@@ -184,21 +184,25 @@ namespace Js static PropertyId GetCachedFuncCount(const PropertyIdArray *propIds) { + AssertOrFailFast(propIds->extraSlots > 0); return ActivationObjectEx::GetCachedScopeInfo(propIds)[0]; } static PropertyId GetFirstFuncSlot(const PropertyIdArray *propIds) { + AssertOrFailFast(propIds->extraSlots > 1); return ActivationObjectEx::GetCachedScopeInfo(propIds)[1]; } static PropertyId GetFirstVarSlot(const PropertyIdArray *propIds) { + AssertOrFailFast(propIds->extraSlots > 2); return ActivationObjectEx::GetCachedScopeInfo(propIds)[2]; } static PropertyId GetLiteralObjectRef(const PropertyIdArray *propIds) { + AssertOrFailFast(propIds->extraSlots > 3); return ActivationObjectEx::GetCachedScopeInfo(propIds)[3]; }
lib/Runtime/Types/SimpleDictionaryTypeHandler.cpp+10 −10 modified@@ -1349,16 +1349,6 @@ namespace Js instance->ChangeType(); } - if(isUnordered) - { - TPropertyIndex propertyIndex; - if(AsUnordered()->TryUndeleteProperty(instance, descriptor->propertyIndex, &propertyIndex)) - { - Assert(PropertyRecordStringHashComparer<TMapKey>::Equals(propertyMap->GetKeyAt(propertyIndex), TMapKey_OptionalConvertPropertyIdToPropertyRecord(scriptContext, propertyKey))); - descriptor = propertyMap->GetReferenceAt(propertyIndex); - } - } - if (IsNotExtensibleSupported) { bool isForce = (flags & PropertyOperation_Force) != 0; @@ -1371,6 +1361,16 @@ namespace Js } } + if(isUnordered) + { + TPropertyIndex propertyIndex; + if(AsUnordered()->TryUndeleteProperty(instance, descriptor->propertyIndex, &propertyIndex)) + { + Assert(PropertyRecordStringHashComparer<TMapKey>::Equals(propertyMap->GetKeyAt(propertyIndex), TMapKey_OptionalConvertPropertyIdToPropertyRecord(scriptContext, propertyKey))); + descriptor = propertyMap->GetReferenceAt(propertyIndex); + } + } + if(SupportsSwitchingToUnordered(scriptContext)) { --numDeletedProperties;
lib/Runtime/Types/SimpleDictionaryUnorderedTypeHandler.cpp+5 −1 modified@@ -101,7 +101,11 @@ namespace Js Assert(this->propertyMap->GetValueAt(existingPropertyIndex).Attributes & PropertyDeleted); const bool reused = TryReuseDeletedPropertyIndex(object, propertyIndex); - Assert(reused); // at least one property index must have been free-listed since we're adding an existing deleted property + if (!reused) + { + Assert(0); // at least one property index must have been free-listed since we're adding an existing deleted property + return false; + } if(*propertyIndex == existingPropertyIndex) {
test/AsmJs/badFunctionType.baseline+77 −0 added@@ -0,0 +1,77 @@ + +Running test 0: kFunctionIsAccessor +invalid function flags detected +Asm.js compilation failed. + +Running test 1: kFunctionIsClassConstructor & kFunctionIsBaseClassConstructor +invalid function flags detected +Asm.js compilation failed. + +Running test 2: kFunctionIsStaticMember +static functions are not allowed +Asm.js compilation failed. + +Running test 3: kFunctionIsClassMember +invalid function flags detected +Asm.js compilation failed. + +Running test 4: kFunctionIsMethod +invalid function flags detected +Asm.js compilation failed. + +Running test 5: kFunctionCallsEval +invalid function flags detected +Asm.js compilation failed. + +Running test 6: kFunctionChildCallsEval +invalid function flags detected +Asm.js compilation failed. + +Running test 7: kFunctionUsesArguments +invalid function flags detected +Asm.js compilation failed. + +Running test 8: kFunctionHasWithStmt +invalid function flags detected +Asm.js compilation failed. + +Running test 9: kFunctionIsLambda +lambda functions are not allowed +Asm.js compilation failed. + +Running test 10: kFunctionHasNonSimpleParameterList: Module destructuring +default, rest & destructuring args not allowed +Asm.js compilation failed. + +Running test 11: kFunctionHasNonSimpleParameterList: function destructuring +default, rest & destructuring args not allowed +Asm.js compilation failed. + +Running test 12: kFunctionHasNonSimpleParameterList: rest +default, rest & destructuring args not allowed +Asm.js compilation failed. + +Running test 13: kFunctionHasDefaultArguments +default, rest & destructuring args not allowed +Asm.js compilation failed. + +Running test 14: kFunctionIsModule +Warning test disabled + +Running test 15: asm.js function in Module +Warning test disabled + +Running test 16: kFunctionIsDefaultModuleExport +Warning test disabled + +Running test 17: kFunctionHasSuperReference +invalid function flags detected +Asm.js compilation failed. + +Running test 18: kFunctionIsGenerator +generator functions are not allowed +Asm.js compilation failed. + +Running test 19: kFunctionIsAsync +async functions are not allowed +Asm.js compilation failed.
test/AsmJs/badFunctionType.js+267 −0 added@@ -0,0 +1,267 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- +const simpleAsmDef = ` +function x(v) { + v = v | 0; + return v | 0; +} +return x;`; + +const tests = [{ + name: "kFunctionIsAccessor", + test() { eval(` + var o = { + set m(stdlib) { + "use asm" + var I32 = stdlib.Int32Array; + ${simpleAsmDef} + } + } + o.m = 5; + `);} +}, { + name: "kFunctionIsClassConstructor & kFunctionIsBaseClassConstructor", + test() { eval(` + class BaseClass {} + class MyClass extends BaseClass { + f(a,b,c,d,e) { + print(a); + } + + constructor() { + "use asm"; + ${simpleAsmDef} + } + } + var x = new MyClass("df"); + x(3); + `); + } +}, { + name: "kFunctionIsStaticMember", + test() { eval(` + class MyClass { + static asmModuleClassMember() { + "use asm"; + ${simpleAsmDef} + } + } + MyClass.asmModuleClassMember()(3); + `); + } +}, { + name: "kFunctionIsClassMember", + test() { eval(` + class MyClass { + asmModuleClassMember() { + "use asm"; + ${simpleAsmDef} + } + } + var v = new MyClass("df") + v.asmModuleClassMember()(3); + `) + } +}, { + name: "kFunctionIsMethod", + test() {eval(` + var obj = { + asmModuleMethod() { + "use asm"; + ${simpleAsmDef} + } + } + obj.asmModuleMethod()(3); + `) + } +}, { + name: "kFunctionCallsEval", + test() { eval(` + function asmModuleEval() { + "use asm"; + eval("function bar() {console.log('in bar')}"); + ${simpleAsmDef} + } + asmModuleEval()(3); + `) + } +}, { + name: "kFunctionChildCallsEval", + test() { eval(` + function asmModuleChildEval() { + "use asm"; + function x(v) { + v = v | 0; + eval("function bar() {console.log('in bar')}"); + return v | 0; + } + return x; + } + asmModuleChildEval()(); + `) + } +}, { + name: "kFunctionUsesArguments", + test() { eval(` + function asmModuleArguments() { + "use asm"; + arguments; + ${simpleAsmDef} + } + asmModuleArguments(); + `) + } +}, { + name: "kFunctionHasWithStmt", + test() { eval(` + function asmModuleWith() { + "use asm"; + with(5) { + ${simpleAsmDef} + } + } + asmModuleWith()(3); + `) + } +}, { + name: "kFunctionIsLambda", + test() { eval(` + const asmModuleLambda = () => { + "use asm"; + ${simpleAsmDef} + } + asmModuleLambda()(3); + `) + } +}, { + name: "kFunctionHasNonSimpleParameterList: Module destructuring", + test() { eval(` + function asmModuleDestructuring({Math: {sin}}) { + "use asm"; + function x(v) { + v = +v; + return +sin(+v); + } + return x; + } + asmModuleDestructuring({Math})(3); + `) + } +}, { + name: "kFunctionHasNonSimpleParameterList: function destructuring", + test() { eval(` + function asmModuleDestructuringChild() { + "use asm"; + function x({v}) { + v = +v; + return +v; + } + return x; + } + asmModuleDestructuringChild()({v: 3}); + `) + } +}, { + name: "kFunctionHasNonSimpleParameterList: rest", + test() { eval(` + function asmModuleRest(...rest) { + "use asm"; + ${simpleAsmDef} + } + asmModuleRest()(3); + `) + } +}, { + name: "kFunctionHasDefaultArguments", + test() { eval(` + function asmModuleDefault(stdlib = {Math}) { + "use asm"; + var sin = stdlib.Math.sin; + function x(v) { + v = +v; + return +(+sin(+v)); + } + return x; + } + asmModuleDefault()(3); + `) + } +}, { + // Todo:: bug #12464098 + disabled: true, + name: "kFunctionIsModule", + test() { WScript.LoadModule(` + "use asm" + export function x(v) { + v = v | 0; + return v | 0; + }`) + } +}, { + // found bugs in jsrt api + disabled: true, + name: "asm.js function in Module", + test() { + WScript.LoadModule(` + function AsmDefaultExport() { + "use asm" + ${simpleAsmDef} + }`) + } +}, { + disabled: true, + name: "kFunctionIsDefaultModuleExport", + test() { + WScript.LoadModule(` + export default function AsmDefaultExport() { + "use asm" + ${simpleAsmDef} + }`) + } +}, { + name: "kFunctionHasSuperReference", + test() { eval(` + var obj = { + asmModuleSuper() { + "use asm"; + var a = super.toString; + ${simpleAsmDef} + } + } + obj.asmModuleSuper()(3); + `) + } +}, { + name: "kFunctionIsGenerator", + test() { eval(` + function* asmModuleGenerator() { + "use asm"; + ${simpleAsmDef} + } + asmModuleGenerator().next().value(3); + `) + } +}, { + name: "kFunctionIsAsync", + test() { eval(` + async function asmModuleAsync() { + "use asm"; + ${simpleAsmDef} + } + asmModuleAsync().then(f => f(3)); + `) + } +}]; + +const start = WScript.Arguments[0] || 0; +for (let i = start; i < tests.length; ++i) { + const {disabled, name, test} = tests[i]; + console.log(`\nRunning test ${i}: ${name}`); + if (disabled) { + console.log("Warning test disabled"); + } else { + test(); + } +} \ No newline at end of file
test/AsmJs/bug12239366.js+25 −0 added@@ -0,0 +1,25 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +function asmModule() { + "use asm"; + + let a = [1]; + for (let i = 0; i < 2; i++) { // JIT + a[0] = 1; + if (i > 0) { + a[0] = {}; // the array type changed, bailout!! + } + } + + function f(v) { + v = v | 0; + return v | 0; + } + return f; +} + +asmModule(1); +print("PASSED");
test/AsmJs/rlexe.xml+18 −0 modified@@ -919,6 +919,19 @@ <compile-flags>-testtrace:asmjs -maic:1</compile-flags> </default> </test> + <test> + <default> + <files>bug12239366.js</files> + <compile-flags>-lic:1 -bgjit-</compile-flags> + </default> + </test> + <test> + <default> + <files>badFunctionType.js</files> + <baseline>badFunctionType.baseline</baseline> + <compile-flags>-testtrace:asmjs</compile-flags> + </default> + </test> <test> <default> <files>bugGH2270.js</files> @@ -982,4 +995,9 @@ <tags>exclude_drt</tags> </default> </test> + <test> + <default> + <files>trackdeferredonreparse.js</files> + </default> + </test> </regress-exe>
test/AsmJs/trackdeferredonreparse.js+174 −0 added@@ -0,0 +1,174 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft Corporation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +print = function(){}; +(function () { + + assertPromiseResult = function(promise, success, fail) { + + if (!success) success = () => {}; + + failWithMessage = (msg) => eval("print(msg)"); + if (!fail) { + fail = result => failWithMessage("assertPromiseResult failed: " + result); + } + + var test_promise = + promise.then( + result => { + try { + success(result); + } catch (e) { + failWithMessage(e); + } + }, + result => { + fail(result); + } + ) + .then((x)=> { + if (--promiseTestCount == 0) testRunner.notifyDone(); + }); + + if (!promiseTestChain) promiseTestChain = Promise.resolve(); + // waitUntilDone is idempotent. + testRunner.waitUntilDone(); + ++promiseTestCount; + return promiseTestChain.then(test_promise); + }; + + + assertUnoptimized = function assertUnoptimized(fun, sync_opt, name_opt) { + if (sync_opt === undefined) sync_opt = ""; + var opt_status = OptimizationStatus(fun, sync_opt); + // Tests that use assertOptimized() do not make sense if --always-opt + // option is provided. Such tests must add --no-always-opt to flags comment. + assertFalse((opt_status & V8OptimizationStatus.kAlwaysOptimize) !== 0, + "test does not make sense with --always-opt"); + assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, name_opt); + if ((opt_status & V8OptimizationStatus.kMaybeDeopted) !== 0) { + // When --deopt-every-n-times flag is specified it's no longer guaranteed + // that particular function is still deoptimized, so keep running the test + // to stress test the deoptimizer. + return; + } + assertFalse((opt_status & V8OptimizationStatus.kOptimized) !== 0, name_opt); + } + + + + + + + + assertOptimized = function assertOptimized(fun, sync_opt, name_opt) { + if (sync_opt === undefined) sync_opt = ""; + var opt_status = OptimizationStatus(fun, sync_opt); + // Tests that use assertOptimized() do not make sense if --no-opt + // option is provided. Such tests must add --opt to flags comment. + assertFalse((opt_status & V8OptimizationStatus.kNeverOptimize) !== 0, + "test does not make sense with --no-opt"); + assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, name_opt); + if ((opt_status & V8OptimizationStatus.kMaybeDeopted) !== 0) { + // When --deopt-every-n-times flag is specified it's no longer guaranteed + // that particular function is still optimized, so keep running the test + // to stress test the deoptimizer. + return; + } + assertTrue((opt_status & V8OptimizationStatus.kOptimized) !== 0, name_opt); + } + + isNeverOptimize = function isNeverOptimize() { + var opt_status = OptimizationStatus(undefined, ""); + return (opt_status & V8OptimizationStatus.kNeverOptimize) !== 0; + } + + isAlwaysOptimize = function isAlwaysOptimize() { + var opt_status = OptimizationStatus(undefined, ""); + return (opt_status & V8OptimizationStatus.kAlwaysOptimize) !== 0; + } + + isInterpreted = function isInterpreted(fun) { + var opt_status = OptimizationStatus(fun, ""); + assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, + "not a function"); + return (opt_status & V8OptimizationStatus.kOptimized) === 0 && + (opt_status & V8OptimizationStatus.kInterpreted) !== 0; + } + + isOptimized = function isOptimized(fun) { + var opt_status = OptimizationStatus(fun, ""); + assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, + "not a function"); + return (opt_status & V8OptimizationStatus.kOptimized) !== 0; + } + + isCrankshafted = function isCrankshafted(fun) { + var opt_status = OptimizationStatus(fun, ""); + assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, + "not a function"); + return (opt_status & V8OptimizationStatus.kOptimized) !== 0 && + (opt_status & V8OptimizationStatus.kTurboFanned) === 0; + } + + isTurboFanned = function isTurboFanned(fun) { + var opt_status = OptimizationStatus(fun, ""); + assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, + "not a function"); + return (opt_status & V8OptimizationStatus.kOptimized) !== 0 && + (opt_status & V8OptimizationStatus.kTurboFanned) !== 0; + } + +})(); + + +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --allow-natives-syntax + +assertEquals = print; + +var m = (function() { + "use asm"; + function f(x) { + return x < 0; + } + function g(x) { + return 0 < x; + } + return { f: f, g: g }; +})(); +var f = m.f; +var g = m.g; + +var counter = 0; + +function deopt(f) { + return { + toString : function() { + print(f); + counter++; + return "2"; + } + }; +} + +assertEquals(false, f(deopt(f))); +assertEquals(1, counter); + +assertEquals(true, g(deopt(g))); +assertEquals(2, counter); + +print(f); +assertEquals(false, f(deopt(f))); +assertEquals(3, counter); + +print(g); +assertEquals(true, g(deopt(g))); +assertEquals(4, counter); + +WScript.Echo('pass');
test/Basics/DeleteAndReAddNonExtensible.js+20 −0 added@@ -0,0 +1,20 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft Corporation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +var kNumProperties = 32; + +var o = {}; +for (var i = 0; i < kNumProperties; ++i) +o['a' + i] = i; + +Object.preventExtensions(o); // IsNotExtensibleSupported && !this->VerifyIsExtensible + +for (var i = 0; i < kNumProperties; ++i) +delete o['a' + i]; + +for (var i = 0; i < 0x21; ++i) +o['a0'] = 1; // calling TryUndeleteProperty again again + +WScript.Echo('pass');
test/Basics/rlexe.xml+5 −0 modified@@ -311,6 +311,11 @@ <compile-flags>-Intl-</compile-flags> </default> </test> + <test> + <default> + <files>DeleteAndReAddNonExtensible.js</files> + </default> + </test> <test> <default> <files>Accessors.js</files>
test/es6/destructuring_catch.js+8 −0 modified@@ -51,6 +51,14 @@ var tests = [ assert.throws(function () { eval("function foo() {try {} catch([x]) { let x = 10;} }"); }, SyntaxError, "Catch param as a pattern and matching name with let/const variable in body is not valid syntax", "Let/Const redeclaration"); assert.throws(function () { eval("function foo() {try {} catch([x]) { function x() {} } }"); }, SyntaxError, "Catch param as a pattern and matching name with function name in body is not valid syntax", "Let/Const redeclaration"); assert.doesNotThrow(function () { eval("function foo() {try {} catch([x]) { var x = 10;} }"); }, "Catch param as a pattern and matching name with var declared name in body is valid syntax"); + + (function () { + try { + } catch ({x}) { + var x = 1; + } + assert.areEqual(x, undefined, "Assignment inside the catch block should assign the value to the catch param not the body var"); + })(); } }, {
test/es6/destructuring_params_arguments_override.js+24 −0 added@@ -0,0 +1,24 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft Corporation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +function f() { + ({a = () => { + let arguments; + }} = 1); + + arguments.x; +} + +f(); + +function g() { + ({a = ([arguments]) => {}} = 1); + + arguments.x; +} + +g(); + +WScript.Echo('pass');
test/es6/ProxyCall.js+12 −0 added@@ -0,0 +1,12 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft Corporation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +function f() { + arguments; + WScript.Echo('pass'); +} + +let call = new Proxy(Function.prototype.call, {}); // proxy calls set the flag +call.call(f);
test/es6/rlexe.xml+10 −0 modified@@ -728,6 +728,11 @@ <files>proxybug.js</files> </default> </test> + <test> + <default> + <files>proxycall.js</files> + </default> + </test> <test> <default> <files>proxyenumremoval.js</files> @@ -900,6 +905,11 @@ <compile-flags>-ES6Rest -ES6Classes -ES6Destructuring -ES6DefaultArgs -args summary -endargs</compile-flags> </default> </test> + <test> + <default> + <files>destructuring_params_arguments_override.js</files> + </default> + </test> <test> <default> <files>destructuring_catch.js</files>
test/Function/argumentsLimits.baseline+1 −1 modified@@ -1 +1 @@ -65532 +Out of memory
test/Function/argumentsLimits.js+6 −1 modifiedtest/Function/deferredstuboob.js+17 −0 added@@ -0,0 +1,17 @@ +//------------------------------------------------------------------------------------------------------- +// Copyright (C) Microsoft Corporation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. +//------------------------------------------------------------------------------------------------------- + +function test0() { +var func12 = function (arg1 = ( { get : function f1 () { }} +, { get : function f213 () { }} +, { get : function f214 () { }} +,(function f2() {}))) +{ +}; +func12(); +} +test0(); + +WScript.Echo('pass');
test/Function/rlexe.xml+6 −0 modified@@ -187,6 +187,12 @@ <tags>exclude_dynapogo</tags> </default> </test> + <test> + <default> + <files>deferredStubOob.js</files> + <compile-flags>-force:deferparse -pageheap:2</compile-flags> + </default> + </test> <test> <default> <files>deferredWith.js</files>
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2017-8658nvdPatchVendor AdvisoryWEB
- www.securityfocus.com/bid/100036nvdThird Party AdvisoryVDB Entry
- github.com/advisories/GHSA-8v2h-4jpm-3wfmghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2017-8658ghsaADVISORY
- github.com/chakra-core/ChakraCore/commit/2500e1cdc12cb35af73d5c8c9b85656aba6bab4dghsaWEB
- github.com/chakra-core/ChakraCore/pull/3509ghsaWEB
- web.archive.org/web/20210124090857/http://www.securityfocus.com/bid/100036ghsaWEB
News mentions
0No linked articles in our index yet.