/* 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 #include #include #include #include #include using namespace std; using namespace solidity::util; using namespace solidity::test::fuzzer; using SolP = solidity::test::fuzzer::SolidityParser; string Generator::operator()(std::string&& _template, std::map&& _args) { 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(); } string SolidityMutator::genRandomUserDefinedTypeName(unsigned _numIds, unsigned _len, char _start, char _end) { 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, "."); } 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(); } 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(); } 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(); }