diff --git a/test/tools/ossfuzz/SolProtoAdaptor.cpp b/test/tools/ossfuzz/SolProtoAdaptor.cpp index ef56272dc..549749ca5 100644 --- a/test/tools/ossfuzz/SolProtoAdaptor.cpp +++ b/test/tools/ossfuzz/SolProtoAdaptor.cpp @@ -141,13 +141,11 @@ string libraryFunctionMutability(SolLibraryFunctionStateMutability _mut) SolInterfaceFunction::SolInterfaceFunction( std::string _functionName, - SolFunctionStateMutability _mutability, - bool _override + SolFunctionStateMutability _mutability ) { m_functionName = _functionName; m_mutability = _mutability; - m_override = _override; } bool SolInterfaceFunction::operator==(SolInterfaceFunction const& _rhs) const @@ -164,23 +162,10 @@ bool SolInterfaceFunction::operator!=(SolInterfaceFunction const& _rhs) const string SolInterfaceFunction::str() const { - string bodyStr = Whiskers(R"( - { - return ; - })") - ("uint", "0") - .render(); - return Whiskers(R"( - function () external override - virtual returns (uint); - )") + function () external returns (uint);)") ("functionName", name()) ("stateMutability", functionMutability(mutability())) - ("isOverride", override()) - ("isVirtual", isVirtual()) - ("isImplemented", implement()) - ("body", bodyStr) .render(); } @@ -295,7 +280,7 @@ void SolInterface::overrideHelper( { // Report error if state mutability of identically // named functions differ - if (m.first->mutability() != _function->mutability()) + if (m.first->mutability() != mutability) assertThrow( false, langutil::FuzzerError, @@ -303,23 +288,47 @@ void SolInterface::overrideHelper( " and parameter types but different mutability." ); // Add new base to list of overridden bases - m_overrideMap[m.first].push_back(_base); + m_overrideMap[m.first].push_back( + shared_ptr( + make_shared( + IFunctionOverride( + _base, + _function, + this, + false, + false, + true, + "" + ) + ) + ) + ); multipleOverride = true; break; } } - // If function has not been overridden, add new override + // Use a pseudo-random coin flip to decide whether to override explicitly + // or not. Implicit override means that the overridden function is not + // redeclared with the override keyword. + bool explicitOverride = coinFlip(); + // If function has not been overridden, add new override pseudo-randomly if (!multipleOverride) m_overrideMap.insert( pair( - make_shared( - SolInterfaceFunction( - functionName, - mutability, - true + _function, + vector>{ + make_shared( + IFunctionOverride( + _base, + _function, + this, + false, + false, + explicitOverride, + "" + ) ) - ), - vector>{_base} + } ) ); } @@ -343,7 +352,9 @@ void SolInterface::addBases(Interface const& _interface) { auto base = make_shared(SolInterface(b, newBaseName(), m_prng)); m_baseInterfaces.push_back(base); +#if 0 cout << "Added " << base->name() << " as base" << endl; +#endif // Worst case, we override all base functions so we // increment derived contract's function index by // this amount. @@ -367,7 +378,9 @@ void SolInterface::addFunctions(Interface const& _interface) SolInterface::SolInterface(Interface const& _interface, string _name, shared_ptr _prng) { +#if 0 cout << "Constructing " << _name << endl; +#endif m_prng = _prng; m_interfaceName = _name; m_lastBaseName = m_interfaceName; @@ -411,12 +424,14 @@ string SolInterface::overrideStr() const { overriddenBaseNames << Whiskers(R"()") ("sep", sep) - ("name", b->name()) + ("name", b->baseName()) .render(); if (sep.empty()) sep = ", "; } } + else if (coinFlip()) + continue; overriddenFunctions << Whiskers(R"( function () external override() returns (uint);)") ("functionName", f.first->name()) @@ -659,4 +674,101 @@ string CFunctionOverride::str() const // return get>(base)->name(); // solAssert(interfaceFunction(), "Sol proto fuzzer: Invalid override function type"); // return get>(base)->name(); -//} \ No newline at end of file +//} + +IFunctionOverride::IFunctionOverride( + std::shared_ptr _baseInterface, + std::shared_ptr _baseFunction, + std::variant _derivedProgram, + bool _implement, + bool _virtual, + bool _explicitInherit, + std::string _returnValue +) +{ + if (std::holds_alternative(_derivedProgram)) + { + auto derived = std::get(_derivedProgram); + m_derivedType = derived->abstract() ? DerivedType::ABSTRACTCONTRACT : DerivedType::CONTRACT; + if (!derived->abstract()) + assertThrow( + _explicitInherit && !_returnValue.empty() && _implement, + langutil::FuzzerError, + "Contract overrides base interface function either without" + " implementing it or implictly overrides." + ); + else + if (_explicitInherit) + assertThrow( + _virtual || (_implement && !_returnValue.empty()), + langutil::FuzzerError, + "Abstract contract overrides base interface function either" + " without implementing it or without marking it virtual." + ); + } + else + { + assertThrow(holds_alternative(_derivedProgram), + langutil::FuzzerError, + "Derived program neither an interface nor a contract" + ); + m_derivedType = DerivedType::INTERFACE; + if (_explicitInherit) + assertThrow( + !_virtual && !_implement && !_returnValue.empty(), + langutil::FuzzerError, + "Interface overrides base interface function with invalid parameters." + ); + } + + m_baseInterface = _baseInterface; + m_baseFunction = _baseFunction; + m_derivedProgram = _derivedProgram; + m_implemented = _implement; + m_virtualized = _virtual; + m_explicitlyInherited = _explicitInherit; + m_returnValue = _returnValue; +} + +std::string IFunctionOverride::str() const +{ + switch (m_derivedType) + { + case DerivedType::INTERFACE: + return interfaceStr(); + case DerivedType::ABSTRACTCONTRACT: + case DerivedType::CONTRACT: + return contractStr(); + } +} + +string IFunctionOverride::interfaceStr() const +{ + return Whiskers(R"( + function () external override returns(uint); +)") + ("functionName", m_baseFunction->name()) + ("mutability", functionMutability(m_baseFunction->mutability())) + .render(); +} + +string IFunctionOverride::contractStr() const +{ + string bodyStr = Whiskers(R"( + { + return ; + })") + ("uint", returnValue()) + .render(); + + return Whiskers(R"( + function () external override + virtual returns(uint); +)") + ("functionName", m_baseFunction->name()) + ("mutability", functionMutability(m_baseFunction->mutability())) + ("isVirtual", virtualized()) + ("isImplemented", implemented()) + ("body", bodyStr) + .render(); +} \ No newline at end of file diff --git a/test/tools/ossfuzz/SolProtoAdaptor.h b/test/tools/ossfuzz/SolProtoAdaptor.h index 76c1c36d1..44e6b567a 100644 --- a/test/tools/ossfuzz/SolProtoAdaptor.h +++ b/test/tools/ossfuzz/SolProtoAdaptor.h @@ -90,8 +90,7 @@ struct SolInterfaceFunction { SolInterfaceFunction( std::string _functionName, - SolFunctionStateMutability _mutability, - bool _override = false + SolFunctionStateMutability _mutability ); bool operator==(SolInterfaceFunction const& _rhs) const; bool operator!=(SolInterfaceFunction const& _rhs) const; @@ -105,24 +104,9 @@ struct SolInterfaceFunction { return m_mutability; } - bool override() const - { - return m_override; - } - bool isVirtual() const - { - return false; - } - bool implement() const - { - return false; - } std::string m_functionName; SolFunctionStateMutability m_mutability = SolFunctionStateMutability::PURE; - bool m_override = false; - bool m_virtual = false; - bool m_implement = false; }; struct SolContractFunction @@ -387,7 +371,7 @@ struct SolInterface std::string m_interfaceName; std::vector> m_interfaceFunctions; std::vector> m_baseInterfaces; - InterfaceOverrideMap m_overrideMap; + std::map, std::vector>> m_overrideMap; std::shared_ptr m_prng; }; @@ -466,25 +450,48 @@ struct CFunctionOverride } }; +/* 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 { -IFunctionOverride( - std::vector> _base, - std::unique_ptr _function, - bool _implement, - bool _virtual, - bool _redeclare, - std::string _returnValue = "" -) -{ - m_function = std::make_pair(_base, std::move(_function)); - m_implemented = _implement; - m_virtualized = _virtual; - m_redeclared = _redeclare; - m_returnValue = _returnValue; -} + 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; bool implemented() const { @@ -496,9 +503,9 @@ IFunctionOverride( return m_virtualized; } - bool redeclared() const + bool explicitlyInherited() const { - return m_redeclared; + return m_explicitlyInherited; } std::string returnValue() const @@ -506,15 +513,24 @@ IFunctionOverride( return m_returnValue; } - OverrideIFunction m_function; + 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_redeclared = false; + 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