Skip to content

Commit 760822c

Browse files
rajatdMikeHolman
authored andcommitted
[CVE-2017-11893] JIT Op_MaxInAnArray and Op_MinInAnArray can explicitly call user defined JavaScript functions - Google, Inc.
1 parent 69e03c3 commit 760822c

File tree

2 files changed

+59
-38
lines changed

2 files changed

+59
-38
lines changed

lib/Backend/Inline.cpp

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,19 +1983,7 @@ Inline::InlineBuiltInFunction(IR::Instr *callInstr, const FunctionJITTimeInfo *
19831983
StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym();
19841984
bool originalCallTargetOpndIsJITOpt = callInstr->GetSrc1()->GetIsJITOptimizedReg();
19851985

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

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

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

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

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

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

23652351
// TODO: OOP JIT enable assert (readprocessmemory?)
23662352
//Assert((inlineeData->GetFunctionInfo()->GetAttributes() & Js::FunctionInfo::Attributes::BuiltInInlinableAsLdFldInlinee) != 0);
2367-
return InlineApplyWithArray(callInstr, applyData, Js::JavascriptLibrary::GetBuiltInForFuncInfo(inlineeData->GetFunctionInfoAddr(), this->topFunc->GetThreadContextInfo()));
2353+
return InlineApplyBuiltInTargetWithArray(callInstr, applyData, inlineeData);
23682354
}
23692355
else
23702356
{
@@ -2477,15 +2463,33 @@ IR::Instr * Inline::InlineApplyWithArgumentsObject(IR::Instr * callInstr, IR::In
24772463
/*
24782464
This method will only do CallDirect style inlining of built-in targets. No script function inlining.
24792465
*/
2480-
IR::Instr * Inline::InlineApplyWithArray(IR::Instr * callInstr, const FunctionJITTimeInfo * funcInfo, Js::BuiltinFunction builtInId)
2466+
IR::Instr * Inline::InlineApplyBuiltInTargetWithArray(IR::Instr * callInstr, const FunctionJITTimeInfo * applyInfo, const FunctionJITTimeInfo * builtInInfo)
24812467
{
24822468
IR::Instr * implicitThisArgOut = nullptr;
24832469
IR::Instr * explicitThisArgOut = nullptr;
24842470
IR::Instr * arrayArgOut = nullptr;
24852471
uint argOutCount = 0;
24862472
this->GetArgInstrsForCallAndApply(callInstr, &implicitThisArgOut, &explicitThisArgOut, &arrayArgOut, argOutCount);
24872473

2488-
TryFixedMethodAndPrepareInsertionPoint(callInstr, funcInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);
2474+
Js::OpCode originalCallOpcode = callInstr->m_opcode;
2475+
IR::Opnd * originalCallSrc1 = callInstr->GetSrc1()->Copy(this->topFunc);
2476+
IR::AutoReuseOpnd autoReuseOriginalCallSrc1(originalCallSrc1, this->topFunc);
2477+
2478+
IR::Instr* applyLdInstr = nullptr;
2479+
IR::Instr* applyTargetLdInstr = nullptr;
2480+
if (!TryGetApplyAndTargetLdInstrs(callInstr, &applyLdInstr, &applyTargetLdInstr))
2481+
{
2482+
return callInstr;
2483+
}
2484+
2485+
// Fixed function/function object checks for target built-in
2486+
callInstr->ReplaceSrc1(applyTargetLdInstr->GetDst());
2487+
EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, builtInInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);
2488+
2489+
// Fixed function/function object checks for .apply
2490+
callInstr->m_opcode = originalCallOpcode;
2491+
callInstr->ReplaceSrc1(originalCallSrc1);
2492+
EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, applyInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);
24892493

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

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

2546-
TryFixedMethodAndPrepareInsertionPoint(callInstr, applyInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);
2551+
EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, applyInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);
25472552

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

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

2624+
bool Inline::TryGetApplyAndTargetLdInstrs(IR::Instr * callInstr, _Outptr_result_maybenull_ IR::Instr ** applyLdInstr, _Outptr_result_maybenull_ IR::Instr ** applyTargetLdInstr)
2625+
{
2626+
IR::Opnd* applyOpnd = callInstr->GetSrc1();
2627+
Assert(applyOpnd->IsRegOpnd());
2628+
StackSym* applySym = applyOpnd->AsRegOpnd()->m_sym->AsStackSym();
2629+
if (!applySym->IsSingleDef())
2630+
{
2631+
*applyLdInstr = nullptr;
2632+
*applyTargetLdInstr = nullptr;
2633+
return false;
2634+
}
2635+
*applyLdInstr = applySym->GetInstrDef();;
2636+
*applyTargetLdInstr = (*applyLdInstr)->m_prev;
2637+
return true;
2638+
}
2639+
26192640
/*
26202641
This method only inlines targets which are script functions, under the
26212642
condition that the second argument (if any) passed to apply is arguments object.
@@ -2637,16 +2658,13 @@ bool Inline::InlineApplyScriptTarget(IR::Instr *callInstr, const FunctionJITTime
26372658

26382659
// Begin inlining apply target
26392660

2640-
IR::Opnd* applyOpnd = callInstr->GetSrc1();
2641-
Assert(applyOpnd->IsRegOpnd());
2642-
StackSym* applySym = applyOpnd->AsRegOpnd()->m_sym->AsStackSym();
2643-
if (!applySym->IsSingleDef())
2661+
IR::Instr* applyLdInstr = nullptr;
2662+
IR::Instr* applyTargetLdInstr = nullptr;
2663+
if (!TryGetApplyAndTargetLdInstrs(callInstr, &applyLdInstr, &applyTargetLdInstr))
26442664
{
26452665
return false;
26462666
}
2647-
IR::Instr* applyLdInstr = applySym->GetInstrDef();
2648-
IR::Instr* applyTargetLdInstr = applyLdInstr->m_prev;
2649-
2667+
26502668
if(applyTargetLdInstr->m_opcode != Js::OpCode::LdFldForCallApplyTarget ||
26512669
((applyTargetLdInstr->AsProfiledInstr()->u.FldInfo().flags & Js::FldInfo_FromAccessor) != 0))
26522670
{
@@ -2908,7 +2926,7 @@ Inline::InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, co
29082926

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

2911-
TryFixedMethodAndPrepareInsertionPoint(callInstr, funcInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);
2929+
EmitFixedMethodOrFunctionObjectChecksForBuiltIns(callInstr, callInstr, funcInfo, false /*isPolymorphic*/, true /*isBuiltIn*/, false /*isCtor*/, true /*isInlined*/);
29122930

29132931
InsertInlineeBuiltInStartEndTags(callInstr, actualCount);
29142932

@@ -4225,26 +4243,29 @@ Inline::PrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo *f
42254243
return primaryBailOutInstr;
42264244
}
42274245

4228-
void
4229-
Inline::TryFixedMethodAndPrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined)
4246+
IR::ByteCodeUsesInstr*
4247+
Inline::EmitFixedMethodOrFunctionObjectChecksForBuiltIns(IR::Instr *callInstr, IR::Instr * funcObjCheckInsertInstr, const FunctionJITTimeInfo * inlineeInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined)
42304248
{
42314249
StackSym* originalCallTargetStackSym = callInstr->GetSrc1()->GetStackSym();
42324250
bool originalCallTargetIsJITOpt = callInstr->GetSrc1()->GetIsJITOptimizedReg();
42334251

4252+
IR::ByteCodeUsesInstr * useCallTargetInstr = nullptr;
42344253
bool safeThis = false;
42354254
if (TryOptimizeCallInstrWithFixedMethod(callInstr, inlineeInfo, isPolymorphic, isBuiltIn, isCtor, isInlined, safeThis))
42364255
{
42374256
Assert(callInstr->m_opcode == Js::OpCode::CallIFixed);
4238-
4257+
Assert(callInstr->GetFixedFunction()->GetFuncInfoAddr() == inlineeInfo->GetFunctionInfoAddr());
42394258
// 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.
4240-
IR::ByteCodeUsesInstr * useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr);
4259+
useCallTargetInstr = IR::ByteCodeUsesInstr::New(callInstr);
42414260
useCallTargetInstr->SetRemovedOpndSymbol(originalCallTargetIsJITOpt, originalCallTargetStackSym->m_id);
42424261
callInstr->InsertBefore(useCallTargetInstr);
42434262
}
42444263
else
42454264
{
4246-
PrepareInsertionPoint(callInstr, inlineeInfo, callInstr);
4265+
IR::BailOutInstr * bailOutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotBuiltIn, IR::BailOutOnInlineFunction, callInstr, callInstr->m_func);
4266+
InsertFunctionObjectCheck(callInstr, funcObjCheckInsertInstr, bailOutInstr, inlineeInfo);
42474267
}
4268+
return useCallTargetInstr;
42484269
}
42494270

42504271
uint Inline::CountActuals(IR::Instr *callInstr)

lib/Backend/Inline.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ class Inline
4747
IR::Instr * SimulateCallForGetterSetter(IR::Instr *accessorInstr, IR::Instr* insertInstr, IR::PropertySymOpnd* methodOpnd, bool isGetter);
4848

4949
IR::Instr * InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo * applyData, const FunctionJITTimeInfo * inlinerData, const StackSym *symThis, bool* pIsInlined, uint callSiteId, uint recursiveInlineDepth, uint argsCount);
50-
IR::Instr * InlineApplyWithArray(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeInfo, Js::BuiltinFunction builtInId);
50+
IR::Instr * InlineApplyBuiltInTargetWithArray(IR::Instr *callInstr, const FunctionJITTimeInfo * applyInfo, const FunctionJITTimeInfo * builtInInfo);
5151
IR::Instr * InlineApplyWithArgumentsObject(IR::Instr * callInstr, IR::Instr * argsObjectArgInstr, const FunctionJITTimeInfo * inlineeInfo);
5252
IR::Instr * InlineApplyWithoutArrayArgument(IR::Instr *callInstr, const FunctionJITTimeInfo * applyInfo, const FunctionJITTimeInfo * applyTargetInfo);
5353
bool InlineApplyScriptTarget(IR::Instr *callInstr, const FunctionJITTimeInfo* inlinerData, const FunctionJITTimeInfo** pInlineeData, const FunctionJITTimeInfo * applyFuncInfo,
5454
const StackSym *symThis, IR::Instr ** returnInstr, uint recursiveInlineDepth, bool isArrayOpndArgumentsObject, uint argsCount);
5555
void GetArgInstrsForCallAndApply(IR::Instr* callInstr, IR::Instr** implicitThisArgOut, IR::Instr** explicitThisArgOut, IR::Instr** argumentsOrArrayArgOut, uint &argOutCount);
56-
56+
bool TryGetApplyAndTargetLdInstrs(IR::Instr * callInstr, _Outptr_result_maybenull_ IR::Instr ** applyLdInstr, _Outptr_result_maybenull_ IR::Instr ** applyTargetLdInstr);
5757
IR::Instr * InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeData, const FunctionJITTimeInfo * inlinerData, const StackSym *symThis, bool* pIsInlined, uint callSiteId, uint recursiveInlineDepth);
5858
bool InlineCallTarget(IR::Instr *callInstr, const FunctionJITTimeInfo* inlinerData, const FunctionJITTimeInfo** pInlineeData, const FunctionJITTimeInfo *callFuncInfo,
5959
const StackSym *symThis, IR::Instr ** returnInstr, uint recursiveInlineDepth);
@@ -83,7 +83,7 @@ class Inline
8383
void FixupExtraActualParams(IR::Instr * instr, IR::Instr *argOuts[], IR::Instr *argOutsExtra[], uint index, uint actualCount, Js::ProfileId callSiteId);
8484
void RemoveExtraFixupArgouts(IR::Instr* instr, uint argoutRemoveCount, Js::ProfileId callSiteId);
8585
IR::Instr* PrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, IR::Instr *insertBeforeInstr, IR::BailOutKind bailOutKind = IR::BailOutOnInlineFunction);
86-
void TryFixedMethodAndPrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined);
86+
IR::ByteCodeUsesInstr* EmitFixedMethodOrFunctionObjectChecksForBuiltIns(IR::Instr *callInstr, IR::Instr * funcObjCheckInsertInstr, const FunctionJITTimeInfo * inlineeInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined);
8787
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);
8888
uint32 CountActuals(IR::Instr *callIntr);
8989
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);

0 commit comments

Comments
 (0)