diff --git a/test/tools/ossfuzz/SolProtoAdaptor.cpp b/test/tools/ossfuzz/SolProtoAdaptor.cpp index 9d7ae4967..0b235a33c 100644 --- a/test/tools/ossfuzz/SolProtoAdaptor.cpp +++ b/test/tools/ossfuzz/SolProtoAdaptor.cpp @@ -25,8 +25,8 @@ using namespace solidity::test::solprotofuzzer::adaptor; using namespace solidity::test::solprotofuzzer; -using namespace std; using namespace solidity::util; +using namespace std; namespace { @@ -721,46 +721,31 @@ bool SolContract::validTest() } /// Must be called only when validTest() returns true. -tuple SolContract::validContractTest() +void SolContract::validContractTests(map>& _testSet) { string chosenContractName; string chosenFunctionName{}; string expectedOutput{}; - // If we can provide a test case right away, do so. + // Add test cases in current contract if (!m_contractFunctionMap.empty()) { chosenContractName = name(); - unsigned functionIdx = random() % m_contractFunctionMap.size(); - unsigned mapIdx = 0; + _testSet[chosenContractName] = map{}; for (auto &e: m_contractFunctionMap) - { - if (functionIdx == mapIdx) - { - chosenFunctionName = e.first; - expectedOutput = e.second; - break; - } - mapIdx++; - } - solAssert(m_contractFunctionMap.count(chosenFunctionName), "Sol proto adaptor: Invalid contract function chosen"); - return make_tuple(chosenContractName, chosenFunctionName, expectedOutput); + _testSet[chosenContractName].insert(make_pair(e.first, e.second)); } - // Otherwise, continue search in base contracts - else - { - for (auto &b: m_baseContracts) - if (b->type() == SolBaseContract::BaseType::CONTRACT) - if (b->contract()->validTest()) - return b->contract()->validContractTest(); - } - solAssert(false, "Sol proto adaptor: No contract test found"); + // Continue search in base contracts + for (auto &b: m_baseContracts) + if (b->type() == SolBaseContract::BaseType::CONTRACT) + if (b->contract()->validTest()) + b->contract()->validContractTests(_testSet); } -tuple SolContract::pseudoRandomTest() -{ - solAssert(validTest(), "Sol proto adaptor: Please call validTest()"); - return validContractTest(); -} +//tuple SolContract::pseudoRandomTest() +//{ +// solAssert(validTest(), "Sol proto adaptor: Please call validTest()"); +// return validContractTests(); +//} void SolContract::addFunctions(Contract const& _contract) { @@ -1294,7 +1279,7 @@ string SolContract::str() ("bases", bases.str()) ("isAbstract", abstract()) ("contractName", name()) - ("inheritance", m_baseContracts.size() > 0) + ("inheritance", !m_baseContracts.empty()) ("baseNames", baseNames()) ("functions", functions.str()) .render(); @@ -1369,12 +1354,12 @@ library { bool SolLibrary::validTest() const { - return m_publicFunctionMap.size() > 0; + return !m_publicFunctionMap.empty(); } pair SolLibrary::pseudoRandomTest() { - solAssert(m_publicFunctionMap.size() > 0, "Sol proto adaptor: Empty library map"); + solAssert(!m_publicFunctionMap.empty(), "Sol proto adaptor: Empty library map"); string chosenFunction; unsigned numFunctions = m_publicFunctionMap.size(); unsigned functionIndex = randomNumber() % numFunctions; @@ -1390,4 +1375,4 @@ pair SolLibrary::pseudoRandomTest() } solAssert(m_publicFunctionMap.count(chosenFunction), "Sol proto adaptor: Invalid library function chosen"); return pair(chosenFunction, m_publicFunctionMap[chosenFunction]); -} +} \ No newline at end of file diff --git a/test/tools/ossfuzz/SolProtoAdaptor.h b/test/tools/ossfuzz/SolProtoAdaptor.h index 1817d6a2d..959f2f9d4 100644 --- a/test/tools/ossfuzz/SolProtoAdaptor.h +++ b/test/tools/ossfuzz/SolProtoAdaptor.h @@ -26,6 +26,8 @@ namespace solidity::test::solprotofuzzer::adaptor { + +/// Forward declarations /// Solidity contract abstraction class struct SolContract; /// Solidity interface abstraction class @@ -38,31 +40,6 @@ struct SolInterfaceFunction; struct SolContractFunction; /// Solidity library function abstraction class struct SolLibraryFunction; -/// Solidity contract function override abstraction class -struct CFunctionOverride; -/// Solidity interface function override abstraction class -struct IFunctionOverride; - -/// Type that defines a contract function override as a variant of interface or -/// contract function types. -using OverrideFunction = std::variant, std::unique_ptr>; -/// Type that defines a list of base contracts as a list of variants of interface or -/// contract types. -using BaseContracts = std::vector, std::shared_ptr>>; -/// Type that defines a contract function override as a pair of base contracts -/// and its (their) function. -using OverrideCFunction = std::pair; -/// Type that defines an interface function override as a pair of interface -/// and its function. -using OverrideIFunction = std::pair>, std::unique_ptr>; -/// Type that defines a map of interface overrides -using InterfaceOverrideMap = std::map, std::vector>>; -/// Type that defines an interface function as either a vanilla interface function -/// or an override function. -using IFunction = std::variant, OverrideIFunction>; -/// Type that defines a contract function as either a vanilla contract function -/// or an override function. -using CFunction = std::variant, OverrideCFunction>; /// Variant type that points to one of contract, interface protobuf messages using ProtoBaseContract = std::variant; @@ -369,7 +346,7 @@ struct SolLibrary } std::string newReturnValue() { - return std::to_string(m_returnValue++); + return std::to_string(random()); } std::string m_libraryName; @@ -392,8 +369,6 @@ struct SolBaseContract std::shared_ptr _prng ); - std::variant>, std::vector>> - baseFunctions(); BaseType type() const; std::string name(); std::string str(); @@ -409,56 +384,6 @@ struct SolBaseContract std::string lastBaseName(); std::variant, std::shared_ptr> m_base; - std::string m_baseName; - std::shared_ptr m_prng; -}; - -struct SolFunction -{ - enum Type - { - INTERFACE, - CONTRACT - }; - using FunctionVariant = std::variant, std::shared_ptr>; - - Type operator()(FunctionVariant _var) const; -}; - -struct ContractFunctionAttributes -{ - ContractFunctionAttributes( - std::variant, std::shared_ptr> _solFunction - ) - { - if (SolFunction{}(_solFunction) == SolFunction::INTERFACE) - { - auto f = std::get>(_solFunction); - m_name = f->name(); - m_mutability = f->mutability(); - m_visibility = SolFunctionVisibility::EXTERNAL; - } - else - { - auto f = std::get>(_solFunction); - m_name = f->name(); - m_mutability = f->mutability(); - m_visibility = f->visibility(); - } - } - bool namesake(ContractFunctionAttributes const& _rhs) const; - bool operator==(ContractFunctionAttributes const& _rhs) const; - bool operator!=(ContractFunctionAttributes const& _rhs) const; - void merge(ContractFunctionAttributes const& _rhs); - - std::string m_name; - SolFunctionVisibility m_visibility; - SolFunctionStateMutability m_mutability; - std::string m_returnValue; - bool m_virtual; - bool m_override; - bool m_implemeted; - std::vector m_bases; }; struct SolContract @@ -473,25 +398,14 @@ struct SolContract void merge(); std::string str(); - std::string interfaceOverrideStr(); - std::string contractOverrideStr(); - void disallowedBase(std::shared_ptr _base1, std::shared_ptr _base2); void addFunctions(Contract const& _contract); void addBases(Contract const& _contract); - void addOverrides(); - void interfaceFunctionOverride( - std::shared_ptr _base, - std::shared_ptr _function - ); - void contractFunctionOverride( - std::shared_ptr _base, - std::shared_ptr _function - ); bool validTest(); std::string baseNames() const; - std::tuple validContractTest(); - std::tuple pseudoRandomTest(); + void validContractTests( + std::map>& _testSet + ); unsigned randomNumber() const { @@ -593,7 +507,6 @@ struct SolInterface std::string str() const; - /// Returns the Solidity code for all base interfaces /// inherited by this interface. std::string baseInterfaceStr() const; @@ -619,198 +532,4 @@ struct SolInterface std::vector> m_baseInterfaces; std::shared_ptr m_prng; }; - -/* Contract functions may be overridden by other contracts. Base and derived contracts - * may either be abstract or non-abstract. That gives us four possibilities: - * - both abstract - * - both non abstract - * - one of them abstract, the other non abstract - */ - -struct CFunctionOverride -{ - enum class DerivedType - { - ABSTRACTCONTRACT, - CONTRACT - }; - - CFunctionOverride( - std::variant, std::shared_ptr> _base, - std::shared_ptr _function, - SolContract* _derived, - bool _implemented, - bool _virtualized, - bool _explicitInheritance, - std::string _returnValue - ) - { - m_baseContract = _base; - m_baseFunction = _function; - m_derivedProgram = _derived; - m_implemented = _implemented; - m_virtualized = _virtualized; - m_explicitlyInherited = _explicitInheritance; - m_returnValue = _returnValue; - m_derivedType = _derived->abstract() ? DerivedType::ABSTRACTCONTRACT : DerivedType::CONTRACT; - } - - std::string str() const; - - std::string name() const; - - bool contractFunction() const; - - SolFunctionVisibility visibility() const; - - SolFunctionStateMutability mutability() const; - - std::string commaSeparatedBaseNames() const; - - std::string baseName() const; - -// std::shared_ptr baseContract() const -// { -// return m_baseContract; -// } - - std::shared_ptr baseFunction() const - { - return m_baseFunction; - } - - std::variant, std::shared_ptr> m_baseContract; - std::shared_ptr m_baseFunction; - SolContract* m_derivedProgram; - - /// Flag that is true if overridden function is implemented in derived contract - bool m_implemented = false; - /// Flag that is true if overridden function implemented in derived contract is - /// marked virtual - bool m_virtualized = false; - /// Flag that is true if overridden function is redeclared but not implemented - bool m_explicitlyInherited = false; - /// The uint value to be returned if the overridden interface function is implemented - std::string m_returnValue; - DerivedType m_derivedType; - - bool implemented() const - { - return m_implemented; - } - - bool virtualized() const - { - return m_virtualized; - } - - bool explicitlyInherited() const - { - return m_explicitlyInherited; - } - - std::string returnValue() const - { - return m_returnValue; - } -}; - -/* Difference between interface function declaration and interface - * function override is that the former can not be implemented and - * should not be marked virtual i.e., virtual is implicit. - * - * Interface function declarations may be implicitly or explicitly - * inherited by derived interfaces. To explicitly inherit base - * interface's function declaration, derived base must redeclare - * the said function and mark it override. If base interface function - * does not redeclare base interface function, it implicitly inherits - * it from base and exposes it to its derived interfaces. - * - * Interface functions inherited by contracts may be implicitly or - * explicitly inherited. Derived non abstract contracts must explicitly - * override and implement inherited interface functions unless they have - * already been implemented by one of its bases. Abstract contracts - * may implicitly or explicitly inherit base interface functions. If - * explicitly inherited, they must be redeclared and marked override. - * When a base interface function is explicitly inherited by a contract - * it may be marked virtual. - */ -struct IFunctionOverride -{ - enum class DerivedType - { - INTERFACE, - ABSTRACTCONTRACT, - CONTRACT - }; - - IFunctionOverride( - std::shared_ptr _baseInterface, - std::shared_ptr _baseFunction, - std::variant _derivedProgram, - bool _implement, - bool _virtual, - bool _explicitInherit, - std::string _returnValue - ); - - std::string str() const; - std::string interfaceStr() const; - std::string contractStr() const; - - void setImplement() - { - m_implemented = true; - } - - void setVirtual() - { - m_virtualized = true; - } - - void setExplicitInherit() - { - m_explicitlyInherited = true; - } - - bool implemented() const - { - return m_implemented; - } - - bool virtualized() const - { - return m_virtualized; - } - - bool explicitlyInherited() const - { - return m_explicitlyInherited; - } - - std::string returnValue() const - { - return m_returnValue; - } - - std::string baseName() const - { - return m_baseInterface->name(); - } - - std::shared_ptr m_baseInterface; - std::shared_ptr m_baseFunction; - std::variant m_derivedProgram; - - /// Flag that is true if overridden function is implemented in derived contract - bool m_implemented = false; - /// Flag that is true if overridden function implemented in derived contract is - /// marked virtual - bool m_virtualized = false; - /// Flag that is true if overridden function is redeclared but not implemented - bool m_explicitlyInherited = false; - /// The uint value to be returned if the overridden interface function is implemented - std::string m_returnValue; - DerivedType m_derivedType; -}; } \ No newline at end of file diff --git a/test/tools/ossfuzz/protoToSol.cpp b/test/tools/ossfuzz/protoToSol.cpp index 594ebd393..ee0eb0e4a 100644 --- a/test/tools/ossfuzz/protoToSol.cpp +++ b/test/tools/ossfuzz/protoToSol.cpp @@ -37,7 +37,7 @@ string ProtoConverter::protoToSolidity(Program const& _p) string ProtoConverter::visit(TestContract const& _testContract) { - string testCode; + ostringstream testCode; string usingLibDecl; m_libraryTest = false; @@ -47,7 +47,7 @@ string ProtoConverter::visit(TestContract const& _testContract) { if (emptyLibraryTests()) { - testCode = Whiskers(R"( + testCode << Whiskers(R"( return 0;)") .render(); } @@ -60,7 +60,7 @@ string ProtoConverter::visit(TestContract const& _testContract) using for uint;)") ("libraryName", get<0>(testTuple)) .render(); - testCode = Whiskers(R"( + testCode << Whiskers(R"( uint x; if (x.() != ) return 1; @@ -73,21 +73,45 @@ string ProtoConverter::visit(TestContract const& _testContract) } case TestContract::CONTRACT: if (emptyContractTests()) - testCode = Whiskers(R"( + testCode << Whiskers(R"( return 0;)") .render(); else { - auto testTuple = pseudoRandomContractTest(); - testCode = Whiskers(R"( - testContract = new (); - if (testContract.() != ) - return 1; - return 0;)") - ("contractName", get<0>(testTuple)) - ("testFunction", get<1>(testTuple)) - ("expectedOutput", get<2>(testTuple)) - .render(); + unsigned errorCode = 1; + unsigned contractVarIndex = 0; + for (auto &testTuple: m_contractTests) + { + // Do this to avoid stack too deep errors + // We require uint as a return var, so we + // cannot have more than 16 variables without + // running into stack too deep errors + if (contractVarIndex >= s_maxVars) + break; + string contractName = testTuple.first; + string contractVarName = "tc" + to_string(contractVarIndex); + testCode << Whiskers(R"( + = new ();)") + ("contractName", contractName) + ("contractVarName", contractVarName) + .render(); + for (auto &t: testTuple.second) + { + testCode << Whiskers(R"( + if (.() != ) + return ;)") + ("contractVarName", contractVarName) + ("testFunction", t.first) + ("expectedOutput", t.second) + ("errorCode", to_string(errorCode)) + .render(); + errorCode++; + } + contractVarIndex++; + } + // Expected return value + testCode << Whiskers(R"( + return 0;)").render(); } break; } @@ -101,7 +125,7 @@ contract C { )") ("isLibrary", m_libraryTest) ("usingDecl", usingLibDecl) - ("testCode", testCode) + ("testCode", testCode.str()) .render(); } @@ -161,7 +185,16 @@ string ProtoConverter::visit(Contract const& _contract) auto contract = SolContract(_contract, programName(&_contract), m_randomGen); if (contract.validTest()) { - m_contractTests.push_back(contract.pseudoRandomTest()); + map> testSet; + contract.validContractTests(testSet); + for (auto &contractTestSet: testSet) + { + m_contractTests.insert(pair(contractTestSet.first, map{})); + for (auto &contractTest: contractTestSet.second) + m_contractTests[contractTestSet.first].insert( + make_pair(contractTest.first, contractTest.second) + ); + } return contract.str(); } // There is no point in generating a contract that can not provide @@ -220,12 +253,12 @@ tuple ProtoConverter::pseudoRandomLibraryTest() return m_libraryTests[index]; } -tuple ProtoConverter::pseudoRandomContractTest() -{ - solAssert(m_contractTests.size() > 0, "Sol proto fuzzer: No contract tests found"); - unsigned index = randomNumber() % m_contractTests.size(); - return m_contractTests[index]; -} +//tuple ProtoConverter::pseudoRandomContractTest() +//{ +// solAssert(m_contractTests.size() > 0, "Sol proto fuzzer: No contract tests found"); +// unsigned index = randomNumber() % m_contractTests.size(); +// return m_contractTests[index]; +//} void ProtoConverter::openProgramScope(CIL _program) { diff --git a/test/tools/ossfuzz/protoToSol.h b/test/tools/ossfuzz/protoToSol.h index b0b2a5ec2..a147307a0 100644 --- a/test/tools/ossfuzz/protoToSol.h +++ b/test/tools/ossfuzz/protoToSol.h @@ -64,11 +64,11 @@ private: std::string visit(Contract const& _contract); std::string programName(CIL _program); std::tuple pseudoRandomLibraryTest(); - std::tuple pseudoRandomContractTest(); +// std::tuple pseudoRandomContractTest(); void openProgramScope(CIL _program); unsigned randomNumber(); - bool emptyLibrary(Library const& _library) + static bool emptyLibrary(Library const& _library) { return _library.funcdef_size() == 0; } @@ -86,7 +86,10 @@ private: std::shared_ptr m_randomGen; std::map m_programNameMap; std::vector> m_libraryTests; - std::vector> m_contractTests; + std::map> m_contractTests; std::string m_libraryName; + + /// Maximum number of local variables in test function + static unsigned constexpr s_maxVars = 15; }; } \ No newline at end of file