/* This file is part of solidity. solidity is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. solidity is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with solidity. If not, see . */ #include #include #include #include #include using namespace solidity::test::solprotofuzzer; using namespace solidity::test::solprotofuzzer::adaptor; using namespace std; using namespace solidity::util; string ProtoConverter::protoToSolidity(Program const& _p) { m_randomGen = make_shared(_p.seed()); return visit(_p); } string ProtoConverter::visit(TestContract const& _testContract) { string testCode; string usingLibDecl; m_libraryTest = false; switch (_testContract.type()) { case TestContract::LIBRARY: { if (emptyLibraryTests()) { testCode = Whiskers(R"( return 0;)") .render(); } else { m_libraryTest = true; auto testTuple = pseudoRandomLibraryTest(); m_libraryName = get<0>(testTuple); usingLibDecl = Whiskers(R"( using for uint;)") ("libraryName", get<0>(testTuple)) .render(); testCode = Whiskers(R"( uint x; if (x.() != ) return 1; return 0;)") ("testFunction", get<1>(testTuple)) ("expectedOutput", get<2>(testTuple)) .render(); } break; } case TestContract::CONTRACT: if (emptyContractTests()) testCode = Whiskers(R"( return 0;)") .render(); #if 0 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(); } #endif break; } return Whiskers(R"( contract C { function test() public returns (uint) { } } )") ("isLibrary", m_libraryTest) ("usingDecl", usingLibDecl) ("testCode", testCode) .render(); } bool ProtoConverter::libraryTest() const { return m_libraryTest; } string ProtoConverter::libraryName() const { return m_libraryName; } string ProtoConverter::visit(Program const& _p) { ostringstream program; ostringstream contracts; for (auto &contract: _p.contracts()) contracts << visit(contract); program << Whiskers(R"( pragma solidity >=0.0; )") ("contracts", contracts.str()) ("testContract", visit(_p.test())) .render(); return program.str(); } 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()); return ""; 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 ""; } } string ProtoConverter::visit(ContractOrInterface const& _contractOrInterface) { switch (_contractOrInterface.contract_or_interface_oneof_case()) { case ContractOrInterface::kC: // return visit(_contractOrInterface.c()); return ""; case ContractOrInterface::kI: // return visit(_contractOrInterface.i()); return ""; case ContractOrInterface::CONTRACT_OR_INTERFACE_ONEOF_NOT_SET: return ""; } } bool ProtoConverter::contractFunctionImplemented( Contract const* _contract, CIFunc _function ) { auto v = m_contractFunctionMap[_contract]; for (auto &e: v) if (get<0>(e) == _function) return get<1>(e); return false; } bool ProtoConverter::contractFunctionVirtual( Contract const* _contract, CIFunc _function ) { auto v = m_contractFunctionMap[_contract]; for (auto &e: v) if (get<0>(e) == _function) return get<2>(e); return false; } #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) return ""; openProgramScope(&_contract); try { auto contract = SolContract(_contract, programName(&_contract), m_randomGen); if (contract.validTest()) { m_contractTests.push_back(contract.pseudoRandomTest()); return contract.str(); } // There is no point in generating a contract that can not provide // a valid test case, so we simply bail. else return ""; } catch (langutil::FuzzerError const&) { // Return empty string if input specification is invalid. return ""; } } string ProtoConverter::visit(Interface const& _interface) { if (_interface.funcdef_size() == 0 && _interface.bases_size() == 0) return ""; openProgramScope(&_interface); try { auto interface = SolInterface(_interface, programName(&_interface), m_randomGen); return interface.str(); } catch (langutil::FuzzerError const&) { // Return empty string if input specification is invalid. return ""; } } string ProtoConverter::visit(Library const& _library) { if (emptyLibrary(_library)) return ""; openProgramScope(&_library); auto lib = SolLibrary(_library, programName(&_library), m_randomGen); if (lib.validTest()) { auto libTestPair = lib.pseudoRandomTest(); m_libraryTests.push_back({lib.name(), libTestPair.first, libTestPair.second}); } return lib.str(); } tuple ProtoConverter::pseudoRandomLibraryTest() { solAssert(m_libraryTests.size() > 0, "Sol proto fuzzer: No library tests found"); unsigned index = randomNumber() % m_libraryTests.size(); 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]; } void ProtoConverter::openProgramScope(CIL _program) { string programNamePrefix; if (holds_alternative(_program)) programNamePrefix = "C"; else if (holds_alternative(_program)) programNamePrefix = "I"; else 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) { solAssert(m_programNameMap.count(_program), "Sol proto fuzzer: Unregistered program"); return m_programNameMap[_program]; } unsigned ProtoConverter::randomNumber() { solAssert(m_randomGen, "Sol proto fuzzer: Uninitialized random number generator"); return m_randomGen->operator()(); } #if 0 bool ProtoConverter::disallowedContractFunction(SolContractFunction const& _contractFunction, bool _isVirtual) { string visibility = functionVisibility(_contractFunction.m_visibility); string mutability = functionMutability(_contractFunction.m_mutability); // Private virtual functions are disallowed if (visibility == "private" && _isVirtual) return true; // Private payable functions are disallowed else if (visibility == "private" && mutability == "payable") return true; // Internal payable functions are disallowed else if (visibility == "internal" && mutability == "payable") return true; return false; } string ProtoConverter::functionName(CILFunc _function) { solAssert(m_functionNameMap.count(_function), "Sol proto fuzzer: Unregistered function"); return m_functionNameMap[_function]; } #endif