diff --git a/test/tools/ossfuzz/Generators.h b/test/tools/ossfuzz/Generators.h index bd7c4b1b0..e02bddd7a 100644 --- a/test/tools/ossfuzz/Generators.h +++ b/test/tools/ossfuzz/Generators.h @@ -47,6 +47,7 @@ MACRO(ContinueStmtGenerator) SEP \ MACRO(ContractGenerator) SEP \ MACRO(ExpressionStmtGenerator) SEP \ + MACRO(ForStmtGenerator) SEP \ MACRO(FunctionCallGenerator) SEP \ MACRO(FunctionGenerator) SEP \ MACRO(IfStmtGenerator) SEP \ @@ -55,4 +56,5 @@ MACRO(SourceUnitGenerator) SEP \ MACRO(StatementGenerator) SEP \ MACRO(TestCaseGenerator) SEP \ + MACRO(VarDeclStmtGenerator) SEP \ MACRO(WhileStmtGenerator) ENDSEP diff --git a/test/tools/ossfuzz/SolidityGenerator.cpp b/test/tools/ossfuzz/SolidityGenerator.cpp index 0d71a8516..ee59e34b3 100644 --- a/test/tools/ossfuzz/SolidityGenerator.cpp +++ b/test/tools/ossfuzz/SolidityGenerator.cpp @@ -416,11 +416,11 @@ string AssignmentStmtGenerator::visit() auto lhs = exprGen.randomLValueExpression(); exprGen.resetNestingDepth(); if (!lhs.has_value()) - return "\n"; + return {}; auto rhs = exprGen.rLValueOrLiteral(lhs.value()); exprGen.resetNestingDepth(); if (!rhs.has_value()) - return "\n"; + return {}; auto operation = assignOp(lhs.value().first); return indentation() + lhs.value().second + assignOp(operation) + rhs.value().second + ";\n"; } @@ -434,7 +434,30 @@ string ExpressionStmtGenerator::visit() if (expression.has_value()) return indentation() + expression.value().second + ";\n"; else - return "\n"; + return {}; +} + +string VarDeclStmtGenerator::visit() +{ + ExpressionGenerator exprGen{state}; + auto randomType = TypeProvider{state}.type(); + pair randomTypeName = {randomType, {}}; + auto expression = exprGen.rLValueOrLiteral(randomTypeName); + string varName = state->currentFunctionState()->addLocal(randomTypeName.first); + string varDeclStmt = indentation() + + std::visit( + GenericVisitor{[](auto const& _it) { return _it->toString(); }}, + randomTypeName.first + ) + + " " + + varName; + if (expression.has_value()) + varDeclStmt += " = " + + expression.value().second + + ";\n"; + else + varDeclStmt += ";\n"; + return varDeclStmt; } void IfStmtGenerator::setup() @@ -538,10 +561,91 @@ string WhileStmtGenerator::visit() whileStmt << indentation() << "while (" << expression.value().second - << ")\n"; + << ");\n"; return whileStmt.str(); } +void ForStmtGenerator::setup() +{ + set> dependsOn = { + {mutator->generator(), 1}, + {mutator->generator(), 1}, + {mutator->generator(), 1} + }; + addGenerators(std::move(dependsOn)); +} + +string ForStmtGenerator::visit() +{ + state->enterLoop(); + ScopeGuard exitLoop([&]() { state->exitLoop(); }); + size_t preStmtChoice = uRandDist()->distributionOneToN(3); + string simpleStmt; + bool forInitIsVarDecl = false; + switch (preStmtChoice) + { + case 1: + simpleStmt = mutator->generator()->generate(); + if (simpleStmt.empty()) + { + forInitIsVarDecl = true; + simpleStmt = mutator->generator()->generate(); + } + break; + case 2: + simpleStmt = mutator->generator()->generate(); + forInitIsVarDecl = true; + break; + case 3: + simpleStmt = ";\n"; + break; + } + string forCondition; + bool hasCondition = uRandDist()->probable(2); + if (hasCondition) + { + ExpressionGenerator exprGen{state}; + auto boolType = make_shared(); + pair boolTypeName = {boolType, {}}; + auto expression = exprGen.rLValueOrLiteral(boolTypeName); + if (expression.has_value()) + forCondition = expression.value().second + ";\n"; + else + forCondition = ";\n"; + } + else + forCondition = ";\n"; + string postStmt; + bool hasPostStmt = uRandDist()->probable(2); + if (hasPostStmt) + { + ExpressionGenerator exprGen{state}; + auto randomType = TypeProvider{state}.type(); + pair randomTypeName = {randomType, {}}; + auto expression = exprGen.rLValueOrLiteral(randomTypeName); + if (expression.has_value()) + postStmt = expression.value().second; + } + // Make sure block stmt generator does not output an unchecked block + mutator->generator()->unchecked(false); + ostringstream forBlock; + forBlock << mutator->generator()->generate(); + if (forBlock.str().empty()) + forBlock << indentation() << "{ }\n"; + string forStmt; + forStmt = indentation() + + "for(" + + simpleStmt + + forCondition + + postStmt + + ")\n" + + forBlock.str(); + // Manually out of scope variable declaration in for init stmt. + if (forInitIsVarDecl) + state->currentFunctionState()->scopes.back()->variables.pop_back(); + return forStmt; +} + void StatementGenerator::setup() { set> dependsOn = { @@ -552,7 +656,9 @@ void StatementGenerator::setup() {mutator->generator(), 2}, {mutator->generator(), 1}, {mutator->generator(), 1}, - {mutator->generator(), 1} + {mutator->generator(), 1}, + {mutator->generator(), 1}, + {mutator->generator(), 1} }; addGenerators(std::move(dependsOn)); } @@ -1371,8 +1477,7 @@ string FunctionCallGenerator::lhs(vector>& _functi { auto newVars = _functionReturnTypeNames | ranges::views::transform([&](auto const& _item) -> string { - state->currentFunctionState()->addLocal(_item.first); - string varName = state->currentFunctionState()->scopes.back()->variables.back().second; + string varName = state->currentFunctionState()->addLocal(_item.first); return std::visit( GenericVisitor{[](auto const& _it) { return _it->toString(); }}, _item.first @@ -1474,7 +1579,7 @@ string FunctionCallGenerator::visit() if (state->insideContract) availableFunctions += state->currentContractState()->functions; if (availableFunctions.empty()) - return "\n"; + return {}; shared_ptr callee; if (availableFunctions.size() > 1) @@ -1489,7 +1594,7 @@ string FunctionCallGenerator::visit() if (callee) return callStmt(callee); else - return "\n"; + return {}; } template diff --git a/test/tools/ossfuzz/SolidityGenerator.h b/test/tools/ossfuzz/SolidityGenerator.h index 35649f25a..5c75e26f9 100644 --- a/test/tools/ossfuzz/SolidityGenerator.h +++ b/test/tools/ossfuzz/SolidityGenerator.h @@ -466,9 +466,11 @@ struct FunctionState type->addOutput(_output); outputs.emplace_back(std::move(_output), "o" + std::to_string(numOutpus++)); } - void addLocal(SolidityTypePtr _local) + std::string addLocal(SolidityTypePtr _local) { - scopes.back()->variables.emplace_back(std::move(_local), "l" + std::to_string(numLocals++)); + std::string varName = "l" + std::to_string(numLocals++); + scopes.back()->variables.emplace_back(std::move(_local), varName); + return varName; } std::string params(Params _p); @@ -1098,6 +1100,19 @@ public: } }; +class VarDeclStmtGenerator: public GeneratorBase +{ +public: + explicit VarDeclStmtGenerator(SolidityGenerator* _mutator): + GeneratorBase(std::move(_mutator)) + {} + std::string visit() override; + std::string name() override + { + return "VarDecl statement generator"; + } +}; + class IfStmtGenerator: public GeneratorBase { public: @@ -1162,6 +1177,17 @@ public: std::string name() override { return "While statement generator"; } }; +class ForStmtGenerator: public GeneratorBase +{ +public: + explicit ForStmtGenerator(SolidityGenerator* _mutator): + GeneratorBase(std::move(_mutator)) + {} + void setup() override; + std::string visit() override; + std::string name() override { return "For statement generator"; } +}; + class AssignmentStmtGenerator: public GeneratorBase { public: