diff --git a/test/tools/ossfuzz/SolCustomMutator.cpp b/test/tools/ossfuzz/SolCustomMutator.cpp
new file mode 100644
index 000000000..f6707b0cc
--- /dev/null
+++ b/test/tools/ossfuzz/SolCustomMutator.cpp
@@ -0,0 +1,88 @@
+/*
+ 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
+
+using namespace std;
+using namespace antlr4;
+using namespace solidity::test::fuzzer;
+
+namespace {
+/// Forward declare libFuzzer's default mutator definition
+extern "C" size_t LLVMFuzzerMutate(uint8_t* _data, size_t _size, size_t _maxSize);
+
+/// Define Solidity's custom mutator by implementing libFuzzer's
+/// custom mutator external interface.
+extern "C" size_t LLVMFuzzerCustomMutator(
+ uint8_t* _data,
+ size_t _size,
+ size_t _maxSize,
+ unsigned int _seed
+)
+{
+ if (_maxSize <= _size || _size == 0)
+ return LLVMFuzzerMutate(_data, _size, _maxSize);
+ return SolCustomMutator{_data, _size, _maxSize, _seed}.mutate();
+}
+}
+
+SolCustomMutator::SolCustomMutator(uint8_t* _data, size_t _size, size_t _maxSize, unsigned int _seed)
+{
+ Data = _data;
+ Size = _size;
+ In = string(_data, _data + _size),
+ MaxMutantSize = _maxSize;
+ Seed = _seed;
+}
+
+size_t SolCustomMutator::mutate()
+{
+ try
+ {
+ antlr4::ANTLRInputStream AStream(In);
+ SolidityLexer Lexer(&AStream);
+ Lexer.removeErrorListeners();
+ antlr4::CommonTokenStream Tokens(&Lexer);
+ Tokens.fill();
+ SolidityParser Parser(&Tokens);
+ Parser.removeErrorListeners();
+ SolidityMutator Mutator(Seed);
+ Parser.sourceUnit()->accept(&Mutator);
+ Out = Mutator.toString();
+
+ // If mutant is empty, default to libFuzzer's mutator
+ if (Out.empty())
+ return Size;
+ else
+ {
+ size_t mutantSize = Out.size() >= MaxMutantSize ? MaxMutantSize - 1 : Out.size();
+ fill_n(Data, MaxMutantSize, 0);
+ copy(Out.data(), Out.data() + mutantSize, Data);
+ return mutantSize;
+ }
+ }
+ // Range error is thrown by antlr4 runtime's ANTLRInputStream
+ // See https://github.com/antlr/antlr4/issues/2036
+ catch (range_error const&)
+ {
+ // Default to libFuzzer's mutator
+ return Size;
+ }
+}
diff --git a/test/tools/ossfuzz/SolCustomMutator.h b/test/tools/ossfuzz/SolCustomMutator.h
new file mode 100644
index 000000000..d62f5643d
--- /dev/null
+++ b/test/tools/ossfuzz/SolCustomMutator.h
@@ -0,0 +1,43 @@
+/*
+ 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 .
+*/
+
+#pragma once
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+namespace solidity::test::fuzzer
+{
+struct SolCustomMutator
+{
+ SolCustomMutator(uint8_t* _data, size_t _size, size_t _maxSize, unsigned _seed);
+ size_t mutate();
+
+ uint8_t* Data = nullptr;
+ size_t Size = 0;
+ std::string In{};
+ std::string Out{};
+ size_t MaxMutantSize = 0;
+ unsigned int Seed = 0;
+};
+}
\ No newline at end of file
diff --git a/test/tools/ossfuzz/SolidityMutator.cpp b/test/tools/ossfuzz/SolidityMutator.cpp
index b588c36e3..e8862c3eb 100644
--- a/test/tools/ossfuzz/SolidityMutator.cpp
+++ b/test/tools/ossfuzz/SolidityMutator.cpp
@@ -16,44 +16,766 @@
*/
// SPDX-License-Identifier: GPL-3.0
+#include
+
#include
+#include
+
#include
+#include
using namespace std;
using namespace solidity::util;
using namespace solidity::test::fuzzer;
+using SolP = solidity::test::fuzzer::SolidityParser;
-antlrcpp::Any SolidityMutator::visitSourceUnit(SolidityParser::SourceUnitContext* _ctx)
+string Generator::operator()(std::string&& _template, std::map&& _args)
{
- if (_ctx->pragmaDirective().empty())
- m_out << Whiskers(s_pragmaDirective)("directive", "experimental SMTChecker").render();
- else
- visitChildren(_ctx);
+ Whiskers w(move(_template));
+ for (auto &&item: _args)
+ w(move(item.first), move(item.second));
+ return w.render();
+}
+
+template
+void SolidityMutator::fuzzRule(T* _ctx)
+{
+ mutate(_ctx);
+// if (coinToss())
+// gen();
+}
+
+void SolidityMutator::gen()
+{
+ static const vector validPragmas = {
+ "experimental SMTChecker",
+ "experimental ABIEncoderV2"
+ };
+ m_out << generator(
+ [&]() {
+ return Whiskers(R"(pragma ;)")
+ ("string", validPragmas[m_rand() % validPragmas.size()])
+ .render();
+ },
+ randOneToN(s_maxPragmas),
+ "\n",
+ true
+ );
+}
+
+void SolidityMutator::mutate(SolP::PragmaDirectiveContext*)
+{
+ m_out << Whiskers(R"(pragma ;)")
+ ("mutation", mutatePragma())
+ .render();
+}
+
+string SolidityMutator::eventParam()
+{
+ return Whiskers(R"( )")
+ ("typename", typeName())
+ ("indexed", coinToss() ? "indexed" : "")
+ ("id", coinToss() ? genRandomId("ev"): "")
+ .render();
+}
+
+string SolidityMutator::visibility()
+{
+ if (interfaceScope())
+ return "external";
+
+ switch (randOneToN(4))
+ {
+ case 1:
+ return "public";
+ case 2:
+ return "external";
+ case 3:
+ return "private";
+ case 4:
+ return "internal";
+ }
+ assert(false);
+}
+
+string SolidityMutator::mutability()
+{
+ switch (randOneToN(4))
+ {
+ case 1:
+ return "view";
+ case 2:
+ return "pure";
+ case 3:
+ if (libraryScope() || globalScope())
+ return "";
+ else
+ return "payable";
+ case 4:
+ return "";
+ }
+ assert(false);
+}
+
+string SolidityMutator::elementaryType(bool _allowAddressPayable)
+{
+ switch (randOneToN(8))
+ {
+ case 1:
+ return "address";
+ case 2:
+ if (_allowAddressPayable)
+ return "address payable";
+ else
+ return "address";
+ case 3:
+ return "bool";
+ case 4:
+ return "string";
+ case 5:
+ return "bytes";
+ case 6:
+ return "int" + bitWidth();
+ case 7:
+ return "uint" + bitWidth();
+ case 8:
+ return "bytes" + byteWidth();
+ }
+ assert(false);
+}
+
+string SolidityMutator::typeName()
+{
+ switch (randOneToN(5))
+ {
+ case 1:
+ // TODO: Implement function type mutator
+ return "function () external view returns (uint)";
+ case 2:
+ return Whiskers(R"(mapping ( => ))")
+ ("keyType", elementaryType(false))
+ ("typeName", typeName())
+ .render();
+ case 3:
+ if (m_numStructs > 0)
+ return "s" + to_string(m_rand() % m_numStructs);
+ else
+ return elementaryType();
+ case 4:
+ // TODO: Implement complex types
+ return "int[]";
+ case 5:
+ return elementaryType();
+ }
+ assert(false);
+}
+
+string SolidityMutator::dataLocation()
+{
+ switch (randOneToN(3))
+ {
+ case 1:
+ return "storage";
+ case 2:
+ return "memory";
+ case 3:
+ return "calldata";
+ }
+ assert(false);
+}
+
+template
+antlrcpp::Any SolidityMutator::genericVisitor(C* _ctx, std::string _ctxName)
+{
+ visitChildren(_ctx);
return antlrcpp::Any();
}
-antlrcpp::Any SolidityMutator::visitPragmaDirective(SolidityParser::PragmaDirectiveContext*)
+string SolidityMutator::genRandomUserDefinedTypeName(unsigned _numIds, unsigned _len, char _start, char _end)
{
- auto PickLiteral = [&]() -> string
- {
- static const vector alphanum = {
- "0",
- "experimental",
- "solidity < 142857",
- "solidity >=0.0.0",
- "solidity >=0.0.0 <0.8.0",
- "experimental __test",
- "experimental SMTChecker",
- "experimental ABIEncoderV2",
- "experimental \"xyz\""
- };
+ uniform_int_distribution dist(_start, _end);
+ vector ids{};
+ generate_n(back_inserter(ids), m_rand() % _numIds + 1, [&](){ return genRandString(_len, _start, _end); });
+ return boost::algorithm::join(ids, ".");
+}
- return alphanum[randomOneToN(alphanum.size()) - 1];
+string SolidityMutator::genRandString(unsigned int _maxLen, char _start, char _end)
+{
+ uniform_int_distribution dist(_start, _end);
+ string result{};
+ generate_n(back_inserter(result), m_rand() % _maxLen + 1, [&]{ return dist(m_rand); });
+ return result;
+}
+
+template
+void SolidityMutator::visitItemsOrGenerate(T _container, F&& _generator, Args&&... _args)
+{
+ if (_container.size() == 0)
+ mayRun(forward(_generator), forward(_args)...);
+ else
+ for (auto item: _container)
+ item->accept(this);
+}
+
+template
+auto SolidityMutator::mayRun(F&& _func, Args&&... _args, unsigned _n)
+{
+ if (m_rand() % _n == 0)
+ return invoke(forward(_func), forward(_args)...);
+ else
+ return result_of::type();
+}
+
+antlrcpp::Any SolidityMutator::visitSourceUnit(SolP::SourceUnitContext* _ctx)
+{
+// for (auto &item: _ctx->getRuleContexts())
+// visitItemsOrGenerate(item, gen, this, item);
+
+// mayRun(
+// [](SolP::SourceUnitContext* _ctx) {
+// visitItemsOrGenerate(
+// _ctx->pragmaDirective(),
+// [&]() { m_out << "pragma solidity >= 0.0.0;"; }
+// );
+// visitItemsOrGenerate(
+// _ctx->importDirective(),
+// []() {}
+// );
+// visitItemsOrGenerate(
+// _ctx->contractDefinition(),
+// [&]() { genContract(); }
+// );
+// visitItemsOrGenerate(
+// _ctx->interfaceDefinition(),
+// [&]() {
+// m_out << Whiskers(R"(interface { function f0() external; })")
+// ("id", "I" + to_string(m_numInterfaces++))
+// .render();
+// }
+// );
+// visitItemsOrGenerate(
+// _ctx->libraryDefinition(),
+// [&]() {
+// m_out << Whiskers(R"(library { function f0() public {} })")
+// ("id", "L" + to_string(m_numLibraries++))
+// .render();
+// }
+// );
+// visitItemsOrGenerate(
+// _ctx->functionDefinition(),
+// [&]() {
+// m_out << "function func() {}";
+// }
+// );
+// visitItemsOrGenerate(
+// _ctx->structDefinition(),
+// [&]() {
+// m_out << "struct S { uint sm0; }";
+// }
+// );
+// visitItemsOrGenerate(
+// _ctx->enumDefinition(),
+// [&]() {
+// m_out << "enum E { first }";
+// }
+// );
+// }
+// );
+
+
+ return antlrcpp::Any();
+}
+
+//void SolidityMutator::gen(SolP::ModifierDefinitionContext*)
+//{
+// constexpr string s = R"(modifier () ;)";
+// Whiskers mod(R"(modifier () ;)");
+//}
+//
+//void SolidityMutator::mutate(SolP::ModifierDefinitionContext* _ctx)
+//{
+// for (auto &rule: _ctx->getRuleContexts())
+// std::cout << rule->getText() << std::endl;
+//}
+
+antlrcpp::Any SolidityMutator::visitModifierDefinition(SolidityParser::ModifierDefinitionContext* _ctx)
+{
+ Whiskers mod(R"(modifier () ;)");
+
+ bool noDef = coinToss();
+ m_out << mod
+ ("id", genRandomId("m", 2))
+ ("params", _ctx->arguments ? _ctx->arguments->accept(this).as() : "")
+ ("virtual", coinToss() ? "virtual" : "")
+ ("override", coinToss() ? "override" : "")
+ ("noDef", noDef)
+ .render();
+ if (!noDef && _ctx->body)
+ _ctx->body->accept(this);
+ return antlrcpp::Any();
+}
+
+antlrcpp::Any SolidityMutator::visitStructDefinition(SolP::StructDefinitionContext* _ctx)
+{
+ Whiskers structDef(R"(struct { })");
+ unsigned numMembers = 0;
+ auto fieldGenerator = [&]() {
+ return Whiskers(R"( ;)")
+ ("typeName", typeName())
+ ("id", "sm" + to_string(numMembers++))
+ .render();
};
+ structDef("members", generator(fieldGenerator, randOneToN(5), " ", true));
+ structDef("id", "s" + to_string(m_numStructs++));
+ m_out << structDef.render();
+ return antlrcpp::Any();
+}
- string pragma = PickLiteral();
+antlrcpp::Any SolidityMutator::visitEnumDefinition(SolP::EnumDefinitionContext* _ctx)
+{
+ unsigned numEnums = 0;
+ string elements = generator(
+ [&]() { return "e" + to_string(numEnums++); },
+ randOneToN(5),
+ ", ",
+ false
+ );
+ m_out << Whiskers(R"(enum {})")
+ ("id", "en" + to_string(m_numEnums++))
+ ("elements", elements)
+ .render();
+ return antlrcpp::Any();
+}
- m_out << Whiskers(s_pragmaDirective)("directive", pragma).render();
+string SolidityMutator::generator(function _gen, unsigned _n, string _separator, bool _removeDups)
+{
+ vector g{};
+ generate_n(
+ back_inserter(g),
+ _n,
+ _gen
+ );
+ if (_removeDups)
+ {
+ sort(g.begin(), g.end());
+ g.erase(unique(g.begin(), g.end()), g.end());
+ }
+ return boost::algorithm::join(g, _separator);
+}
+
+string SolidityMutator::mutatePragma()
+{
+ string minVersion = generator(
+ [&]() { return genRandString(1, '0', '9'); },
+ 3,
+ ".",
+ false
+ );
+ string maxVersion = generator(
+ [&]() { return genRandString(1, '0', '9'); },
+ 3,
+ ".",
+ false
+ );
+ string lSign = coinToss() ? ">" : ">=";
+ string uSign = coinToss() ? "<" : "<=";
+ return Whiskers(R"(solidity )")
+ ("l", coinToss())
+ ("u", coinToss())
+ ("lSign", lSign)
+ ("minV", minVersion)
+ ("uSign", uSign)
+ ("maxV", maxVersion)
+ .render();
+}
+
+void SolidityMutator::genContract()
+{
+ m_out << Whiskers(R"(contract { function f0() public {} })")
+ ("id", "C" + to_string(m_numContracts++))
+ .render();
+}
+
+antlrcpp::Any SolidityMutator::visitPragmaDirective(SolP::PragmaDirectiveContext* _ctx)
+{
+ fuzzRule(_ctx);
+ return antlrcpp::Any();
+}
+
+string SolidityMutator::genImport()
+{
+ string path = genRandomPath();
+ string id = genRandomId("Im");
+
+ Whiskers importPathAsId(R"(import as ;)");
+ Whiskers importStarAsIdFromPath(R"(import * as from ;)");
+ Whiskers importPath(R"(import ;)");
+
+ switch (randOneToN(4))
+ {
+ case 1:
+ return importPathAsId("path", path)("id", id).render();
+ case 2:
+ return importStarAsIdFromPath("id", id)("path", path).render();
+ case 3:
+ {
+ vector symbols{};
+ unsigned numElements = randOneToN(10);
+ generate_n(
+ back_inserter(symbols),
+ numElements,
+ [&](){
+ return Whiskers((R"( as )"))
+ ("as", coinToss())
+ ("path", genRandomPath())
+ ("id", genRandomId("Im"))
+ ("symbol", genRandString(5, 'A', 'z'))
+ .render();
+ }
+ );
+ return Whiskers(R"(import {} from ;)")
+ ("syms", boost::algorithm::join(symbols, ", "))
+ ("path", path)
+ .render();
+ }
+ case 4:
+ return importPath("path", path).render();
+ }
+ assert(false);
+}
+
+antlrcpp::Any SolidityMutator::visitImportDirective(SolP::ImportDirectiveContext* _ctx)
+{
+ m_out << regex_replace(
+ _ctx->getText(),
+ regex("as|from|*|"),
+ " $& "
+ );
+ return antlrcpp::Any();
+}
+
+template
+void SolidityMutator::interfaceContractLibraryVisitor(T* _ctx)
+{
+ assert(
+ antlrcpp::is(_ctx) ||
+ antlrcpp::is(_ctx) ||
+ antlrcpp::is(_ctx)
+ );
+
+ string baseNames{};
+ bool atLeastOneBaseContract = m_numContracts > 0;
+ bool atLeastOneBaseInterface = m_numInterfaces > 0;
+ if (atLeastOneBaseContract || atLeastOneBaseInterface)
+ baseNames = generator(
+ [&]() {
+ if (contractScope())
+ {
+ if (atLeastOneBaseContract && coinToss())
+ return "C" + to_string(m_rand() % m_numContracts);
+ else if (atLeastOneBaseInterface && coinToss())
+ return "I" + to_string(m_rand() % m_numInterfaces);
+ else
+ return string{};
+ }
+ else if (interfaceScope() && atLeastOneBaseInterface)
+ return "I" + to_string(m_rand() % m_numInterfaces);
+ else
+ return string{};
+ },
+ randOneToN(3),
+ ", ",
+ true
+ );
+
+ string name{};
+ if (contractScope())
+ name = "C" + to_string(m_numContracts++);
+ else if (interfaceScope())
+ name = "I" + to_string(m_numInterfaces++);
+ else
+ name = "L" + to_string(m_numLibraries++);
+
+ Whiskers contractDef(R"(abstract contractinterfacelibrary is {)");
+
+ m_out << contractDef
+ ("abs", absContractScope())
+ ("c", contractScope())
+ ("i", interfaceScope())
+ ("l", libraryScope())
+ ("id", name)
+ ("inh", !libraryScope() && atLeastOneBaseContract && !baseNames.empty())
+ ("iid", baseNames)
+ .render();
+ constructorDefined = false;
+ if (coinToss())
+ visitChildren(_ctx);
+ else
+ {
+ switch (randOneToN(10))
+ {
+ case 1:
+ if ((contractScope() || absContractScope()) && !constructorDefined)
+ m_out << "constructor() external {}";
+ break;
+ case 2:
+ if (!interfaceScope())
+ m_out << "function f() public {}";
+ else
+ m_out << "function f() external;";
+ break;
+ case 3:
+ genModifier();
+ break;
+ case 4:
+ if (contractScope() || absContractScope())
+ m_out << "fallback() external {}";
+ else if (interfaceScope())
+ m_out << "fallback() external;";
+ break;
+ case 5:
+ if (contractScope() || absContractScope())
+ m_out << "receive() external payable {}";
+ else if (interfaceScope())
+ m_out << "receive() external payable;";
+ break;
+ case 6:
+ m_out << "struct s { uint x; }";
+ break;
+ case 7:
+ m_out << "enum e { first }";
+ break;
+ case 8:
+ if (contractScope() || absContractScope())
+ m_out << "uint x;";
+ else if (libraryScope())
+ m_out << "uint constant x = 1337;";
+ break;
+ case 9:
+ m_out << "event e() anonymous;";
+ break;
+ case 10:
+ if (!interfaceScope())
+ m_out << "using L for *;";
+ break;
+ }
+ }
+
+ m_out << "}\n";
+}
+
+antlrcpp::Any SolidityMutator::visitContractDefinition(SolP::ContractDefinitionContext* _ctx)
+{
+ unsigned numGlobalFuncs = m_numFunctions;
+ m_numFunctions = 0;
+ ScopeGuard s( [&]() { m_type = Type::GLOBAL; m_numFunctions = numGlobalFuncs; });
+ m_type = _ctx->Abstract() ? Type::ABSCONTRACT : Type::CONTRACT;
+ interfaceContractLibraryVisitor(_ctx);
+ if (coinToss())
+ genContract();
+ return antlrcpp::Any();
+}
+
+antlrcpp::Any SolidityMutator::visitInterfaceDefinition(SolP::InterfaceDefinitionContext* _ctx)
+{
+ unsigned numGlobalFuncs = m_numFunctions;
+ m_numFunctions = 0;
+ ScopeGuard s( [&]() { m_type = Type::GLOBAL; m_numFunctions = numGlobalFuncs; });
+ m_type = Type::INTERFACE;
+ interfaceContractLibraryVisitor(_ctx);
+ return antlrcpp::Any();
+}
+
+antlrcpp::Any SolidityMutator::visitLibraryDefinition(SolP::LibraryDefinitionContext* _ctx)
+{
+ unsigned numGlobalFuncs = m_numFunctions;
+ m_numFunctions = 0;
+ ScopeGuard s( [&]() { m_type = Type::GLOBAL; m_numFunctions = numGlobalFuncs; });
+ m_type = Type::LIBRARY;
+ interfaceContractLibraryVisitor(_ctx);
+ return antlrcpp::Any();
+}
+
+template
+void SolidityMutator::visitItemOrGenerate(T _item, function _generator)
+{
+ if (_item)
+ _item->accept(this);
+ else
+ _generator();
+}
+
+antlrcpp::Any SolidityMutator::visitContractBodyElement(SolP::ContractBodyElementContext* _ctx)
+{
+ return genericVisitor(_ctx, "contractbodyelement");
+}
+
+antlrcpp::Any SolidityMutator::visitTypeName(SolP::TypeNameContext*)
+{
+ m_out << typeName();
+ return antlrcpp::Any();
+}
+
+antlrcpp::Any SolidityMutator::visitParameterList(SolP::ParameterListContext* _ctx)
+{
+ vector paramDecls{};
+ for (auto &i: _ctx->parameterDeclaration())
+ paramDecls.push_back(i->accept(this).as());
+ generate_n(
+ back_inserter(paramDecls),
+ randOneToN(3),
+ [&]() {
+ return Whiskers(R"( )")
+ ("typeName", typeName())
+ ("dataLoc", dataLocation())
+ ("id", genRandomId("p", 10))
+ .render();
+ }
+ );
+ return boost::algorithm::join(paramDecls, ", ");
+}
+
+antlrcpp::Any SolidityMutator::visitParameterDeclaration(SolP::ParameterDeclarationContext* _ctx)
+{
+ Whiskers paramDecl(R"( )");
+ return paramDecl("typeName", typeName())("dataLoc", dataLocation())("id", genRandomId("p", 10)).render();
+}
+
+antlrcpp::Any SolidityMutator::visitConstructorDefinition(SolP::ConstructorDefinitionContext* _ctx)
+{
+ // Skip over redundant constructor defs
+ if (!constructorDefined)
+ {
+ Whiskers constructorDef(R"(constructor () )");
+ string args = _ctx->arguments ? _ctx->arguments->accept(this).as() : "";
+ string visibility = coinToss() ? "public" : "internal";
+ string payable = coinToss() ? "payable" : "";
+ m_out << constructorDef("args", args)("payable", payable)("visibility", visibility).render();
+ constructorDefined = true;
+ if (_ctx->body)
+ _ctx->body->accept(this);
+ else
+ m_out << "{}";
+ }
+ return antlrcpp::Any();
+}
+
+antlrcpp::Any SolidityMutator::visitEventParameter(SolP::EventParameterContext* _ctx)
+{
+ return eventParam();
+}
+
+antlrcpp::Any SolidityMutator::visitEventDefinition(SolP::EventDefinitionContext* _ctx)
+{
+ Whiskers eventDef(R"(event () ;))");
+ eventDef("id", genRandomId("ev"));
+
+ vector params{};
+ for (auto &i: _ctx->parameters)
+ params.push_back(i->accept(this).as());
+ eventDef("params", boost::algorithm::join(params, ", "));
+ eventDef("anonymous", coinToss() ? "anonymous" : "");
+
+ m_out << eventDef.render();
+ vector addEvents;
+ generate_n(
+ back_inserter(addEvents),
+ randOneToN(3),
+ [&]() {
+ Whiskers t = Whiskers(R"(event () ;)");
+ vector params{};
+ generate_n(
+ back_inserter(params),
+ randOneToN(3),
+ [&]() { return eventParam(); }
+ );
+ return t
+ ("id", genRandomId("p", 10))
+ ("params", boost::algorithm::join(params, ", "))
+ ("anon", coinToss() ? "anonymous" : "")
+ .render();
+ }
+ );
+ m_out << boost::algorithm::join(addEvents, "");
+ return antlrcpp::Any();
+}
+
+string SolidityMutator::genFunctionName()
+{
+ if (likely())
+ return "f" + to_string(m_numFunctions++);
+ else
+ {
+ if (coinToss())
+ return "fallback";
+ else
+ return "receive";
+ }
+}
+
+void SolidityMutator::genModifier()
+{
+ Whiskers modDef(R"(modifier () )");
+ modDef("id", "m" + to_string(m_numModifiers++));
+ modDef("params", "");
+ modDef("virtual", coinToss() ? "virtual" : "");
+ modDef("override", coinToss() ? "override": "");
+ modDef("SemicolonOrBody", coinToss() ? ";" : "{ _; }");
+ m_out << modDef.render();
+}
+
+antlrcpp::Any SolidityMutator::visitFunctionDefinition(SolP::FunctionDefinitionContext* _ctx)
+{
+ // Free functions cannot
+ // - have visibility
+ // - are not payable
+ // - are not virtual
+ // - cannot be overridden
+ string vis{};
+ if (interfaceScope())
+ vis = "external";
+ else if (contractScope() || libraryScope())
+ vis = visibility();
+ string mut = mutability();
+ if ((vis == "internal" || vis == "private") && mut == "payable")
+ vis = "public";
+ string id = genFunctionName();
+
+ // Must implement
+ bool mustImplement = contractScope() || libraryScope() || globalScope();
+ // May implement
+ bool mayImplement = absContractScope() && coinToss();
+
+ Whiskers functionDef(
+ R"(function () virtual override )"
+ );
+ bool mayVirtualise = (contractScope() || absContractScope()) && (vis == "public" || vis == "external");
+ bool mayOverride = contractScope() || absContractScope() || interfaceScope();
+ m_out << functionDef
+ ("id", id)
+ ("args", _ctx->arguments ? _ctx->arguments->accept(this).as() : "")
+ ("vis", vis)
+ ("mut", mut)
+ ("virt", mayVirtualise && coinToss())
+ ("oride", mayOverride && coinToss())
+ ("mod", "")
+ .render();
+ if (_ctx->body && (mustImplement || mayImplement))
+ _ctx->body->accept(this);
+ else
+ {
+ if (mustImplement || mayImplement)
+ m_out << "{}";
+ else
+ m_out << ";";
+ }
+
+ return antlrcpp::Any();
+}
+
+antlrcpp::Any SolidityMutator::visitBlock(SolP::BlockContext* _ctx)
+{
+ m_out << "{}";
return antlrcpp::Any();
}
\ No newline at end of file
diff --git a/test/tools/ossfuzz/SolidityMutator.h b/test/tools/ossfuzz/SolidityMutator.h
index 87cdaa58f..e09492449 100644
--- a/test/tools/ossfuzz/SolidityMutator.h
+++ b/test/tools/ossfuzz/SolidityMutator.h
@@ -31,9 +31,14 @@
#include
namespace solidity::test::fuzzer {
+
using RandomEngine = std::mt19937_64;
using Dist = std::uniform_int_distribution;
+struct Generator {
+ std::string operator()(std::string&& _template, std::map&& _args);
+};
+
class SolidityMutator: public SolidityBaseVisitor
{
public:
@@ -45,12 +50,201 @@ public:
antlrcpp::Any visitSourceUnit(SolidityParser::SourceUnitContext* _ctx) override;
antlrcpp::Any visitPragmaDirective(SolidityParser::PragmaDirectiveContext* _ctx) override;
+
+ antlrcpp::Any visitImportDirective(SolidityParser::ImportDirectiveContext* _ctx) override;
+ antlrcpp::Any visitContractDefinition(SolidityParser::ContractDefinitionContext* _ctx) override;
+ antlrcpp::Any visitContractBodyElement(SolidityParser::ContractBodyElementContext* _ctx) override;
+ antlrcpp::Any visitConstructorDefinition(SolidityParser::ConstructorDefinitionContext* _ctx) override;
+ antlrcpp::Any visitParameterList(SolidityParser::ParameterListContext* _ctx) override;
+ antlrcpp::Any visitParameterDeclaration(SolidityParser::ParameterDeclarationContext* _ctx) override;
+ antlrcpp::Any visitBlock(SolidityParser::BlockContext* _ctx) override;
+ antlrcpp::Any visitFunctionDefinition(SolidityParser::FunctionDefinitionContext* _ctx) override;
+ antlrcpp::Any visitEnumDefinition(SolidityParser::EnumDefinitionContext* _ctx) override;
+ antlrcpp::Any visitStructDefinition(SolidityParser::StructDefinitionContext* _ctx) override;
+ antlrcpp::Any visitEventDefinition(SolidityParser::EventDefinitionContext* _ctx) override;
+ antlrcpp::Any visitEventParameter(SolidityParser::EventParameterContext* _ctx) override;
+ antlrcpp::Any visitInterfaceDefinition(SolidityParser::InterfaceDefinitionContext* _ctx) override;
+ antlrcpp::Any visitLibraryDefinition(SolidityParser::LibraryDefinitionContext* _ctx) override;
+ antlrcpp::Any visitModifierDefinition(SolidityParser::ModifierDefinitionContext* _ctx) override;
+ /// Types
+ antlrcpp::Any visitTypeName(SolidityParser::TypeNameContext* _ctx) override;
+
private:
+ enum class Type
+ {
+ CONTRACT,
+ ABSCONTRACT,
+ INTERFACE,
+ LIBRARY,
+ GLOBAL
+ };
+
+ bool globalScope()
+ {
+ return m_type == Type::GLOBAL;
+ }
+
+ bool absContractScope()
+ {
+ return m_type == Type::ABSCONTRACT;
+ }
+
+ bool contractScope()
+ {
+ return m_type == Type::CONTRACT;
+ }
+
+ bool libraryScope()
+ {
+ return m_type == Type::LIBRARY;
+ }
+
+ bool interfaceScope()
+ {
+ return m_type == Type::INTERFACE;
+ }
+
+ template
+ antlrcpp::Any genericVisitor(C* _ctx, std::string _ctxName);
+
+ template
+ void interfaceContractLibraryVisitor(T* _ctx);
+
+ template
+ void visitItemsOrGenerate(T _container, std::function _generator);
+
+ template
+ void visitItemOrGenerate(T _item, std::function _generator);
+
+ bool likely()
+ {
+ return m_rand() % s_largePrime != 0;
+ }
+
/// @returns either true or false with roughly the same probability
bool coinToss()
{
return m_rand() % 2 == 0;
}
+
+ /// @returns a pseudo randomly chosen unsigned number between one
+ /// and @param _n
+ unsigned randOneToN(unsigned _n = 20)
+ {
+ return m_rand() % _n + 1;
+ }
+
+ /// @returns a pseudo randomly generated path string of
+ /// the form `.sol` where `` is a single
+ /// character in the ascii range [A-Z].
+ std::string genRandomPath()
+ {
+ return "\"" + genRandString(1, 'A', 'Z') + ".sol" + "\"";
+ }
+
+ /// @returns a pseudo randomly generated identifier that
+ /// comprises a single character in the ascii range [a-z].
+ std::string genRandomId(std::string _prefix, unsigned _n = 3)
+ {
+ return _prefix + std::to_string(m_rand() % _n);
+ }
+
+ template
+ void genRule(T* _ctx);
+
+ template
+ void mutateRule(T* _ctx);
+
+ void genContract();
+
+ void genModifier();
+
+ std::string generator(std::function _gen, unsigned _n, std::string _separator, bool _removeDups);
+
+ std::string genImport();
+
+ std::string genFunctionName();
+
+ /// @returns bit width as a string where bit width is a multiple
+ /// of 8 and in the range [8, 256].
+ std::string bitWidth()
+ {
+ return std::to_string(randOneToN(32) * 8);
+ }
+
+ /// @returns byte width as a string where byte width is in
+ /// the range [1, 32].
+ std::string byteWidth()
+ {
+ return std::to_string(randOneToN(32));
+ }
+
+ /// @returns a Solidity event parameter as string
+ std::string eventParam();
+
+ /// @returns a pseudo randomly chosen elementary Solidity type name
+ /// as a string including address payable type if @param _allowAddressPayable
+ /// is true (default), excluding otherwise.
+ std::string elementaryType(bool _allowAddressPayable = true);
+
+ /// @returns a pseudo randomly chosen Solidity type name as a string.
+ std::string typeName();
+
+ /// @returns a pseudo randomly chosen Solidity data location
+ std::string dataLocation();
+
+ /// @returns a pseudo randomly chosen Solidity function visibility.
+ std::string visibility();
+
+ /// @returns a pseudo randomly chosen Solidity function state mutability.
+ std::string mutability();
+
+ /// @returns a pseudo randomly generated user defined
+ /// type name that comprises at most @param _numIds identifiers separated
+ /// by a period ('.').
+ /// Each identifier is at most @param _len characters long each
+ /// character being choosen uniformly at random in the character
+ /// range between [@param _start, @param _end]
+ std::string genRandomUserDefinedTypeName(
+ unsigned _numIds = 2,
+ unsigned _len = 1,
+ char _start = 'a',
+ char _end = 'z'
+ );
+
+
+ /// @returns a pseudo randomly generated string of length at most
+ /// @param _maxLen characters where each character in the string
+ /// is in the ASCII character range between @param _start and
+ /// @param _end characters.
+ std::string genRandString(unsigned _maxLen, char _start = '!', char _end = '~');
+
+ /// Flag that indicates if contract constructor has been defined
+ bool constructorDefined = false;
+
+ unsigned m_numStructs = 0;
+ unsigned m_numFunctions = 0;
+ unsigned m_numLibraries = 0;
+ unsigned m_numContracts = 0;
+ unsigned m_numInterfaces = 0;
+ unsigned m_numEnums = 0;
+ unsigned m_numModifiers = 0;
+ Type m_type = Type::GLOBAL;
+
+ static constexpr unsigned s_maxPragmas = 2;
+ static constexpr unsigned s_maxImports = 2;
+
+ static constexpr unsigned s_largePrime = 1009;
+ void gen(SolidityParser::PragmaDirectiveContext*);
+ std::string mutatePragma();
+ template
+ void fuzzRule(T* _ctx);
+ void mutate(SolidityParser::PragmaDirectiveContext*);
+ template
+ auto mayRun(F&& _func, Args&&... _args, unsigned int _n = 101);
+ template
+ void visitItemsOrGenerate(T _container, F&& _generator, Args&&... _args);
+
/// @returns a pseudo randomly chosen unsigned integer between one
/// and @param _n
uint32_t randomOneToN(uint32_t _n)
@@ -64,5 +258,6 @@ private:
RandomEngine m_rand;
/// Whisker template strings for Solidity syntax
const std::string s_pragmaDirective = R"(pragma ;)";
+ void gen();
};
}
\ No newline at end of file