Changeset 221607 in webkit
- Timestamp:
- Sep 4, 2017, 9:10:53 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 21 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/JSTests/ChangeLog
r221601 r221607 1 2017-09-04 Saam Barati <[email protected]> 2 3 typeCheckHoistingPhase may emit a CheckStructure on the empty value which leads to a dereference of zero on 64 bit platforms 4 https://bugs.webkit.org/show_bug.cgi?id=176317 5 6 Reviewed by Keith Miller. 7 8 * stress/dont-crash-when-hoist-check-structure-on-tdz.js: Added. 9 (Foo): 10 1 11 2017-09-03 Yusuke Suzuki <[email protected]> 2 12 -
trunk/Source/JavaScriptCore/ChangeLog
r221602 r221607 1 2017-09-04 Saam Barati <[email protected]> 2 3 typeCheckHoistingPhase may emit a CheckStructure on the empty value which leads to a dereference of zero on 64 bit platforms 4 https://bugs.webkit.org/show_bug.cgi?id=176317 5 6 Reviewed by Keith Miller. 7 8 It turns out that TypeCheckHoistingPhase may hoist a CheckStructure up to 9 the SetLocal of a particular value where the value is the empty JSValue. 10 On 64-bit platforms, the empty value is zero. This means that the empty value 11 passes a cell check. This will lead to a crash when we dereference null to load 12 the value's structure. This patch teaches TypeCheckHoistingPhase to be conservative 13 in the structure checks it hoists. On 64-bit platforms, instead of emitting a 14 CheckStructure node, we now emit a CheckStructureOrEmpty node. This node allows 15 the empty value to flow through. If the value isn't empty, it'll perform the normal 16 structure check that CheckStructure performs. For now, we only emit CheckStructureOrEmpty 17 on 64-bit platforms since a cell check on 32-bit platforms does not allow the empty 18 value to flow through. 19 20 * dfg/DFGAbstractInterpreterInlines.h: 21 (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): 22 * dfg/DFGArgumentsEliminationPhase.cpp: 23 * dfg/DFGClobberize.h: 24 (JSC::DFG::clobberize): 25 * dfg/DFGConstantFoldingPhase.cpp: 26 (JSC::DFG::ConstantFoldingPhase::foldConstants): 27 * dfg/DFGDoesGC.cpp: 28 (JSC::DFG::doesGC): 29 * dfg/DFGFixupPhase.cpp: 30 (JSC::DFG::FixupPhase::fixupNode): 31 * dfg/DFGNode.h: 32 (JSC::DFG::Node::convertCheckStructureOrEmptyToCheckStructure): 33 (JSC::DFG::Node::hasStructureSet): 34 * dfg/DFGNodeType.h: 35 * dfg/DFGObjectAllocationSinkingPhase.cpp: 36 * dfg/DFGPredictionPropagationPhase.cpp: 37 * dfg/DFGSafeToExecute.h: 38 (JSC::DFG::SafeToExecuteEdge::SafeToExecuteEdge): 39 (JSC::DFG::SafeToExecuteEdge::operator()): 40 (JSC::DFG::SafeToExecuteEdge::maySeeEmptyChild): 41 (JSC::DFG::safeToExecute): 42 * dfg/DFGSpeculativeJIT.cpp: 43 (JSC::DFG::SpeculativeJIT::emitStructureCheck): 44 (JSC::DFG::SpeculativeJIT::compileCheckStructure): 45 * dfg/DFGSpeculativeJIT.h: 46 * dfg/DFGSpeculativeJIT32_64.cpp: 47 (JSC::DFG::SpeculativeJIT::compile): 48 * dfg/DFGSpeculativeJIT64.cpp: 49 (JSC::DFG::SpeculativeJIT::compile): 50 * dfg/DFGTypeCheckHoistingPhase.cpp: 51 (JSC::DFG::TypeCheckHoistingPhase::run): 52 * dfg/DFGValidate.cpp: 53 * ftl/FTLCapabilities.cpp: 54 (JSC::FTL::canCompile): 55 * ftl/FTLLowerDFGToB3.cpp: 56 (JSC::FTL::DFG::LowerDFGToB3::compileNode): 57 (JSC::FTL::DFG::LowerDFGToB3::compileCheckStructureOrEmpty): 58 1 59 2017-09-04 Saam Barati <[email protected]> 2 60 -
trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
r221602 r221607 2407 2407 break; 2408 2408 } 2409 2410 case CheckStructureOrEmpty: { 2411 AbstractValue& value = forNode(node->child1()); 2412 2413 bool mayBeEmpty = value.m_type & SpecEmpty; 2414 if (!mayBeEmpty) 2415 m_state.setFoundConstants(true); 2416 2417 SpeculatedType admittedTypes = mayBeEmpty ? SpecEmpty : SpecNone; 2418 filter(value, node->structureSet(), admittedTypes); 2419 break; 2420 } 2409 2421 2410 2422 case CheckStructureImmediate: { -
trunk/Source/JavaScriptCore/dfg/DFGArgumentsEliminationPhase.cpp
r221528 r221607 371 371 break; 372 372 373 case CheckStructureOrEmpty: 373 374 case CheckStructure: { 374 375 if (!m_candidates.contains(node->child1().node())) … … 1097 1098 } 1098 1099 1100 case CheckStructureOrEmpty: 1099 1101 case CheckStructure: 1100 1102 if (!isEliminatedAllocation(node->child1().node())) -
trunk/Source/JavaScriptCore/dfg/DFGClobberize.h
r221602 r221607 959 959 } 960 960 961 case CheckStructureOrEmpty: 961 962 case CheckStructure: 962 963 read(JSCell_structureID); -
trunk/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
r221601 r221607 162 162 } 163 163 164 164 case CheckStructureOrEmpty: { 165 const AbstractValue& value = m_state.forNode(node->child1()); 166 if (value.m_type & SpecEmpty) 167 break; 168 node->convertCheckStructureOrEmptyToCheckStructure(); 169 changed = true; 170 FALLTHROUGH; 171 } 165 172 case CheckStructure: 166 173 case ArrayifyToStructure: { -
trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
r221602 r221607 116 116 case DeleteByVal: 117 117 case CheckStructure: 118 case CheckStructureOrEmpty: 119 case CheckStructureImmediate: 118 120 case GetExecutable: 119 121 case GetButterfly: … … 264 266 case ForwardVarargs: 265 267 case PutHint: 266 case CheckStructureImmediate:267 268 case PutStack: 268 269 case KillStack: -
trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
r221602 r221607 1584 1584 case PutHint: 1585 1585 case CheckStructureImmediate: 1586 case CheckStructureOrEmpty: 1586 1587 case MaterializeNewObject: 1587 1588 case MaterializeCreateActivation: -
trunk/Source/JavaScriptCore/dfg/DFGNode.h
r221602 r221607 431 431 setOpAndDefaultFlags(CheckStructure); 432 432 m_opInfo = set; 433 } 434 435 void convertCheckStructureOrEmptyToCheckStructure() 436 { 437 ASSERT(op() == CheckStructureOrEmpty); 438 setOpAndDefaultFlags(CheckStructure); 433 439 } 434 440 … … 1717 1723 switch (op()) { 1718 1724 case CheckStructure: 1725 case CheckStructureOrEmpty: 1719 1726 case CheckStructureImmediate: 1720 1727 case MaterializeNewObject: -
trunk/Source/JavaScriptCore/dfg/DFGNodeType.h
r221602 r221607 204 204 macro(DeleteByVal, NodeResultBoolean | NodeMustGenerate) \ 205 205 macro(CheckStructure, NodeMustGenerate) \ 206 macro(CheckStructureOrEmpty, NodeMustGenerate) \ 206 207 macro(GetExecutable, NodeResultJS) \ 207 208 macro(PutStructure, NodeMustGenerate) \ -
trunk/Source/JavaScriptCore/dfg/DFGObjectAllocationSinkingPhase.cpp
r221560 r221607 893 893 break; 894 894 895 case CheckStructureOrEmpty: 895 896 case CheckStructure: { 896 897 Allocation* allocation = m_heap.onlyLocalAllocation(node->child1().node()); -
trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
r221602 r221607 1070 1070 case PutHint: 1071 1071 case CheckStructureImmediate: 1072 case CheckStructureOrEmpty: 1072 1073 case MaterializeNewObject: 1073 1074 case MaterializeCreateActivation: … … 1082 1083 case LazyJSConstant: 1083 1084 case CallDOM: { 1084 // This node should never be visible at this stage of compilation. It is 1085 // inserted by fixup(), which follows this phase. 1085 // This node should never be visible at this stage of compilation. 1086 1086 DFG_CRASH(m_graph, m_currentNode, "Unexpected node during prediction propagation"); 1087 1087 break; -
trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
r221602 r221607 37 37 SafeToExecuteEdge(AbstractStateType& state) 38 38 : m_state(state) 39 , m_result(true)40 39 { 41 40 } … … 43 42 void operator()(Node*, Edge edge) 44 43 { 44 m_maySeeEmptyChild |= !!(m_state.forNode(edge).m_type & SpecEmpty); 45 45 46 switch (edge.useKind()) { 46 47 case UntypedUse: … … 111 112 112 113 bool result() const { return m_result; } 114 bool maySeeEmptyChild() const { return m_maySeeEmptyChild; } 113 115 private: 114 116 AbstractStateType& m_state; 115 bool m_result; 117 bool m_result { true }; 118 bool m_maySeeEmptyChild { false }; 116 119 }; 117 120 … … 134 137 if (!safeToExecuteEdge.result()) 135 138 return false; 139 140 if (safeToExecuteEdge.maySeeEmptyChild()) { 141 // We conservatively assume if the empty value flows into a node, 142 // it might not be able to handle it (e.g, crash). In general, the bytecode generator 143 // emits code in such a way that most node types don't need to worry about the empty value 144 // because they will never see it. However, code motion has to consider the empty 145 // value so it does not insert/move nodes to a place where they will crash. E.g, the 146 // type check hoisting phase needs to insert CheckStructureOrEmpty instead of CheckStructure 147 // for hoisted structure checks because it can not guarantee that a particular local is not 148 // the empty value. 149 switch (node->op()) { 150 case CheckNotEmpty: 151 case CheckStructureOrEmpty: 152 break; 153 default: 154 return false; 155 } 156 } 136 157 137 158 // NOTE: This tends to lie when it comes to effectful nodes, because it knows that they aren't going to … … 215 236 case DefineAccessorProperty: 216 237 case CheckStructure: 238 case CheckStructureOrEmpty: 217 239 case GetExecutable: 218 240 case GetButterfly: -
trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
r221602 r221607 7892 7892 } 7893 7893 7894 void SpeculativeJIT:: compileCheckStructure(Node* node, GPRReg cellGPR, GPRReg tempGPR)7894 void SpeculativeJIT::emitStructureCheck(Node* node, GPRReg cellGPR, GPRReg tempGPR) 7895 7895 { 7896 7896 ASSERT(node->structureSet().size()); … … 7937 7937 case KnownCellUse: { 7938 7938 SpeculateCellOperand cell(this, node->child1()); 7939 compileCheckStructure(node, cell.gpr(), InvalidGPRReg);7939 emitStructureCheck(node, cell.gpr(), InvalidGPRReg); 7940 7940 noResult(node); 7941 7941 return; … … 7955 7955 JITCompiler::Jump done = m_jit.jump(); 7956 7956 cell.link(&m_jit); 7957 compileCheckStructure(node, valueRegs.payloadGPR(), tempGPR);7957 emitStructureCheck(node, valueRegs.payloadGPR(), tempGPR); 7958 7958 done.link(&m_jit); 7959 7959 noResult(node); -
trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
r221601 r221607 2907 2907 void compileIsFunction(Node*); 2908 2908 void compileTypeOf(Node*); 2909 void compileCheckStructure(Node*, GPRReg cellGPR, GPRReg tempGPR);2910 2909 void compileCheckStructure(Node*); 2910 void emitStructureCheck(Node*, GPRReg cellGPR, GPRReg tempGPR); 2911 2911 void compilePutAccessorById(Node*); 2912 2912 void compilePutGetterSetterById(Node*); -
trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
r221602 r221607 5658 5658 break; 5659 5659 } 5660 5661 case CheckStructureOrEmpty: 5662 DFG_CRASH(m_jit.graph(), node, "CheckStructureOrEmpty only used in 64-bit DFG"); 5663 break; 5660 5664 5661 5665 case LastNodeType: -
trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
r221602 r221607 4635 4635 } 4636 4636 4637 case CheckStructureOrEmpty: { 4638 SpeculateCellOperand cell(this, node->child1()); 4639 GPRReg cellGPR = cell.gpr(); 4640 MacroAssembler::Jump isEmpty; 4641 if (m_interpreter.forNode(node->child1()).m_type & SpecEmpty) 4642 isEmpty = m_jit.branchTest64(MacroAssembler::Zero, cellGPR); 4643 4644 emitStructureCheck(node, cellGPR, InvalidGPRReg); 4645 4646 if (isEmpty.isSet()) 4647 isEmpty.link(&m_jit); 4648 4649 noResult(node); 4650 break; 4651 } 4652 4637 4653 case CheckStructure: { 4638 4654 compileCheckStructure(node); -
trunk/Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp
r221196 r221607 179 179 180 180 if (iter->value.m_structure) { 181 // Note: On 64-bit platforms, cell checks allow the empty value to flow through. 182 // This means that this structure check may see the empty value as input. We need 183 // to emit a node that explicitly handles the empty value. Most of the time, CheckStructureOrEmpty 184 // will be folded to CheckStructure because AI proves that the incoming value is 185 // definitely not empty. 186 static_assert(is64Bit() || !(SpecCellCheck & SpecEmpty), ""); 181 187 insertionSet.insertNode( 182 indexForChecks, SpecNone, CheckStructure,188 indexForChecks, SpecNone, is64Bit() ? CheckStructureOrEmpty : CheckStructure, 183 189 originForChecks.withSemantic(origin.semantic), 184 190 OpInfo(m_graph.addStructureSet(iter->value.m_structure)), -
trunk/Source/JavaScriptCore/dfg/DFGValidate.cpp
r221602 r221607 277 277 VALIDATE((node), !!node->child1()); 278 278 VALIDATE((node), !!node->cellOperand()->value() && node->cellOperand()->value().isCell()); 279 break; 280 case CheckStructureOrEmpty: 281 VALIDATE((node), is64Bit()); 282 VALIDATE((node), !!node->child1()); 283 VALIDATE((node), node->child1().useKind() == CellUse); 279 284 break; 280 285 case CheckStructure: -
trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
r221602 r221607 66 66 case BitURShift: 67 67 case CheckStructure: 68 case CheckStructureOrEmpty: 68 69 case DoubleAsInt32: 69 70 case ArrayifyToStructure: -
trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
r221602 r221607 645 645 compileCheckStructure(); 646 646 break; 647 case CheckStructureOrEmpty: 648 compileCheckStructureOrEmpty(); 649 break; 647 650 case CheckCell: 648 651 compileCheckCell(); … … 2762 2765 DFG_CRASH(m_graph, m_node, "Bad use kind"); 2763 2766 return; 2767 } 2768 } 2769 2770 void compileCheckStructureOrEmpty() 2771 { 2772 ExitKind exitKind; 2773 if (m_node->child1()->hasConstant()) 2774 exitKind = BadConstantCache; 2775 else 2776 exitKind = BadCache; 2777 2778 LValue cell = lowCell(m_node->child1()); 2779 bool maySeeEmptyValue = m_interpreter.forNode(m_node->child1()).m_type & SpecEmpty; 2780 LBasicBlock notEmpty; 2781 LBasicBlock continuation; 2782 LBasicBlock lastNext; 2783 if (maySeeEmptyValue) { 2784 notEmpty = m_out.newBlock(); 2785 continuation = m_out.newBlock(); 2786 m_out.branch(m_out.isZero64(cell), unsure(continuation), unsure(notEmpty)); 2787 lastNext = m_out.appendTo(notEmpty, continuation); 2788 } 2789 2790 checkStructure( 2791 m_out.load32(cell, m_heaps.JSCell_structureID), jsValueValue(cell), 2792 exitKind, m_node->structureSet(), 2793 [&] (RegisteredStructure structure) { 2794 return weakStructureID(structure); 2795 }); 2796 2797 if (maySeeEmptyValue) { 2798 m_out.jump(continuation); 2799 m_out.appendTo(continuation, lastNext); 2764 2800 } 2765 2801 }
Note:
See TracChangeset
for help on using the changeset viewer.