solidity/test/tools/ossfuzz/protoToSol.cpp
2020-04-16 01:02:48 +02:00

746 lines
21 KiB
C++

/*
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/protoToSol.h>
#include <test/tools/ossfuzz/SolProtoAdaptor.h>
#include <liblangutil/Exceptions.h>
#include <libsolutil/Whiskers.h>
#include <sstream>
using namespace solidity::test::solprotofuzzer;
using namespace solidity::test::solprotofuzzer::adaptor;
using namespace std;
using namespace solidity::util;
string ProtoConverter::protoToSolidity(Program const& _p)
{
m_randomGen = make_shared<SolRandomNumGenerator>(_p.seed());
return visit(_p);
}
string ProtoConverter::visit(TestContract const& _testContract)
{
string testCode;
string usingLibDecl;
m_libraryTest = false;
switch (_testContract.type())
{
case TestContract::LIBRARY:
{
if (emptyLibraryTests())
{
testCode = Whiskers(R"(
return 0;)")
.render();
}
else
{
m_libraryTest = true;
auto testTuple = pseudoRandomLibraryTest();
m_libraryName = get<0>(testTuple);
usingLibDecl = Whiskers(R"(
using <libraryName> for uint;)")
("libraryName", get<0>(testTuple))
.render();
testCode = Whiskers(R"(
uint x;
if (x.<testFunction>() != <expectedOutput>)
return 1;
return 0;)")
("testFunction", get<1>(testTuple))
("expectedOutput", get<2>(testTuple))
.render();
}
break;
}
case TestContract::CONTRACT:
if (emptyContractTests())
testCode = Whiskers(R"(
return 0;)")
.render();
#if 0
else
{
auto testTuple = pseudoRandomContractTest();
testCode = Whiskers(R"(
<contractName> testContract = new <contractName>();
if (testContract.<testFunction>() != <expectedOutput>)
return 1;
return 0;)")
("contractName", get<0>(testTuple))
("testFunction", get<1>(testTuple))
("expectedOutput", get<2>(testTuple))
.render();
}
#endif
break;
}
return Whiskers(R"(
contract C {<?isLibrary><usingDecl></isLibrary>
function test() public returns (uint)
{<testCode>
}
}
)")
("isLibrary", m_libraryTest)
("usingDecl", usingLibDecl)
("testCode", testCode)
.render();
}
bool ProtoConverter::libraryTest() const
{
return m_libraryTest;
}
string ProtoConverter::libraryName() const
{
return m_libraryName;
}
string ProtoConverter::visit(Program const& _p)
{
ostringstream program;
ostringstream contracts;
for (auto &contract: _p.contracts())
contracts << visit(contract);
program << Whiskers(R"(
pragma solidity >=0.0;
<contracts>
<testContract>
)")
("contracts", contracts.str())
("testContract", visit(_p.test()))
.render();
return program.str();
}
string ProtoConverter::visit(ContractType const& _contractType)
{
m_mostDerivedAbstractContract = false;
switch (_contractType.contract_type_oneof_case())
{
case ContractType::kC:
m_mostDerivedAbstractContract = _contractType.c().abstract();
m_mostDerivedProgram = MostDerivedProgram::CONTRACT;
// return visit(_contractType.c());
return "";
case ContractType::kL:
m_mostDerivedProgram = MostDerivedProgram::LIBRARY;
return visit(_contractType.l());
case ContractType::kI:
m_mostDerivedProgram = MostDerivedProgram::INTERFACE;
return visit(_contractType.i());
case ContractType::CONTRACT_TYPE_ONEOF_NOT_SET:
return "";
}
}
string ProtoConverter::visit(ContractOrInterface const& _contractOrInterface)
{
switch (_contractOrInterface.contract_or_interface_oneof_case())
{
case ContractOrInterface::kC:
// return visit(_contractOrInterface.c());
return "";
case ContractOrInterface::kI:
// return visit(_contractOrInterface.i());
return "";
case ContractOrInterface::CONTRACT_OR_INTERFACE_ONEOF_NOT_SET:
return "";
}
}
bool ProtoConverter::contractFunctionImplemented(
Contract const* _contract,
CIFunc _function
)
{
auto v = m_contractFunctionMap[_contract];
for (auto &e: v)
if (get<0>(e) == _function)
return get<1>(e);
return false;
}
bool ProtoConverter::contractFunctionVirtual(
Contract const* _contract,
CIFunc _function
)
{
auto v = m_contractFunctionMap[_contract];
for (auto &e: v)
if (get<0>(e) == _function)
return get<2>(e);
return false;
}
#if 0
string ProtoConverter::mostDerivedInterfaceOverrides(Interface const& _interface)
{
ostringstream funcs;
for (auto base = _interface.bases().rbegin(); base != _interface.bases().rend(); base++)
{
for (auto& f: base->funcdef())
{
// An interface may override base interface's function
bool override = pseudoRandomCoinFlip();
if (!override)
continue;
funcs << overrideFunction(&f, false, false);
}
funcs << mostDerivedInterfaceOverrides(*base);
}
return funcs.str();
}
string ProtoConverter::mostDerivedContractOverrides(Interface const& _interface)
{
ostringstream funcs;
for (auto base = _interface.bases().rbegin(); base != _interface.bases().rend(); base++)
{
for (auto& f: base->funcdef())
{
bool override = pseudoRandomCoinFlip() || mostDerivedProgramAbstractContract();
/// We can arrive here from contract through other contracts and interfaces.
/// We define most derived contract (MDC) as the most derived contract in
/// the inheritence chain to which the visited interface belongs to.
/// When MDC is not abstract we must implement (with override) all
/// interface functions that MDC inherits unless it has been implemented
/// by some other contract in the inheritence chain that MDC also derives
/// from.
/// In other words, if an interface function has never been implemented
/// thus far in the inheritence chain, it must be implemented.
/// If it has already been implemented, it may be reimplemented provided
/// it is virtual.
/// When reimplementing, it may be revirtualized.
/// When MDC is abstract, we may or may not redeclare interface function.
/// If interface function has been implemented in the inheritence chain,
/// and we redeclare it, we must reimplement it.
/// If inheritence function has not been implemented and we redeclare it,
/// we may implement it.
/// If we implement it, it may be marked virtual.
/// If we don't implement it, it must be marked virtual.
/// . We may virtualize.
/// We create a pair <interfaceFunction, virtualized> and add it to list of
/// implemented interface functions.
/// When IAC is abstract:
/// - we may redeclare
/// - if redeclared, we may implement
/// - if we do not implement redeclared function then:
/// - we must override and virtualize
/// - add to list of unimplemented interface functions
/// - if we implement then:
/// - we must override
/// - we may virtualize
/// - create a pair <interfaceFunction, virtualized> and add it to list of
/// implemented interface functions.
/// - ancestor contract is not abstract and this interface function
/// has been implemented by some other contract in the traversal path
/// that also virtualizes the function.
/// When we override, we may mark it as virtual.
/// We may override when ancestor contract is abstract
/// If ancestor contract is overriding, we may or may not implement it.
/// If we do not implement it, we must mark it virtual since otherwise
/// we are left with unimplementable function.
/// If we implement it, we may mark it as virtual.
string funcStr = visit(
f,
index++,
override,
programName(&*base)
);
if (override)
funcs << funcStr;
}
funcs << mostDerivedContractOverrides(*base);
}
return funcs.str();
}
pair<bool, bool> ProtoConverter::contractFunctionParams(
Contract const* _contract,
ContractFunction const* _function
)
{
// If contract is abstract, we may implement this function. If contract
// is not abstract, we must implement this function.
bool implement = !_contract->abstract() || pseudoRandomCoinFlip();
// We may mark a non-overridden function as virtual.
// We must mark an unimplemented abstract contract function as
// virtual.
bool virtualFunc = _function->virtualfunc() || (_contract->abstract() && !implement);
return pair(implement, virtualFunc);
}
pair<bool, bool> ProtoConverter::contractFunctionOverrideParams(
Contract const* _base,
Contract const* _derived,
CIFunc _f
)
{
bool baseAbstract = _base->abstract();
bool derivedAbstract = _derived->abstract();
bool implement = false;
bool revirtualize = false;
// There are four possibilities here:
// 1. both base and derived are abstract,
// 2. base abstract, derived not
// 3. base not abstract, derived is
// 4. both base and derived are not abstract
if (baseAbstract && derivedAbstract)
{
// Case 1: Both base and derived are abstract
// virtual base functions may be redeclared (with override)
// if redeclared virtual base function has been implemented, it must be reimplemented but
// may be revirtualized
// if revirtualized, there is nothing to be changed in list of <ContractFunction, bool>
// if not revirtualized, we changed the boolean to false in the list of implemented contract
// functions.
// if redeclared virtual base function has not been implemented, it may be implemented.
// if it is implemented (with override), it may be revirtualized.
// if it is not implemented, it must be marked virtual.
// if virtual base function not redeclared, there is no status change
bool virtualImplemented = contractFunctionImplemented(_base, _f);
implement = virtualImplemented || pseudoRandomCoinFlip();
revirtualize = (!implement && !virtualImplemented) || pseudoRandomCoinFlip();
}
else if (baseAbstract && !derivedAbstract)
{
// Case 2: Base abstract, derived not
// If base function appears in list of unimplemented virtual contract functions, we
// must implement it (with override). Remove from unimplemented virtual contract functions. We may
// revirtualize. Add to list of implemented contract functions.
// Unimplemented virtual functions must be implemented (with override)
// Implemented virtual functions may be implemented (with override)
//
}
else if (!baseAbstract && derivedAbstract)
{
// Case 3: Base not abstract, derived is
// All base functions are implemented. Base functions marked virtual may be overridden.
// If they are overridden they must be reimplemented. They may be revirtualized.
// Base functions that are not virtual may not be redeclared.
}
else
{
// Case 4: Neither base nor derived are abstract
// All base functions are implemented. Base functions marked virtual may be overridden.
// If they are overridden they must be reimplemented. They may be revirtualized.
// Base functions that are not virtual may not be redeclared.
}
return pair(implement, revirtualize);
}
string ProtoConverter::traverseOverrides(Contract const& _contract)
{
ostringstream funcs;
for (auto base = _contract.bases().rbegin(); base != _contract.bases().rend(); base++)
{
if (base->contract_or_interface_oneof_case() == ContractOrInterface::CONTRACT_OR_INTERFACE_ONEOF_NOT_SET)
continue;
if (base->has_c())
{
for (auto& f: base->c().funcdef())
{
// We may redeclare virtual functions in base contract
bool redeclareVirtual = f.virtualfunc() && pseudoRandomCoinFlip();
// If base function is not virtual or if we choose to not
// redeclare the virtual function, we skip to the next function
// after incrementing the function index.
if (!f.virtualfunc() || !redeclareVirtual)
continue;
// Check if overridden function may be implemented/revirtualized
// by the derived contract.
auto [implement, revirtualize] = contractFunctionOverrideParams(
&base->c(),
&_contract,
&f
);
funcs << overrideFunction(&f, revirtualize, implement);
// Update contract function map
m_contractFunctionMap[&_contract].push_back(CITuple(&f, implement, revirtualize));
}
// Override revirtualized functions.
// TODO: Function that returns all revirtualized functions and their implementation
// status.
for (auto &tuple: m_contractFunctionMap[&base->c()])
{
auto function = get<0>(tuple);
bool overrideRevirtualized = true;
if (holds_alternative<ContractFunction const*>(function))
{
auto contractFunction = get<ContractFunction const*>(function);
auto [implementRevirtualized, revirtualizeRevirtualized] = contractFunctionOverrideParams(
&base->c(),
&_contract,
get<0>(tuple)
);
funcs << visit(
*contractFunction,
index++,
overrideRevirtualized,
revirtualizeRevirtualized,
implementRevirtualized,
programName(&base->c())
);
}
}
// Traverse base
funcs << traverseOverrides(base->c());
}
else if (base->has_i())
{
for (auto& f: base->i().funcdef())
{
bool implement = pseudoRandomCoinFlip();
m_counter += base->i().funcdef_size();
bool override = pseudoRandomCoinFlip();
if (_contract.abstract() && !implement && !override)
continue;
funcs << overrideFunction(
&f,
!_contract.abstract() || implement,
pseudoRandomCoinFlip() || (override && _contract.abstract())
);
}
funcs << mostDerivedContractOverrides(base->i(), !_contract.abstract());
}
}
return funcs.str();
}
/// This function is called when root is interface.
tuple<string, string, string> ProtoConverter::visitMostDerivedInterface(Interface const& _interface)
{
ostringstream bases;
ostringstream baseNames;
ostringstream funcs;
string separator{};
for (auto &base: _interface.bases())
{
string baseStr = visit(base);
if (baseStr.empty())
continue;
bases << baseStr;
baseNames << separator
<< programName(&base);
if (separator.empty())
separator = ", ";
}
// First define overridden functions
bool overrides = _interface.bases_size() > 0 && !baseNames.str().empty();
if (overrides)
funcs << mostDerivedInterfaceOverrides(_interface);
unsigned index = 0;
// Define non-overridden functions
for (auto &f: _interface.funcdef())
funcs << registerAndVisitFunction(
&_interface,
&f,
index++,
false,
false,
false
);
return make_tuple(bases.str(), baseNames.str(), funcs.str());
}
void ProtoConverter::registerFunctionName(CIL _program, CILFunc _function, unsigned _index)
{
string pName = programName(_program);
string fName = createFunctionName(_program, _index);
solAssert(!m_functionNameMap.count(_function), "Sol proto fuzzer: Duplicate function registration");
m_functionNameMap.insert(pair(_function, fName));
}
string ProtoConverter::registerAndVisitFunction(
CIL _program,
CILFunc _function,
unsigned _index,
bool _override,
bool _virtual,
bool _implement
)
{
registerFunctionName(_program, _function, _index);
return visit(_function, _override, _virtual, _implement);
}
tuple<string, string, string> ProtoConverter::visitProgramHelper(CIL _program)
{
ostringstream bases;
ostringstream funcs;
ostringstream baseNames;
string pName = programName(_program);
string separator{};
if (holds_alternative<Contract const*>(_program))
{
Contract const* contract = get<Contract const*>(_program);
for (auto &base: contract->bases())
{
string baseStr = visit(base);
if (baseStr.empty())
continue;
bases << baseStr;
baseNames << separator
<< (base.has_c() ? programName(&base.c()) : programName(&base.i()));
if (separator.empty())
separator = ", ";
}
// First define overridden functions
bool overrides = contract->bases_size() > 0 && !baseNames.str().empty();
if (overrides)
{
funcs << traverseOverrides(*contract);
}
// Declare/define non-overridden functions
unsigned index = 0;
for (auto &f: contract->funcdef())
{
auto [implement, virtualize] = contractFunctionParams(contract, &f);
funcs << registerAndVisitFunction(
_program,
&f,
index++,
false,
virtualize,
implement
);
// Update contract function map
m_contractFunctionMap[contract].push_back(CITuple(&f, implement, virtualize));
}
}
else if (holds_alternative<Interface const*>(_program))
{
// If we are here, it means most derived program is a contract.
auto interface = get<Interface const*>(_program);
for (auto &base: interface->bases())
{
string baseStr = visit(base);
if (baseStr.empty())
continue;
bases << baseStr;
baseNames << separator
<< programName(&base);
if (separator.empty())
separator = ", ";
}
// First define overridden functions
bool overrides = interface->bases_size() > 0 && !baseNames.str().empty();
if (overrides)
{
funcs << mostDerivedContractOverrides(*interface, false);
}
unsigned index = 0;
// Declare non-overridden functions
for (auto &f: interface->funcdef())
funcs << registerAndVisitFunction(
_program,
&f,
index++,
false,
false,
false
);
}
else
{
auto library = get<Library const*>(_program);
unsigned index = 0;
for (auto &f: library->funcdef())
funcs << registerAndVisitFunction(
_program,
&f,
index++,
false,
false,
false
);
}
return make_tuple(bases.str(), baseNames.str(), funcs.str());
}
#endif
string ProtoConverter::visit(Contract const& _contract)
{
if (_contract.funcdef_size() == 0 && _contract.bases_size() == 0)
return "";
openProgramScope(&_contract);
try {
auto contract = SolContract(_contract, programName(&_contract), m_randomGen);
if (contract.validTest())
{
m_contractTests.push_back(contract.pseudoRandomTest());
return contract.str();
}
// There is no point in generating a contract that can not provide
// a valid test case, so we simply bail.
else
return "";
}
catch (langutil::FuzzerError const&)
{
// Return empty string if input specification is invalid.
return "";
}
}
string ProtoConverter::visit(Interface const& _interface)
{
if (_interface.funcdef_size() == 0 && _interface.bases_size() == 0)
return "";
openProgramScope(&_interface);
try {
auto interface = SolInterface(_interface, programName(&_interface), m_randomGen);
return interface.str();
}
catch (langutil::FuzzerError const&)
{
// Return empty string if input specification is invalid.
return "";
}
}
string ProtoConverter::visit(Library const& _library)
{
if (emptyLibrary(_library))
return "";
openProgramScope(&_library);
auto lib = SolLibrary(_library, programName(&_library), m_randomGen);
if (lib.validTest())
{
auto libTestPair = lib.pseudoRandomTest();
m_libraryTests.push_back({lib.name(), libTestPair.first, libTestPair.second});
}
return lib.str();
}
tuple<string, string, string> ProtoConverter::pseudoRandomLibraryTest()
{
solAssert(m_libraryTests.size() > 0, "Sol proto fuzzer: No library tests found");
unsigned index = randomNumber() % m_libraryTests.size();
return m_libraryTests[index];
}
tuple<string, string, string> ProtoConverter::pseudoRandomContractTest()
{
solAssert(m_contractTests.size() > 0, "Sol proto fuzzer: No contract tests found");
unsigned index = randomNumber() % m_contractTests.size();
return m_contractTests[index];
}
void ProtoConverter::openProgramScope(CIL _program)
{
string programNamePrefix;
if (holds_alternative<Contract const*>(_program))
programNamePrefix = "C";
else if (holds_alternative<Interface const*>(_program))
programNamePrefix = "I";
else
programNamePrefix = "L";
string programName = programNamePrefix + to_string(m_numPrograms++);
m_programNameMap.insert(pair(_program, programName));
if (holds_alternative<Contract const*>(_program))
m_contractFunctionMap.insert(pair(get<Contract const*>(_program), vector<CITuple>{}));
}
string ProtoConverter::programName(CIL _program)
{
solAssert(m_programNameMap.count(_program), "Sol proto fuzzer: Unregistered program");
return m_programNameMap[_program];
}
unsigned ProtoConverter::randomNumber()
{
solAssert(m_randomGen, "Sol proto fuzzer: Uninitialized random number generator");
return m_randomGen->operator()();
}
#if 0
bool ProtoConverter::disallowedContractFunction(SolContractFunction const& _contractFunction, bool _isVirtual)
{
string visibility = functionVisibility(_contractFunction.m_visibility);
string mutability = functionMutability(_contractFunction.m_mutability);
// Private virtual functions are disallowed
if (visibility == "private" && _isVirtual)
return true;
// Private payable functions are disallowed
else if (visibility == "private" && mutability == "payable")
return true;
// Internal payable functions are disallowed
else if (visibility == "internal" && mutability == "payable")
return true;
return false;
}
string ProtoConverter::functionName(CILFunc _function)
{
solAssert(m_functionNameMap.count(_function), "Sol proto fuzzer: Unregistered function");
return m_functionNameMap[_function];
}
#endif