Add adaptor and support library and interface inheritance

This commit is contained in:
Bhargava Shastry 2020-04-10 18:29:03 +02:00
parent 1d533694e5
commit 8973ca3c85
13 changed files with 2185 additions and 911 deletions

View File

@ -42,6 +42,7 @@ struct InternalCompilerError: virtual util::Exception {};
struct FatalError: virtual util::Exception {};
struct UnimplementedFeatureError: virtual util::Exception {};
struct InvalidAstError: virtual util::Exception {};
struct FuzzerError: virtual util::Exception {};
/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
#define solAssert(CONDITION, DESCRIPTION) \

View File

@ -219,8 +219,9 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
else
solUnimplemented("Copying of type " + _sourceType.toString(false) + " to storage not yet supported.");
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] <source_value>...
solAssert(
assertThrow(
2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16,
CompilerError,
"Stack too deep, try removing local variables."
);
// fetch target storage reference

View File

@ -455,7 +455,11 @@ void CompilerUtils::encodeToMemory(
// leave end_of_mem as dyn head pointer
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
dynPointers++;
solAssert((argSize + dynPointers) < 16, "Stack too deep, try using fewer variables.");
assertThrow(
(argSize + dynPointers) < 16,
CompilerError,
"Stack too deep, try using fewer variables."
);
}
else
{
@ -497,8 +501,9 @@ void CompilerUtils::encodeToMemory(
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
{
// copy tail pointer (=mem_end - mem_start) to memory
solAssert(
assertThrow(
(2 + dynPointers) <= 16,
CompilerError,
"Stack too deep(" + to_string(2 + dynPointers) + "), try using fewer variables."
);
m_context << dupInstruction(2 + dynPointers) << Instruction::DUP2;
@ -1251,7 +1256,11 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
{
solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables.");
assertThrow(
_stackDepth <= 16,
CompilerError,
"Stack too deep, try removing local variables."
);
for (unsigned i = 0; i < _itemSize; ++i)
m_context << dupInstruction(_stackDepth);
}
@ -1273,14 +1282,22 @@ void CompilerUtils::moveIntoStack(unsigned _stackDepth, unsigned _itemSize)
void CompilerUtils::rotateStackUp(unsigned _items)
{
solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
assertThrow(
_items - 1 <= 16,
CompilerError,
"Stack too deep, try removing local variables."
);
for (unsigned i = 1; i < _items; ++i)
m_context << swapInstruction(_items - i);
}
void CompilerUtils::rotateStackDown(unsigned _items)
{
solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
assertThrow(
_items - 1 <= 16,
CompilerError,
"Stack too deep, try removing local variables."
);
for (unsigned i = 1; i < _items; ++i)
m_context << swapInstruction(i);
}

View File

@ -208,8 +208,9 @@ void CodeGenerator::assemble(
}
catch (StackTooDeepError const& _e)
{
yulAssert(
assertThrow(
false,
CompilerError,
"Stack too deep when compiling inline assembly" +
(_e.comment() ? ": " + *_e.comment() : ".")
);

View File

@ -85,7 +85,7 @@ void FuzzerUtil::testCompiler(string const& _input, bool _optimize)
compiler.setOptimiserSettings(optimiserSettings);
try
{
compiler.compile();
solAssert(compiler.compile(), "Compilation failed");
}
catch (Error const&)
{
@ -96,6 +96,12 @@ void FuzzerUtil::testCompiler(string const& _input, bool _optimize)
catch (UnimplementedFeatureError const&)
{
}
catch (CompilerError const&)
{
}
catch (StackTooDeepException const&)
{
}
}
void FuzzerUtil::runCompiler(string const& _input, bool _quiet)

View File

@ -0,0 +1,662 @@
/*
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/SolProtoAdaptor.h>
#include <liblangutil/Exceptions.h>
#include <libsolutil/Whiskers.h>
#include <boost/algorithm/string/case_conv.hpp>
#include <sstream>
using namespace solidity::test::solprotofuzzer::adaptor;
using namespace solidity::test::solprotofuzzer;
using namespace std;
using namespace solidity::util;
namespace
{
SolFunctionStateMutability mutabilityConverter(InterfaceFunction_StateMutability _mut)
{
switch (_mut)
{
case InterfaceFunction_StateMutability_PURE:
return SolFunctionStateMutability::PURE;
case InterfaceFunction_StateMutability_VIEW:
return SolFunctionStateMutability::VIEW;
case InterfaceFunction_StateMutability_PAYABLE:
return SolFunctionStateMutability::PAYABLE;
}
}
SolFunctionStateMutability mutabilityConverter(ContractFunction_StateMutability _mut)
{
switch (_mut)
{
case ContractFunction_StateMutability_PURE:
return SolFunctionStateMutability::PURE;
case ContractFunction_StateMutability_VIEW:
return SolFunctionStateMutability::VIEW;
case ContractFunction_StateMutability_PAYABLE:
return SolFunctionStateMutability::PAYABLE;
}
}
SolLibraryFunctionStateMutability mutabilityConverter(LibraryFunction_StateMutability _mut)
{
switch (_mut)
{
case LibraryFunction_StateMutability_PURE:
return SolLibraryFunctionStateMutability::PURE;
case LibraryFunction_StateMutability_VIEW:
return SolLibraryFunctionStateMutability::VIEW;
}
}
SolFunctionVisibility visibilityConverter(ContractFunction_Visibility _vis)
{
switch (_vis)
{
case ContractFunction_Visibility_PUBLIC:
return SolFunctionVisibility::PUBLIC;
case ContractFunction_Visibility_PRIVATE:
return SolFunctionVisibility::PRIVATE;
case ContractFunction_Visibility_EXTERNAL:
return SolFunctionVisibility::EXTERNAL;
case ContractFunction_Visibility_INTERNAL:
return SolFunctionVisibility::INTERNAL;
}
}
SolFunctionVisibility visibilityConverter(LibraryFunction_Visibility _vis)
{
switch (_vis)
{
case LibraryFunction_Visibility_PUBLIC:
return SolFunctionVisibility::PUBLIC;
case LibraryFunction_Visibility_PRIVATE:
return SolFunctionVisibility::PRIVATE;
case LibraryFunction_Visibility_EXTERNAL:
return SolFunctionVisibility::EXTERNAL;
case LibraryFunction_Visibility_INTERNAL:
return SolFunctionVisibility::INTERNAL;
}
}
string functionVisibility(SolFunctionVisibility _vis)
{
switch (_vis)
{
case SolFunctionVisibility::PUBLIC:
return "public";
case SolFunctionVisibility::PRIVATE:
return "private";
case SolFunctionVisibility::EXTERNAL:
return "external";
case SolFunctionVisibility::INTERNAL:
return "internal";
}
}
string functionMutability(SolFunctionStateMutability _mut)
{
switch (_mut)
{
case SolFunctionStateMutability::PURE:
return "pure";
case SolFunctionStateMutability::VIEW:
return "view";
case SolFunctionStateMutability::PAYABLE:
return "payable";
}
}
string libraryFunctionMutability(SolLibraryFunctionStateMutability _mut)
{
switch (_mut)
{
case SolLibraryFunctionStateMutability::PURE:
return "pure";
case SolLibraryFunctionStateMutability::VIEW:
return "view";
}
}
}
SolInterfaceFunction::SolInterfaceFunction(
std::string _functionName,
SolFunctionStateMutability _mutability,
bool _override
)
{
m_functionName = _functionName;
m_mutability = _mutability;
m_override = _override;
}
bool SolInterfaceFunction::operator==(SolInterfaceFunction const& _rhs) const
{
// TODO: Change this once we permit arbitrary function parameter types
return name() == _rhs.name();
}
bool SolInterfaceFunction::operator!=(SolInterfaceFunction const& _rhs) const
{
// TODO: Change this once we permit arbitrary function parameter types
return name() != _rhs.name();
}
string SolInterfaceFunction::str() const
{
string bodyStr = Whiskers(R"(
{
return <uint>;
})")
("uint", "0")
.render();
return Whiskers(R"(
function <functionName>() external <stateMutability><?isOverride> override</isOverride>
<?isVirtual> virtual</isVirtual> returns (uint)<?isImplemented><body><!isImplemented>;</isImplemented>
)")
("functionName", name())
("stateMutability", functionMutability(mutability()))
("isOverride", override())
("isVirtual", isVirtual())
("isImplemented", implement())
("body", bodyStr)
.render();
}
SolContractFunction::SolContractFunction(
ContractFunction const& _function,
std::string _contractName,
std::string _functionName,
std::string _returnValue
)
{
m_contractName = _contractName;
m_functionName = _functionName;
m_visibility = visibilityConverter(_function.vis());
m_mutability = mutabilityConverter(_function.mut());
m_virtual = _function.virtualfunc();
m_returnValue = _returnValue;
}
bool SolContractFunction::operator==(SolContractFunction const& _rhs) const
{
return this->m_visibility == _rhs.m_visibility && this->m_mutability == _rhs.m_mutability;
}
bool SolContractFunction::operator!=(SolContractFunction const& _rhs) const
{
return this->m_visibility != _rhs.m_visibility || this->m_mutability != _rhs.m_mutability;
}
string SolContractFunction::str() const
{
string bodyStr = Whiskers(R"(
{
return <uint>;
})")
("uint", returnValue())
.render();
return Whiskers(R"(
function <functionName>()<?isOverride> override</isOverride>
<?isVirtual> virtual</isVirtual> <visibility> <stateMutability>
returns (uint)<?isImplemented><body><!isImplemented>;</isImplemented>)")
("functionName", name())
("isVirtual", isVirtual())
("isOverride", "_override")
("visibility", functionVisibility(visibility()))
("stateMutability", functionMutability(mutability()))
("body", bodyStr)
("isImplemented", "_implement")
.render();
}
SolLibraryFunction::SolLibraryFunction(
LibraryFunction const& _function,
std::string _libraryName,
std::string _functionName,
std::string _returnValue
)
{
m_libraryName = _libraryName;
m_functionName = _functionName;
m_visibility = visibilityConverter(_function.vis());
m_mutability = mutabilityConverter(_function.mut());
m_returnValue = _returnValue;
}
string SolLibraryFunction::str() const
{
string bodyStr = Whiskers(R"(
{
return <uint>;
})")
("uint", returnValue())
.render();
return Whiskers(R"(
function <functionName>(uint) <visibility> <stateMutability> returns (uint)<body>)")
("functionName", name())
("visibility", functionVisibility(visibility()))
("stateMutability", libraryFunctionMutability(mutability()))
("body", bodyStr)
.render();
}
SolBaseContract::SolBaseContract(ProtoBaseContract _base, string _name, shared_ptr<SolRandomNumGenerator> _prng)
{
if (auto c = get<Contract const*>(_base))
m_base.push_back(
make_shared<SolContract>(SolContract(*c, _name, _prng))
);
else if (auto i = get<Interface const*>(_base))
m_base.push_back(
make_shared<SolInterface>(SolInterface(*i, _name, _prng))
);
}
void SolInterface::overrideHelper(
shared_ptr<SolInterfaceFunction> _function,
shared_ptr<SolInterface> _base
)
{
auto functionName = _function->name();
auto mutability = _function->mutability();
// Check if two or more bases define this function
bool multipleOverride = false;
// If function has already been overridden, add
// new base to list of overridden bases
for (auto &m: m_overrideMap)
{
// Must override if two or more bases define the
// same function
if (m.first->operator==(*_function))
{
// Report error if state mutability of identically
// named functions differ
if (m.first->mutability() != _function->mutability())
assertThrow(
false,
langutil::FuzzerError,
"Input specifies multiple function overrides with identical names"
" and parameter types but different mutability."
);
// Add new base to list of overridden bases
m_overrideMap[m.first].push_back(_base);
multipleOverride = true;
break;
}
}
// If function has not been overridden, add new override
if (!multipleOverride)
m_overrideMap.insert(
pair(
make_shared<SolInterfaceFunction>(
SolInterfaceFunction(
functionName,
mutability,
true
)
),
vector<shared_ptr<SolInterface>>{_base}
)
);
}
void SolInterface::addOverrides()
{
for (auto &base: m_baseInterfaces)
{
// Override base interface functions
for (auto &f: base->m_interfaceFunctions)
overrideHelper(f, base);
// Override base interface functions that are themselves overrides
for (auto &e: base->m_overrideMap)
overrideHelper(e.first, base);
}
}
void SolInterface::addBases(Interface const& _interface)
{
for (auto &b: _interface.bases())
{
auto base = make_shared<SolInterface>(SolInterface(b, newBaseName(), m_prng));
m_baseInterfaces.push_back(base);
cout << "Added " << base->name() << " as base" << endl;
// Worst case, we override all base functions so we
// increment derived contract's function index by
// this amount.
m_functionIndex += base->functionIndex();
m_lastBaseName = base->lastBaseName();
}
}
void SolInterface::addFunctions(Interface const& _interface)
{
for (auto &f: _interface.funcdef())
m_interfaceFunctions.push_back(
make_shared<SolInterfaceFunction>(
SolInterfaceFunction(
newFunctionName(),
mutabilityConverter(f.mut())
)
)
);
}
SolInterface::SolInterface(Interface const& _interface, string _name, shared_ptr<SolRandomNumGenerator> _prng)
{
cout << "Constructing " << _name << endl;
m_prng = _prng;
m_interfaceName = _name;
m_lastBaseName = m_interfaceName;
addBases(_interface);
addOverrides();
addFunctions(_interface);
}
string SolInterface::baseNames() const
{
ostringstream bases;
string separator{};
for (auto &b: m_baseInterfaces)
{
bases << separator << b->name();
if (separator.empty())
separator = ", ";
}
return bases.str();
}
string SolInterface::baseInterfaceStr() const
{
ostringstream baseInterfaces;
for (auto &b: m_baseInterfaces)
baseInterfaces << b->str();
return baseInterfaces.str();
}
string SolInterface::overrideStr() const
{
ostringstream overriddenFunctions;
for (auto &f: m_overrideMap)
{
ostringstream overriddenBaseNames;
if (f.second.size() > 1)
{
string sep{};
for (auto &b: f.second)
{
overriddenBaseNames << Whiskers(R"(<sep><name>)")
("sep", sep)
("name", b->name())
.render();
if (sep.empty())
sep = ", ";
}
}
overriddenFunctions << Whiskers(R"(
function <functionName>() external <stateMutability> override<?multiple>(<baseNames>)</multiple> returns (uint);)")
("functionName", f.first->name())
("stateMutability", functionMutability(f.first->mutability()))
("multiple", f.second.size() > 1)
("baseNames", overriddenBaseNames.str())
.render();
}
return overriddenFunctions.str();
}
string SolInterface::str() const
{
ostringstream functions;
ostringstream bases;
// Print overridden functions
functions << overrideStr();
// Print non-overridden functions
for (auto &f: m_interfaceFunctions)
functions << f->str();
for (auto &b: m_baseInterfaces)
bases << b->str();
return Whiskers(R"(
<bases>
interface <programName><?inheritance> is <baseNames></inheritance> {
<functionDefs>
})")
("bases", bases.str())
("programName", name())
("inheritance", m_baseInterfaces.size() > 0)
("baseNames", baseNames())
("functionDefs", functions.str())
.render();
}
SolContract::SolContract(Contract const& _contract, std::string _name, shared_ptr<SolRandomNumGenerator> _prng)
{
m_prng = _prng;
m_contractName = _name;
m_abstract = _contract.abstract();
for (auto &f: _contract.funcdef())
m_contractFunctions.push_back(
make_unique<SolContractFunction>(
SolContractFunction(
f,
m_contractName,
newFunctionName(),
newReturnValue()
)
)
);
for (auto &b: _contract.bases())
{
switch (b.contract_or_interface_oneof_case())
{
case ContractOrInterface::kC:
m_baseContracts.push_back(
make_unique<SolBaseContract>(
SolBaseContract(&b.c(), newContractBaseName(), m_prng)
)
);
break;
case ContractOrInterface::kI:
m_baseContracts.push_back(
make_unique<SolBaseContract>(
SolBaseContract(&b.i(), newInterfaceBaseName(), m_prng)
)
);
break;
case ContractOrInterface::CONTRACT_OR_INTERFACE_ONEOF_NOT_SET:
break;
}
}
}
void SolLibrary::addFunction(LibraryFunction const& _function)
{
// Register function name and return value
string functionName = newFunctionName();
string outputStr = newReturnValue();
SolFunctionVisibility visibility = visibilityConverter(_function.vis());
if (visibility == SolFunctionVisibility::PUBLIC || visibility == SolFunctionVisibility::EXTERNAL)
{
solAssert(!m_publicFunctionMap.count(functionName), "Sol proto adapter: Duplicate library function");
m_publicFunctionMap.insert(pair(functionName, outputStr));
}
// Create and add function to library
m_functions.push_back(
make_unique<SolLibraryFunction>(
SolLibraryFunction(_function,
name(),
functionName,
outputStr
)
)
);
}
SolLibrary::SolLibrary(Library const& _library, string _name)
{
m_libraryName = _name;
for (LibraryFunction const& f: _library.funcdef())
addFunction(f);
}
string SolLibrary::str() const
{
ostringstream functions;
for (auto &f: m_functions)
functions << f->str();
return Whiskers(R"(
library <name> {
<functions>
})")
("name", name())
("functions", functions.str())
.render();
}
bool SolLibrary::validTest() const
{
return m_publicFunctionMap.size() > 0;
}
pair<string, string> SolLibrary::pseudoRandomTest(unsigned _randomIdx)
{
solAssert(m_publicFunctionMap.size() > 0, "Sol proto adaptor: Empty library map");
string chosenFunction;
unsigned numFunctions = m_publicFunctionMap.size();
unsigned functionIndex = _randomIdx % numFunctions;
unsigned mapIdx = 0;
for (auto &e: m_publicFunctionMap)
{
if (functionIndex == mapIdx)
chosenFunction = e.first;
mapIdx++;
}
solAssert(m_publicFunctionMap.count(chosenFunction), "Sol proto adaptor: Invalid library function chosen");
return pair(chosenFunction, m_publicFunctionMap[chosenFunction]);
}
CFunctionOverride::CFunctionOverrideType CFunctionOverride::functionType() const
{
if (holds_alternative<unique_ptr<SolContractFunction const>>(m_function.second))
return CFunctionOverrideType::CONTRACT;
solAssert(interfaceFunction(), "Sol proto fuzzer: Invalid override function type");
return CFunctionOverrideType::INTERFACE;
}
string CFunctionOverride::name() const
{
if (holds_alternative<unique_ptr<SolContractFunction const>>(m_function.second))
return get<unique_ptr<SolContractFunction const>>(m_function.second)->name();
solAssert(interfaceFunction(), "Sol proto fuzzer: Invalid override function type");
return get<unique_ptr<SolInterfaceFunction const>>(m_function.second)->name();
}
bool CFunctionOverride::interfaceFunction() const
{
return functionType() == CFunctionOverrideType::INTERFACE;
}
bool CFunctionOverride::contractFunction() const
{
return functionType() == CFunctionOverrideType::CONTRACT;
}
SolFunctionVisibility CFunctionOverride::visibility() const
{
if (contractFunction())
return get<unique_ptr<SolContractFunction const>>(m_function.second)->visibility();
solAssert(interfaceFunction(), "Sol proto fuzzer: Invalid override function type");
return SolFunctionVisibility::EXTERNAL;
}
SolFunctionStateMutability CFunctionOverride::mutability() const
{
if (contractFunction())
return get<unique_ptr<SolContractFunction const>>(m_function.second)->mutability();
solAssert(interfaceFunction(), "Sol proto fuzzer: Invalid override function type");
return get<unique_ptr<SolInterfaceFunction const>>(m_function.second)->mutability();
}
string CFunctionOverride::str() const
{
string bodyStr = Whiskers(R"(
{
return <uint>;
})")
("uint", returnValue())
.render();
return Whiskers(R"(
function <functionName>() override <?isVirtual> virtual</isVirtual> <visibility> <stateMutability>
returns (uint)<?isImplemented><body><!isImplemented>;</isImplemented>)")
("functionName", name())
("isVirtual", virtualized() || !implemented())
("visibility", functionVisibility(visibility()))
("stateMutability", functionMutability(mutability()))
("body", bodyStr)
("isImplemented", implemented())
.render();
}
//string CFunctionOverride::commaSeparatedBaseNames()
//{
// ostringstream baseNames;
// string separator{};
// for (auto &override: m_function)
// {
// auto base = override.first;
// string baseName;
// if (auto b = get<SolInterface const*>(base))
// baseName = b->name();
// else
// baseName = get<SolContract const*>(base)->name();
//
// baseNames << Whiskers(R"(<sep><base>)")
// ("sep", separator)
// ("base", baseName)
// .render();
// if (separator.empty())
// separator = ", ";
// }
// return baseNames.str();
//}
//string CFunctionOverride::baseName() const
//{
// auto base = m_function.first;
// if (contractFunction())
// return get<shared_ptr<SolContract const>>(base)->name();
// solAssert(interfaceFunction(), "Sol proto fuzzer: Invalid override function type");
// return get<shared_ptr<SolInterface const>>(base)->name();
//}

View File

@ -0,0 +1,520 @@
/*
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/protoToSol.h>
#include <iostream>
#include <string>
#include <utility>
#include <variant>
namespace solidity::test::solprotofuzzer::adaptor
{
/// Solidity contract abstraction class
struct SolContract;
/// Solidity interface abstraction class
struct SolInterface;
/// Solidity library abstraction class
struct SolLibrary;
/// Solidity interface function abstraction class
struct SolInterfaceFunction;
/// Solidity contract function abstraction class
struct SolContractFunction;
/// Solidity library function abstraction class
struct SolLibraryFunction;
/// Solidity contract function override abstraction class
struct CFunctionOverride;
/// Solidity interface function override abstraction class
struct IFunctionOverride;
/// Type that defines a contract function override as a variant of interface or
/// contract function types.
using OverrideFunction = std::variant<std::unique_ptr<SolInterfaceFunction const>, std::unique_ptr<SolContractFunction const>>;
/// Type that defines a list of base contracts as a list of variants of interface or
/// contract types.
using BaseContracts = std::vector<std::variant<std::shared_ptr<SolInterface const>, std::shared_ptr<SolContract const>>>;
/// Type that defines a contract function override as a pair of base contracts
/// and its (their) function.
using OverrideCFunction = std::pair<BaseContracts, OverrideFunction>;
/// Type that defines an interface function override as a pair of interface
/// and its function.
using OverrideIFunction = std::pair<std::vector<std::shared_ptr<SolInterface const>>, std::unique_ptr<SolInterfaceFunction const>>;
/// Type that defines a map of interface overrides
using InterfaceOverrideMap = std::map<std::shared_ptr<SolInterfaceFunction>, std::vector<std::shared_ptr<SolInterface>>>;
/// Type that defines an interface function as either a vanilla interface function
/// or an override function.
using IFunction = std::variant<std::unique_ptr<SolInterfaceFunction>, OverrideIFunction>;
/// Type that defines a contract function as either a vanilla contract function
/// or an override function.
using CFunction = std::variant<std::unique_ptr<SolContractFunction>, OverrideCFunction>;
/// Variant type that points to one of contract, interface protobuf messages
using ProtoBaseContract = std::variant<Contract const*, Interface const*>;
enum class SolFunctionVisibility
{
PUBLIC,
PRIVATE,
INTERNAL,
EXTERNAL
};
enum class SolFunctionStateMutability
{
PURE,
VIEW,
PAYABLE
};
enum class SolLibraryFunctionStateMutability
{
PURE,
VIEW
};
struct SolInterfaceFunction
{
SolInterfaceFunction(
std::string _functionName,
SolFunctionStateMutability _mutability,
bool _override = false
);
bool operator==(SolInterfaceFunction const& _rhs) const;
bool operator!=(SolInterfaceFunction const& _rhs) const;
std::string str() const;
std::string name() const
{
return m_functionName;
}
SolFunctionStateMutability mutability() const
{
return m_mutability;
}
bool override() const
{
return m_override;
}
bool isVirtual() const
{
return false;
}
bool implement() const
{
return false;
}
std::string m_functionName;
SolFunctionStateMutability m_mutability = SolFunctionStateMutability::PURE;
bool m_override = false;
bool m_virtual = false;
bool m_implement = false;
};
struct SolContractFunction
{
SolContractFunction(
ContractFunction const& _function,
std::string _contractName,
std::string _functionName,
std::string _returnValue
);
bool operator==(SolContractFunction const& _rhs) const;
bool operator!=(SolContractFunction const& _rhs) const;
std::string str() const;
std::string name() const
{
return m_functionName;
}
std::string contractName() const
{
return m_contractName;
}
bool isVirtual() const
{
return m_virtual;
}
std::string returnValue() const
{
return m_returnValue;
}
SolFunctionVisibility visibility() const
{
return m_visibility;
}
SolFunctionStateMutability mutability() const
{
return m_mutability;
}
std::string m_contractName;
std::string m_functionName;
SolFunctionVisibility m_visibility = SolFunctionVisibility::PUBLIC;
SolFunctionStateMutability m_mutability = SolFunctionStateMutability::PURE;
bool m_virtual = false;
std::string m_returnValue;
};
struct SolLibraryFunction
{
SolLibraryFunction(
LibraryFunction const& _function,
std::string _libraryName,
std::string _functionName,
std::string _returnValue
);
std::string str() const;
std::string name() const
{
return m_functionName;
}
std::string libraryName() const
{
return m_libraryName;
}
std::string returnValue() const
{
return m_returnValue;
}
SolFunctionVisibility visibility() const
{
return m_visibility;
}
SolLibraryFunctionStateMutability mutability() const
{
return m_mutability;
}
std::string m_libraryName;
std::string m_functionName;
SolFunctionVisibility m_visibility = SolFunctionVisibility::PUBLIC;
SolLibraryFunctionStateMutability m_mutability = SolLibraryFunctionStateMutability::PURE;
std::string m_returnValue;
};
struct SolLibrary
{
SolLibrary(Library const& _library, std::string _name);
std::vector<std::unique_ptr<SolLibraryFunction>> m_functions;
/// Maps publicly exposed function name to expected output
std::map<std::string, std::string> m_publicFunctionMap;
void addFunction(LibraryFunction const& _function);
bool validTest() const;
/// Returns a pair of function name and expected output
/// that is pseudo randomly chosen from the list of all
/// library functions.
/// @param _randomIdx A pseudo randomly generated number that is
/// used to index the list of all library functions.
std::pair<std::string, std::string> pseudoRandomTest(unsigned _randomIdx);
std::string str() const;
std::string name() const
{
return m_libraryName;
}
std::string newFunctionName()
{
return "f" + std::to_string(m_functionIndex++);
}
std::string newReturnValue()
{
return std::to_string(m_returnValue++);
}
std::string m_libraryName;
unsigned m_functionIndex = 0;
unsigned m_returnValue = 0;
};
struct SolBaseContract
{
SolBaseContract(ProtoBaseContract _base, std::string _name, std::shared_ptr<SolRandomNumGenerator> _prng);
BaseContracts m_base;
std::string m_baseName;
std::shared_ptr<SolRandomNumGenerator> m_prng;
};
struct SolContract
{
SolContract(Contract const& _contract, std::string _name, std::shared_ptr<SolRandomNumGenerator> _prng);
std::string str() const;
unsigned randomNumber()
{
return m_prng->operator()();
}
std::string name() const
{
return m_contractName;
}
bool abstract() const
{
return m_abstract;
}
std::string newFunctionName()
{
return "f" + std::to_string(m_functionIndex++);
}
std::string newContractBaseName()
{
return name() + "B" + std::to_string(m_baseIndex++);
}
std::string newInterfaceBaseName()
{
return "IB" + std::to_string(m_baseIndex++);
}
std::string newReturnValue()
{
return std::to_string(m_returnValue++);
}
std::string m_contractName;
bool m_abstract = false;
unsigned m_baseIndex = 0;
unsigned m_functionIndex = 0;
unsigned m_returnValue = 0;
std::vector<std::unique_ptr<SolContractFunction>> m_contractFunctions;
std::vector<std::unique_ptr<SolBaseContract>> m_baseContracts;
std::vector<std::unique_ptr<CFunctionOverride>> m_overriddenFunctions;
std::shared_ptr<SolRandomNumGenerator> m_prng;
};
struct SolInterface
{
SolInterface(
Interface const& _interface,
std::string _interfaceName,
std::shared_ptr<SolRandomNumGenerator> _prng
);
std::string name() const
{
return m_interfaceName;
}
unsigned randomNumber() const
{
return m_prng->operator()();
}
bool coinFlip() const
{
return randomNumber() % 2 == 0;
}
std::string newFunctionName()
{
return "f" + std::to_string(m_functionIndex++);
}
void incrementFunctionIndex()
{
m_functionIndex++;
}
void resetFunctionIndex()
{
m_functionIndex = 0;
}
void setFunctionIndex(unsigned _index)
{
m_functionIndex = _index;
}
unsigned functionIndex() const
{
return m_functionIndex;
}
std::string newBaseName()
{
m_lastBaseName += "B";
return m_lastBaseName;
}
std::string lastBaseName()
{
return m_lastBaseName;
}
std::string str() const;
std::string overrideStr() const;
/// Returns the Solidity code for all base interfaces
/// inherited by this interface.
std::string baseInterfaceStr() const;
/// Returns comma-space separated names of base interfaces inherited by
/// this interface.
std::string baseNames() const;
/// Add base contracts in a depth-first manner
void addBases(Interface const& _interface);
/// Add functions
void addFunctions(Interface const& _interface);
/// Add overrides
void addOverrides();
/// Helper for adding overrides
void overrideHelper(
std::shared_ptr<SolInterfaceFunction> _function,
std::shared_ptr<SolInterface> _interface
);
unsigned m_functionIndex = 0;
std::string m_lastBaseName;
std::string m_interfaceName;
std::vector<std::shared_ptr<SolInterfaceFunction>> m_interfaceFunctions;
std::vector<std::shared_ptr<SolInterface>> m_baseInterfaces;
InterfaceOverrideMap m_overrideMap;
std::shared_ptr<SolRandomNumGenerator> m_prng;
};
struct CFunctionOverride
{
CFunctionOverride(
BaseContracts _base,
OverrideFunction _function,
bool _implemented,
bool _virtualized,
bool _redeclared,
std::string _returnValue
)
{
m_function = std::make_pair(_base, std::move(_function));
m_implemented = _implemented;
m_virtualized = _virtualized;
m_redeclared = _redeclared;
m_returnValue = _returnValue;
}
enum class CFunctionOverrideType
{
INTERFACE,
CONTRACT
};
std::string str() const;
std::string name() const;
CFunctionOverrideType functionType() const;
bool interfaceFunction() const;
bool contractFunction() const;
SolFunctionVisibility visibility() const;
SolFunctionStateMutability mutability() const;
std::string commaSeparatedBaseNames() const;
std::string baseName() const;
/// Overridden function
OverrideCFunction m_function;
/// Flag that is true if overridden function is implemented
bool m_implemented = true;
/// Flag that is true if overridden function is marked virtual
bool m_virtualized = false;
/// Flag that is true if overridden function is redeclared but not implemented
bool m_redeclared = false;
/// The uint value to be returned by the overridden function if it is
/// implemented
std::string m_returnValue;
bool implemented() const
{
return m_implemented;
}
bool virtualized() const
{
return m_virtualized;
}
bool redeclared() const
{
return m_redeclared;
}
std::string returnValue() const
{
return m_returnValue;
}
};
struct IFunctionOverride
{
IFunctionOverride(
std::vector<std::shared_ptr<SolInterface const>> _base,
std::unique_ptr<SolInterfaceFunction const> _function,
bool _implement,
bool _virtual,
bool _redeclare,
std::string _returnValue = ""
)
{
m_function = std::make_pair(_base, std::move(_function));
m_implemented = _implement;
m_virtualized = _virtual;
m_redeclared = _redeclare;
m_returnValue = _returnValue;
}
std::string str() const;
bool implemented() const
{
return m_implemented;
}
bool virtualized() const
{
return m_virtualized;
}
bool redeclared() const
{
return m_redeclared;
}
std::string returnValue() const
{
return m_returnValue;
}
OverrideIFunction m_function;
/// Flag that is true if overridden function is implemented in derived contract
bool m_implemented = false;
/// Flag that is true if overridden function implemented in derived contract is
/// marked virtual
bool m_virtualized = false;
/// Flag that is true if overridden function is redeclared but not implemented
bool m_redeclared = false;
/// The uint value to be returned if the overridden interface function is implemented
std::string m_returnValue;
};
}

View File

@ -9,13 +9,16 @@ SolidityCompilationFramework::SolidityCompilationFramework(langutil::EVMVersion
solidity::bytes SolidityCompilationFramework::compileContract(
std::string const& _sourceCode,
std::string const& _contractName
std::string const& _contractName,
std::map<std::string, solidity::util::h160> const& _libraryAddresses,
frontend::OptimiserSettings _optimization
)
{
std::string sourceCode = _sourceCode;
m_compiler.setSources({{"", sourceCode}});
m_compiler.setLibraries(_libraryAddresses);
m_compiler.setEVMVersion(m_evmVersion);
m_compiler.setOptimiserSettings(m_optimiserSettings);
m_compiler.setOptimiserSettings(_optimization);
if (!m_compiler.compile())
{
langutil::SourceReferenceFormatter formatter(std::cerr);

View File

@ -23,12 +23,13 @@ public:
}
bytes compileContract(
std::string const& _sourceCode,
std::string const& _contractName = {}
std::string const& _contractName,
std::map<std::string, solidity::util::h160> const& _libraryAddresses = {},
frontend::OptimiserSettings _optimization = frontend::OptimiserSettings::minimal()
);
protected:
frontend::CompilerStack m_compiler;
langutil::EVMVersion m_evmVersion;
frontend::OptimiserSettings m_optimiserSettings = frontend::OptimiserSettings::none();
};
}

File diff suppressed because it is too large Load Diff

View File

@ -14,48 +14,29 @@
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 <string>
#include <test/tools/ossfuzz/solProto.pb.h>
#include <random>
#include <string>
#include <utility>
#include <variant>
namespace solidity::test::solprotofuzzer
{
struct ProgramInfo
struct SolRandomNumGenerator
{
enum class ProgramType
{
LIBRARY,
CONTRACT,
INTERFACE
};
std::string m_name;
std::string m_userDefinedTypes;
ProgramType m_programType;
};
using RandomEngine = std::minstd_rand;
struct FunctionInfo
{
enum class FunctionVisibility
explicit SolRandomNumGenerator(unsigned _seed): m_random(RandomEngine(_seed)) {}
unsigned operator()()
{
PUBLIC,
PRIVATE,
INTERNAL,
EXTERNAL
};
enum class StateMutability
{
PURE,
VIEW,
PAYABLE
};
std::string m_name;
std::string m_params;
std::string m_returns;
FunctionVisibility m_visibility;
StateMutability m_mutability;
bool m_override;
bool m_virtual;
return m_random();
}
RandomEngine m_random;
};
class ProtoConverter
@ -65,82 +46,155 @@ public:
ProtoConverter(ProtoConverter const&) = delete;
ProtoConverter(ProtoConverter&&) = delete;
std::string protoToSolidity(Program const&);
private:
using CI = std::variant<Contract const*, Interface const*>;
/// @returns true if test calls a library function, false
/// otherwise
bool libraryTest() const;
/// @returns name of the library under test
std::string libraryName() const;
private:
enum MostDerivedProgram {
CONTRACT,
INTERFACE,
LIBRARY
};
/// Variant type that points to one of contract, interface, library protobuf messages
using CIL = std::variant<Contract const*, Interface const*, Library const*>;
/// Variant type that points to one of contract, interface, library function protobuf
/// messages
using CILFunc = std::variant<ContractFunction const*, InterfaceFunction const*, LibraryFunction const*>;
/// Variant type that points to one of contract, interface protobuf messages
using CI = std::variant<Contract const*, Interface const*>;
/// Variant type that points to one of contract, interface function protobuf messages
using CIFunc = std::variant<ContractFunction const*, InterfaceFunction const*>;
/// Tuple of contract or interface function variant and a boolean stating whether
/// the function is implemented (true) or not and a second boolean stating whether
/// the function is virtual (true) or not.
using CITuple = std::tuple<CIFunc, bool, bool>;
/// Protobuf message visitors that accept a const reference to a protobuf message
/// type and return its solidity translation.
std::string visit(Program const&);
std::string visit(TestContract const&);
std::string visit(ContractType const&);
std::string visit(ContractOrInterface const&);
std::string visit(Interface const&);
std::string visit(Interface const& _interface);
/// Visitor for most derived interface messages.
/// @param _interface is a const reference to interface protobuf message
/// @returns a 3-tuple containing Solidity translation of all base contracts
/// this interface derives from, names of all base contracts, and the Solidity
/// translation of this interface.
std::tuple<std::string, std::string, std::string>
visitMostDerivedInterface(Interface const& _interface);
std::string visit(Contract const&);
std::string traverseOverrides(
Interface const&,
bool _isOverride,
bool _implement,
bool _inheritedByContract,
bool _isVirtual
/// Define overrides for most derived interface.
std::string mostDerivedInterfaceOverrides(Interface const& _interface);
/// Define overrides for most derived contract.
std::string mostDerivedContractOverrides(Interface const& _interface);
std::string traverseOverrides(Contract const&);
std::string registerAndVisitFunction(
CIL _program,
CILFunc _func,
unsigned _index,
bool _override,
bool _virtual,
bool _implement
);
std::string traverseOverrides(
Contract const&,
bool _isAbstract
std::string overrideFunction(CILFunc _function, bool _virtual, bool _implement);
std::pair<bool, bool> contractFunctionParams(
Contract const* _contract,
ContractFunction const* _function
);
std::string visit(CILFunc _function, bool _override, bool _virtual, bool _implement);
std::string visit(Library const&);
std::pair<std::string, std::string> visit(Block const&);
std::pair<std::string, std::string> visit(Statement const&);
std::pair<std::string, std::string> visit(solidity::test::abiv2fuzzer::VarDecl const&, bool _stateVar);
std::pair<std::string, std::string> visit(IfStmt const&);
std::string visit(ForStmt const&);
std::string visit(SwitchStmt const&);
std::string visit(BreakStmt const&);
std::string visit(ContinueStmt const&);
std::string visit(ReturnStmt const&);
std::pair<std::string, std::string> visit(DoStmt const&);
std::pair<std::string, std::string> visit(WhileStmt const&);
std::string visit(Expression const&);
std::string visit(Literal const&);
std::string visit(BinaryOp const&);
std::string visit(UnaryOp const&);
std::string visit(VarRef const&);
std::tuple<std::string, std::string, std::string> visit(
FunctionParamsAndReturns const& _pR,
bool _isExternal,
std::string _programName
);
std::string visit(
InterfaceFunction const&,
unsigned _index,
bool _isOverride,
std::string _programName,
bool _implement = false,
bool _inheritedByContract = false,
bool _isVirtual = false
);
std::string visit(LibraryFunction const& _func, unsigned _index, std::string _programName);
std::string visit(
ContractFunction const&,
unsigned _index,
bool _isOverride,
bool _isAbstractContract,
bool _isVirtual,
bool _isImplemented,
std::string _programName
);
std::tuple<std::string, std::string, std::string> visitContractHelper(CI _cOrI, std::string _programName);
std::string visit(Modifier const&);
static std::string functionVisibility(ContractFunction::Visibility _vis);
static std::string functionVisibility(LibraryFunction::Visibility _vis);
static std::string stateMutability(ContractFunction::StateMutability _mut);
static std::string stateMutability(LibraryFunction::StateMutability _mut);
static std::string stateMutability(InterfaceFunction::StateMutability _mut);
static bool disallowedContractFunction(ContractFunction const& _contractFunction, bool _isVirtual);
std::string programName(CIL _program);
std::string createFunctionName(CIL _program, unsigned _index);
std::string functionName(CILFunc _function);
void registerFunctionName(CIL _program, CILFunc _function, unsigned _index);
std::tuple<std::string, std::string, std::string> visitProgramHelper(CIL _program);
bool contractFunctionImplemented(Contract const* _contract, CIFunc _function);
bool contractFunctionVirtual(Contract const* _contract, CIFunc _function);
std::tuple<bool, bool, bool> mostDerivedContractOverrideParams();
std::pair<bool, bool> contractFunctionOverrideParams(
Contract const* _base,
Contract const* _derived,
CIFunc _baseFunc
);
std::tuple<std::string, std::string, std::string> pseudoRandomLibraryTest(unsigned _randomIdx);
bool emptyLibrary(Library const& _library)
{
return _library.funcdef_size() == 0;
}
bool emptyLibraryTests()
{
return m_libraryTests.size() == 0;
}
void openProgramScope(CIL _program);
bool pseudoRandomCoinFlip()
{
return m_counter++ % 2 == 0;
}
bool mostDerivedProgramContract()
{
return m_mostDerivedProgram == MostDerivedProgram::CONTRACT;
}
bool mostDerivedProgramInterface()
{
return m_mostDerivedProgram == MostDerivedProgram::INTERFACE;
}
bool mostDerivedProgramAbstractContract()
{
return m_mostDerivedAbstractContract;
}
unsigned randomNumber();
#if 0
static bool disallowedContractFunction(SolContractFunction const& _contractFunction, bool _isVirtual);
#endif
unsigned m_numVars = 0;
unsigned m_numStructs = 0;
unsigned m_numMods = 0;
unsigned m_numContracts = 0;
bool m_isImplemented = false;
unsigned m_numPrograms = 0;
unsigned m_counter = 0;
bool m_mostDerivedAbstractContract = false;
bool m_libraryTest = false;
std::shared_ptr<SolRandomNumGenerator> m_randomGen;
MostDerivedProgram m_mostDerivedProgram = MostDerivedProgram::CONTRACT;
/// Map whose key is pointer to protobuf interface message
/// and whose value is its contract name
std::map<Interface const*, std::string> m_interfaceNameMap;
/// Map whose key is pointer to protobuf contract message
/// and whose value is its contract name
std::map<Contract const*, std::string> m_contractNameMap;
/// Map whose key is library name and whose value is the
/// number of implemented functions in it.
std::map<std::string, unsigned> m_libraryFuncMap;
/// Map whose key is a const pointer to protobuf contract
/// message and whose value is a list of 3-tuples that
/// store a const pointer to a protobuf interface or contract
/// function belonging to the keyed contract, a boolean flag that is
/// true when the function is implemented, false otherwise, and
/// a second boolean flag that is true when the function is virtualized
/// false otherwise.
std::map<Contract const*, std::vector<CITuple>> m_contractFunctionMap;
/// Map whose key is a const pointer to protobuf contract, interface or
/// library function message type and whose value is the function name
/// assigned to it.
std::map<CILFunc, std::string> m_functionNameMap;
std::map<CIL, std::string> m_programNameMap;
/// Map whose key is a const pointer to protobuf contract or interface
/// function message type and whose value is a pair of const pointer to
/// protobuf contract or interface it belongs to and its declaration
/// position (which is an unsigned integer that starts from 0).
std::map<CIFunc, std::pair<CI, unsigned>> m_functionProgramMap;
std::map<CI, std::vector<std::pair<CIFunc, std::string>>> m_programFunctionNameMap;
std::vector<std::tuple<std::string, std::string, std::string>> m_libraryTests;
std::string m_libraryName;
static auto constexpr s_interfaceFunctionPrefix = "i";
static auto constexpr s_libraryFunctionPrefix = "l";

View File

@ -17,22 +17,13 @@
syntax = "proto2";
import "abiV2Proto.proto";
message FunctionParamsAndReturns {
repeated solidity.test.abiv2fuzzer.Type params = 1;
repeated solidity.test.abiv2fuzzer.Type returns = 2;
}
message InterfaceFunction {
enum StateMutability {
PURE = 0;
VIEW = 1;
PAYABLE = 2;
}
required FunctionParamsAndReturns paramandret = 1;
required StateMutability mut = 2;
required bool override = 3;
required StateMutability mut = 1;
}
message LibraryFunction {
@ -47,11 +38,8 @@ message LibraryFunction {
INTERNAL = 2;
PRIVATE = 3;
}
required FunctionParamsAndReturns paramandret = 1;
required Visibility vis = 2;
required StateMutability mut = 3;
required Block b = 4;
optional Modifier m = 5;
required Visibility vis = 1;
required StateMutability mut = 2;
}
message ContractFunction {
@ -66,144 +54,25 @@ message ContractFunction {
INTERNAL = 2;
PRIVATE = 3;
}
required FunctionParamsAndReturns paramandret = 1;
required Visibility vis = 2;
required StateMutability mut = 3;
required Block b = 4;
optional Modifier m = 5;
required bool implemented = 6;
required bool virtualfunc = 7;
required bool override = 8;
}
message Modifier {
repeated solidity.test.abiv2fuzzer.Type params = 1;
required Block b = 2;
}
/// Expressions
message Expression {
oneof expr_oneof {
Literal lit = 1;
BinaryOp bop = 2;
UnaryOp uop = 3;
VarRef ref = 4;
}
}
message VarRef {
required int32 varnum = 1;
}
message Literal {
oneof literal_oneof {
bool blit = 1;
string slit = 2;
}
}
message BinaryOp {
enum Operation {
ADD = 1;
SUB = 2;
MUL = 3;
DIV = 4;
}
required Operation op = 1;
required Expression arg1 = 2;
required Expression arg2 = 3;
}
message UnaryOp {
enum Operation {
INC = 1;
DEC = 2;
}
required Operation op = 1;
required Expression arg = 2;
}
/// Statements
message ElseStmt {
required Block statements = 1;
}
message IfStmt {
required Expression condition = 1;
required Block statements = 2;
optional ElseStmt else = 3;
}
message ForStmt {
required Block pre = 1;
required Expression condition = 2;
required Block post = 3;
required Block body = 4;
}
message CaseStmt {
required Literal lit = 1;
required Block block = 2;
}
message SwitchStmt {
required Expression condition = 1;
repeated CaseStmt cases = 2;
optional Block default = 3;
}
message BreakStmt {}
message ContinueStmt {}
message ReturnStmt {
required Expression value = 1;
}
message DoStmt {
required Expression condition = 1;
required Block statements = 2;
}
message WhileStmt {
required Expression condition = 1;
required Block statements = 2;
}
message Statement {
oneof stmt_oneof {
solidity.test.abiv2fuzzer.VarDecl var = 1;
IfStmt ifstmt = 2;
ForStmt forstmt = 3;
SwitchStmt switchstmt = 4;
BreakStmt breakstmt = 5;
ContinueStmt continuestmt = 6;
ReturnStmt returnstmt = 7;
DoStmt dostmt = 8;
WhileStmt whilestmt = 9;
}
}
message Block {
repeated Statement statements = 1;
required Visibility vis = 1;
required StateMutability mut = 2;
required bool virtualfunc = 3;
}
message Library {
repeated solidity.test.abiv2fuzzer.VarDecl state_vars = 1;
repeated LibraryFunction funcdef = 2;
repeated LibraryFunction funcdef = 1;
required uint64 random = 2;
}
message Interface {
repeated solidity.test.abiv2fuzzer.VarDecl state_vars = 1;
repeated InterfaceFunction funcdef = 2;
repeated Interface ancestors = 3;
repeated InterfaceFunction funcdef = 1;
repeated Interface bases = 2;
}
message Contract {
repeated solidity.test.abiv2fuzzer.VarDecl state_vars = 1;
repeated ContractFunction funcdef = 2;
required bool abstract = 3;
repeated ContractOrInterface ancestors = 4;
repeated ContractFunction funcdef = 1;
required bool abstract = 2;
repeated ContractOrInterface bases = 3;
}
message ContractOrInterface {
@ -221,8 +90,19 @@ message ContractType {
}
}
message Program {
repeated ContractType contracts = 1;
message TestContract {
enum Type {
LIBRARY = 0;
CONTRACT = 1;
}
required Type type = 1;
required uint64 programidx = 2;
}
package solidity.test.solprotofuzzer;
message Program {
repeated ContractType contracts = 1;
required TestContract test = 2;
required uint64 seed = 3;
}
package solidity.test.solprotofuzzer;

View File

@ -17,16 +17,178 @@
#include <test/tools/ossfuzz/protoToSol.h>
#include <test/tools/ossfuzz/solProto.pb.h>
#include <test/tools/ossfuzz/abiV2FuzzerCommon.h>
#include <test/EVMHost.h>
#include <test/tools/fuzzer_common.h>
#include <evmone/evmone.h>
#include <src/libfuzzer/libfuzzer_macro.h>
#include <fstream>
static evmc::VM evmone = evmc::VM{evmc_create_evmone()};
using namespace solidity;
using namespace solidity::test;
using namespace solidity::test::solprotofuzzer;
using namespace solidity::util;
using namespace solidity::test::abiv2fuzzer;
using namespace std;
namespace
{
/// Compares two runs of EVMC returing true if they are
/// equal and false otherwise.
bool isOutputExpected(evmc::result const& _run1, evmc::result const& _run2)
{
if (_run1.output_size != _run2.output_size)
return false;
if (getenv("SOL_DEBUG_FILE") != nullptr)
{
unsigned r = _run1.output_data[31] | _run1.output_data[30] << 8 |
_run1.output_data[29] << 16 | _run1.output_data[28] << 24;
std::cout << r << std::endl;
}
return (memcmp(_run1.output_data, _run2.output_data, _run1.output_size) == 0);
}
/// Accepts a reference to a user-specified input and returns an
/// evmc_message with all of its fields zero initialized except
/// gas and input fields.
/// The gas field is set to the maximum permissible value so that we
/// don't run into out of gas errors. The input field is copied from
/// user input.
evmc_message initializeMessage(bytes const& _input)
{
// Zero initialize all message fields
evmc_message msg = {};
// Gas available (value of type int64_t) is set to its maximum
// value.
msg.gas = std::numeric_limits<int64_t>::max();
msg.input_data = _input.data();
msg.input_size = _input.size();
return msg;
}
/// Accepts host context implementation, and keccak256 hash of the function
/// to be called at a specified address in the simulated blockchain as
/// input and returns the result of the execution of the called function.
evmc::result executeContract(
EVMHost& _hostContext,
bytes const& _functionHash,
evmc_address _deployedAddress
)
{
evmc_message message = initializeMessage(_functionHash);
message.destination = _deployedAddress;
message.kind = EVMC_CALL;
return _hostContext.call(message);
}
/// Accepts a reference to host context implementation and byte code
/// as input and deploys it on the simulated blockchain. Returns the
/// result of deployment.
evmc::result deployContract(EVMHost& _hostContext, bytes const& _code)
{
evmc_message message = initializeMessage(_code);
message.kind = EVMC_CREATE;
return _hostContext.call(message);
}
std::pair<bytes, Json::Value> compileContract(
std::string _sourceCode,
std::string _contractName,
std::map<std::string, solidity::util::h160> const& _libraryAddresses = {},
frontend::OptimiserSettings _optimization = frontend::OptimiserSettings::minimal()
)
{
try
{
// Compile contract generated by the proto fuzzer
SolidityCompilationFramework solCompilationFramework;
return std::make_pair(
solCompilationFramework.compileContract(_sourceCode, _contractName, _libraryAddresses, _optimization),
solCompilationFramework.getMethodIdentifiers()
);
}
// Ignore stack too deep errors during compilation
catch (evmasm::StackTooDeepException const&)
{
return std::make_pair(bytes{}, Json::Value(0));
}
}
evmc::result deployAndExecute(EVMHost& _hostContext, bytes _byteCode, std::string _hexEncodedInput)
{
// Deploy contract and signal failure if deploy failed
evmc::result createResult = deployContract(_hostContext, _byteCode);
solAssert(
createResult.status_code == EVMC_SUCCESS,
"Proto solc fuzzer: Contract creation failed"
);
// Execute test function and signal failure if EVM reverted or
// did not return expected output on successful execution.
evmc::result callResult = executeContract(
_hostContext,
fromHex(_hexEncodedInput),
createResult.create_address
);
// We don't care about EVM One failures other than EVMC_REVERT
solAssert(callResult.status_code != EVMC_REVERT, "Proto solc fuzzer: EVM One reverted");
return callResult;
}
evmc::result compileDeployAndExecute(
std::string _sourceCode,
std::string _contractName,
std::string _methodName,
frontend::OptimiserSettings _optimization,
std::string _libraryName = {}
)
{
bytes libraryBytecode;
Json::Value libIds;
// We target the default EVM which is the latest
langutil::EVMVersion version = {};
EVMHost hostContext(version, evmone);
std::map<std::string, solidity::util::h160> _libraryAddressMap;
// First deploy library
if (!_libraryName.empty())
{
tie(libraryBytecode, libIds) = compileContract(
_sourceCode,
_libraryName,
{},
_optimization
);
// Deploy contract and signal failure if deploy failed
evmc::result createResult = deployContract(hostContext, libraryBytecode);
solAssert(
createResult.status_code == EVMC_SUCCESS,
"Proto solc fuzzer: Library deployment failed"
);
_libraryAddressMap[_libraryName] = EVMHost::convertFromEVMC(createResult.create_address);
}
auto [minimalBytecode, ids] = compileContract(
_sourceCode,
_contractName,
_libraryAddressMap,
_optimization
);
return deployAndExecute(
hostContext,
minimalBytecode,
ids[_methodName].asString()
);
}
}
DEFINE_PROTO_FUZZER(Program const& _input)
{
ProtoConverter converter;
@ -39,6 +201,47 @@ DEFINE_PROTO_FUZZER(Program const& _input)
ofstream of(dump_path);
of.write(sol_source.data(), sol_source.size());
}
FuzzerUtil::testCompiler(sol_source, /*optimize=*/false);
if (const char* dump_path = getenv("SOL_DEBUG_FILE"))
{
sol_source.clear();
// With libFuzzer binary run this to generate a YUL source file x.yul:
// PROTO_FUZZER_LOAD_PATH=x.yul ./a.out proto-input
ifstream ifstr(dump_path);
sol_source = {
std::istreambuf_iterator<char>(ifstr),
std::istreambuf_iterator<char>()
};
std::cout << sol_source << std::endl;
}
auto minimalResult = compileDeployAndExecute(
sol_source,
":C",
"test()",
frontend::OptimiserSettings::minimal(),
converter.libraryTest() ? converter.libraryName() : ""
);
auto optResult = compileDeployAndExecute(
sol_source,
":C",
"test()",
frontend::OptimiserSettings::standard(),
converter.libraryTest() ? converter.libraryName() : ""
);
// Both executions should either return success or identical evmone status code
bool successState = minimalResult.status_code == EVMC_SUCCESS && optResult.status_code == EVMC_SUCCESS;
bool identicalState = minimalResult.status_code == optResult.status_code;
bool executeState = successState || identicalState;
solAssert(executeState, "Proto solc fuzzer: Different execution status");
if (successState)
solAssert(
isOutputExpected(minimalResult, optResult),
"Proto solc fuzzer: Output mismatch"
);
return;
}
}