Changeset 264105 in webkit
- Timestamp:
- Jul 8, 2020, 9:42:54 AM (6 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 19 edited
-
ChangeLog (modified) (1 diff)
-
b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp (modified) (1 diff)
-
b3/air/AirCode.cpp (modified) (1 diff)
-
b3/air/AirCode.h (modified) (1 diff)
-
b3/air/AirGenerate.cpp (modified) (1 diff)
-
bytecode/BytecodeList.rb (modified) (1 diff)
-
bytecode/CodeBlock.cpp (modified) (2 diffs)
-
bytecode/CodeBlock.h (modified) (1 diff)
-
dfg/DFGCapabilities.cpp (modified) (1 diff)
-
dfg/DFGSpeculativeJIT64.cpp (modified) (1 diff)
-
ftl/FTLLowerDFGToB3.cpp (modified) (2 diffs)
-
jit/JITOpcodes.cpp (modified) (1 diff)
-
llint/LLIntSlowPaths.cpp (modified) (1 diff)
-
llint/LowLevelInterpreter32_64.asm (modified) (1 diff)
-
llint/LowLevelInterpreter64.asm (modified) (1 diff)
-
offlineasm/mips.rb (modified) (1 diff)
-
runtime/OptionsList.h (modified) (1 diff)
-
runtime/VM.cpp (modified) (1 diff)
-
runtime/VM.h (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r264059 r264105 1 2020-07-08 Saam Barati <sbarati@apple.com> 2 3 Add a way to return early from detected infinite loops to aid the fuzzer 4 https://bugs.webkit.org/show_bug.cgi?id=214067 5 6 Reviewed by Yusuke Suzuki. 7 8 It's useful for the fuzzer to not get stuck in infinite loops so its 9 test cases can make forward progress trying to find bugs. This patch 10 adds a new mechanism where we can early return if we've exceeded a total 11 execution count for a static loop in bytecode. Note: this is not on a 12 per-frame basis, but it's a way to implement this in a non-invasive way 13 which is also practical for the fuzzer to use. 14 15 * b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp: 16 (JSC::B3::Air::GenerateAndAllocateRegisters::generate): 17 * b3/air/AirCode.cpp: 18 (JSC::B3::Air::Code::emitEpilogue): 19 * b3/air/AirCode.h: 20 * b3/air/AirGenerate.cpp: 21 (JSC::B3::Air::generateWithAlreadyAllocatedRegisters): 22 * bytecode/BytecodeList.rb: 23 * bytecode/CodeBlock.cpp: 24 (JSC::CodeBlock::finishCreation): 25 (JSC::CodeBlock::~CodeBlock): 26 * dfg/DFGCapabilities.cpp: 27 (JSC::DFG::capabilityLevel): 28 * dfg/DFGSpeculativeJIT64.cpp: 29 (JSC::DFG::SpeculativeJIT::compile): 30 * ftl/FTLLowerDFGToB3.cpp: 31 (JSC::FTL::DFG::LowerDFGToB3::compileNode): 32 (JSC::FTL::DFG::LowerDFGToB3::compileLoopHint): 33 * jit/JITOpcodes.cpp: 34 (JSC::JIT::emit_op_loop_hint): 35 * llint/LLIntSlowPaths.cpp: 36 (JSC::LLInt::LLINT_SLOW_PATH_DECL): 37 * llint/LowLevelInterpreter32_64.asm: 38 * llint/LowLevelInterpreter64.asm: 39 * offlineasm/mips.rb: 40 * runtime/OptionsList.h: 41 * runtime/VM.cpp: 42 (JSC::VM::addLoopHintExecutionCounter): 43 (JSC::VM::getLoopHintExecutionCounter): 44 (JSC::VM::removeLoopHintExecutionCounter): 45 * runtime/VM.h: 46 1 47 2020-07-07 Yusuke Suzuki <ysuzuki@apple.com> 2 48 -
trunk/Source/JavaScriptCore/b3/air/AirAllocateRegistersAndStackAndGenerateCode.cpp
r254788 r264105 788 788 // We currently don't represent the full epilogue in Air, so we need to 789 789 // have this override. 790 if (m_code.frameSize()) { 791 m_jit->emitRestore(m_code.calleeSaveRegisterAtOffsetList()); 792 m_jit->emitFunctionEpilogue(); 793 } else 794 m_jit->emitFunctionEpilogueWithEmptyFrame(); 795 m_jit->ret(); 790 m_code.emitEpilogue(*m_jit); 796 791 } 797 792 -
trunk/Source/JavaScriptCore/b3/air/AirCode.cpp
r256003 r264105 106 106 } 107 107 108 void Code::emitEpilogue(CCallHelpers& jit) 109 { 110 if (frameSize()) { 111 jit.emitRestore(calleeSaveRegisterAtOffsetList()); 112 jit.emitFunctionEpilogue(); 113 } else 114 jit.emitFunctionEpilogueWithEmptyFrame(); 115 jit.ret(); 116 } 117 108 118 void Code::setRegsInPriorityOrder(Bank bank, const Vector<Reg>& regs) 109 119 { -
trunk/Source/JavaScriptCore/b3/air/AirCode.h
r256003 r264105 347 347 348 348 void emitDefaultPrologue(CCallHelpers&); 349 void emitEpilogue(CCallHelpers&); 349 350 350 351 std::unique_ptr<GenerateAndAllocateRegisters> m_generateAndAllocateRegisters; -
trunk/Source/JavaScriptCore/b3/air/AirGenerate.cpp
r261755 r264105 271 271 // have this override. 272 272 auto start = jit.labelIgnoringWatchpoints(); 273 if (code.frameSize()) { 274 jit.emitRestore(code.calleeSaveRegisterAtOffsetList()); 275 jit.emitFunctionEpilogue(); 276 } else 277 jit.emitFunctionEpilogueWithEmptyFrame(); 278 jit.ret(); 273 code.emitEpilogue(jit); 279 274 addItem(block->last()); 280 275 auto end = jit.labelIgnoringWatchpoints(); -
trunk/Source/JavaScriptCore/bytecode/BytecodeList.rb
r263035 r264105 1390 1390 op :checkpoint_osr_exit_from_inlined_call_trampoline 1391 1391 op :checkpoint_osr_exit_trampoline 1392 op :fuzzer_return_early_from_loop_hint 1392 1393 op :handleUncaughtException 1393 1394 op :op_call_return_location -
trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp
r264049 r264105 769 769 break; 770 770 } 771 772 case op_loop_hint: { 773 if (Options::returnEarlyFromInfiniteLoopsForFuzzing()) 774 vm.addLoopHintExecutionCounter(instruction.ptr()); 775 break; 776 } 771 777 772 778 default: … … 811 817 { 812 818 VM& vm = *m_vm; 819 820 if (Options::returnEarlyFromInfiniteLoopsForFuzzing() && JITCode::isBaselineCode(jitType())) { 821 for (const auto& instruction : instructions()) { 822 if (instruction->is<OpLoopHint>()) 823 vm.removeLoopHintExecutionCounter(instruction.ptr()); 824 } 825 } 813 826 814 827 #if ENABLE(DFG_JIT) -
trunk/Source/JavaScriptCore/bytecode/CodeBlock.h
r263117 r264105 923 923 const void* instructionsRawPointer() { return m_instructionsRawPointer; } 924 924 925 bool loopHintsAreEligibleForFuzzingEarlyReturn() 926 { 927 // Some builtins are required to always complete the loops they run. 928 return !m_unlinkedCode->isBuiltinFunction(); 929 } 930 925 931 protected: 926 932 void finalizeLLIntInlineCaches(); -
trunk/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
r263117 r264105 318 318 case checkpoint_osr_exit_trampoline: 319 319 case handleUncaughtException: 320 case fuzzer_return_early_from_loop_hint: 320 321 case op_iterator_open_return_location: 321 322 case op_iterator_next_return_location: -
trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
r263824 r264105 4964 4964 4965 4965 case PhantomLocal: 4966 // This is a no-op. 4967 noResult(node); 4968 break; 4969 4966 4970 case LoopHint: 4971 if (Options::returnEarlyFromInfiniteLoopsForFuzzing()) { 4972 CodeBlock* baselineCodeBlock = m_jit.graph().baselineCodeBlockFor(node->origin.semantic); 4973 if (baselineCodeBlock->loopHintsAreEligibleForFuzzingEarlyReturn()) { 4974 BytecodeIndex bytecodeIndex = node->origin.semantic.bytecodeIndex(); 4975 const Instruction* instruction = baselineCodeBlock->instructions().at(bytecodeIndex.offset()).ptr(); 4976 4977 uint64_t* ptr = vm().getLoopHintExecutionCounter(instruction); 4978 m_jit.pushToSave(GPRInfo::regT0); 4979 m_jit.load64(ptr, GPRInfo::regT0); 4980 auto skipEarlyReturn = m_jit.branch64(CCallHelpers::Below, GPRInfo::regT0, CCallHelpers::TrustedImm64(Options::earlyReturnFromInfiniteLoopsLimit())); 4981 4982 m_jit.popToRestore(GPRInfo::regT0); 4983 m_jit.move(CCallHelpers::TrustedImm64(JSValue::encode(jsUndefined())), GPRInfo::returnValueGPR); 4984 m_jit.emitRestoreCalleeSaves(); 4985 m_jit.emitFunctionEpilogue(); 4986 m_jit.ret(); 4987 4988 skipEarlyReturn.link(&m_jit); 4989 m_jit.add64(CCallHelpers::TrustedImm32(1), GPRInfo::regT0); 4990 m_jit.store64(GPRInfo::regT0, ptr); 4991 m_jit.popToRestore(GPRInfo::regT0); 4992 } 4993 } 4994 4967 4995 // This is a no-op. 4968 4996 noResult(node); -
trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
r263470 r264105 1621 1621 break; 1622 1622 1623 case LoopHint: { 1624 compileLoopHint(); 1625 break; 1626 } 1627 1623 1628 case PhantomLocal: 1624 case LoopHint:1625 1629 case MovHint: 1626 1630 case ZombieHint: … … 14697 14701 RELEASE_ASSERT_NOT_REACHED(); 14698 14702 } 14703 } 14704 14705 void compileLoopHint() 14706 { 14707 if (!Options::returnEarlyFromInfiniteLoopsForFuzzing()) 14708 return; 14709 14710 CodeBlock* baselineCodeBlock = m_graph.baselineCodeBlockFor(m_node->origin.semantic); 14711 if (!baselineCodeBlock->loopHintsAreEligibleForFuzzingEarlyReturn()) 14712 return; 14713 14714 BytecodeIndex bytecodeIndex = m_node->origin.semantic.bytecodeIndex(); 14715 const Instruction* instruction = baselineCodeBlock->instructions().at(bytecodeIndex.offset()).ptr(); 14716 uint64_t* ptr = vm().getLoopHintExecutionCounter(instruction); 14717 14718 PatchpointValue* patchpoint = m_out.patchpoint(Void); 14719 patchpoint->effects = Effects::none(); 14720 patchpoint->effects.exitsSideways = true; 14721 patchpoint->effects.writesLocalState = true; 14722 patchpoint->setGenerator([ptr] (CCallHelpers& jit, const StackmapGenerationParams& params) { 14723 auto restore = [&] { 14724 jit.popToRestore(GPRInfo::regT2); 14725 jit.popToRestore(GPRInfo::regT1); 14726 jit.popToRestore(GPRInfo::regT0); 14727 }; 14728 14729 jit.pushToSave(GPRInfo::regT0); 14730 jit.pushToSave(GPRInfo::regT1); 14731 jit.pushToSave(GPRInfo::regT2); 14732 14733 jit.move(CCallHelpers::TrustedImm64(Options::earlyReturnFromInfiniteLoopsLimit()), GPRInfo::regT2); 14734 jit.move(CCallHelpers::TrustedImmPtr(ptr), GPRInfo::regT0); 14735 jit.load64(CCallHelpers::Address(GPRInfo::regT0), GPRInfo::regT1); 14736 auto skipEarlyReturn = jit.branch64(CCallHelpers::Below, GPRInfo::regT1, GPRInfo::regT2); 14737 14738 restore(); 14739 jit.move(CCallHelpers::TrustedImm64(JSValue::encode(jsUndefined())), GPRInfo::returnValueGPR); 14740 params.code().emitEpilogue(jit); 14741 14742 skipEarlyReturn.link(&jit); 14743 jit.add64(CCallHelpers::TrustedImm32(1), GPRInfo::regT1); 14744 jit.store64(GPRInfo::regT1, CCallHelpers::Address(GPRInfo::regT0)); 14745 restore(); 14746 }); 14699 14747 } 14700 14748 -
trunk/Source/JavaScriptCore/jit/JITOpcodes.cpp
r263824 r264105 1161 1161 #endif // USE(JSVALUE64) 1162 1162 1163 void JIT::emit_op_loop_hint(const Instruction*) 1164 { 1163 void JIT::emit_op_loop_hint(const Instruction* instruction) 1164 { 1165 #if USE(JSVALUE64) 1166 if (Options::returnEarlyFromInfiniteLoopsForFuzzing() && m_codeBlock->loopHintsAreEligibleForFuzzingEarlyReturn()) { 1167 uint64_t* ptr = vm().getLoopHintExecutionCounter(instruction); 1168 load64(ptr, regT0); 1169 auto skipEarlyReturn = branch64(Below, regT0, TrustedImm64(Options::earlyReturnFromInfiniteLoopsLimit())); 1170 1171 moveValue(jsUndefined(), JSValueRegs { GPRInfo::returnValueGPR }); 1172 checkStackPointerAlignment(); 1173 emitRestoreCalleeSaves(); 1174 emitFunctionEpilogue(); 1175 ret(); 1176 1177 skipEarlyReturn.link(this); 1178 add64(TrustedImm32(1), regT0); 1179 store64(regT0, ptr); 1180 } 1181 #endif 1182 1165 1183 // Emit the JIT optimization check: 1166 1184 if (canBeOptimized()) { -
trunk/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
r263117 r264105 463 463 *codeBlock, ": Entered loop_osr with executeCounter = ", 464 464 codeBlock->llintExecuteCounter()); 465 466 if (UNLIKELY(Options::returnEarlyFromInfiniteLoopsForFuzzing() && codeBlock->loopHintsAreEligibleForFuzzingEarlyReturn())) { 467 uint64_t* ptr = vm.getLoopHintExecutionCounter(pc); 468 *ptr += codeBlock->llintExecuteCounter().m_activeThreshold; 469 if (*ptr >= Options::earlyReturnFromInfiniteLoopsLimit()) 470 LLINT_RETURN_TWO(LLInt::getCodePtr<JSEntryPtrTag>(fuzzer_return_early_from_loop_hint).executableAddress(), callFrame->topOfFrame()); 471 } 472 465 473 466 474 auto loopOSREntryBytecodeIndex = BytecodeIndex(codeBlock->bytecodeOffset(pc)); -
trunk/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
r262613 r264105 2703 2703 dispatch() 2704 2704 end) 2705 2706 2707 op(fuzzer_return_early_from_loop_hint, macro () 2708 notSupported() 2709 end) -
trunk/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
r263546 r264105 2945 2945 hasStructurePropertyImpl(size, get, dispatch, return, _slow_path_in_structure_property) 2946 2946 end) 2947 2948 op(fuzzer_return_early_from_loop_hint, macro () 2949 move ValueUndefined, r0 2950 doReturn() 2951 end) 2952 -
trunk/Source/JavaScriptCore/offlineasm/mips.rb
r260310 r264105 685 685 # and we should name it properly. 686 686 # https://bugs.webkit.org/show_bug.cgi?id=208236 687 if node.name =~ /^.*_return_location(?:_(?:wide16|wide32))?$/ or node.name.start_with?("_checkpoint_osr_exit_from_inlined_call_trampoline") 687 if node.name =~ /^.*_return_location(?:_(?:wide16|wide32))?$/ or node.name.start_with?("_checkpoint_osr_exit_from_inlined_call_trampoline") or node.name.start_with?("_fuzzer_return_early_from_loop_hint") 688 688 # We need to have a special case for return location labels because they are always 689 689 # reached from a `ret` instruction. In this case, we need to proper reconfigure `$gp` -
trunk/Source/JavaScriptCore/runtime/OptionsList.h
r263380 r264105 511 511 v(Bool, allowUnsupportedTiers, false, Normal, "If true, we will not disable DFG or FTL when an experimental feature is enabled.") \ 512 512 v(Bool, usePrivateClassFields, false, Normal, "If true, the parser will understand private data fields inside classes.") \ 513 v(Bool, returnEarlyFromInfiniteLoopsForFuzzing, false, Normal, nullptr) \ 514 v(Size, earlyReturnFromInfiniteLoopsLimit, 1300000000, Normal, "When returnEarlyFromInfiniteLoopsForFuzzing is true, this determines the number of executions a loop can run for before just returning. This is helpful for the fuzzer so it doesn't get stuck in infinite loops.") \ 513 515 514 516 enum OptionEquivalence { -
trunk/Source/JavaScriptCore/runtime/VM.cpp
r263674 r264105 1553 1553 } 1554 1554 1555 void VM::addLoopHintExecutionCounter(const Instruction* instruction) 1556 { 1557 auto locker = holdLock(m_loopHintExecutionCountLock); 1558 auto addResult = m_loopHintExecutionCounts.add(instruction, std::pair<unsigned, std::unique_ptr<uint64_t>>(0, nullptr)); 1559 if (addResult.isNewEntry) { 1560 auto ptr = WTF::makeUniqueWithoutFastMallocCheck<uint64_t>(); 1561 *ptr = 0; 1562 addResult.iterator->value.second = WTFMove(ptr); 1563 } 1564 ++addResult.iterator->value.first; 1565 } 1566 1567 uint64_t* VM::getLoopHintExecutionCounter(const Instruction* instruction) 1568 { 1569 auto locker = holdLock(m_loopHintExecutionCountLock); 1570 auto iter = m_loopHintExecutionCounts.find(instruction); 1571 return iter->value.second.get(); 1572 } 1573 1574 void VM::removeLoopHintExecutionCounter(const Instruction* instruction) 1575 { 1576 auto locker = holdLock(m_loopHintExecutionCountLock); 1577 auto iter = m_loopHintExecutionCounts.find(instruction); 1578 RELEASE_ASSERT(!!iter->value.first); 1579 --iter->value.first; 1580 if (!iter->value.first) 1581 m_loopHintExecutionCounts.remove(iter); 1582 } 1583 1555 1584 } // namespace JSC -
trunk/Source/JavaScriptCore/runtime/VM.h
r263117 r264105 1100 1100 }; 1101 1101 1102 void addLoopHintExecutionCounter(const Instruction*); 1103 uint64_t* getLoopHintExecutionCounter(const Instruction*); 1104 void removeLoopHintExecutionCounter(const Instruction*); 1105 1102 1106 private: 1103 1107 friend class LLIntOffsetsExtractor; … … 1225 1229 uintptr_t m_currentWeakRefVersion { 0 }; 1226 1230 1231 Lock m_loopHintExecutionCountLock; 1232 HashMap<const Instruction*, std::pair<unsigned, std::unique_ptr<uint64_t>>> m_loopHintExecutionCounts; 1233 1227 1234 VM* m_prev; // Required by DoublyLinkedListNode. 1228 1235 VM* m_next; // Required by DoublyLinkedListNode.
Note:
See TracChangeset
for help on using the changeset viewer.