diff --git a/test/tools/ossfuzz/SolidityGenerator.cpp b/test/tools/ossfuzz/SolidityGenerator.cpp index 65a66c291..b30193765 100644 --- a/test/tools/ossfuzz/SolidityGenerator.cpp +++ b/test/tools/ossfuzz/SolidityGenerator.cpp @@ -222,6 +222,47 @@ string ContractGenerator::visit() return os.str(); } +string FunctionType::toString() +{ + auto typeString = [](std::vector& _types) + { + std::string sep; + std::string typeStr; + for (auto const& i: _types) + { + typeStr += sep + std::visit(GenericVisitor{ + [&](auto const& _item) { return _item->toString(); } + }, i); + if (sep.empty()) + sep = ","; + } + return typeStr; + }; + + std::string retString = std::string("function ") + "(" + typeString(inputs) + ")"; + if (outputs.empty()) + return retString + " external pure"; + else + return retString + " external pure returns (" + typeString(outputs) + ")"; +} + +string FunctionState::params(Params _p) +{ + vector params = (_p == Params::INPUT ? inputs : outputs) | + ranges::views::transform( + [](auto& _item) -> string + { + return visit( + GenericVisitor{[](auto const& _item) { + return _item->toString(); + }}, _item.first) + + " " + + _item.second; + }) | + ranges::to>(); + return "(" + boost::algorithm::join(params, ",") + ")"; +} + string FunctionGenerator::visit() { string visibility; @@ -230,16 +271,47 @@ string FunctionGenerator::visit() if (!m_freeFunction) visibility = "external"; - auto inputType = TypeGenerator{state}.type(); - state->currentFunctionState()->addInput(pair>("i1", inputType)); - string inputParams = inputType->toString() + " i1"; - return indentation(state->indentationLevel) + - "function " + name + "(" + inputParams + ") " + visibility + " pure {}\n"; + // Add I/O + if (uRandDist->likely(s_maxInputs + 1)) + for (unsigned i = 0; i < uRandDist->distributionOneToN(s_maxInputs); i++) + state->currentFunctionState()->addInput(TypeProvider{state}.type()); + + if (uRandDist->likely(s_maxOutputs + 1)) + for (unsigned i = 0; i < uRandDist->distributionOneToN(s_maxOutputs); i++) + state->currentFunctionState()->addOutput(TypeProvider{state}.type()); + + ostringstream function; + function << indentation(state->indentationLevel) + << "function " + << name + << state->currentFunctionState()->params(FunctionState::Params::INPUT) + << " " + << visibility + << " pure" + << " returns" + << state->currentFunctionState()->params(FunctionState::Params::OUTPUT) + << " {}\n"; + return function.str(); } -shared_ptr TypeGenerator::type() +optional TypeProvider::type(SolidityTypePtr _type) { - switch (typeCategory()) + vector matchingTypes = state->currentFunctionState()->inputs | + ranges::views::filter([&_type](auto& _item) { + return _item.first == _type; + }) | + ranges::views::transform([](auto& _item) { return _item.first; }) | + ranges::to>(); + + if (matchingTypes.empty()) + return nullopt; + else + return matchingTypes[state->uRandDist->distributionOneToN(matchingTypes.size()) - 1]; +} + +SolidityTypePtr TypeProvider::type() +{ + switch (randomTypeCategory()) { case Type::INTEGER: { @@ -250,10 +322,10 @@ shared_ptr TypeGenerator::type() ); // Choose signed/unsigned type with probability of 1/2 = 0.5 bool signedType = state->uRandDist->probable(2); - return dynamic_pointer_cast(make_shared(b, signedType)); + return make_shared(b, signedType); } case Type::BOOL: - return dynamic_pointer_cast(make_shared()); + return make_shared(); case Type::FIXEDBYTES: { FixedBytesType::Bytes w = static_cast( @@ -261,18 +333,18 @@ shared_ptr TypeGenerator::type() static_cast(FixedBytesType::Bytes::W32) ) ); - return dynamic_pointer_cast(make_shared(w)); + return make_shared(w); } case Type::BYTES: - return dynamic_pointer_cast(make_shared()); + return make_shared(); case Type::ADDRESS: - return dynamic_pointer_cast(make_shared()); + return make_shared(); case Type::FUNCTION: - return dynamic_pointer_cast(make_shared()); + return make_shared(); case Type::CONTRACT: if (state->sourceUnitState[state->currentPath()]->contractType()) return state->sourceUnitState[state->currentPath()]->randomContractType(); - return dynamic_pointer_cast(make_shared()); + return make_shared(); default: solAssert(false, ""); } diff --git a/test/tools/ossfuzz/SolidityGenerator.h b/test/tools/ossfuzz/SolidityGenerator.h index 34703f0ab..3e2fb789c 100644 --- a/test/tools/ossfuzz/SolidityGenerator.h +++ b/test/tools/ossfuzz/SolidityGenerator.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include @@ -31,6 +32,7 @@ #include #include +#include #include #include #include @@ -42,22 +44,25 @@ class SolidityGenerator; /// Type declarations #define SEMICOLON() ; -#define FORWARDDECLAREGENERATORS(G) class G -GENERATORLIST(FORWARDDECLAREGENERATORS, SEMICOLON(), SEMICOLON()) -#undef FORWARDDECLAREGENERATORS +#define FORWARDDECLARE(G) class G +GENERATORLIST(FORWARDDECLARE, SEMICOLON(), SEMICOLON()) +TYPELIST(FORWARDDECLARE, SEMICOLON(), SEMICOLON()) +#undef FORWARDDECLARE #undef SEMICOLON #define COMMA() , -using GeneratorPtr = std::variant< -#define VARIANTOFSHARED(G) std::shared_ptr -GENERATORLIST(VARIANTOFSHARED, COMMA(), ) ->; -#undef VARIANTOFSHARED using Generator = std::variant< #define VARIANTOFGENERATOR(G) G GENERATORLIST(VARIANTOFGENERATOR, COMMA(), ) >; -#undef VARIANTOFGENERATOR +using GeneratorPtr = std::variant< +#define VARIANTOFSHARED(G) std::shared_ptr +GENERATORLIST(VARIANTOFSHARED, COMMA(), ) +>; +using SolidityTypePtr = std::variant< +TYPELIST(VARIANTOFSHARED, COMMA(), ) +>; +#undef VARIANTOFSHARED #undef COMMA using RandomEngine = std::mt19937_64; using Distribution = std::uniform_int_distribution; @@ -116,14 +121,16 @@ struct ContractState std::shared_ptr uRandDist; }; -struct SolidityType +class SolType { - virtual ~SolidityType() = default; +public: + virtual ~SolType() = default; virtual std::string toString() = 0; }; -struct IntegerType: SolidityType +class IntegerType: public SolType { +public: enum class Bits: size_t { B8 = 1, @@ -167,6 +174,11 @@ struct IntegerType: SolidityType signedType(_signed), numBits(static_cast(_bits) * 8) {} + bool operator==(IntegerType const& _rhs) + { + return this->signedType == _rhs.signedType && + this->numBits == _rhs.numBits; + } std::string toString() override { return (signedType ? "int" : "uint") + std::to_string(numBits); @@ -175,25 +187,36 @@ struct IntegerType: SolidityType size_t numBits; }; -struct BoolType: SolidityType +class BoolType: public SolType { +public: std::string toString() override { return "bool"; } + bool operator==(BoolType const&) + { + return true; + } }; -struct AddressType: SolidityType +class AddressType: public SolType { +public: // TODO: Implement address payable std::string toString() override { return "address"; } + bool operator==(AddressType const&) + { + return true; + } }; -struct FixedBytesType: SolidityType +class FixedBytesType: public SolType { +public: enum class Bytes: size_t { W1 = 1, @@ -232,6 +255,10 @@ struct FixedBytesType: SolidityType FixedBytesType(Bytes _width): numBytes(static_cast(_width)) {} + bool operator==(FixedBytesType const& _rhs) + { + return this->numBytes == _rhs.numBytes; + } std::string toString() override { return "bytes" + std::to_string(numBytes); @@ -239,16 +266,22 @@ struct FixedBytesType: SolidityType size_t numBytes; }; -struct BytesType: SolidityType +class BytesType: public SolType { +public: std::string toString() override { return "bytes memory"; } + bool operator==(BytesType const&) + { + return true; + } }; -struct ContractType: SolidityType +class ContractType: public SolType { +public: ContractType(std::string _name): contractName(_name) {} std::string toString() override @@ -259,11 +292,16 @@ struct ContractType: SolidityType { return contractName; } + bool operator==(ContractType const&) + { + return true; + } std::string contractName; }; -struct FunctionType: SolidityType +class FunctionType: public SolType { +public: FunctionType() = default; ~FunctionType() override { @@ -271,40 +309,30 @@ struct FunctionType: SolidityType outputs.clear(); } - void addInput(std::shared_ptr _input) + void addInput(SolidityTypePtr _input) { inputs.emplace_back(_input); } - void addOutput(std::shared_ptr _output) + void addOutput(SolidityTypePtr _output) { outputs.emplace_back(_output); } - std::string toString() override + std::string toString() override; + bool operator==(FunctionType const& _rhs) { - auto typeString = [](std::vector>& _types) - { - std::string sep; - std::string typeStr; - for (auto const& i: _types) - { - typeStr += sep + i->toString(); - if (sep.empty()) - sep = ","; - } - return typeStr; - }; - - std::string retString = std::string("function ") + "(" + typeString(inputs) + ")"; - if (outputs.empty()) - return retString + " external pure"; - else - return retString + " external pure returns (" + typeString(outputs) + ")"; + if (_rhs.inputs.size() != this->inputs.size() || _rhs.outputs.size() != this->outputs.size()) + return false; + if (!std::equal(_rhs.inputs.begin(), _rhs.inputs.end(), this->inputs.begin()) || + !std::equal(_rhs.outputs.begin(), _rhs.outputs.end(), this->outputs.begin()) + ) + return false; + return true; } - std::vector> inputs; - std::vector> outputs; + std::vector inputs; + std::vector outputs; }; /// Forward declaration @@ -318,7 +346,7 @@ struct SourceState {} void addFreeFunction(std::string& _functionName) { - exports[std::dynamic_pointer_cast(std::make_shared())] = _functionName; + exports[std::make_shared()] = _functionName; } bool freeFunction(std::string const& _functionName) { @@ -327,37 +355,39 @@ struct SourceState bool contractType() { return !(exports | ranges::views::filter([](auto& _i) { - return bool(std::dynamic_pointer_cast(_i.first)); + return std::holds_alternative>(_i.first); })).empty(); } std::string randomContract() { auto contracts = exports | ranges::views::filter([](auto& _item) -> bool { - return bool(std::dynamic_pointer_cast(_item.first)); + return std::holds_alternative>( + _item.first + ); }) | ranges::views::transform([](auto& _item) -> std::string { return _item.second; }) | ranges::to>; return contracts[uRandDist->distributionOneToN(contracts.size()) - 1]; } - std::shared_ptr randomContractType() + std::shared_ptr randomContractType() { auto contracts = exports | ranges::views::filter([](auto& _item) -> bool { - return bool(std::dynamic_pointer_cast(_item.first)); + return std::holds_alternative>(_item.first); }) | - ranges::views::transform([](auto& _item) -> std::shared_ptr { - return _item.first; + ranges::views::transform([](auto& _item) -> std::shared_ptr { + return std::get>(_item.first); }) | - ranges::to>>; + ranges::to>>; return contracts[uRandDist->distributionOneToN(contracts.size()) - 1]; } void addImportedSourcePath(std::string& _sourcePath) { importedSources.emplace(_sourcePath); } - void resolveImports(std::map, std::string> _imports) + void resolveImports(std::map _imports) { for (auto const& item: _imports) exports.emplace(item); @@ -374,28 +404,37 @@ struct SourceState void print(std::ostream& _os) const; std::shared_ptr uRandDist; std::set importedSources; - std::map, std::string> exports; + std::map exports; }; struct FunctionState { + enum class Params + { + INPUT, + OUTPUT + }; FunctionState() = default; ~FunctionState() { inputs.clear(); outputs.clear(); } - void addInput(std::pair> _input) + using TypeId = std::pair; + void addInput(SolidityTypePtr _input) { - inputs.emplace(_input); + inputs.emplace(_input, "i" + std::to_string(numInputs++)); } - void addOutput(std::pair> _output) + void addOutput(SolidityTypePtr _output) { - outputs.emplace(_output); + outputs.emplace(_output, "o" + std::to_string(numOutpus++)); } + std::string params(Params _p); - std::map> inputs; - std::map> outputs; + std::map inputs; + std::map outputs; + unsigned numInputs = 0; + unsigned numOutpus = 0; }; struct TestState @@ -547,9 +586,9 @@ struct TestState std::string const functionPrefix = "f"; }; -struct TypeGenerator +struct TypeProvider { - TypeGenerator(std::shared_ptr _state): state(std::move(_state)) + TypeProvider(std::shared_ptr _state): state(std::move(_state)) {} enum class Type: size_t @@ -564,9 +603,10 @@ struct TypeGenerator TYPEMAX }; - std::shared_ptr type(); + SolidityTypePtr type(); + std::optional type(SolidityTypePtr _typePtr); - Type typeCategory() + Type randomTypeCategory() { return static_cast(state->uRandDist->distributionOneToN(static_cast(Type::TYPEMAX) - 1)); } @@ -574,6 +614,22 @@ struct TypeGenerator std::shared_ptr state; }; +struct ExpressionGenerator +{ + ExpressionGenerator(std::shared_ptr _state): state(std::move(_state)) + {} + + enum class Expr: size_t + { + VARREF = 1, + TYPEMAX + }; + + std::string expression(SolidityTypePtr _type); + + std::shared_ptr state; +}; + struct GeneratorBase { explicit GeneratorBase(std::shared_ptr _mutator); @@ -742,7 +798,8 @@ public: } private: bool m_freeFunction; - + static constexpr unsigned s_maxInputs = 4; + static constexpr unsigned s_maxOutputs = 4; }; class SolidityGenerator: public std::enable_shared_from_this diff --git a/test/tools/ossfuzz/Types.h b/test/tools/ossfuzz/Types.h new file mode 100644 index 000000000..e3ae8ebdd --- /dev/null +++ b/test/tools/ossfuzz/Types.h @@ -0,0 +1,56 @@ +/* +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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Convenience macros for Solidity generator type declarations. + */ + +#pragma once + +/* + * Alphabetically sorted Solidity types. + * SEP must appear between two elements and ENDSEP must + * appear after the last element. + * + * This macro applies another macro (MACRO) that is + * passed as input to this macro on the list of Generator + * types. Example that uses forward declaration: + * + * #define MACRO(T) struct T; + * #define SEMICOLON() ; + * + * TYPELIST(MACRO, SEMICOLON(), SEMICOLON()) + * + * produces + * + * struct PragmaGenerator;class SourceUnitGenerator;class TestCaseGenerator; + * + */ +#define TYPELIST(MACRO, SEP, ENDSEP) \ + MACRO(AddressType) SEP \ + MACRO(BoolType) SEP \ + MACRO(BytesType) SEP \ + MACRO(ContractType) SEP \ + MACRO(FixedBytesType) SEP \ + MACRO(FunctionType) SEP \ + MACRO(IntegerType) ENDSEP + + + + + +