mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add sol proto adaptor
This commit is contained in:
parent
1426decff2
commit
05431616f2
@ -42,6 +42,7 @@ struct InternalCompilerError: virtual util::Exception {};
|
|||||||
struct FatalError: virtual util::Exception {};
|
struct FatalError: virtual util::Exception {};
|
||||||
struct UnimplementedFeatureError: virtual util::Exception {};
|
struct UnimplementedFeatureError: virtual util::Exception {};
|
||||||
struct InvalidAstError: 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.
|
/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
|
||||||
#define solAssert(CONDITION, DESCRIPTION) \
|
#define solAssert(CONDITION, DESCRIPTION) \
|
||||||
|
|||||||
@ -219,8 +219,9 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
else
|
else
|
||||||
solUnimplemented("Copying of type " + _sourceType.toString(false) + " to storage not yet supported.");
|
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>...
|
// 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,
|
2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16,
|
||||||
|
CompilerError,
|
||||||
"Stack too deep, try removing local variables."
|
"Stack too deep, try removing local variables."
|
||||||
);
|
);
|
||||||
// fetch target storage reference
|
// fetch target storage reference
|
||||||
|
|||||||
@ -455,7 +455,11 @@ void CompilerUtils::encodeToMemory(
|
|||||||
// leave end_of_mem as dyn head pointer
|
// leave end_of_mem as dyn head pointer
|
||||||
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
|
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
|
||||||
dynPointers++;
|
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
|
else
|
||||||
{
|
{
|
||||||
@ -497,8 +501,9 @@ void CompilerUtils::encodeToMemory(
|
|||||||
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
|
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
|
||||||
{
|
{
|
||||||
// copy tail pointer (=mem_end - mem_start) to memory
|
// copy tail pointer (=mem_end - mem_start) to memory
|
||||||
solAssert(
|
assertThrow(
|
||||||
(2 + dynPointers) <= 16,
|
(2 + dynPointers) <= 16,
|
||||||
|
CompilerError,
|
||||||
"Stack too deep(" + to_string(2 + dynPointers) + "), try using fewer variables."
|
"Stack too deep(" + to_string(2 + dynPointers) + "), try using fewer variables."
|
||||||
);
|
);
|
||||||
m_context << dupInstruction(2 + dynPointers) << Instruction::DUP2;
|
m_context << dupInstruction(2 + dynPointers) << Instruction::DUP2;
|
||||||
@ -1251,7 +1256,11 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
|||||||
|
|
||||||
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
|
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)
|
for (unsigned i = 0; i < _itemSize; ++i)
|
||||||
m_context << dupInstruction(_stackDepth);
|
m_context << dupInstruction(_stackDepth);
|
||||||
}
|
}
|
||||||
@ -1273,14 +1282,22 @@ void CompilerUtils::moveIntoStack(unsigned _stackDepth, unsigned _itemSize)
|
|||||||
|
|
||||||
void CompilerUtils::rotateStackUp(unsigned _items)
|
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)
|
for (unsigned i = 1; i < _items; ++i)
|
||||||
m_context << swapInstruction(_items - i);
|
m_context << swapInstruction(_items - i);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::rotateStackDown(unsigned _items)
|
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)
|
for (unsigned i = 1; i < _items; ++i)
|
||||||
m_context << swapInstruction(i);
|
m_context << swapInstruction(i);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -218,8 +218,9 @@ void CodeGenerator::assemble(
|
|||||||
}
|
}
|
||||||
catch (StackTooDeepError const& _e)
|
catch (StackTooDeepError const& _e)
|
||||||
{
|
{
|
||||||
yulAssert(
|
assertThrow(
|
||||||
false,
|
false,
|
||||||
|
CompilerError,
|
||||||
"Stack too deep when compiling inline assembly" +
|
"Stack too deep when compiling inline assembly" +
|
||||||
(_e.comment() ? ": " + *_e.comment() : ".")
|
(_e.comment() ? ": " + *_e.comment() : ".")
|
||||||
);
|
);
|
||||||
|
|||||||
@ -85,7 +85,7 @@ void FuzzerUtil::testCompiler(string const& _input, bool _optimize)
|
|||||||
compiler.setOptimiserSettings(optimiserSettings);
|
compiler.setOptimiserSettings(optimiserSettings);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
compiler.compile();
|
solAssert(compiler.compile(), "Compilation failed");
|
||||||
}
|
}
|
||||||
catch (Error const&)
|
catch (Error const&)
|
||||||
{
|
{
|
||||||
@ -96,6 +96,12 @@ void FuzzerUtil::testCompiler(string const& _input, bool _optimize)
|
|||||||
catch (UnimplementedFeatureError const&)
|
catch (UnimplementedFeatureError const&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
catch (CompilerError const&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
catch (StackTooDeepException const&)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FuzzerUtil::runCompiler(string const& _input, bool _quiet)
|
void FuzzerUtil::runCompiler(string const& _input, bool _quiet)
|
||||||
|
|||||||
@ -10,7 +10,7 @@ add_dependencies(ossfuzz
|
|||||||
|
|
||||||
if (OSSFUZZ)
|
if (OSSFUZZ)
|
||||||
add_custom_target(ossfuzz_proto)
|
add_custom_target(ossfuzz_proto)
|
||||||
add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz)
|
add_dependencies(ossfuzz_proto yul_proto_ossfuzz yul_proto_diff_ossfuzz sol_proto_ossfuzz)
|
||||||
|
|
||||||
add_custom_target(ossfuzz_abiv2)
|
add_custom_target(ossfuzz_abiv2)
|
||||||
add_dependencies(ossfuzz_abiv2 abiv2_proto_ossfuzz)
|
add_dependencies(ossfuzz_abiv2 abiv2_proto_ossfuzz)
|
||||||
@ -60,6 +60,25 @@ if (OSSFUZZ)
|
|||||||
)
|
)
|
||||||
set_target_properties(yul_proto_diff_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
set_target_properties(yul_proto_diff_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
||||||
|
|
||||||
|
# add_executable(yul_proto_diff_custom_mutate_ossfuzz
|
||||||
|
# yulProto_diff_ossfuzz.cpp
|
||||||
|
# yulFuzzerCommon.cpp
|
||||||
|
# protoToYul.cpp
|
||||||
|
# yulProto.pb.cc
|
||||||
|
# protomutators/YulProtoMutator.cpp
|
||||||
|
# )
|
||||||
|
# target_include_directories(yul_proto_diff_custom_mutate_ossfuzz PRIVATE /usr/include/libprotobuf-mutator
|
||||||
|
# /home/bhargava/work/github/solidity/deps/libprotobuf-mutator
|
||||||
|
# /home/bhargava/work/github/solidity/deps/LPM/external.protobuf/include
|
||||||
|
# )
|
||||||
|
# target_link_libraries(yul_proto_diff_custom_mutate_ossfuzz PRIVATE yul
|
||||||
|
# yulInterpreter
|
||||||
|
# protobuf-mutator-libfuzzer.a
|
||||||
|
# protobuf-mutator.a
|
||||||
|
# protobuf.a
|
||||||
|
# )
|
||||||
|
# set_target_properties(yul_proto_diff_custom_mutate_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
||||||
|
|
||||||
add_executable(abiv2_proto_ossfuzz
|
add_executable(abiv2_proto_ossfuzz
|
||||||
../../EVMHost.cpp
|
../../EVMHost.cpp
|
||||||
abiV2ProtoFuzzer.cpp
|
abiV2ProtoFuzzer.cpp
|
||||||
@ -78,6 +97,26 @@ if (OSSFUZZ)
|
|||||||
protobuf.a
|
protobuf.a
|
||||||
)
|
)
|
||||||
set_target_properties(abiv2_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
set_target_properties(abiv2_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
||||||
|
|
||||||
|
add_executable(sol_proto_ossfuzz
|
||||||
|
solProtoFuzzer.cpp
|
||||||
|
protoToSol.cpp
|
||||||
|
solProto.pb.cc
|
||||||
|
abiV2FuzzerCommon.cpp
|
||||||
|
../../EVMHost.cpp
|
||||||
|
SolProtoAdaptor.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(sol_proto_ossfuzz PRIVATE
|
||||||
|
/usr/include/libprotobuf-mutator
|
||||||
|
)
|
||||||
|
target_link_libraries(sol_proto_ossfuzz PRIVATE solidity libsolc
|
||||||
|
evmc
|
||||||
|
evmone-standalone
|
||||||
|
protobuf-mutator-libfuzzer.a
|
||||||
|
protobuf-mutator.a
|
||||||
|
protobuf.a
|
||||||
|
)
|
||||||
|
set_target_properties(sol_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
|
||||||
else()
|
else()
|
||||||
add_library(solc_opt_ossfuzz
|
add_library(solc_opt_ossfuzz
|
||||||
solc_opt_ossfuzz.cpp
|
solc_opt_ossfuzz.cpp
|
||||||
|
|||||||
1530
test/tools/ossfuzz/SolProtoAdaptor.cpp
Normal file
1530
test/tools/ossfuzz/SolProtoAdaptor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
607
test/tools/ossfuzz/SolProtoAdaptor.h
Normal file
607
test/tools/ossfuzz/SolProtoAdaptor.h
Normal file
@ -0,0 +1,607 @@
|
|||||||
|
/*
|
||||||
|
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>
|
||||||
|
#include <boost/variant/static_visitor.hpp>
|
||||||
|
|
||||||
|
namespace solidity::test::solprotofuzzer::adaptor
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Forward declarations
|
||||||
|
/// 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;
|
||||||
|
/// 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,
|
||||||
|
NONPAYABLE
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SolLibraryFunctionStateMutability
|
||||||
|
{
|
||||||
|
PURE,
|
||||||
|
VIEW,
|
||||||
|
NONPAYABLE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SolInterfaceFunction
|
||||||
|
{
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
MEMBERFUNCTION,
|
||||||
|
IMPLICITOVERRIDE,
|
||||||
|
EXPLICITOVERRIDE
|
||||||
|
};
|
||||||
|
|
||||||
|
SolInterfaceFunction(
|
||||||
|
std::string _functionName,
|
||||||
|
SolFunctionStateMutability _mutability,
|
||||||
|
Type _type,
|
||||||
|
std::string _contractName
|
||||||
|
);
|
||||||
|
bool operator==(SolInterfaceFunction const& _rhs) const;
|
||||||
|
bool operator!=(SolInterfaceFunction const& _rhs) const;
|
||||||
|
bool operator==(SolContractFunction const& _rhs) const;
|
||||||
|
bool operator!=(SolContractFunction const& _rhs) const;
|
||||||
|
void merge(SolInterfaceFunction const& _rhs);
|
||||||
|
bool namesake(SolInterfaceFunction const& _rhs) const;
|
||||||
|
bool namesake(SolContractFunction const& _rhs) const;
|
||||||
|
void markExplicitOverride(std::string _contractName);
|
||||||
|
|
||||||
|
std::string overriddenFromBaseNames() const;
|
||||||
|
unsigned numOverriddenFromBases() const
|
||||||
|
{
|
||||||
|
return m_overriddenFrom.size();
|
||||||
|
}
|
||||||
|
void resetOverriddenBases()
|
||||||
|
{
|
||||||
|
m_overriddenFrom.clear();
|
||||||
|
m_overriddenFrom.push_back(m_contractName);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string str() const;
|
||||||
|
|
||||||
|
std::string name() const
|
||||||
|
{
|
||||||
|
return m_functionName;
|
||||||
|
}
|
||||||
|
SolFunctionStateMutability mutability() const
|
||||||
|
{
|
||||||
|
return m_mutability;
|
||||||
|
}
|
||||||
|
bool explicitOverride() const
|
||||||
|
{
|
||||||
|
return m_type == Type::EXPLICITOVERRIDE;
|
||||||
|
}
|
||||||
|
bool implicitOverride() const
|
||||||
|
{
|
||||||
|
return m_type == Type::IMPLICITOVERRIDE;
|
||||||
|
}
|
||||||
|
bool memberFunction() const
|
||||||
|
{
|
||||||
|
return m_type == Type::MEMBERFUNCTION;
|
||||||
|
}
|
||||||
|
void markImplicitOverride()
|
||||||
|
{
|
||||||
|
m_type = Type::IMPLICITOVERRIDE;
|
||||||
|
}
|
||||||
|
bool multipleBases() const
|
||||||
|
{
|
||||||
|
return m_overriddenFrom.size() > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string m_functionName;
|
||||||
|
SolFunctionStateMutability m_mutability = SolFunctionStateMutability::PURE;
|
||||||
|
std::vector<std::string> m_overriddenFrom;
|
||||||
|
std::string m_contractName;
|
||||||
|
Type m_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SolContractFunction
|
||||||
|
{
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
MEMBERFUNCTION,
|
||||||
|
IMPLICITOVERRIDECONTRACT,
|
||||||
|
IMPLICITOVERRIDEINTERFACE,
|
||||||
|
EXPLICITOVERRIDECONTRACT,
|
||||||
|
EXPLICITOVERRIDEINTERFACE
|
||||||
|
};
|
||||||
|
|
||||||
|
SolContractFunction(
|
||||||
|
ContractFunction const& _function,
|
||||||
|
std::string _contractName,
|
||||||
|
std::string _functionName,
|
||||||
|
Type _type,
|
||||||
|
bool _implement,
|
||||||
|
std::string _returnValue
|
||||||
|
);
|
||||||
|
SolContractFunction(
|
||||||
|
std::vector<std::string> _overriddenFrom,
|
||||||
|
SolFunctionStateMutability _mutability,
|
||||||
|
std::string _contractName,
|
||||||
|
std::string _functionName,
|
||||||
|
Type _type,
|
||||||
|
bool _implement,
|
||||||
|
bool _virtual,
|
||||||
|
std::string _returnValue,
|
||||||
|
SolFunctionVisibility _vis = SolFunctionVisibility::EXTERNAL
|
||||||
|
);
|
||||||
|
|
||||||
|
bool memberFunction() const
|
||||||
|
{
|
||||||
|
return m_type == Type::MEMBERFUNCTION;
|
||||||
|
}
|
||||||
|
bool explicitInterfaceOverride() const
|
||||||
|
{
|
||||||
|
return m_type == Type::EXPLICITOVERRIDEINTERFACE;
|
||||||
|
}
|
||||||
|
bool explicitContractOverride() const
|
||||||
|
{
|
||||||
|
return m_type == Type::EXPLICITOVERRIDECONTRACT;
|
||||||
|
}
|
||||||
|
bool implicitInterfaceOverride() const
|
||||||
|
{
|
||||||
|
return m_type == Type::IMPLICITOVERRIDEINTERFACE;
|
||||||
|
}
|
||||||
|
bool implicitContractOverride() const
|
||||||
|
{
|
||||||
|
return m_type == Type::IMPLICITOVERRIDECONTRACT;
|
||||||
|
}
|
||||||
|
bool explicitOverride() const
|
||||||
|
{
|
||||||
|
return explicitContractOverride() || explicitInterfaceOverride();
|
||||||
|
}
|
||||||
|
bool implicitOverride() const
|
||||||
|
{
|
||||||
|
return implicitContractOverride() || implicitInterfaceOverride();
|
||||||
|
}
|
||||||
|
void markImplicitContractOverride()
|
||||||
|
{
|
||||||
|
m_type = Type::IMPLICITOVERRIDECONTRACT;
|
||||||
|
}
|
||||||
|
void markImplicitInterfaceOverride()
|
||||||
|
{
|
||||||
|
m_type = Type::IMPLICITOVERRIDEINTERFACE;
|
||||||
|
}
|
||||||
|
void markExplicitContractOverride(std::string _contractName)
|
||||||
|
{
|
||||||
|
m_type = Type::EXPLICITOVERRIDECONTRACT;
|
||||||
|
m_contractName = _contractName;
|
||||||
|
m_overriddenFrom.clear();
|
||||||
|
m_overriddenFrom.push_back(m_contractName);
|
||||||
|
}
|
||||||
|
void markExplicitInterfaceOverride(std::string _contractName)
|
||||||
|
{
|
||||||
|
m_type = Type::EXPLICITOVERRIDEINTERFACE;
|
||||||
|
m_contractName = _contractName;
|
||||||
|
m_overriddenFrom.clear();
|
||||||
|
m_overriddenFrom.push_back(m_contractName);
|
||||||
|
}
|
||||||
|
std::string overriddenFromBaseNames() const;
|
||||||
|
unsigned numOverriddenFromBases() const
|
||||||
|
{
|
||||||
|
return m_overriddenFrom.size();
|
||||||
|
}
|
||||||
|
void resetOverriddenBases()
|
||||||
|
{
|
||||||
|
m_overriddenFrom.clear();
|
||||||
|
m_overriddenFrom.push_back(m_contractName);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool namesake(SolContractFunction const& _rhs) const;
|
||||||
|
bool namesake(SolInterfaceFunction const& _rhs) const;
|
||||||
|
bool operator==(SolContractFunction const& _rhs) const;
|
||||||
|
bool operator!=(SolContractFunction const& _rhs) const;
|
||||||
|
bool operator==(SolInterfaceFunction const& _rhs) const;
|
||||||
|
bool operator!=(SolInterfaceFunction const& _rhs) const;
|
||||||
|
void merge(SolContractFunction const& _rhs);
|
||||||
|
void merge(SolInterfaceFunction const& _rhs);
|
||||||
|
bool disallowed() 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;
|
||||||
|
}
|
||||||
|
bool implemented() const
|
||||||
|
{
|
||||||
|
return m_implemented;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
bool m_implemented = true;
|
||||||
|
Type m_type;
|
||||||
|
std::vector<std::string> m_overriddenFrom;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 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::shared_ptr<SolRandomNumGenerator> _prng);
|
||||||
|
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;
|
||||||
|
unsigned randomNumber() const
|
||||||
|
{
|
||||||
|
return m_prng->operator()();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a pair of function name and expected output
|
||||||
|
/// that is pseudo randomly chosen from the list of all
|
||||||
|
/// library functions.
|
||||||
|
std::pair<std::string, std::string> pseudoRandomTest();
|
||||||
|
|
||||||
|
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(random());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string m_libraryName;
|
||||||
|
unsigned m_functionIndex = 0;
|
||||||
|
unsigned m_returnValue = 0;
|
||||||
|
std::shared_ptr<SolRandomNumGenerator> m_prng;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SolBaseContract
|
||||||
|
{
|
||||||
|
enum BaseType
|
||||||
|
{
|
||||||
|
INTERFACE,
|
||||||
|
CONTRACT
|
||||||
|
};
|
||||||
|
|
||||||
|
SolBaseContract(
|
||||||
|
ProtoBaseContract _base,
|
||||||
|
std::string _name,
|
||||||
|
std::shared_ptr<SolRandomNumGenerator> _prng
|
||||||
|
);
|
||||||
|
|
||||||
|
SolBaseContract(
|
||||||
|
ProtoBaseContract _base,
|
||||||
|
std::string _name,
|
||||||
|
std::shared_ptr<SolBaseContract> _cyclicBase,
|
||||||
|
std::shared_ptr<SolRandomNumGenerator> _prng
|
||||||
|
);
|
||||||
|
|
||||||
|
SolBaseContract(std::shared_ptr<SolInterface> _base);
|
||||||
|
|
||||||
|
BaseType type() const;
|
||||||
|
std::string name();
|
||||||
|
std::string str();
|
||||||
|
std::shared_ptr<SolInterface> interface()
|
||||||
|
{
|
||||||
|
return std::get<std::shared_ptr<SolInterface>>(m_base);
|
||||||
|
}
|
||||||
|
std::shared_ptr<SolContract> contract()
|
||||||
|
{
|
||||||
|
return std::get<std::shared_ptr<SolContract>>(m_base);
|
||||||
|
}
|
||||||
|
std::shared_ptr<SolBaseContract> randomBase();
|
||||||
|
bool atleastOneBase();
|
||||||
|
|
||||||
|
unsigned functionIndex();
|
||||||
|
std::string lastBaseName();
|
||||||
|
|
||||||
|
std::variant<std::shared_ptr<SolInterface>, std::shared_ptr<SolContract>> m_base;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SolContract
|
||||||
|
{
|
||||||
|
enum class Function
|
||||||
|
{
|
||||||
|
INTERFACE,
|
||||||
|
CONTRACT
|
||||||
|
};
|
||||||
|
|
||||||
|
using FunctionType = std::variant<std::shared_ptr<SolContractFunction>, std::shared_ptr<SolInterfaceFunction>>;
|
||||||
|
using FunctionList = std::vector<FunctionType>;
|
||||||
|
|
||||||
|
SolContract(
|
||||||
|
Contract const& _contract,
|
||||||
|
std::string _name,
|
||||||
|
std::shared_ptr<SolBaseContract> _cyclicBase,
|
||||||
|
std::shared_ptr<SolRandomNumGenerator> _prng
|
||||||
|
);
|
||||||
|
|
||||||
|
bool mergeFunctionBasetoContract(FunctionType _f1, FunctionType _f2);
|
||||||
|
void explicitOverrides();
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
bool requiresMerge(T1 _f1, T2 _f2)
|
||||||
|
{
|
||||||
|
return _f1->namesake(*_f2);
|
||||||
|
}
|
||||||
|
std::shared_ptr<SolBaseContract> randomBase()
|
||||||
|
{
|
||||||
|
return m_baseContracts[randomNumber() % m_baseContracts.size()];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool atleastOneBase();
|
||||||
|
void merge();
|
||||||
|
std::string str();
|
||||||
|
void addFunctions(Contract const& _contract);
|
||||||
|
void addBases(Contract const& _contract);
|
||||||
|
void copyFunctions(std::shared_ptr<SolBaseContract> _base, FunctionList& _globalList);
|
||||||
|
|
||||||
|
bool validTest();
|
||||||
|
std::string baseNames() const;
|
||||||
|
void validContractTests(
|
||||||
|
std::map<std::string, std::map<std::string, std::string>>& _testSet
|
||||||
|
);
|
||||||
|
|
||||||
|
unsigned randomNumber() const
|
||||||
|
{
|
||||||
|
return m_prng->operator()();
|
||||||
|
}
|
||||||
|
bool coinToss() const
|
||||||
|
{
|
||||||
|
return randomNumber() % 2 == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name() const
|
||||||
|
{
|
||||||
|
return m_contractName;
|
||||||
|
}
|
||||||
|
bool abstract() const
|
||||||
|
{
|
||||||
|
return m_abstract;
|
||||||
|
}
|
||||||
|
std::string newFunctionName()
|
||||||
|
{
|
||||||
|
return "f" + std::to_string(m_functionIndex++);
|
||||||
|
}
|
||||||
|
unsigned functionIndex() const
|
||||||
|
{
|
||||||
|
return m_functionIndex;
|
||||||
|
}
|
||||||
|
std::string newBaseName()
|
||||||
|
{
|
||||||
|
m_lastBaseName += "B";
|
||||||
|
return m_lastBaseName;
|
||||||
|
}
|
||||||
|
std::string lastBaseName() const
|
||||||
|
{
|
||||||
|
return m_lastBaseName;
|
||||||
|
}
|
||||||
|
std::string newReturnValue()
|
||||||
|
{
|
||||||
|
return std::to_string(randomNumber());
|
||||||
|
}
|
||||||
|
Function functionType(FunctionType _type)
|
||||||
|
{
|
||||||
|
if (std::holds_alternative<std::shared_ptr<SolContractFunction>>(_type))
|
||||||
|
return Function::CONTRACT;
|
||||||
|
else
|
||||||
|
return Function::INTERFACE;
|
||||||
|
}
|
||||||
|
bool contractFunction(FunctionType _type)
|
||||||
|
{
|
||||||
|
return functionType(_type) == Function::CONTRACT;
|
||||||
|
}
|
||||||
|
bool interfaceFunction(FunctionType _type)
|
||||||
|
{
|
||||||
|
return functionType(_type) == Function::INTERFACE;
|
||||||
|
}
|
||||||
|
std::shared_ptr<SolContractFunction> getContractFunction(FunctionType _type)
|
||||||
|
{
|
||||||
|
return std::get<std::shared_ptr<SolContractFunction>>(_type);
|
||||||
|
}
|
||||||
|
std::shared_ptr<SolInterfaceFunction> getInterfaceFunction(FunctionType _type)
|
||||||
|
{
|
||||||
|
return std::get<std::shared_ptr<SolInterfaceFunction>>(_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string m_contractName;
|
||||||
|
bool m_abstract = false;
|
||||||
|
unsigned m_functionIndex = 0;
|
||||||
|
unsigned m_returnValue = 0;
|
||||||
|
std::string m_lastBaseName;
|
||||||
|
FunctionList m_functions;
|
||||||
|
std::vector<std::shared_ptr<SolBaseContract>> m_baseContracts;
|
||||||
|
/// Maps non abstract contract name to list of publicly exposed function name
|
||||||
|
/// and their expected output
|
||||||
|
std::map<std::string, std::string> m_contractFunctionMap;
|
||||||
|
std::shared_ptr<SolRandomNumGenerator> m_prng;
|
||||||
|
std::shared_ptr<SolBaseContract> m_cyclicBase;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SolInterface
|
||||||
|
{
|
||||||
|
SolInterface(
|
||||||
|
Interface const& _interface,
|
||||||
|
std::string _interfaceName,
|
||||||
|
std::shared_ptr<SolInterface> _cyclicBase,
|
||||||
|
std::shared_ptr<SolRandomNumGenerator> _prng
|
||||||
|
);
|
||||||
|
|
||||||
|
void merge();
|
||||||
|
|
||||||
|
std::string name() const
|
||||||
|
{
|
||||||
|
return m_interfaceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned randomNumber() const
|
||||||
|
{
|
||||||
|
return m_prng->operator()();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool coinToss() const
|
||||||
|
{
|
||||||
|
return randomNumber() % 2 == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool atleastOneBase()
|
||||||
|
{
|
||||||
|
return !m_baseInterfaces.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<SolInterface> randomBase()
|
||||||
|
{
|
||||||
|
return m_baseInterfaces[randomNumber() % m_baseInterfaces.size()];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string newFunctionName()
|
||||||
|
{
|
||||||
|
return "f" + std::to_string(m_functionIndex++);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// Returns the Solidity code for all base interfaces
|
||||||
|
/// inherited by this interface.
|
||||||
|
std::string baseInterfaceStr() const;
|
||||||
|
std::string functionStr() const;
|
||||||
|
bool bases() const
|
||||||
|
{
|
||||||
|
return m_baseInterfaces.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
|
||||||
|
unsigned m_functionIndex = 0;
|
||||||
|
std::string m_lastBaseName;
|
||||||
|
std::string m_interfaceName;
|
||||||
|
std::vector<std::shared_ptr<SolInterfaceFunction>> m_functions;
|
||||||
|
std::vector<std::shared_ptr<SolInterface>> m_baseInterfaces;
|
||||||
|
std::shared_ptr<SolInterface> m_cyclicBaseInterface;
|
||||||
|
std::shared_ptr<SolRandomNumGenerator> m_prng;
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -704,7 +704,7 @@ void TypeVisitor::structDefinition(StructType const& _type)
|
|||||||
// Commence struct declaration
|
// Commence struct declaration
|
||||||
string structDef = lineString(
|
string structDef = lineString(
|
||||||
"struct " +
|
"struct " +
|
||||||
string(s_structNamePrefix) +
|
m_structPrefix +
|
||||||
to_string(m_structCounter) +
|
to_string(m_structCounter) +
|
||||||
" {"
|
" {"
|
||||||
);
|
);
|
||||||
@ -718,7 +718,7 @@ void TypeVisitor::structDefinition(StructType const& _type)
|
|||||||
if (!ValidityVisitor().visit(t))
|
if (!ValidityVisitor().visit(t))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
TypeVisitor tVisitor(m_structCounter + 1);
|
TypeVisitor tVisitor(m_structCounter + 1, m_indentation - 1, m_structPrefix);
|
||||||
type = tVisitor.visit(t);
|
type = tVisitor.visit(t);
|
||||||
m_structCounter += tVisitor.numStructs();
|
m_structCounter += tVisitor.numStructs();
|
||||||
m_structDef << tVisitor.structDef();
|
m_structDef << tVisitor.structDef();
|
||||||
@ -749,7 +749,7 @@ string TypeVisitor::visit(StructType const& _type)
|
|||||||
// Set last dyn param if struct contains a dyn param e.g., bytes, array etc.
|
// Set last dyn param if struct contains a dyn param e.g., bytes, array etc.
|
||||||
m_isLastDynParamRightPadded = DynParamVisitor().visit(_type);
|
m_isLastDynParamRightPadded = DynParamVisitor().visit(_type);
|
||||||
// If top-level struct is a non-emtpy struct, assign the name S<suffix>
|
// If top-level struct is a non-emtpy struct, assign the name S<suffix>
|
||||||
m_baseType = s_structTypeName + to_string(m_structStartCounter);
|
m_baseType = m_structPrefix + to_string(m_structStartCounter);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_baseType = {};
|
m_baseType = {};
|
||||||
|
|||||||
@ -152,7 +152,7 @@ public:
|
|||||||
ProtoConverter(ProtoConverter const&) = delete;
|
ProtoConverter(ProtoConverter const&) = delete;
|
||||||
ProtoConverter(ProtoConverter&&) = delete;
|
ProtoConverter(ProtoConverter&&) = delete;
|
||||||
std::string contractToString(Contract const& _input);
|
std::string contractToString(Contract const& _input);
|
||||||
private:
|
|
||||||
enum class Delimiter
|
enum class Delimiter
|
||||||
{
|
{
|
||||||
ADD,
|
ADD,
|
||||||
@ -205,7 +205,7 @@ private:
|
|||||||
/// Solidity code to be placed inside test function scope.
|
/// Solidity code to be placed inside test function scope.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::pair<std::string, std::string> processType(T const& _type, bool _isValueType);
|
std::pair<std::string, std::string> processType(T const& _type, bool _isValueType);
|
||||||
|
private:
|
||||||
/// Convert a protobuf type @a _T into Solidity variable assignment and check
|
/// Convert a protobuf type @a _T into Solidity variable assignment and check
|
||||||
/// statements to be placed inside contract and test function scopes.
|
/// statements to be placed inside contract and test function scopes.
|
||||||
/// @param: _varName is the name of the Solidity variable
|
/// @param: _varName is the name of the Solidity variable
|
||||||
@ -348,6 +348,8 @@ private:
|
|||||||
return s_paramNamePrefix;
|
return s_paramNamePrefix;
|
||||||
case Contract_Test::Contract_Test_RETURNDATA_CODER:
|
case Contract_Test::Contract_Test_RETURNDATA_CODER:
|
||||||
return s_localVarNamePrefix;
|
return s_localVarNamePrefix;
|
||||||
|
default:
|
||||||
|
return s_localVarNamePrefix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,6 +377,7 @@ private:
|
|||||||
std::ostringstream m_typedReturn;
|
std::ostringstream m_typedReturn;
|
||||||
/// Argument names to be passed to coder functions
|
/// Argument names to be passed to coder functions
|
||||||
std::ostringstream m_argsCoder;
|
std::ostringstream m_argsCoder;
|
||||||
|
public:
|
||||||
/// Predicate that is true if we are in contract scope
|
/// Predicate that is true if we are in contract scope
|
||||||
bool m_isStateVar;
|
bool m_isStateVar;
|
||||||
unsigned m_counter;
|
unsigned m_counter;
|
||||||
@ -588,12 +591,13 @@ private:
|
|||||||
class TypeVisitor: public AbiV2ProtoVisitor<std::string>
|
class TypeVisitor: public AbiV2ProtoVisitor<std::string>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TypeVisitor(unsigned _structSuffix = 0):
|
TypeVisitor(unsigned _structSuffix = 0, unsigned _indentation = 1, std::string _structPrefix = "S"):
|
||||||
m_indentation(1),
|
m_indentation(_indentation),
|
||||||
m_structCounter(_structSuffix),
|
m_structCounter(_structSuffix),
|
||||||
m_structStartCounter(_structSuffix),
|
m_structStartCounter(_structSuffix),
|
||||||
m_structFieldCounter(0),
|
m_structFieldCounter(0),
|
||||||
m_isLastDynParamRightPadded(false)
|
m_isLastDynParamRightPadded(false),
|
||||||
|
m_structPrefix(_structPrefix)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
std::string visit(BoolType const&) override;
|
std::string visit(BoolType const&) override;
|
||||||
@ -649,8 +653,7 @@ private:
|
|||||||
unsigned m_structStartCounter;
|
unsigned m_structStartCounter;
|
||||||
unsigned m_structFieldCounter;
|
unsigned m_structFieldCounter;
|
||||||
bool m_isLastDynParamRightPadded;
|
bool m_isLastDynParamRightPadded;
|
||||||
|
std::string m_structPrefix;
|
||||||
static auto constexpr s_structTypeName = "S";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns a pair of strings, first of which contains assignment statements
|
/// Returns a pair of strings, first of which contains assignment statements
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <test/tools/ossfuzz/protoToSol.h>
|
#include <test/tools/ossfuzz/protoToSol.h>
|
||||||
|
#include <test/tools/ossfuzz/SolProtoAdaptor.h>
|
||||||
|
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
@ -24,8 +25,9 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
using namespace solidity::test::solprotofuzzer;
|
using namespace solidity::test::solprotofuzzer;
|
||||||
using namespace solidity::util;
|
using namespace solidity::test::solprotofuzzer::adaptor;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace solidity::util;
|
||||||
|
|
||||||
string ProtoConverter::protoToSolidity(Program const& _p)
|
string ProtoConverter::protoToSolidity(Program const& _p)
|
||||||
{
|
{
|
||||||
@ -78,6 +80,7 @@ pair<string, string> ProtoConverter::generateTestCase(TestContract const& _testC
|
|||||||
break;
|
break;
|
||||||
string contractName = testTuple.first;
|
string contractName = testTuple.first;
|
||||||
string contractVarName = "tc" + to_string(contractVarIndex);
|
string contractVarName = "tc" + to_string(contractVarIndex);
|
||||||
|
|
||||||
Whiskers init(R"(<endl><ind><contractName> <contractVarName> = new <contractName>();)");
|
Whiskers init(R"(<endl><ind><contractName> <contractVarName> = new <contractName>();)");
|
||||||
init("endl", "\n");
|
init("endl", "\n");
|
||||||
init("ind", "\t\t");
|
init("ind", "\t\t");
|
||||||
@ -179,20 +182,72 @@ string ProtoConverter::visit(ContractType const& _contractType)
|
|||||||
|
|
||||||
string ProtoConverter::visit(Contract const& _contract)
|
string ProtoConverter::visit(Contract const& _contract)
|
||||||
{
|
{
|
||||||
|
if (_contract.funcdef_size() == 0 && _contract.bases_size() == 0)
|
||||||
|
return "";
|
||||||
|
|
||||||
openProgramScope(&_contract);
|
openProgramScope(&_contract);
|
||||||
return "";
|
try {
|
||||||
|
auto contract = SolContract(_contract, programName(&_contract), nullptr, m_randomGen);
|
||||||
|
if (contract.validTest())
|
||||||
|
{
|
||||||
|
map<string, map<string, string>> testSet;
|
||||||
|
contract.validContractTests(testSet);
|
||||||
|
for (auto &contractTestSet: testSet)
|
||||||
|
{
|
||||||
|
m_contractTests.insert(pair(contractTestSet.first, map<string, string>{}));
|
||||||
|
for (auto &contractTest: contractTestSet.second)
|
||||||
|
m_contractTests[contractTestSet.first].insert(
|
||||||
|
make_pair(contractTest.first, contractTest.second)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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 exception thrown when input specification is invalid e.g.
|
||||||
|
// invalid inheritance hierarchy
|
||||||
|
catch (langutil::FuzzerError const& error)
|
||||||
|
{
|
||||||
|
// Return empty string if input specification is invalid.
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string ProtoConverter::visit(Interface const& _interface)
|
string ProtoConverter::visit(Interface const& _interface)
|
||||||
{
|
{
|
||||||
|
if (_interface.funcdef_size() == 0 && _interface.bases_size() == 0)
|
||||||
|
return "";
|
||||||
|
|
||||||
openProgramScope(&_interface);
|
openProgramScope(&_interface);
|
||||||
return "";
|
try {
|
||||||
|
auto interface = SolInterface(_interface, programName(&_interface), nullptr, m_randomGen);
|
||||||
|
return interface.str();
|
||||||
|
}
|
||||||
|
catch (langutil::FuzzerError const& error)
|
||||||
|
{
|
||||||
|
// Return empty string if input specification is invalid.
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string ProtoConverter::visit(Library const& _library)
|
string ProtoConverter::visit(Library const& _library)
|
||||||
{
|
{
|
||||||
|
if (emptyLibrary(_library))
|
||||||
|
return "";
|
||||||
|
|
||||||
openProgramScope(&_library);
|
openProgramScope(&_library);
|
||||||
return "";
|
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()
|
tuple<string, string, string> ProtoConverter::pseudoRandomLibraryTest()
|
||||||
|
|||||||
@ -111,6 +111,7 @@ struct SolRandomNumGenerator
|
|||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class ProtoConverter
|
class ProtoConverter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|||||||
275
test/tools/ossfuzz/solProtoFuzzer.cpp
Normal file
275
test/tools/ossfuzz/solProtoFuzzer.cpp
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
/*
|
||||||
|
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/solProto.pb.h>
|
||||||
|
#include <test/tools/ossfuzz/abiV2FuzzerCommon.h>
|
||||||
|
#include <test/EVMHost.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
|
||||||
|
{
|
||||||
|
/// Test function returns a uint256 value
|
||||||
|
static size_t const expectedOutputLength = 32;
|
||||||
|
/// Expected output value is decimal 0
|
||||||
|
static uint8_t const expectedOutput[expectedOutputLength] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Compares the contents of the memory address pointed to
|
||||||
|
/// by `_result` of `_length` bytes to the expected output.
|
||||||
|
/// Returns true if `_result` matches expected output, false
|
||||||
|
/// otherwise.
|
||||||
|
bool isOutputExpected(evmc::result const& _run)
|
||||||
|
{
|
||||||
|
if (_run.output_size != expectedOutputLength)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return (memcmp(_run.output_data, expectedOutput, expectedOutputLength) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compares two runs of EVMC returing true if they are
|
||||||
|
/// equal and false otherwise.
|
||||||
|
#if 0
|
||||||
|
bool isDifferentialOutputExpected(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);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// 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 [bytecode, ids] = compileContract(
|
||||||
|
_sourceCode,
|
||||||
|
_contractName,
|
||||||
|
_libraryAddressMap,
|
||||||
|
_optimization
|
||||||
|
);
|
||||||
|
|
||||||
|
return deployAndExecute(
|
||||||
|
hostContext,
|
||||||
|
bytecode,
|
||||||
|
ids[_methodName].asString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_PROTO_FUZZER(Program const& _input)
|
||||||
|
{
|
||||||
|
ProtoConverter converter;
|
||||||
|
string sol_source = converter.protoToSolidity(_input);
|
||||||
|
|
||||||
|
if (const char* dump_path = getenv("PROTO_FUZZER_DUMP_PATH"))
|
||||||
|
{
|
||||||
|
// With libFuzzer binary run this to generate a YUL source file x.yul:
|
||||||
|
// PROTO_FUZZER_DUMP_PATH=x.yul ./a.out proto-input
|
||||||
|
ofstream of(dump_path);
|
||||||
|
of.write(sol_source.data(), sol_source.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
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() : ""
|
||||||
|
);
|
||||||
|
bool successState = minimalResult.status_code == EVMC_SUCCESS;
|
||||||
|
if (successState)
|
||||||
|
solAssert(
|
||||||
|
isOutputExpected(minimalResult),
|
||||||
|
"Proto solc fuzzer: Output incorrect"
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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(
|
||||||
|
// isDifferentialOutputExpected(minimalResult, optResult),
|
||||||
|
// "Proto solc fuzzer: Output mismatch"
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// return;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user