Skip to content

Commit

Permalink
[CVE-2017-8601] Generate bailouts for cases of typed array stores whe…
Browse files Browse the repository at this point in the history
…re conversion of the source value can cause implicit calls. For BailOutOnImplicitCalls, check implicit call bit on return from the conversion helper. For BailOutOnArrayAccessHelperCall, bail out instead of calling the conversion helper.
  • Loading branch information
pleath committed Jul 11, 2017
1 parent 3915540 commit 9326bda
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 74 deletions.
98 changes: 74 additions & 24 deletions lib/Backend/Lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16924,20 +16924,17 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef)
const IR::AutoReuseOpnd autoReuseReg(reg, m_func);
InsertMove(reg, src, stElem);

bool bailOutOnHelperCall = stElem->HasBailOutInfo() && (stElem->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall);

// Convert to float, and assign to indirOpnd
if (baseValueType.IsLikelyOptimizedVirtualTypedArray())
{
IR::RegOpnd* dstReg = IR::RegOpnd::New(indirOpnd->GetType(), this->m_func);
m_lowererMD.EmitLoadFloat(dstReg, reg, stElem, bailOutOnHelperCall);
m_lowererMD.EmitLoadFloat(dstReg, reg, stElem, stElem, labelHelper);
InsertMove(indirOpnd, dstReg, stElem);
}
else
{
m_lowererMD.EmitLoadFloat(indirOpnd, reg, stElem, bailOutOnHelperCall);
m_lowererMD.EmitLoadFloat(indirOpnd, reg, stElem, stElem, labelHelper);
}

}
}
else if (objectType == ObjectType::Uint8ClampedArray || objectType == ObjectType::Uint8ClampedVirtualArray || objectType == ObjectType::Uint8ClampedMixedArray)
Expand Down Expand Up @@ -17120,7 +17117,7 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef)
// Any pointer is larger than 512 because first 64k memory is reserved by the OS
// #endif

IR::LabelInstr *labelInlineSet = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
IR::LabelInstr *labelInlineSet = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
#ifndef _M_ARM
// TEST src, ~(TaggedInt(255)) -- Check for tagged int >= 255 and <= 0
// JEQ $inlineSet
Expand All @@ -17142,29 +17139,53 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef)

// Uint8ClampedArray::DirectSetItem(array, index, value);

m_lowererMD.LoadHelperArgument(stElem, regSrc);
IR::Opnd *indexOpnd = indirOpnd->GetIndexOpnd();
if (indexOpnd == nullptr)
// Inserting a helper call. Make sure it observes the main instructions's requirements regarding implicit calls.
if (!instrIsInHelperBlock)
{
indexOpnd = IR::IntConstOpnd::New(indirOpnd->GetOffset(), TyInt32, this->m_func);
stElem->InsertBefore(IR::LabelInstr::New(Js::OpCode::Label, m_func, true));
}
else

if (stElem->HasBailOutInfo() && (stElem->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall))
{
Assert(indirOpnd->GetOffset() == 0);
// Bail out instead of doing the helper call.
Assert(labelHelper);
this->InsertBranch(Js::OpCode::Br, labelHelper, stElem);
}
m_lowererMD.LoadHelperArgument(stElem, indexOpnd);
m_lowererMD.LoadHelperArgument(stElem, stElem->GetDst()->AsIndirOpnd()->GetBaseOpnd());
else
{
IR::Instr *instr = IR::Instr::New(Js::OpCode::Call, this->m_func);
stElem->InsertBefore(instr);

IR::Instr *instr = IR::Instr::New(Js::OpCode::Call, this->m_func);
if (stElem->HasBailOutInfo() && BailOutInfo::IsBailOutOnImplicitCalls(stElem->GetBailOutKind()))
{
// Bail out if this helper triggers implicit calls.
instr = instr->ConvertToBailOutInstr(stElem->GetBailOutInfo(), stElem->GetBailOutKind());
if (stElem->GetBailOutInfo()->bailOutInstr == stElem)
{
IR::Instr * instrShare = stElem->ShareBailOut();
LowerBailTarget(instrShare);
}
}

Assert(objectType == ObjectType::Uint8ClampedArray || objectType == ObjectType::Uint8ClampedMixedArray || objectType == ObjectType::Uint8ClampedVirtualArray);
instr->SetSrc1(IR::HelperCallOpnd::New(IR::HelperUint8ClampedArraySetItem, this->m_func));
m_lowererMD.LoadHelperArgument(instr, regSrc);
IR::Opnd *indexOpnd = indirOpnd->GetIndexOpnd();
if (indexOpnd == nullptr)
{
indexOpnd = IR::IntConstOpnd::New(indirOpnd->GetOffset(), TyInt32, this->m_func);
}
else
{
Assert(indirOpnd->GetOffset() == 0);
}
m_lowererMD.LoadHelperArgument(instr, indexOpnd);
m_lowererMD.LoadHelperArgument(instr, stElem->GetDst()->AsIndirOpnd()->GetBaseOpnd());

stElem->InsertBefore(instr);
m_lowererMD.LowerCall(instr, 0);
Assert(objectType == ObjectType::Uint8ClampedArray || objectType == ObjectType::Uint8ClampedMixedArray || objectType == ObjectType::Uint8ClampedVirtualArray);
m_lowererMD.ChangeToHelperCall(instr, IR::JnHelperMethod::HelperUint8ClampedArraySetItem);

// JMP $fallThrough
InsertBranch(Js::OpCode::Br, labelFallThru, stElem);
// JMP $fallThrough
InsertBranch(Js::OpCode::Br, labelFallThru, stElem);
}

//$inlineSet
stElem->InsertBefore(labelInlineSet);
Expand Down Expand Up @@ -17207,9 +17228,27 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef)
AssertMsg(AutoSystemInfo::Data.SSE2Available(), "GloOpt shouldn't have specialized Uint32Array StElemI to float64 if SSE2 is unavailable.");
#endif

bool bailOutOnHelperCall = stElem->HasBailOutInfo() ? !!(stElem->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall) : false;
if (bailOutOnHelperCall)
{
if(!GlobOpt::DoEliminateArrayAccessHelperCall(this->m_func))
{
// Array access helper call removal is already off for some reason. Prevent trying to rejit again
// because it won't help and the same thing will happen again. Just abort jitting this function.
if(PHASE_TRACE(Js::BailOutPhase, this->m_func))
{
Output::Print(_u(" Aborting JIT because EliminateArrayAccessHelperCall is already off\n"));
Output::Flush();
}
throw Js::OperationAbortedException();
}

throw Js::RejitException(RejitReason::ArrayAccessHelperCallEliminationDisabled);
}

IR::RegOpnd *const reg = IR::RegOpnd::New(TyInt32, this->m_func);
const IR::AutoReuseOpnd autoReuseReg(reg, m_func);
m_lowererMD.EmitFloatToInt(reg, src, stElem);
m_lowererMD.EmitFloatToInt(reg, src, stElem, stElem, labelHelper);

// MOV indirOpnd, reg
InsertMove(indirOpnd, reg, stElem);
Expand Down Expand Up @@ -17239,12 +17278,23 @@ Lowerer::GenerateFastStElemI(IR::Instr *& stElem, bool *instrIsInHelperBlockRef)
// FromVar reg, Src
IR::RegOpnd *const reg = IR::RegOpnd::New(TyInt32, this->m_func);
const IR::AutoReuseOpnd autoReuseReg(reg, m_func);
IR::Instr *const instr = IR::Instr::New(Js::OpCode::FromVar, reg, regSrc, stElem->m_func);
IR::Instr * instr = IR::Instr::New(Js::OpCode::FromVar, reg, regSrc, stElem->m_func);
stElem->InsertBefore(instr);

// Convert reg to int32
// Note: ToUint32 is implemented as (uint32)ToInt32()
bool bailOutOnHelperCall = (stElem->HasBailOutInfo() && (stElem->GetBailOutKind() & IR::BailOutOnArrayAccessHelperCall));
IR::BailOutKind bailOutKind = stElem->HasBailOutInfo() ? stElem->GetBailOutKind() : IR::BailOutInvalid;
if (BailOutInfo::IsBailOutOnImplicitCalls(bailOutKind))
{
instr = instr->ConvertToBailOutInstr(stElem->GetBailOutInfo(), bailOutKind);
if (stElem->GetBailOutInfo()->bailOutInstr == stElem)
{
IR::Instr * instrShare = stElem->ShareBailOut();
LowerBailTarget(instrShare);
}
}

bool bailOutOnHelperCall = !!(bailOutKind & IR::BailOutOnArrayAccessHelperCall);
m_lowererMD.EmitLoadInt32(instr, true /*conversionFromObjectAllowed*/, bailOutOnHelperCall, labelHelper);

// MOV indirOpnd, reg
Expand Down
70 changes: 48 additions & 22 deletions lib/Backend/LowerMDShared.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6732,8 +6732,8 @@ LowererMD::EmitLoadFloatCommon(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertIn
return labelDone;
}

IR::RegOpnd *
LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, bool bailOutOnHelperCall)
void
LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, IR::Instr * instrBailOut, IR::LabelInstr * labelBailOut)
{
IR::LabelInstr *labelDone;
IR::Instr *instr;
Expand All @@ -6742,24 +6742,17 @@ LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, b
if (labelDone == nullptr)
{
// We're done
return nullptr;
return;
}

if (bailOutOnHelperCall)
IR::BailOutKind bailOutKind = instrBailOut && instrBailOut->HasBailOutInfo() ? instrBailOut->GetBailOutKind() : IR::BailOutInvalid;
if (bailOutKind & IR::BailOutOnArrayAccessHelperCall)
{
if(!GlobOpt::DoEliminateArrayAccessHelperCall(this->m_func))
{
// Array access helper call removal is already off for some reason. Prevent trying to rejit again
// because it won't help and the same thing will happen again. Just abort jitting this function.
if(PHASE_TRACE(Js::BailOutPhase, this->m_func))
{
Output::Print(_u(" Aborting JIT because EliminateArrayAccessHelperCall is already off\n"));
Output::Flush();
}
throw Js::OperationAbortedException();
}

throw Js::RejitException(RejitReason::ArrayAccessHelperCallEliminationDisabled);
// Bail out instead of making the helper call.
Assert(labelBailOut);
m_lowerer->InsertBranch(Js::OpCode::Br, labelBailOut, insertInstr);
insertInstr->InsertBefore(labelDone);
return;
}

IR::Opnd *memAddress = dst;
Expand All @@ -6785,6 +6778,16 @@ LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, b
instr->SetSrc2(reg3Opnd);
insertInstr->InsertBefore(instr);

if (BailOutInfo::IsBailOutOnImplicitCalls(bailOutKind))
{
instr = instr->ConvertToBailOutInstr(instrBailOut->GetBailOutInfo(), bailOutKind);
if (instrBailOut->GetBailOutInfo()->bailOutInstr == instrBailOut)
{
IR::Instr * instrShare = instrBailOut->ShareBailOut();
m_lowerer->LowerBailTarget(instrShare);
}
}

IR::JnHelperMethod helper;
if (dst->GetType() == TyFloat32)
{
Expand Down Expand Up @@ -6813,8 +6816,6 @@ LowererMD::EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, b
}
// $Done
insertInstr->InsertBefore(labelDone);

return nullptr;
}

void
Expand Down Expand Up @@ -8362,13 +8363,26 @@ LowererMD::InsertConvertFloat64ToInt32(const RoundMode roundMode, IR::Opnd *cons
}

void
LowererMD::EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert)
LowererMD::EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert, IR::Instr *instrBailOut, IR::LabelInstr * labelBailOut)
{
#ifdef _M_IX86
// We should only generate this if sse2 is available
Assert(AutoSystemInfo::Data.SSE2Available());
#endif

IR::BailOutKind bailOutKind = IR::BailOutInvalid;
if (instrBailOut && instrBailOut->HasBailOutInfo())
{
bailOutKind = instrBailOut->GetBailOutKind();
if (bailOutKind & IR::BailOutOnArrayAccessHelperCall)
{
// Bail out instead of calling helper. If this is happening unconditionally, the caller should instead throw a rejit exception.
Assert(labelBailOut);
m_lowerer->InsertBranch(Js::OpCode::Br, labelBailOut, instrInsert);
return;
}
}

IR::LabelInstr *labelDone = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
IR::LabelInstr *labelHelper = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
IR::Instr *instr;
Expand All @@ -8385,11 +8399,23 @@ LowererMD::EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert)

EmitFloat32ToFloat64(arg, src, instrInsert);
}
// dst = ToInt32Core(src);
LoadDoubleHelperArgument(instrInsert, arg);

instr = IR::Instr::New(Js::OpCode::CALL, dst, this->m_func);
instrInsert->InsertBefore(instr);

if (BailOutInfo::IsBailOutOnImplicitCalls(bailOutKind))
{
instr = instr->ConvertToBailOutInstr(instrBailOut->GetBailOutInfo(), bailOutKind);
if (instrBailOut->GetBailOutInfo()->bailOutInstr == instrBailOut)
{
IR::Instr * instrShare = instrBailOut->ShareBailOut();
m_lowerer->LowerBailTarget(instrShare);
}
}

// dst = ToInt32Core(src);
LoadDoubleHelperArgument(instr, arg);

this->ChangeToHelperCall(instr, IR::HelperConv_ToInt32Core);

// $Done
Expand Down
4 changes: 2 additions & 2 deletions lib/Backend/LowerMDShared.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,13 @@ class LowererMD
void EmitIntToLong(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
void EmitUIntToLong(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
void EmitLongToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
void EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
void EmitFloatToInt(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert, IR::Instr * instrBailOut = nullptr, IR::LabelInstr * labelBailOut = nullptr);
void EmitInt64toFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
void EmitFloat32ToFloat64(IR::Opnd *dst, IR::Opnd *src, IR::Instr *instrInsert);
static IR::Instr *InsertConvertFloat64ToInt32(const RoundMode roundMode, IR::Opnd *const dst, IR::Opnd *const src, IR::Instr *const insertBeforeInstr);
void ConvertFloatToInt32(IR::Opnd* intOpnd, IR::Opnd* floatOpnd, IR::LabelInstr * labelHelper, IR::LabelInstr * labelDone, IR::Instr * instInsert);
void EmitLoadFloatFromNumber(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr);
IR::RegOpnd * EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, bool bailOutOnHelperCall = false);
void EmitLoadFloat(IR::Opnd *dst, IR::Opnd *src, IR::Instr *insertInstr, IR::Instr * instrBailOut = nullptr, IR::LabelInstr * labelBailOut = nullptr);
static void EmitNon32BitOvfCheck(IR::Instr *instr, IR::Instr *insertInstr, IR::LabelInstr* bailOutLabel);

static void LowerInt4NegWithBailOut(IR::Instr *const instr, const IR::BailOutKind bailOutKind, IR::LabelInstr *const bailOutLabel, IR::LabelInstr *const skipBailOutLabel);
Expand Down
Loading

0 comments on commit 9326bda

Please sign in to comment.