From 65b8329b249e3910892b639bc628191178102e63 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Thu, 15 Apr 2021 13:40:31 +0200 Subject: [PATCH] Filter recursive functions in fuzzer harness. --- .../ossfuzz/YulToEvmDifferentialFuzzer.cpp | 67 +++++++++++++++---- .../ossfuzz/protomutators/YulProtoMutator.cpp | 3 +- .../ossfuzz/protomutators/YulProtoMutator.h | 6 +- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/test/tools/ossfuzz/YulToEvmDifferentialFuzzer.cpp b/test/tools/ossfuzz/YulToEvmDifferentialFuzzer.cpp index e14241654..8c1297357 100644 --- a/test/tools/ossfuzz/YulToEvmDifferentialFuzzer.cpp +++ b/test/tools/ossfuzz/YulToEvmDifferentialFuzzer.cpp @@ -30,6 +30,9 @@ #include #include +#include +#include + #include #include @@ -52,6 +55,24 @@ using namespace solidity::langutil; using namespace solidity::util; using namespace std; +namespace +{ +/// @returns true if there are no recursive functions, false otherwise. +bool recursiveFunctionExists(Dialect const& _dialect, yul::Object& _object) +{ + auto recursiveFunctions = CallGraphGenerator::callGraph(*_object.code).recursiveFunctions(); + for(auto&& [function, variables]: CompilabilityChecker{ + _dialect, + _object, + true + }.unreachableVariables + ) + if(recursiveFunctions.count(function)) + return true; + return false; +} +} + static evmc::VM evmone = evmc::VM{evmc_create_evmone()}; DEFINE_PROTO_FUZZER(Program const& _input) @@ -101,16 +122,18 @@ DEFINE_PROTO_FUZZER(Program const& _input) } solidity::frontend::OptimiserSettings settings = solidity::frontend::OptimiserSettings::none(); - AssemblyStack stackUnoptimized(version, AssemblyStack::Language::StrictAssembly, settings); + AssemblyStack yulStack(version, AssemblyStack::Language::StrictAssembly, settings); solAssert( - stackUnoptimized.parseAndAnalyze("source", yulSubObject), + yulStack.parseAndAnalyze("source", yulSubObject), "Parsing fuzzer generated input failed." ); ostringstream unoptimizedState; + std::shared_ptr subObject = yulStack.parserResult(); + Dialect const& dialect = EVMDialect::strictAssemblyForEVMObjects(version); yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret( unoptimizedState, - stackUnoptimized.parserResult()->code, - EVMDialect::strictAssemblyForEVMObjects(version), + subObject->code, + dialect, true, 10000, 10000, @@ -119,19 +142,16 @@ DEFINE_PROTO_FUZZER(Program const& _input) if (yulFuzzerUtil::resourceLimitsExceeded(termReason)) return; - AssemblyStack stackOptimized(version, AssemblyStack::Language::StrictAssembly, settings); - solAssert( - stackOptimized.parseAndAnalyze("source", yulSubObject), - "Parsing fuzzer generated input failed." - ); YulOptimizerTestCommon optimizerTest( - stackOptimized.parserResult(), - EVMDialect::strictAssemblyForEVMObjects(version) + subObject, + dialect ); // Run circular references pruner and then stack limit evader. string step = "stackLimitEvader"; optimizerTest.setStep(step); shared_ptr astBlock = optimizerTest.run(); + bool recursiveFunction = recursiveFunctionExists(dialect, *subObject); + string optimisedSubObject = AsmPrinter{}(*astBlock); string optimisedProgram = Whiskers(R"( object "main" { code { @@ -145,12 +165,31 @@ DEFINE_PROTO_FUZZER(Program const& _input) } } )") - ("fuzzerInput", AsmPrinter{}(*astBlock)) + ("fuzzerInput", optimisedSubObject) .render(); - cout << AsmPrinter{}(*astBlock) << endl; + cout << optimisedSubObject << endl; bytes optimisedByteCode; settings.optimizeStackAllocation = true; - optimisedByteCode = YulAssembler{version, settings, optimisedProgram}.assemble(); + try + { + optimisedByteCode = YulAssembler{version, settings, optimisedProgram}.assemble(); + } + catch (yul::StackTooDeepError const&) + { + // If there are recursive functions, stack too deep errors are expected + // even post stack evasion optimisation and hence ignored. Otherwise, + // they are rethrown for further investigation. + if (recursiveFunction) + return; + throw; + } + // InvalidDeposit + catch (Exception const&) + { + if (recursiveFunction) + return; + throw; + } // Reset host before running optimised code. hostContext.reset(); diff --git a/test/tools/ossfuzz/protomutators/YulProtoMutator.cpp b/test/tools/ossfuzz/protomutators/YulProtoMutator.cpp index 572064faa..de52c1a1d 100644 --- a/test/tools/ossfuzz/protomutators/YulProtoMutator.cpp +++ b/test/tools/ossfuzz/protomutators/YulProtoMutator.cpp @@ -12,7 +12,8 @@ using YPM = YulProtoMutator; MutationInfo::MutationInfo(ProtobufMessage const* _message, string const& _info): ScopeGuard([&]{ exitInfo(); }), - m_protobufMsg(_message) + m_protobufMsg(_message), + m_debug(false) { writeLine("----------------------------------"); writeLine("YULMUTATOR: " + _info); diff --git a/test/tools/ossfuzz/protomutators/YulProtoMutator.h b/test/tools/ossfuzz/protomutators/YulProtoMutator.h index 7d2c9c794..e0c47b376 100644 --- a/test/tools/ossfuzz/protomutators/YulProtoMutator.h +++ b/test/tools/ossfuzz/protomutators/YulProtoMutator.h @@ -21,13 +21,15 @@ class MutationInfo: public ScopeGuard public: MutationInfo(ProtobufMessage const* _message, std::string const& _info); - static void writeLine(std::string const& _str) + void writeLine(std::string const& _str) { - std::cout << _str << std::endl; + if (m_debug) + std::cout << _str << std::endl; } void exitInfo(); ProtobufMessage const* m_protobufMsg; + bool m_debug; }; struct YulRandomNumGenerator