From efab3101028045cbfa0cc21bd852f75bcc037dba Mon Sep 17 00:00:00 2001 From: Michael Holman Date: Tue, 4 Jun 2019 12:19:07 -0700 Subject: [PATCH] [CVE-2019-1103] Chakra JIT Type Confusion --- lib/Backend/BackwardPass.cpp | 12 ++++++++---- lib/Backend/BailOut.h | 3 ++- lib/Backend/GlobOpt.cpp | 6 ++++++ lib/Backend/Opnd.h | 3 ++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/Backend/BackwardPass.cpp b/lib/Backend/BackwardPass.cpp index 59210a247b5..de88a05c28f 100644 --- a/lib/Backend/BackwardPass.cpp +++ b/lib/Backend/BackwardPass.cpp @@ -4151,13 +4151,17 @@ BackwardPass::UpdateImplicitCallBailOutKind(IR::Instr *const instr, bool needsBa IR::BailOutKind implicitCallBailOutKind = needsBailOutOnImplicitCall ? IR::BailOutOnImplicitCalls : IR::BailOutInvalid; - const IR::BailOutKind instrBailOutKind = instr->GetBailOutKind(); + IR::BailOutKind instrBailOutKind = instr->GetBailOutKind(); if (instrBailOutKind & IR::BailOutMarkTempObject) { - // Don't remove the implicit call pre op bailout for mark temp object // Remove the mark temp object bit, as we don't need it after the dead store pass - instr->SetBailOutKind(instrBailOutKind & ~IR::BailOutMarkTempObject); - return true; + instrBailOutKind &= ~IR::BailOutMarkTempObject; + instr->SetBailOutKind(instrBailOutKind); + + if (!instr->GetBailOutInfo()->canDeadStore) + { + return true; + } } const IR::BailOutKind instrImplicitCallBailOutKind = instrBailOutKind & ~IR::BailOutKindBits; diff --git a/lib/Backend/BailOut.h b/lib/Backend/BailOut.h index 55af32893b1..208d940552f 100644 --- a/lib/Backend/BailOut.h +++ b/lib/Backend/BailOut.h @@ -27,7 +27,7 @@ class BailOutInfo BailOutInfo(uint32 bailOutOffset, Func* bailOutFunc) : bailOutOffset(bailOutOffset), bailOutFunc(bailOutFunc), byteCodeUpwardExposedUsed(nullptr), polymorphicCacheIndex((uint)-1), startCallCount(0), startCallInfo(nullptr), bailOutInstr(nullptr), - totalOutParamCount(0), argOutSyms(nullptr), bailOutRecord(nullptr), wasCloned(false), isInvertedBranch(false), sharedBailOutKind(true), isLoopTopBailOutInfo(false), + totalOutParamCount(0), argOutSyms(nullptr), bailOutRecord(nullptr), wasCloned(false), isInvertedBranch(false), sharedBailOutKind(true), isLoopTopBailOutInfo(false), canDeadStore(true), outParamInlinedArgSlot(nullptr), liveVarSyms(nullptr), liveLosslessInt32Syms(nullptr), liveFloat64Syms(nullptr), branchConditionOpnd(nullptr), stackLiteralBailOutInfoCount(0), stackLiteralBailOutInfo(nullptr) @@ -69,6 +69,7 @@ class BailOutInfo #endif bool wasCloned; bool isInvertedBranch; + bool canDeadStore; bool sharedBailOutKind; bool isLoopTopBailOutInfo; diff --git a/lib/Backend/GlobOpt.cpp b/lib/Backend/GlobOpt.cpp index 652410f7347..d8d1e276c1b 100644 --- a/lib/Backend/GlobOpt.cpp +++ b/lib/Backend/GlobOpt.cpp @@ -16531,6 +16531,7 @@ GlobOpt::GenerateBailOutMarkTempObjectIfNeeded(IR::Instr * instr, IR::Opnd * opn if (instr->HasBailOutInfo()) { instr->SetBailOutKind(instr->GetBailOutKind() | IR::BailOutMarkTempObject); + instr->GetBailOutInfo()->canDeadStore = false; } else { @@ -16540,6 +16541,11 @@ GlobOpt::GenerateBailOutMarkTempObjectIfNeeded(IR::Instr * instr, IR::Opnd * opn || (instr->m_opcode == Js::OpCode::FromVar && !opnd->GetValueType().IsPrimitive()) || propertySymOpnd == nullptr || !propertySymOpnd->IsTypeCheckProtected()) + { + this->GenerateBailAtOperation(&instr, IR::BailOutMarkTempObject); + instr->GetBailOutInfo()->canDeadStore = false; + } + else if (propertySymOpnd->MayHaveImplicitCall()) { this->GenerateBailAtOperation(&instr, IR::BailOutMarkTempObject); } diff --git a/lib/Backend/Opnd.h b/lib/Backend/Opnd.h index 500f5403364..b44ebff5eb3 100644 --- a/lib/Backend/Opnd.h +++ b/lib/Backend/Opnd.h @@ -1138,7 +1138,8 @@ class PropertySymOpnd sealed : public SymOpnd // fall back on live cache. Similarly, for fixed method checks. bool MayHaveImplicitCall() const { - return !IsRootObjectNonConfigurableFieldLoad() && !UsesFixedValue() && (!IsTypeCheckSeqCandidate() || !IsTypeCheckProtected()); + return !IsRootObjectNonConfigurableFieldLoad() && !UsesFixedValue() && (!IsTypeCheckSeqCandidate() || !IsTypeCheckProtected() + || (IsLoadedFromProto() && NeedsWriteGuardTypeCheck())); } // Is the instruction involving this operand part of a type check sequence? This is different from IsObjTypeSpecOptimized