Manual rebase

This commit is contained in:
Bhargava Shastry 2020-09-10 16:58:39 +02:00
parent af14b8d425
commit 5b6a4dc8c4
4 changed files with 1070 additions and 22 deletions

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <test/tools/ossfuzz/SolCustomMutator.h>
#include <antlr4-runtime.h>
#include <cstring>
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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <test/tools/ossfuzz/SolidityLexer.h>
#include <test/tools/ossfuzz/SolidityMutator.h>
#include <test/tools/ossfuzz/SolidityParser.h>
#include <cstddef>
#include <cstdint>
#include <ostream>
#include <random>
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;
};
}

View File

@ -16,44 +16,766 @@
*/ */
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
#include <libsolutil/Common.h>
#include <test/tools/ossfuzz/SolidityMutator.h> #include <test/tools/ossfuzz/SolidityMutator.h>
#include <boost/algorithm/string/join.hpp>
#include <algorithm> #include <algorithm>
#include <regex>
using namespace std; using namespace std;
using namespace solidity::util; using namespace solidity::util;
using namespace solidity::test::fuzzer; 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<std::string, std::string>&& _args)
{ {
if (_ctx->pragmaDirective().empty()) Whiskers w(move(_template));
m_out << Whiskers(s_pragmaDirective)("directive", "experimental SMTChecker").render(); for (auto &&item: _args)
w(move(item.first), move(item.second));
return w.render();
}
template <typename T>
void SolidityMutator::fuzzRule(T* _ctx)
{
mutate(_ctx);
// if (coinToss())
// gen();
}
void SolidityMutator::gen()
{
static const vector<string> validPragmas = {
"experimental SMTChecker",
"experimental ABIEncoderV2"
};
m_out << generator(
[&]() {
return Whiskers(R"(pragma <string>;)")
("string", validPragmas[m_rand() % validPragmas.size()])
.render();
},
randOneToN(s_maxPragmas),
"\n",
true
);
}
void SolidityMutator::mutate(SolP::PragmaDirectiveContext*)
{
m_out << Whiskers(R"(pragma <mutation>;)")
("mutation", mutatePragma())
.render();
}
string SolidityMutator::eventParam()
{
return Whiskers(R"(<typename> <indexed> <id>)")
("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 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> => <typeName>))")
("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 <class C>
antlrcpp::Any SolidityMutator::genericVisitor(C* _ctx, std::string _ctxName)
{
visitChildren(_ctx); visitChildren(_ctx);
return antlrcpp::Any(); return antlrcpp::Any();
} }
antlrcpp::Any SolidityMutator::visitPragmaDirective(SolidityParser::PragmaDirectiveContext*) string SolidityMutator::genRandomUserDefinedTypeName(unsigned _numIds, unsigned _len, char _start, char _end)
{ {
auto PickLiteral = [&]() -> string uniform_int_distribution<char> dist(_start, _end);
{ vector<string> ids{};
static const vector<string> alphanum = { generate_n(back_inserter(ids), m_rand() % _numIds + 1, [&](){ return genRandString(_len, _start, _end); });
"0", return boost::algorithm::join(ids, ".");
"experimental", }
"solidity < 142857",
"solidity >=0.0.0",
"solidity >=0.0.0 <0.8.0",
"experimental __test",
"experimental SMTChecker",
"experimental ABIEncoderV2",
"experimental \"xyz\""
};
return alphanum[randomOneToN(alphanum.size()) - 1]; string SolidityMutator::genRandString(unsigned int _maxLen, char _start, char _end)
}; {
uniform_int_distribution<char> dist(_start, _end);
string result{};
generate_n(back_inserter(result), m_rand() % _maxLen + 1, [&]{ return dist(m_rand); });
return result;
}
template <typename T, typename F, typename... Args>
void SolidityMutator::visitItemsOrGenerate(T _container, F&& _generator, Args&&... _args)
{
if (_container.size() == 0)
mayRun(forward<F>(_generator), forward<Args>(_args)...);
else
for (auto item: _container)
item->accept(this);
}
template <typename F, typename... Args>
auto SolidityMutator::mayRun(F&& _func, Args&&... _args, unsigned _n)
{
if (m_rand() % _n == 0)
return invoke(forward<F>(_func), forward<Args>(_args)...);
else
return result_of<F>::type();
}
antlrcpp::Any SolidityMutator::visitSourceUnit(SolP::SourceUnitContext* _ctx)
{
// for (auto &item: _ctx->getRuleContexts<SolP::SourceUnitContext>())
// 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 <id> { function f0() external; })")
// ("id", "I" + to_string(m_numInterfaces++))
// .render();
// }
// );
// visitItemsOrGenerate(
// _ctx->libraryDefinition(),
// [&]() {
// m_out << Whiskers(R"(library <id> { 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 }";
// }
// );
// }
// );
string pragma = PickLiteral();
m_out << Whiskers(s_pragmaDirective)("directive", pragma).render(); return antlrcpp::Any();
}
//void SolidityMutator::gen(SolP::ModifierDefinitionContext*)
//{
// constexpr string s = R"(modifier <id> (<params>) <virtual> <override> <?noDef>;</noDef>)";
// Whiskers mod(R"(modifier <id> (<params>) <virtual> <override> <?noDef>;</noDef>)");
//}
//
//void SolidityMutator::mutate(SolP::ModifierDefinitionContext* _ctx)
//{
// for (auto &rule: _ctx->getRuleContexts<SolP::ModifierDefinitionContext>())
// std::cout << rule->getText() << std::endl;
//}
antlrcpp::Any SolidityMutator::visitModifierDefinition(SolidityParser::ModifierDefinitionContext* _ctx)
{
Whiskers mod(R"(modifier <id> (<params>) <virtual> <override> <?noDef>;</noDef>)");
bool noDef = coinToss();
m_out << mod
("id", genRandomId("m", 2))
("params", _ctx->arguments ? _ctx->arguments->accept(this).as<string>() : "")
("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 <id> { <members> })");
unsigned numMembers = 0;
auto fieldGenerator = [&]() {
return Whiskers(R"(<typeName> <id>;)")
("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> {<elements>})")
("id", "en" + to_string(m_numEnums++))
("elements", elements)
.render();
return antlrcpp::Any();
}
string SolidityMutator::generator(function<string()> _gen, unsigned _n, string _separator, bool _removeDups)
{
vector<string> 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><lSign> <minV></l><?u> <uSign> <maxV></u>)")
("l", coinToss())
("u", coinToss())
("lSign", lSign)
("minV", minVersion)
("uSign", uSign)
("maxV", maxVersion)
.render();
}
void SolidityMutator::genContract()
{
m_out << Whiskers(R"(contract <id> { 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 <path> as <id>;)");
Whiskers importStarAsIdFromPath(R"(import * as <id> from <path>;)");
Whiskers importPath(R"(import <path>;)");
switch (randOneToN(4))
{
case 1:
return importPathAsId("path", path)("id", id).render();
case 2:
return importStarAsIdFromPath("id", id)("path", path).render();
case 3:
{
vector<string> symbols{};
unsigned numElements = randOneToN(10);
generate_n(
back_inserter(symbols),
numElements,
[&](){
return Whiskers((R"(<?as><path> as <id><!as><symbol></as>)"))
("as", coinToss())
("path", genRandomPath())
("id", genRandomId("Im"))
("symbol", genRandString(5, 'A', 'z'))
.render();
}
);
return Whiskers(R"(import {<syms>} from <path>;)")
("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<typename T>
void SolidityMutator::interfaceContractLibraryVisitor(T* _ctx)
{
assert(
antlrcpp::is<SolP::ContractDefinitionContext*>(_ctx) ||
antlrcpp::is<SolP::InterfaceDefinitionContext*>(_ctx) ||
antlrcpp::is<SolP::LibraryDefinitionContext*>(_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"(<?abs>abstract </abs><?c>contract</c><?i>interface</i><?l>library</l> <id><?inh> is <iid></inh> {)");
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 <typename T>
void SolidityMutator::visitItemOrGenerate(T _item, function<void()> _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<string> paramDecls{};
for (auto &i: _ctx->parameterDeclaration())
paramDecls.push_back(i->accept(this).as<string>());
generate_n(
back_inserter(paramDecls),
randOneToN(3),
[&]() {
return Whiskers(R"(<typeName> <dataLoc> <id>)")
("typeName", typeName())
("dataLoc", dataLocation())
("id", genRandomId("p", 10))
.render();
}
);
return boost::algorithm::join(paramDecls, ", ");
}
antlrcpp::Any SolidityMutator::visitParameterDeclaration(SolP::ParameterDeclarationContext* _ctx)
{
Whiskers paramDecl(R"(<typeName> <dataLoc> <id>)");
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 (<args>) <payable> <visibility>)");
string args = _ctx->arguments ? _ctx->arguments->accept(this).as<string>() : "";
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 <id>(<params>) <anonymous>;))");
eventDef("id", genRandomId("ev"));
vector<string> params{};
for (auto &i: _ctx->parameters)
params.push_back(i->accept(this).as<string>());
eventDef("params", boost::algorithm::join(params, ", "));
eventDef("anonymous", coinToss() ? "anonymous" : "");
m_out << eventDef.render();
vector<string> addEvents;
generate_n(
back_inserter(addEvents),
randOneToN(3),
[&]() {
Whiskers t = Whiskers(R"(event <id>(<params>) <anon>;)");
vector<string> 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 <id> (<params>) <virtual> <override> <SemicolonOrBody>)");
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 <id>(<args>) <vis> <mut> <?virt>virtual</virt> <?oride>override</oride> <mod>)"
);
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<string>() : "")
("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(); return antlrcpp::Any();
} }

View File

@ -31,9 +31,14 @@
#include <random> #include <random>
namespace solidity::test::fuzzer { namespace solidity::test::fuzzer {
using RandomEngine = std::mt19937_64; using RandomEngine = std::mt19937_64;
using Dist = std::uniform_int_distribution<uint32_t>; using Dist = std::uniform_int_distribution<uint32_t>;
struct Generator {
std::string operator()(std::string&& _template, std::map<std::string, std::string>&& _args);
};
class SolidityMutator: public SolidityBaseVisitor class SolidityMutator: public SolidityBaseVisitor
{ {
public: public:
@ -45,12 +50,201 @@ public:
antlrcpp::Any visitSourceUnit(SolidityParser::SourceUnitContext* _ctx) override; antlrcpp::Any visitSourceUnit(SolidityParser::SourceUnitContext* _ctx) override;
antlrcpp::Any visitPragmaDirective(SolidityParser::PragmaDirectiveContext* _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: 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 <class C>
antlrcpp::Any genericVisitor(C* _ctx, std::string _ctxName);
template <typename T>
void interfaceContractLibraryVisitor(T* _ctx);
template <typename T>
void visitItemsOrGenerate(T _container, std::function<void()> _generator);
template <typename T>
void visitItemOrGenerate(T _item, std::function<void()> _generator);
bool likely()
{
return m_rand() % s_largePrime != 0;
}
/// @returns either true or false with roughly the same probability /// @returns either true or false with roughly the same probability
bool coinToss() bool coinToss()
{ {
return m_rand() % 2 == 0; 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 `<name>.sol` where `<name>` 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 <typename T>
void genRule(T* _ctx);
template <typename T>
void mutateRule(T* _ctx);
void genContract();
void genModifier();
std::string generator(std::function<std::string()> _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<typename T>
void fuzzRule(T* _ctx);
void mutate(SolidityParser::PragmaDirectiveContext*);
template<typename F, typename... Args>
auto mayRun(F&& _func, Args&&... _args, unsigned int _n = 101);
template<typename T, typename F, typename... Args>
void visitItemsOrGenerate(T _container, F&& _generator, Args&&... _args);
/// @returns a pseudo randomly chosen unsigned integer between one /// @returns a pseudo randomly chosen unsigned integer between one
/// and @param _n /// and @param _n
uint32_t randomOneToN(uint32_t _n) uint32_t randomOneToN(uint32_t _n)
@ -64,5 +258,6 @@ private:
RandomEngine m_rand; RandomEngine m_rand;
/// Whisker template strings for Solidity syntax /// Whisker template strings for Solidity syntax
const std::string s_pragmaDirective = R"(pragma <directive>;)"; const std::string s_pragmaDirective = R"(pragma <directive>;)";
void gen();
}; };
} }