From 1b4bdb199630160a342e62ed02bb21cde7258cf8 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Mon, 13 Apr 2020 14:15:17 +0200 Subject: [PATCH] More clean up and bug fixes --- test/tools/ossfuzz/SolProtoAdaptor.cpp | 81 ++++- test/tools/ossfuzz/SolProtoAdaptor.h | 4 + test/tools/ossfuzz/protoToSol.cpp | 441 +------------------------ test/tools/ossfuzz/protoToSol.h | 56 +--- 4 files changed, 73 insertions(+), 509 deletions(-) diff --git a/test/tools/ossfuzz/SolProtoAdaptor.cpp b/test/tools/ossfuzz/SolProtoAdaptor.cpp index 31775bcde..21d2e88c5 100644 --- a/test/tools/ossfuzz/SolProtoAdaptor.cpp +++ b/test/tools/ossfuzz/SolProtoAdaptor.cpp @@ -268,6 +268,28 @@ string SolLibraryFunction::str() const .render(); } +unsigned SolBaseContract::functionIndex() +{ + if (type() == BaseType::INTERFACE) + return interface()->functionIndex(); + else + { + solAssert(type() == BaseType::CONTRACT, "Sol proto adaptor: Invalid base contract"); + return contract()->functionIndex(); + } +} + +string SolBaseContract::lastBaseName() +{ + if (type() == BaseType::INTERFACE) + return interface()->lastBaseName(); + else + { + solAssert(type() == BaseType::CONTRACT, "Sol proto adaptor: Invalid base contract"); + return contract()->lastBaseName(); + } +} + SolBaseContract::BaseType SolBaseContract::type() const { if (holds_alternative>(m_base)) @@ -290,6 +312,17 @@ string SolBaseContract::str() } } +string SolBaseContract::name() +{ + if (type() == BaseType::INTERFACE) + return interface()->name(); + else + { + solAssert(type() == BaseType::CONTRACT, "Sol proto adaptor: Invalid base contract"); + return contract()->name(); + } +} + SolBaseContract::SolBaseContract(ProtoBaseContract _base, string _name, shared_ptr _prng) { if (holds_alternative(_base)) @@ -512,6 +545,19 @@ interface is { .render(); } +string SolContract::baseNames() const +{ + ostringstream bases; + string separator{}; + for (auto &b: m_baseContracts) + { + bases << separator << b->name(); + if (separator.empty()) + separator = ", "; + } + return bases.str(); +} + bool SolContract::validTest() const { // Check if at least one contract has one valid test function @@ -824,27 +870,27 @@ void SolContract::addFunctions(Contract const& _contract) void SolContract::addBases(Contract const& _contract) { + shared_ptr base; for (auto &b: _contract.bases()) { switch (b.contract_or_interface_oneof_case()) { case ContractOrInterface::kC: - m_baseContracts.push_back( - make_shared( - SolBaseContract(&b.c(), newBaseName(), m_prng) - ) - ); + base = make_shared(SolBaseContract(&b.c(), newBaseName(), m_prng)); + m_baseContracts.push_back(base); break; case ContractOrInterface::kI: - m_baseContracts.push_back( - make_shared( - SolBaseContract(&b.i(), newBaseName(), m_prng) - ) - ); + base = make_shared(SolBaseContract(&b.i(), newBaseName(), m_prng)); + m_baseContracts.push_back(base); break; case ContractOrInterface::CONTRACT_OR_INTERFACE_ONEOF_NOT_SET: break; } + // Worst case, we override all base functions so we + // increment derived contract's function index by + // this amount. + m_functionIndex += base->functionIndex(); + m_lastBaseName = base->lastBaseName(); } } @@ -889,7 +935,7 @@ string SolContract::contractOverrideStr() const } overriddenFunctions << Whiskers(R"( function () virtual - override() returns (uint);)") + override() returns (uint);)") ("functionName", f.first->name()) ("visibility", functionVisibility(f.first->visibility())) ("stateMutability", functionMutability(f.first->mutability())) @@ -943,7 +989,7 @@ string SolContract::interfaceOverrideStr() const } overriddenFunctions << Whiskers(R"( function () external - override() returns (uint);)") + override() returns (uint);)") ("functionName", f.first->name()) ("stateMutability", functionMutability(f.first->mutability())) ("multiple", f.second.size() > 1) @@ -962,19 +1008,24 @@ string SolContract::str() const bases << b->str(); ostringstream functions; + + // Print overridden functions + functions << interfaceOverrideStr() << contractOverrideStr(); + + // Print non-overridden functions for (auto &f: m_contractFunctions) functions << f->str(); - functions << interfaceOverrideStr() << contractOverrideStr(); - return Whiskers(R"( -abstract contract { +abstract contract is { })") ("bases", bases.str()) ("isAbstract", abstract()) ("contractName", name()) + ("inheritance", m_baseContracts.size() > 0) + ("baseNames", baseNames()) ("functions", functions.str()) .render(); } diff --git a/test/tools/ossfuzz/SolProtoAdaptor.h b/test/tools/ossfuzz/SolProtoAdaptor.h index b4b0adaf8..43be15663 100644 --- a/test/tools/ossfuzz/SolProtoAdaptor.h +++ b/test/tools/ossfuzz/SolProtoAdaptor.h @@ -253,6 +253,7 @@ struct SolBaseContract std::variant>, std::vector>> baseFunctions(); BaseType type() const; + std::string name(); std::string str(); std::shared_ptr interface() { @@ -262,6 +263,8 @@ struct SolBaseContract { return std::get>(m_base); } + unsigned functionIndex(); + std::string lastBaseName(); std::variant, std::shared_ptr> m_base; std::string m_baseName; @@ -288,6 +291,7 @@ struct SolContract ); bool validTest() const; + std::string baseNames() const; std::tuple validContractTest(); std::tuple pseudoRandomTest(); diff --git a/test/tools/ossfuzz/protoToSol.cpp b/test/tools/ossfuzz/protoToSol.cpp index 66828cf26..56b93af57 100644 --- a/test/tools/ossfuzz/protoToSol.cpp +++ b/test/tools/ossfuzz/protoToSol.cpp @@ -138,447 +138,19 @@ pragma solidity >=0.0; string ProtoConverter::visit(ContractType const& _contractType) { - m_mostDerivedAbstractContract = false; switch (_contractType.contract_type_oneof_case()) { case ContractType::kC: - m_mostDerivedAbstractContract = _contractType.c().abstract(); - m_mostDerivedProgram = MostDerivedProgram::CONTRACT; return visit(_contractType.c()); case ContractType::kL: - m_mostDerivedProgram = MostDerivedProgram::LIBRARY; return visit(_contractType.l()); case ContractType::kI: - m_mostDerivedProgram = MostDerivedProgram::INTERFACE; return visit(_contractType.i()); case ContractType::CONTRACT_TYPE_ONEOF_NOT_SET: return ""; } } -#if 0 -string ProtoConverter::mostDerivedInterfaceOverrides(Interface const& _interface) -{ - ostringstream funcs; - - for (auto base = _interface.bases().rbegin(); base != _interface.bases().rend(); base++) - { - for (auto& f: base->funcdef()) - { - // An interface may override base interface's function - bool override = pseudoRandomCoinFlip(); - if (!override) - continue; - funcs << overrideFunction(&f, false, false); - } - funcs << mostDerivedInterfaceOverrides(*base); - } - return funcs.str(); -} - -string ProtoConverter::mostDerivedContractOverrides(Interface const& _interface) -{ - ostringstream funcs; - - for (auto base = _interface.bases().rbegin(); base != _interface.bases().rend(); base++) - { - for (auto& f: base->funcdef()) - { - bool override = pseudoRandomCoinFlip() || mostDerivedProgramAbstractContract(); - - /// We can arrive here from contract through other contracts and interfaces. - /// We define most derived contract (MDC) as the most derived contract in - /// the inheritence chain to which the visited interface belongs to. - - /// When MDC is not abstract we must implement (with override) all - /// interface functions that MDC inherits unless it has been implemented - /// by some other contract in the inheritence chain that MDC also derives - /// from. - /// In other words, if an interface function has never been implemented - /// thus far in the inheritence chain, it must be implemented. - /// If it has already been implemented, it may be reimplemented provided - /// it is virtual. - /// When reimplementing, it may be revirtualized. - - /// When MDC is abstract, we may or may not redeclare interface function. - /// If interface function has been implemented in the inheritence chain, - /// and we redeclare it, we must reimplement it. - /// If inheritence function has not been implemented and we redeclare it, - /// we may implement it. - /// If we implement it, it may be marked virtual. - /// If we don't implement it, it must be marked virtual. - - /// . We may virtualize. - /// We create a pair and add it to list of - /// implemented interface functions. - - /// When IAC is abstract: - /// - we may redeclare - /// - if redeclared, we may implement - /// - if we do not implement redeclared function then: - /// - we must override and virtualize - /// - add to list of unimplemented interface functions - /// - if we implement then: - /// - we must override - /// - we may virtualize - /// - create a pair and add it to list of - /// implemented interface functions. - - - /// - ancestor contract is not abstract and this interface function - /// has been implemented by some other contract in the traversal path - /// that also virtualizes the function. - /// When we override, we may mark it as virtual. - - /// We may override when ancestor contract is abstract - /// If ancestor contract is overriding, we may or may not implement it. - /// If we do not implement it, we must mark it virtual since otherwise - /// we are left with unimplementable function. - /// If we implement it, we may mark it as virtual. - string funcStr = visit( - f, - index++, - override, - programName(&*base) - ); - - if (override) - funcs << funcStr; - } - funcs << mostDerivedContractOverrides(*base); - } - return funcs.str(); -} - -pair ProtoConverter::contractFunctionParams( - Contract const* _contract, - ContractFunction const* _function -) -{ - // If contract is abstract, we may implement this function. If contract - // is not abstract, we must implement this function. - bool implement = !_contract->abstract() || pseudoRandomCoinFlip(); - // We may mark a non-overridden function as virtual. - // We must mark an unimplemented abstract contract function as - // virtual. - bool virtualFunc = _function->virtualfunc() || (_contract->abstract() && !implement); - return pair(implement, virtualFunc); -} - -pair ProtoConverter::contractFunctionOverrideParams( - Contract const* _base, - Contract const* _derived, - CIFunc _f -) -{ - bool baseAbstract = _base->abstract(); - bool derivedAbstract = _derived->abstract(); - - bool implement = false; - bool revirtualize = false; - - // There are four possibilities here: - // 1. both base and derived are abstract, - // 2. base abstract, derived not - // 3. base not abstract, derived is - // 4. both base and derived are not abstract - if (baseAbstract && derivedAbstract) - { - // Case 1: Both base and derived are abstract - // virtual base functions may be redeclared (with override) - // if redeclared virtual base function has been implemented, it must be reimplemented but - // may be revirtualized - // if revirtualized, there is nothing to be changed in list of - // if not revirtualized, we changed the boolean to false in the list of implemented contract - // functions. - // if redeclared virtual base function has not been implemented, it may be implemented. - // if it is implemented (with override), it may be revirtualized. - // if it is not implemented, it must be marked virtual. - // if virtual base function not redeclared, there is no status change - bool virtualImplemented = contractFunctionImplemented(_base, _f); - implement = virtualImplemented || pseudoRandomCoinFlip(); - revirtualize = (!implement && !virtualImplemented) || pseudoRandomCoinFlip(); - } - else if (baseAbstract && !derivedAbstract) - { - // Case 2: Base abstract, derived not - // If base function appears in list of unimplemented virtual contract functions, we - // must implement it (with override). Remove from unimplemented virtual contract functions. We may - // revirtualize. Add to list of implemented contract functions. - - // Unimplemented virtual functions must be implemented (with override) - - // Implemented virtual functions may be implemented (with override) - - // - } - else if (!baseAbstract && derivedAbstract) - { - // Case 3: Base not abstract, derived is - // All base functions are implemented. Base functions marked virtual may be overridden. - // If they are overridden they must be reimplemented. They may be revirtualized. - // Base functions that are not virtual may not be redeclared. - } - else - { - // Case 4: Neither base nor derived are abstract - // All base functions are implemented. Base functions marked virtual may be overridden. - // If they are overridden they must be reimplemented. They may be revirtualized. - // Base functions that are not virtual may not be redeclared. - } - return pair(implement, revirtualize); -} - -string ProtoConverter::traverseOverrides(Contract const& _contract) -{ - ostringstream funcs; - - for (auto base = _contract.bases().rbegin(); base != _contract.bases().rend(); base++) - { - if (base->contract_or_interface_oneof_case() == ContractOrInterface::CONTRACT_OR_INTERFACE_ONEOF_NOT_SET) - continue; - - if (base->has_c()) - { - for (auto& f: base->c().funcdef()) - { - // We may redeclare virtual functions in base contract - bool redeclareVirtual = f.virtualfunc() && pseudoRandomCoinFlip(); - // If base function is not virtual or if we choose to not - // redeclare the virtual function, we skip to the next function - // after incrementing the function index. - if (!f.virtualfunc() || !redeclareVirtual) - continue; - - // Check if overridden function may be implemented/revirtualized - // by the derived contract. - auto [implement, revirtualize] = contractFunctionOverrideParams( - &base->c(), - &_contract, - &f - ); - funcs << overrideFunction(&f, revirtualize, implement); - // Update contract function map - m_contractFunctionMap[&_contract].push_back(CITuple(&f, implement, revirtualize)); - } - // Override revirtualized functions. - // TODO: Function that returns all revirtualized functions and their implementation - // status. - for (auto &tuple: m_contractFunctionMap[&base->c()]) - { - auto function = get<0>(tuple); - bool overrideRevirtualized = true; - if (holds_alternative(function)) - { - auto contractFunction = get(function); - - auto [implementRevirtualized, revirtualizeRevirtualized] = contractFunctionOverrideParams( - &base->c(), - &_contract, - get<0>(tuple) - ); - funcs << visit( - *contractFunction, - index++, - overrideRevirtualized, - revirtualizeRevirtualized, - implementRevirtualized, - programName(&base->c()) - ); - } - } - // Traverse base - funcs << traverseOverrides(base->c()); - } - else if (base->has_i()) - { - for (auto& f: base->i().funcdef()) - { - bool implement = pseudoRandomCoinFlip(); - m_counter += base->i().funcdef_size(); - bool override = pseudoRandomCoinFlip(); - - if (_contract.abstract() && !implement && !override) - continue; - - funcs << overrideFunction( - &f, - !_contract.abstract() || implement, - pseudoRandomCoinFlip() || (override && _contract.abstract()) - ); - } - funcs << mostDerivedContractOverrides(base->i(), !_contract.abstract()); - } - } - return funcs.str(); -} - -/// This function is called when root is interface. -tuple ProtoConverter::visitMostDerivedInterface(Interface const& _interface) -{ - ostringstream bases; - ostringstream baseNames; - ostringstream funcs; - - string separator{}; - for (auto &base: _interface.bases()) - { - string baseStr = visit(base); - if (baseStr.empty()) - continue; - bases << baseStr; - baseNames << separator - << programName(&base); - if (separator.empty()) - separator = ", "; - } - - // First define overridden functions - bool overrides = _interface.bases_size() > 0 && !baseNames.str().empty(); - if (overrides) - funcs << mostDerivedInterfaceOverrides(_interface); - - unsigned index = 0; - // Define non-overridden functions - for (auto &f: _interface.funcdef()) - funcs << registerAndVisitFunction( - &_interface, - &f, - index++, - false, - false, - false - ); - - return make_tuple(bases.str(), baseNames.str(), funcs.str()); -} - -void ProtoConverter::registerFunctionName(CIL _program, CILFunc _function, unsigned _index) -{ - string pName = programName(_program); - string fName = createFunctionName(_program, _index); - solAssert(!m_functionNameMap.count(_function), "Sol proto fuzzer: Duplicate function registration"); - m_functionNameMap.insert(pair(_function, fName)); -} - -string ProtoConverter::registerAndVisitFunction( - CIL _program, - CILFunc _function, - unsigned _index, - bool _override, - bool _virtual, - bool _implement -) -{ - registerFunctionName(_program, _function, _index); - return visit(_function, _override, _virtual, _implement); -} - -tuple ProtoConverter::visitProgramHelper(CIL _program) -{ - ostringstream bases; - ostringstream funcs; - ostringstream baseNames; - - string pName = programName(_program); - - string separator{}; - if (holds_alternative(_program)) - { - Contract const* contract = get(_program); - - for (auto &base: contract->bases()) - { - string baseStr = visit(base); - if (baseStr.empty()) - continue; - bases << baseStr; - baseNames << separator - << (base.has_c() ? programName(&base.c()) : programName(&base.i())); - if (separator.empty()) - separator = ", "; - } - - // First define overridden functions - bool overrides = contract->bases_size() > 0 && !baseNames.str().empty(); - if (overrides) - { - funcs << traverseOverrides(*contract); - } - - // Declare/define non-overridden functions - unsigned index = 0; - for (auto &f: contract->funcdef()) - { - auto [implement, virtualize] = contractFunctionParams(contract, &f); - - funcs << registerAndVisitFunction( - _program, - &f, - index++, - false, - virtualize, - implement - ); - - // Update contract function map - m_contractFunctionMap[contract].push_back(CITuple(&f, implement, virtualize)); - } - } - else if (holds_alternative(_program)) - { - // If we are here, it means most derived program is a contract. - - auto interface = get(_program); - for (auto &base: interface->bases()) - { - string baseStr = visit(base); - if (baseStr.empty()) - continue; - bases << baseStr; - baseNames << separator - << programName(&base); - if (separator.empty()) - separator = ", "; - } - - // First define overridden functions - bool overrides = interface->bases_size() > 0 && !baseNames.str().empty(); - if (overrides) - { - funcs << mostDerivedContractOverrides(*interface, false); - } - - unsigned index = 0; - // Declare non-overridden functions - for (auto &f: interface->funcdef()) - funcs << registerAndVisitFunction( - _program, - &f, - index++, - false, - false, - false - ); - } - else - { - auto library = get(_program); - unsigned index = 0; - for (auto &f: library->funcdef()) - funcs << registerAndVisitFunction( - _program, - &f, - index++, - false, - false, - false - ); - } - return make_tuple(bases.str(), baseNames.str(), funcs.str()); -} -#endif - string ProtoConverter::visit(Contract const& _contract) { if (_contract.funcdef_size() == 0 && _contract.bases_size() == 0) @@ -661,9 +233,6 @@ void ProtoConverter::openProgramScope(CIL _program) programNamePrefix = "L"; string programName = programNamePrefix + to_string(m_numPrograms++); m_programNameMap.insert(pair(_program, programName)); - - if (holds_alternative(_program)) - m_contractFunctionMap.insert(pair(get(_program), vector{})); } string ProtoConverter::programName(CIL _program) @@ -676,12 +245,4 @@ unsigned ProtoConverter::randomNumber() { solAssert(m_randomGen, "Sol proto fuzzer: Uninitialized random number generator"); return m_randomGen->operator()(); -} - -#if 0 -string ProtoConverter::functionName(CILFunc _function) -{ - solAssert(m_functionNameMap.count(_function), "Sol proto fuzzer: Unregistered function"); - return m_functionNameMap[_function]; -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/test/tools/ossfuzz/protoToSol.h b/test/tools/ossfuzz/protoToSol.h index 73cd1b609..b0b2a5ec2 100644 --- a/test/tools/ossfuzz/protoToSol.h +++ b/test/tools/ossfuzz/protoToSol.h @@ -46,32 +46,14 @@ public: ProtoConverter(ProtoConverter const&) = delete; ProtoConverter(ProtoConverter&&) = delete; std::string protoToSolidity(Program const&); - /// @returns true if test calls a library function, false /// otherwise bool libraryTest() const; /// @returns name of the library under test std::string libraryName() const; private: - enum MostDerivedProgram { - CONTRACT, - INTERFACE, - LIBRARY - }; - /// Variant type that points to one of contract, interface, library protobuf messages using CIL = std::variant; - /// Variant type that points to one of contract, interface, library function protobuf - /// messages - using CILFunc = std::variant; - /// Variant type that points to one of contract, interface protobuf messages - using CI = std::variant; - /// Variant type that points to one of contract, interface function protobuf messages - using CIFunc = std::variant; - /// Tuple of contract or interface function variant and a boolean stating whether - /// the function is implemented (true) or not and a second boolean stating whether - /// the function is virtual (true) or not. - using CITuple = std::tuple; /// Protobuf message visitors that accept a const reference to a protobuf message /// type and return its solidity translation. std::string visit(Program const&); @@ -83,6 +65,8 @@ private: std::string programName(CIL _program); std::tuple pseudoRandomLibraryTest(); std::tuple pseudoRandomContractTest(); + void openProgramScope(CIL _program); + unsigned randomNumber(); bool emptyLibrary(Library const& _library) { @@ -97,46 +81,10 @@ private: return m_contractTests.size() == 0; } - void openProgramScope(CIL _program); - bool pseudoRandomCoinFlip() - { - return m_counter++ % 2 == 0; - } - bool mostDerivedProgramContract() - { - return m_mostDerivedProgram == MostDerivedProgram::CONTRACT; - } - bool mostDerivedProgramInterface() - { - return m_mostDerivedProgram == MostDerivedProgram::INTERFACE; - } - bool mostDerivedProgramAbstractContract() - { - return m_mostDerivedAbstractContract; - } - unsigned randomNumber(); - unsigned m_numPrograms = 0; - unsigned m_counter = 0; - bool m_mostDerivedAbstractContract = false; bool m_libraryTest = false; std::shared_ptr m_randomGen; - - MostDerivedProgram m_mostDerivedProgram = MostDerivedProgram::CONTRACT; - /// Map whose key is a const pointer to protobuf contract - /// message and whose value is a list of 3-tuples that - /// store a const pointer to a protobuf interface or contract - /// function belonging to the keyed contract, a boolean flag that is - /// true when the function is implemented, false otherwise, and - /// a second boolean flag that is true when the function is virtualized - /// false otherwise. - std::map> m_contractFunctionMap; - /// Map whose key is a const pointer to protobuf contract, interface or - /// library function message type and whose value is the function name - /// assigned to it. - std::map m_functionNameMap; std::map m_programNameMap; - std::vector> m_libraryTests; std::vector> m_contractTests; std::string m_libraryName;