Skip to content

Commit

Permalink
[MERGE #4411 @MikeHolman] 17-12 Security Update
Browse files Browse the repository at this point in the history
  • Loading branch information
MikeHolman committed Dec 12, 2017
2 parents 156a58e + 6e6301c commit f5ab042
Show file tree
Hide file tree
Showing 26 changed files with 289 additions and 123 deletions.
2 changes: 1 addition & 1 deletion Build/NuGet/.pack-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.7.4
1.7.5
8 changes: 8 additions & 0 deletions lib/Backend/BackwardPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7853,6 +7853,14 @@ BackwardPass::RemoveEmptyLoopAfterMemOp(Loop *loop)

outerBlock->RemovePred(head, this->func->m_fg);
landingPad->RemoveSucc(head, this->func->m_fg);
Assert(landingPad->GetSuccList()->Count() == 0);

IR::Instr* firstOuterInstr = outerBlock->GetFirstInstr();
AssertOrFailFast(firstOuterInstr->IsLabelInstr() && !landingPad->GetLastInstr()->EndsBasicBlock());
IR::LabelInstr* label = firstOuterInstr->AsLabelInstr();
// Add br to Outer block to keep coherence between branches and flow graph
IR::BranchInstr *outerBr = IR::BranchInstr::New(Js::OpCode::Br, label, this->func);
landingPad->InsertAfter(outerBr);
this->func->m_fg->AddEdge(landingPad, outerBlock);

this->func->m_fg->RemoveBlock(head, nullptr);
Expand Down
10 changes: 6 additions & 4 deletions lib/Backend/GlobOpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12946,7 +12946,9 @@ GlobOpt::ProcessValueKills(IR::Instr *const instr)
Assert(kills.KillsTypedArrayHeadSegmentLengths());

// - Calls need to kill the value types of values in the following list. For instance, calls can transform a JS array
// into an ES5 array, so any definitely-array value types need to be killed. Update the value types.
// into an ES5 array, so any definitely-array value types need to be killed. Also, VirtualTypeArrays do not have
// bounds checks; this can be problematic if the array is detached, so check to ensure that it is a virtual array.
// Update the value types to likley to ensure a bailout that asserts Array type is generated.
// - Calls also need to kill typed array head segment lengths. A typed array's array buffer may be transferred to a web
// worker, in which case the typed array's length is set to zero.
for(auto it = valuesToKillOnCalls->GetIterator(); it.IsValid(); it.MoveNext())
Expand All @@ -12956,7 +12958,7 @@ GlobOpt::ProcessValueKills(IR::Instr *const instr)
Assert(
valueInfo->IsArrayOrObjectWithArray() ||
valueInfo->IsOptimizedTypedArray() && valueInfo->AsArrayValueInfo()->HeadSegmentLengthSym());
if(valueInfo->IsArrayOrObjectWithArray())
if (valueInfo->IsArrayOrObjectWithArray() || valueInfo->IsOptimizedVirtualTypedArray())
{
ChangeValueType(nullptr, value, valueInfo->Type().ToLikely(), false);
continue;
Expand Down Expand Up @@ -18165,8 +18167,8 @@ GlobOpt::TrackTempObjectSyms(IR::Instr * instr, IR::RegOpnd * opnd)
(instr->GetSrc1()->IsRegOpnd() && globOptData.canStoreTempObjectSyms->Test(instr->GetSrc1()->AsRegOpnd()->m_sym->m_id))
&& (!instr->GetSrc2() || (instr->GetSrc2()->IsRegOpnd() && globOptData.canStoreTempObjectSyms->Test(instr->GetSrc2()->AsRegOpnd()->m_sym->m_id))));

Assert(!canStoreTemp || instr->dstIsTempObject);
Assert(!maybeTemp || instr->dstIsTempObject);
AssertOrFailFast(!canStoreTemp || instr->dstIsTempObject);
AssertOrFailFast(!maybeTemp || instr->dstIsTempObject);
}

// Need to get the var equiv sym as assignment of type specialized sym kill the var sym value anyway.
Expand Down
2 changes: 1 addition & 1 deletion lib/Backend/IRBuilderAsmJs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,7 @@ IRBuilderAsmJs::CreateLabel(IR::BranchInstr * branchInstr, uint & offset)
}

IR::LabelInstr * labelInstr;
if (instrPrev && instrPrev->IsLabelInstr() && instrPrev->GetByteCodeOffset() == offset)
if (instrPrev && instrPrev->IsLabelInstr())
{
// Found an existing label at the right offset. Just reuse it.
labelInstr = instrPrev->AsLabelInstr();
Expand Down
92 changes: 57 additions & 35 deletions lib/Backend/Inline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1983,19 +1983,7 @@ Inline::InlineBuiltInFunction(IR::Instr *callInstr, const FunctionJITTimeInfo *
StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym();
bool originalCallTargetOpndIsJITOpt = callInstr->GetSrc1()->GetIsJITOptimizedReg();

// We are committed to inlining, optimize the call instruction for fixed fields now and don't attempt it later.
bool safeThis = false;
if (TryOptimizeCallInstrWithFixedMethod(callInstr, inlineeData, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/, safeThis /*unused here*/))
{
Assert(callInstr->m_opcode == Js::OpCode::CallIFixed);
Assert(callInstr->GetFixedFunction()->GetFuncInfoAddr() == inlineeData->GetFunctionInfoAddr());
}
else
{
// FunctionObject check for built-ins
IR::BailOutInstr * bailOutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotBuiltIn, IR::BailOutOnInlineFunction, callInstr, callInstr->m_func);
InsertFunctionObjectCheck(callInstr, callInstr, bailOutInstr, inlineeData);
}
IR::ByteCodeUsesInstr* useCallTargetInstr = EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, inlineeData, false, true, false, true);

// To push function object for cases when we have to make calls to helper method to assist in inlining
if(inlineCallOpCode == Js::OpCode::CallDirect)
Expand Down Expand Up @@ -2031,11 +2019,9 @@ Inline::InlineBuiltInFunction(IR::Instr *callInstr, const FunctionJITTimeInfo *
}
}

// Insert a byteCodeUsesInstr to make sure the function object's lifetime is extended beyond the last bailout point
// at which we may need to call the inlinee again in the interpreter.
if (useCallTargetInstr)
{
IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr);
useCallTargetInstr->SetRemovedOpndSymbol(originalCallTargetOpndIsJITOpt, originalCallTargetStackSym->m_id);
useCallTargetInstr->Unlink();
callInstr->InsertBefore(useCallTargetInstr);
}

Expand Down Expand Up @@ -2071,7 +2057,7 @@ Inline::InlineBuiltInFunction(IR::Instr *callInstr, const FunctionJITTimeInfo *

// Insert a byteCodeUsesInstr to make sure the function object's lifetime is extended beyond the last bailout point
// at which we may need to call the inlinee again in the interpreter.
IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr->GetPrevRealInstrOrLabel());
useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr->GetPrevRealInstrOrLabel());
useCallTargetInstr->SetRemovedOpndSymbol(originalCallTargetOpndIsJITOpt, originalCallTargetStackSym->m_id);

if(inlineCallOpCode == Js::OpCode::InlineArrayPop)
Expand Down Expand Up @@ -2364,7 +2350,7 @@ IR::Instr* Inline::InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo *

// TODO: OOP JIT enable assert (readprocessmemory?)
//Assert((inlineeData->GetFunctionInfo()->GetAttributes() & Js::FunctionInfo::Attributes::BuiltInInlinableAsLdFldInlinee) != 0);
return InlineApplyWithArray(callInstr, applyData, Js::JavascriptLibrary::GetBuiltInForFuncInfo(inlineeData->GetFunctionInfoAddr(), this->topFunc->GetThreadContextInfo()));
return InlineApplyBuiltInTargetWithArray(callInstr, applyData, inlineeData);
}
else
{
Expand Down Expand Up @@ -2477,15 +2463,33 @@ IR::Instr * Inline::InlineApplyWithArgumentsObject(IR::Instr * callInstr, IR::In
/*
This method will only do CallDirect style inlining of built-in targets. No script function inlining.
*/
IR::Instr * Inline::InlineApplyWithArray(IR::Instr * callInstr, const FunctionJITTimeInfo * funcInfo, Js::BuiltinFunction builtInId)
IR::Instr * Inline::InlineApplyBuiltInTargetWithArray(IR::Instr * callInstr, const FunctionJITTimeInfo * applyInfo, const FunctionJITTimeInfo * builtInInfo)
{
IR::Instr * implicitThisArgOut = nullptr;
IR::Instr * explicitThisArgOut = nullptr;
IR::Instr * arrayArgOut = nullptr;
uint argOutCount = 0;
this->GetArgInstrsForCallAndApply(callInstr, &implicitThisArgOut, &explicitThisArgOut, &arrayArgOut, argOutCount);

TryFixedMethodAndPrepareInsertionPoint(callInstr, funcInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);
Js::OpCode originalCallOpcode = callInstr->m_opcode;
IR::Opnd * originalCallSrc1 = callInstr->GetSrc1()->Copy(this->topFunc);
IR::AutoReuseOpnd autoReuseOriginalCallSrc1(originalCallSrc1, this->topFunc);

IR::Instr* applyLdInstr = nullptr;
IR::Instr* applyTargetLdInstr = nullptr;
if (!TryGetApplyAndTargetLdInstrs(callInstr, &applyLdInstr, &applyTargetLdInstr))
{
return callInstr;
}
AnalysisAssert(applyTargetLdInstr != nullptr);
// Fixed function/function object checks for target built-in
callInstr->ReplaceSrc1(applyTargetLdInstr->GetDst());
EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, builtInInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);

// Fixed function/function object checks for .apply
callInstr->m_opcode = originalCallOpcode;
callInstr->ReplaceSrc1(originalCallSrc1);
EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, applyInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);

IR::Instr* builtInEndInstr = InsertInlineeBuiltInStartEndTags(callInstr, 3); // 3 args (implicit this + explicit this + array = 3)
builtInEndInstr->m_opcode = Js::OpCode::InlineNonTrackingBuiltInEnd; // We will call EndTrackCall when we see CallDirect for reasons explained in GlobOpt::TrackCalls
Expand Down Expand Up @@ -2513,6 +2517,7 @@ IR::Instr * Inline::InlineApplyWithArray(IR::Instr * callInstr, const FunctionJI
argOut = IR::Instr::New(Js::OpCode::ArgOut_A_InlineSpecialized, linkOpnd, implicitThisArgOut->GetSrc1(), argOut->GetDst(), callInstr->m_func);
callInstr->InsertBefore(argOut);

Js::BuiltinFunction builtInId = Js::JavascriptLibrary::GetBuiltInForFuncInfo(builtInInfo->GetFunctionInfoAddr(), this->topFunc->GetThreadContextInfo());
IR::HelperCallOpnd * helperCallOpnd = nullptr;
switch (builtInId)
{
Expand Down Expand Up @@ -2543,7 +2548,7 @@ IR::Instr * Inline::InlineApplyWithoutArrayArgument(IR::Instr *callInstr, const
uint argOutCount = 0;
this->GetArgInstrsForCallAndApply(callInstr, &implicitThisArgOut, &explicitThisArgOut, &dummyInstr, argOutCount);

TryFixedMethodAndPrepareInsertionPoint(callInstr, applyInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);
EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, applyInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);

InsertInlineeBuiltInStartEndTags(callInstr, 2); // 2 args (implicit this + explicit this)

Expand Down Expand Up @@ -2616,6 +2621,22 @@ void Inline::GetArgInstrsForCallAndApply(IR::Instr* callInstr, IR::Instr** impli
linkOpnd->AsRegOpnd()->m_sym->m_isInlinedArgSlot = true;
}

bool Inline::TryGetApplyAndTargetLdInstrs(IR::Instr * callInstr, _Outptr_result_maybenull_ IR::Instr ** applyLdInstr, _Outptr_result_maybenull_ IR::Instr ** applyTargetLdInstr)
{
IR::Opnd* applyOpnd = callInstr->GetSrc1();
Assert(applyOpnd->IsRegOpnd());
StackSym* applySym = applyOpnd->AsRegOpnd()->m_sym->AsStackSym();
if (!applySym->IsSingleDef())
{
*applyLdInstr = nullptr;
*applyTargetLdInstr = nullptr;
return false;
}
*applyLdInstr = applySym->GetInstrDef();;
*applyTargetLdInstr = (*applyLdInstr)->m_prev;
return true;
}

/*
This method only inlines targets which are script functions, under the
condition that the second argument (if any) passed to apply is arguments object.
Expand All @@ -2637,16 +2658,14 @@ bool Inline::InlineApplyScriptTarget(IR::Instr *callInstr, const FunctionJITTime

// Begin inlining apply target

IR::Opnd* applyOpnd = callInstr->GetSrc1();
Assert(applyOpnd->IsRegOpnd());
StackSym* applySym = applyOpnd->AsRegOpnd()->m_sym->AsStackSym();
if (!applySym->IsSingleDef())
IR::Instr* applyLdInstr = nullptr;
IR::Instr* applyTargetLdInstr = nullptr;
if (!TryGetApplyAndTargetLdInstrs(callInstr, &applyLdInstr, &applyTargetLdInstr))
{
return false;
}
IR::Instr* applyLdInstr = applySym->GetInstrDef();
IR::Instr* applyTargetLdInstr = applyLdInstr->m_prev;

AnalysisAssert(applyTargetLdInstr != nullptr);

if(applyTargetLdInstr->m_opcode != Js::OpCode::LdFldForCallApplyTarget ||
((applyTargetLdInstr->AsProfiledInstr()->u.FldInfo().flags & Js::FldInfo_FromAccessor) != 0))
{
Expand Down Expand Up @@ -2908,7 +2927,7 @@ Inline::InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, co

IR::SymOpnd* orgLinkOpnd = callInstr->GetSrc2()->AsSymOpnd();

TryFixedMethodAndPrepareInsertionPoint(callInstr, funcInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);
EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, funcInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);

InsertInlineeBuiltInStartEndTags(callInstr, actualCount);

Expand Down Expand Up @@ -4225,26 +4244,29 @@ Inline::PrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo *f
return primaryBailOutInstr;
}

void
Inline::TryFixedMethodAndPrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined)
IR::ByteCodeUsesInstr*
Inline::EmitFixedMethodOrFunctionObjectChecksForBuiltIns(IR::Instr *callInstr, IR::Instr * funcObjCheckInsertInstr, const FunctionJITTimeInfo * inlineeInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined)
{
StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym();
bool originalCallTargetIsJITOpt = callInstr->GetSrc1()->GetIsJITOptimizedReg();

IR::ByteCodeUsesInstr * useCallTargetInstr = nullptr;
bool safeThis = false;
if (TryOptimizeCallInstrWithFixedMethod(callInstr, inlineeInfo, isPolymorphic, isBuiltIn, isCtor, isInlined, safeThis))
{
Assert(callInstr->m_opcode == Js::OpCode::CallIFixed);

Assert(callInstr->GetFixedFunction()->GetFuncInfoAddr() == inlineeInfo->GetFunctionInfoAddr());
// If we optimized the call instruction for a fixed function, we must extend the function object's lifetime until after the last bailout before the call.
IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr);
useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr);
useCallTargetInstr->SetRemovedOpndSymbol(originalCallTargetIsJITOpt, originalCallTargetStackSym->m_id);
callInstr->InsertBefore(useCallTargetInstr);
}
else
{
PrepareInsertionPoint(callInstr, inlineeInfo, callInstr);
IR::BailOutInstr * bailOutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotBuiltIn, IR::BailOutOnInlineFunction, callInstr, callInstr->m_func);
InsertFunctionObjectCheck(callInstr, funcObjCheckInsertInstr, bailOutInstr, inlineeInfo);
}
return useCallTargetInstr;
}

uint Inline::CountActuals(IR::Instr *callInstr)
Expand Down
6 changes: 3 additions & 3 deletions lib/Backend/Inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ class Inline
IR::Instr * SimulateCallForGetterSetter(IR::Instr *accessorInstr, IR::Instr* insertInstr, IR::PropertySymOpnd* methodOpnd, bool isGetter);

IR::Instr * InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo * applyData, const FunctionJITTimeInfo * inlinerData, const StackSym *symThis, bool* pIsInlined, uint callSiteId, uint recursiveInlineDepth, uint argsCount);
IR::Instr * InlineApplyWithArray(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeInfo, Js::BuiltinFunction builtInId);
IR::Instr * InlineApplyBuiltInTargetWithArray(IR::Instr *callInstr, const FunctionJITTimeInfo * applyInfo, const FunctionJITTimeInfo * builtInInfo);
IR::Instr * InlineApplyWithArgumentsObject(IR::Instr * callInstr, IR::Instr * argsObjectArgInstr, const FunctionJITTimeInfo * inlineeInfo);
IR::Instr * InlineApplyWithoutArrayArgument(IR::Instr *callInstr, const FunctionJITTimeInfo * applyInfo, const FunctionJITTimeInfo * applyTargetInfo);
bool InlineApplyScriptTarget(IR::Instr *callInstr, const FunctionJITTimeInfo* inlinerData, const FunctionJITTimeInfo** pInlineeData, const FunctionJITTimeInfo * applyFuncInfo,
const StackSym *symThis, IR::Instr ** returnInstr, uint recursiveInlineDepth, bool isArrayOpndArgumentsObject, uint argsCount);
void GetArgInstrsForCallAndApply(IR::Instr* callInstr, IR::Instr** implicitThisArgOut, IR::Instr** explicitThisArgOut, IR::Instr** argumentsOrArrayArgOut, uint &argOutCount);

bool TryGetApplyAndTargetLdInstrs(IR::Instr * callInstr, _Outptr_result_maybenull_ IR::Instr ** applyLdInstr, _Outptr_result_maybenull_ IR::Instr ** applyTargetLdInstr);
IR::Instr * InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeData, const FunctionJITTimeInfo * inlinerData, const StackSym *symThis, bool* pIsInlined, uint callSiteId, uint recursiveInlineDepth);
bool InlineCallTarget(IR::Instr *callInstr, const FunctionJITTimeInfo* inlinerData, const FunctionJITTimeInfo** pInlineeData, const FunctionJITTimeInfo *callFuncInfo,
const StackSym *symThis, IR::Instr ** returnInstr, uint recursiveInlineDepth);
Expand Down Expand Up @@ -83,7 +83,7 @@ class Inline
void FixupExtraActualParams(IR::Instr * instr, IR::Instr *argOuts[], IR::Instr *argOutsExtra[], uint index, uint actualCount, Js::ProfileId callSiteId);
void RemoveExtraFixupArgouts(IR::Instr* instr, uint argoutRemoveCount, Js::ProfileId callSiteId);
IR::Instr* PrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, IR::Instr *insertBeforeInstr, IR::BailOutKind bailOutKind = IR::BailOutOnInlineFunction);
void TryFixedMethodAndPrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined);
IR::ByteCodeUsesInstr* EmitFixedMethodOrFunctionObjectChecksForBuiltIns(IR::Instr *callInstr, IR::Instr * funcObjCheckInsertInstr, const FunctionJITTimeInfo * inlineeInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined);
Js::ArgSlot MapActuals(IR::Instr *callInstr, __out_ecount(maxParamCount) IR::Instr *argOuts[], Js::ArgSlot formalCount, Func *inlinee, Js::ProfileId callSiteId, bool *stackArgsArgOutExpanded, IR::Instr *argOutsExtra[] = nullptr, Js::ArgSlot maxParamCount = Js::InlineeCallInfo::MaxInlineeArgoutCount);
uint32 CountActuals(IR::Instr *callIntr);
void MapFormals(Func *inlinee, __in_ecount(formalCount) IR::Instr *argOuts[], uint formalCount, uint actualCount, IR::RegOpnd *retOpnd, IR::Opnd * funcObjOpnd, const StackSym *symCallerThis, bool stackArgsArgOutExpanded, bool fixedFunctionSafeThis = false, IR::Instr *argOutsExtra[] = nullptr);
Expand Down
Loading

0 comments on commit f5ab042

Please sign in to comment.