diff --git a/test/tools/ossfuzz/Generators.h b/test/tools/ossfuzz/Generators.h index e47794b24..918faba6a 100644 --- a/test/tools/ossfuzz/Generators.h +++ b/test/tools/ossfuzz/Generators.h @@ -41,6 +41,7 @@ * */ #define GENERATORLIST(MACRO, SEP, ENDSEP) \ + MACRO(AssignmentStmtGenerator) SEP \ MACRO(BlockStmtGenerator) SEP \ MACRO(ContractGenerator) SEP \ MACRO(FunctionGenerator) SEP \ diff --git a/test/tools/ossfuzz/SolidityGenerator.cpp b/test/tools/ossfuzz/SolidityGenerator.cpp index 0f65bc46e..4f5f09e90 100644 --- a/test/tools/ossfuzz/SolidityGenerator.cpp +++ b/test/tools/ossfuzz/SolidityGenerator.cpp @@ -265,9 +265,51 @@ string FunctionState::params(Params _p) return "(" + boost::algorithm::join(params, ",") + ")"; } +string AssignmentStmtGenerator::visit() +{ + ExpressionGenerator exprGen{state}; + auto lhs = exprGen.expression(); + if (!lhs.has_value()) + return "\n"; + auto rhs = exprGen.expression(lhs.value().first); + if (!rhs.has_value()) + return "\n"; + return lhs.value().second + " = " + rhs.value().second + ";\n"; +} + +void StatementGenerator::setup() +{ + addGenerators({ + {mutator->generator(), 1}, + {mutator->generator(), 1} + }); +} + +string StatementGenerator::visit() +{ + return visitChildren(); +} + +void BlockStmtGenerator::setup() +{ + addGenerators({ + {mutator->generator(), s_maxStatements}, + }); +} + string BlockStmtGenerator::visit() { - return "\n" + indentation() + "{}\n"; + if (nestingTooDeep()) + return "\n"; + + incrementNestingDepth(); + ostringstream block; + block << indentation() + "{\n"; + state->indent(); + block << visitChildren(); + state->unindent(); + block << indentation() << "}\n"; + return block.str(); } void FunctionGenerator::setup() @@ -303,10 +345,54 @@ string FunctionGenerator::visit() if (!state->currentFunctionState()->outputs.empty()) function << " returns" << state->currentFunctionState()->params(FunctionState::Params::OUTPUT); - function << generator()->visit(); + function << "\n" << generator()->visit(); return function.str(); } +pair ExpressionGenerator::randomLValueExpression() +{ + LValueExpr exprType = static_cast( + state->uRandDist->distributionOneToN(static_cast(LValueExpr::TYPEMAX) - 1) + ); + switch (exprType) + { + case LValueExpr::VARREF: + { + auto liveVariables = state->currentFunctionState()->inputs | + ranges::views::transform([](auto& _item) { return _item; }) | + ranges::to>>(); + liveVariables += state->currentFunctionState()->outputs | + ranges::views::transform([](auto& _item) { return _item; }) | + ranges::to>>(); + return liveVariables[state->uRandDist->distributionOneToN(liveVariables.size()) - 1]; + } + default: + solAssert(false, ""); + } +} + +optional> ExpressionGenerator::expression() +{ + auto currentFunctionState = state->currentFunctionState(); + // TODO: Remove this barrier once we support more expression types. + if (currentFunctionState->inputs.empty() && currentFunctionState->outputs.empty()) + return nullopt; + return randomLValueExpression(); +} + +optional> ExpressionGenerator::expression(SolidityTypePtr _type) +{ + auto liveTypedVariables = state->currentFunctionState()->inputs | + ranges::views::filter([&_type](auto& _item) { return _item.first == _type; }) | + ranges::to>>(); + liveTypedVariables += state->currentFunctionState()->outputs | + ranges::views::filter([&_type](auto& _item) { return _item.first == _type; }) | + ranges::to>>(); + if (liveTypedVariables.empty()) + return nullopt; + return liveTypedVariables[state->uRandDist->distributionOneToN(liveTypedVariables.size()) - 1]; +} + optional TypeProvider::type(SolidityTypePtr _type) { vector matchingTypes = state->currentFunctionState()->inputs | diff --git a/test/tools/ossfuzz/SolidityGenerator.h b/test/tools/ossfuzz/SolidityGenerator.h index 52f8d32b5..e806491d2 100644 --- a/test/tools/ossfuzz/SolidityGenerator.h +++ b/test/tools/ossfuzz/SolidityGenerator.h @@ -619,13 +619,15 @@ struct ExpressionGenerator ExpressionGenerator(std::shared_ptr _state): state(std::move(_state)) {} - enum class Expr: size_t + enum class LValueExpr: size_t { VARREF = 1, TYPEMAX }; - std::string expression(SolidityTypePtr _type); + std::optional> expression(SolidityTypePtr _type); + std::optional> expression(); + std::pair randomLValueExpression(); std::shared_ptr state; }; @@ -807,19 +809,47 @@ public: explicit StatementGenerator(std::shared_ptr _mutator): GeneratorBase(std::move(_mutator)) {} - void setup() override {} - std::string visit() override { return {}; } + void setup() override; + std::string visit() override; std::string name() override { return "Statement generator"; } }; +class AssignmentStmtGenerator: public GeneratorBase +{ +public: + explicit AssignmentStmtGenerator(std::shared_ptr _mutator): + GeneratorBase(std::move(_mutator)) + {} + std::string visit() override; + std::string name() override { return "Assignment statement generator"; } +}; + class BlockStmtGenerator: public GeneratorBase { public: explicit BlockStmtGenerator(std::shared_ptr _mutator): - GeneratorBase(std::move(_mutator)) + GeneratorBase(std::move(_mutator)), + m_nestingDepth(0) {} + void endVisit() override + { + m_nestingDepth = 0; + } + void incrementNestingDepth() + { + ++m_nestingDepth; + } + bool nestingTooDeep() + { + return m_nestingDepth > s_maxNestingDepth; + } + void setup() override; std::string visit() override; std::string name() override { return "Block statement generator"; } +private: + size_t m_nestingDepth; + static constexpr unsigned s_maxStatements = 4; + static constexpr unsigned s_maxNestingDepth = 3; }; class SolidityGenerator: public std::enable_shared_from_this