mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add fuzzer for testing clean up of dirty bits via inline assembly
This commit is contained in:
parent
4e58c672bb
commit
7c91a31cce
@ -96,6 +96,7 @@ defaults:
|
|||||||
- test/tools/ossfuzz/strictasm_diff_ossfuzz
|
- test/tools/ossfuzz/strictasm_diff_ossfuzz
|
||||||
- test/tools/ossfuzz/strictasm_opt_ossfuzz
|
- test/tools/ossfuzz/strictasm_opt_ossfuzz
|
||||||
- test/tools/ossfuzz/yul_proto_diff_ossfuzz
|
- test/tools/ossfuzz/yul_proto_diff_ossfuzz
|
||||||
|
- test/tools/ossfuzz/yul_proto_diff_custom_mutate_ossfuzz
|
||||||
- test/tools/ossfuzz/yul_proto_ossfuzz
|
- test/tools/ossfuzz/yul_proto_ossfuzz
|
||||||
|
|
||||||
# test result output directory
|
# test result output directory
|
||||||
|
@ -37,6 +37,8 @@ namespace solidity::langutil
|
|||||||
class Error;
|
class Error;
|
||||||
using ErrorList = std::vector<std::shared_ptr<Error const>>;
|
using ErrorList = std::vector<std::shared_ptr<Error const>>;
|
||||||
|
|
||||||
|
struct FuzzerError: virtual util::Exception {};
|
||||||
|
struct StackTooDeepError: virtual util::Exception {};
|
||||||
struct CompilerError: virtual util::Exception {};
|
struct CompilerError: virtual util::Exception {};
|
||||||
struct InternalCompilerError: virtual util::Exception {};
|
struct InternalCompilerError: virtual util::Exception {};
|
||||||
struct FatalError: virtual util::Exception {};
|
struct FatalError: virtual util::Exception {};
|
||||||
|
@ -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,
|
||||||
|
StackTooDeepError,
|
||||||
"Stack too deep, try removing local variables."
|
"Stack too deep, try removing local variables."
|
||||||
);
|
);
|
||||||
// fetch target storage reference
|
// fetch target storage reference
|
||||||
|
@ -405,7 +405,7 @@ void CompilerContext::appendInlineAssembly(
|
|||||||
stackDiff -= 1;
|
stackDiff -= 1;
|
||||||
if (stackDiff < 1 || stackDiff > 16)
|
if (stackDiff < 1 || stackDiff > 16)
|
||||||
BOOST_THROW_EXCEPTION(
|
BOOST_THROW_EXCEPTION(
|
||||||
CompilerError() <<
|
StackTooDeepError() <<
|
||||||
errinfo_sourceLocation(_identifier.location) <<
|
errinfo_sourceLocation(_identifier.location) <<
|
||||||
util::errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.")
|
util::errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.")
|
||||||
);
|
);
|
||||||
|
@ -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,
|
||||||
|
StackTooDeepError,
|
||||||
|
"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,
|
||||||
|
StackTooDeepError,
|
||||||
"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;
|
||||||
@ -1241,7 +1246,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
|||||||
// move variable starting from its top end in the stack
|
// move variable starting from its top end in the stack
|
||||||
if (stackPosition - size + 1 > 16)
|
if (stackPosition - size + 1 > 16)
|
||||||
BOOST_THROW_EXCEPTION(
|
BOOST_THROW_EXCEPTION(
|
||||||
CompilerError() <<
|
StackTooDeepError() <<
|
||||||
errinfo_sourceLocation(_variable.location()) <<
|
errinfo_sourceLocation(_variable.location()) <<
|
||||||
util::errinfo_comment("Stack too deep, try removing local variables.")
|
util::errinfo_comment("Stack too deep, try removing local variables.")
|
||||||
);
|
);
|
||||||
@ -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,
|
||||||
|
StackTooDeepError,
|
||||||
|
"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,
|
||||||
|
StackTooDeepError,
|
||||||
|
"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,
|
||||||
|
StackTooDeepError,
|
||||||
|
"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);
|
||||||
}
|
}
|
||||||
|
@ -631,7 +631,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
|||||||
|
|
||||||
if (stackLayout.size() > 17)
|
if (stackLayout.size() > 17)
|
||||||
BOOST_THROW_EXCEPTION(
|
BOOST_THROW_EXCEPTION(
|
||||||
CompilerError() <<
|
StackTooDeepError() <<
|
||||||
errinfo_sourceLocation(_function.location()) <<
|
errinfo_sourceLocation(_function.location()) <<
|
||||||
errinfo_comment("Stack too deep, try removing local variables.")
|
errinfo_comment("Stack too deep, try removing local variables.")
|
||||||
);
|
);
|
||||||
@ -795,7 +795,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
solAssert(variable->type()->sizeOnStack() == 1, "");
|
solAssert(variable->type()->sizeOnStack() == 1, "");
|
||||||
if (stackDiff < 1 || stackDiff > 16)
|
if (stackDiff < 1 || stackDiff > 16)
|
||||||
BOOST_THROW_EXCEPTION(
|
BOOST_THROW_EXCEPTION(
|
||||||
CompilerError() <<
|
StackTooDeepError() <<
|
||||||
errinfo_sourceLocation(_inlineAssembly.location()) <<
|
errinfo_sourceLocation(_inlineAssembly.location()) <<
|
||||||
errinfo_comment("Stack too deep, try removing local variables.")
|
errinfo_comment("Stack too deep, try removing local variables.")
|
||||||
);
|
);
|
||||||
@ -828,7 +828,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
int stackDiff = _assembly.stackHeight() - m_context.baseStackOffsetOfVariable(*variable) - 1;
|
int stackDiff = _assembly.stackHeight() - m_context.baseStackOffsetOfVariable(*variable) - 1;
|
||||||
if (stackDiff > 16 || stackDiff < 1)
|
if (stackDiff > 16 || stackDiff < 1)
|
||||||
BOOST_THROW_EXCEPTION(
|
BOOST_THROW_EXCEPTION(
|
||||||
CompilerError() <<
|
StackTooDeepError() <<
|
||||||
errinfo_sourceLocation(_inlineAssembly.location()) <<
|
errinfo_sourceLocation(_inlineAssembly.location()) <<
|
||||||
errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.")
|
errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.")
|
||||||
);
|
);
|
||||||
|
@ -226,7 +226,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), "");
|
solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), "");
|
||||||
if (retSizeOnStack > 15)
|
if (retSizeOnStack > 15)
|
||||||
BOOST_THROW_EXCEPTION(
|
BOOST_THROW_EXCEPTION(
|
||||||
CompilerError() <<
|
StackTooDeepError() <<
|
||||||
errinfo_sourceLocation(_varDecl.location()) <<
|
errinfo_sourceLocation(_varDecl.location()) <<
|
||||||
errinfo_comment("Stack too deep.")
|
errinfo_comment("Stack too deep.")
|
||||||
);
|
);
|
||||||
@ -308,7 +308,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
|
|||||||
{
|
{
|
||||||
if (itemSize + lvalueSize > 16)
|
if (itemSize + lvalueSize > 16)
|
||||||
BOOST_THROW_EXCEPTION(
|
BOOST_THROW_EXCEPTION(
|
||||||
CompilerError() <<
|
StackTooDeepError() <<
|
||||||
errinfo_sourceLocation(_assignment.location()) <<
|
errinfo_sourceLocation(_assignment.location()) <<
|
||||||
errinfo_comment("Stack too deep, try removing local variables.")
|
errinfo_comment("Stack too deep, try removing local variables.")
|
||||||
);
|
);
|
||||||
|
@ -47,7 +47,7 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
|
|||||||
unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset);
|
unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset);
|
||||||
if (stackPos + 1 > 16) //@todo correct this by fetching earlier or moving to memory
|
if (stackPos + 1 > 16) //@todo correct this by fetching earlier or moving to memory
|
||||||
BOOST_THROW_EXCEPTION(
|
BOOST_THROW_EXCEPTION(
|
||||||
CompilerError() <<
|
StackTooDeepError() <<
|
||||||
errinfo_sourceLocation(_location) <<
|
errinfo_sourceLocation(_location) <<
|
||||||
errinfo_comment("Stack too deep, try removing local variables.")
|
errinfo_comment("Stack too deep, try removing local variables.")
|
||||||
);
|
);
|
||||||
@ -61,7 +61,7 @@ void StackVariable::storeValue(Type const&, SourceLocation const& _location, boo
|
|||||||
unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1;
|
unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1;
|
||||||
if (stackDiff > 16)
|
if (stackDiff > 16)
|
||||||
BOOST_THROW_EXCEPTION(
|
BOOST_THROW_EXCEPTION(
|
||||||
CompilerError() <<
|
StackTooDeepError() <<
|
||||||
errinfo_sourceLocation(_location) <<
|
errinfo_sourceLocation(_location) <<
|
||||||
errinfo_comment("Stack too deep, try removing local variables.")
|
errinfo_comment("Stack too deep, try removing local variables.")
|
||||||
);
|
);
|
||||||
|
@ -218,10 +218,10 @@ void CodeGenerator::assemble(
|
|||||||
}
|
}
|
||||||
catch (StackTooDeepError const& _e)
|
catch (StackTooDeepError const& _e)
|
||||||
{
|
{
|
||||||
yulAssert(
|
assertThrow(
|
||||||
false,
|
false,
|
||||||
"Stack too deep when compiling inline assembly" +
|
langutil::StackTooDeepError,
|
||||||
(_e.comment() ? ": " + *_e.comment() : ".")
|
""
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
yulAssert(transform.stackErrors().empty(), "Stack errors present but not thrown.");
|
yulAssert(transform.stackErrors().empty(), "Stack errors present but not thrown.");
|
||||||
|
@ -10,7 +10,12 @@ 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
|
||||||
|
yul_proto_diff_custom_mutate_ossfuzz
|
||||||
|
sol_arith_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 +65,22 @@ 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)
|
||||||
|
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 +99,52 @@ 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_arith_proto_ossfuzz
|
||||||
|
solArithProtoFuzzer.cpp
|
||||||
|
solarithprotoToSol.cpp
|
||||||
|
solArith.pb.cc
|
||||||
|
abiV2FuzzerCommon.cpp
|
||||||
|
../../EVMHost.cpp
|
||||||
|
protoToYul.cpp
|
||||||
|
yulProto.pb.cc
|
||||||
|
yulFuzzerCommon.cpp
|
||||||
|
)
|
||||||
|
target_include_directories(sol_arith_proto_ossfuzz PRIVATE
|
||||||
|
/usr/include/libprotobuf-mutator
|
||||||
|
)
|
||||||
|
target_link_libraries(sol_arith_proto_ossfuzz PRIVATE solidity libsolc
|
||||||
|
yulInterpreter
|
||||||
|
evmc
|
||||||
|
evmone-standalone
|
||||||
|
protobuf-mutator-libfuzzer.a
|
||||||
|
protobuf-mutator.a
|
||||||
|
protobuf.a
|
||||||
|
)
|
||||||
|
set_target_properties(sol_arith_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
|
||||||
|
# /home/bhargava/work/github/solidity/deps/LPM/external.protobuf/include
|
||||||
|
# /home/bhargava/work/github/solidity/deps/evmone/include
|
||||||
|
# /home/bhargava/work/github/solidity/deps/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
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -38,10 +38,14 @@ namespace solidity::yul::test::yul_fuzzer
|
|||||||
class ProtoConverter
|
class ProtoConverter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ProtoConverter()
|
ProtoConverter(unsigned _numGlobalVars)
|
||||||
{
|
{
|
||||||
m_funcVars = std::vector<std::vector<std::vector<std::string>>>{};
|
m_funcVars = std::vector<std::vector<std::vector<std::string>>>{};
|
||||||
m_globalVars = std::vector<std::vector<std::string>>{};
|
m_globalVars = std::vector<std::vector<std::string>>{};
|
||||||
|
if (_numGlobalVars > 0)
|
||||||
|
m_globalVars.push_back({});
|
||||||
|
for (unsigned i = 0; i < _numGlobalVars; i++)
|
||||||
|
m_globalVars.back().push_back("v" + std::to_string(i));
|
||||||
m_inForBodyScope = false;
|
m_inForBodyScope = false;
|
||||||
m_inForInitScope = false;
|
m_inForInitScope = false;
|
||||||
m_inForCond = false;
|
m_inForCond = false;
|
||||||
@ -50,20 +54,12 @@ public:
|
|||||||
m_counter = 0;
|
m_counter = 0;
|
||||||
m_inputSize = 0;
|
m_inputSize = 0;
|
||||||
m_inFunctionDef = false;
|
m_inFunctionDef = false;
|
||||||
m_objectId = 0;
|
|
||||||
m_isObject = false;
|
|
||||||
m_forInitScopeExtEnabled = true;
|
m_forInitScopeExtEnabled = true;
|
||||||
}
|
}
|
||||||
ProtoConverter(ProtoConverter const&) = delete;
|
ProtoConverter(ProtoConverter const&) = delete;
|
||||||
ProtoConverter(ProtoConverter&&) = delete;
|
ProtoConverter(ProtoConverter&&) = delete;
|
||||||
std::string programToString(Program const& _input);
|
std::string programToString(Program const& _input);
|
||||||
|
|
||||||
/// Returns evm version
|
|
||||||
solidity::langutil::EVMVersion version()
|
|
||||||
{
|
|
||||||
return m_evmVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void visit(BinaryOp const&);
|
void visit(BinaryOp const&);
|
||||||
|
|
||||||
@ -75,10 +71,8 @@ private:
|
|||||||
|
|
||||||
std::string visit(Literal const&);
|
std::string visit(Literal const&);
|
||||||
void visit(VarRef const&);
|
void visit(VarRef const&);
|
||||||
|
void visit(FunctionExpr const& _x);
|
||||||
void visit(Expression const&);
|
void visit(Expression const&);
|
||||||
void visit(VarDecl const&);
|
|
||||||
void visit(MultiVarDecl const&);
|
|
||||||
void visit(TypedVarDecl const&);
|
|
||||||
void visit(UnaryOp const&);
|
void visit(UnaryOp const&);
|
||||||
void visit(AssignmentStatement const&);
|
void visit(AssignmentStatement const&);
|
||||||
void visit(IfStmt const&);
|
void visit(IfStmt const&);
|
||||||
@ -90,23 +84,10 @@ private:
|
|||||||
void visit(SwitchStmt const&);
|
void visit(SwitchStmt const&);
|
||||||
void visit(TernaryOp const&);
|
void visit(TernaryOp const&);
|
||||||
void visit(NullaryOp const&);
|
void visit(NullaryOp const&);
|
||||||
void visit(LogFunc const&);
|
|
||||||
void visit(CopyFunc const&);
|
|
||||||
void visit(ExtCodeCopy const&);
|
|
||||||
void visit(StopInvalidStmt const&);
|
|
||||||
void visit(RetRevStmt const&);
|
|
||||||
void visit(SelfDestructStmt const&);
|
|
||||||
void visit(TerminatingStmt const&);
|
|
||||||
void visit(FunctionCall const&);
|
void visit(FunctionCall const&);
|
||||||
void visit(FunctionDef const&);
|
void visit(FunctionDef const&);
|
||||||
void visit(PopStmt const&);
|
void visit(PopStmt const&);
|
||||||
void visit(LeaveStmt const&);
|
void visit(LeaveStmt const&);
|
||||||
void visit(LowLevelCall const&);
|
|
||||||
void visit(Create const&);
|
|
||||||
void visit(UnaryOpData const&);
|
|
||||||
void visit(Object const&);
|
|
||||||
void visit(Data const&);
|
|
||||||
void visit(Code const&);
|
|
||||||
void visit(Program const&);
|
void visit(Program const&);
|
||||||
|
|
||||||
/// Creates a new block scope.
|
/// Creates a new block scope.
|
||||||
@ -140,8 +121,10 @@ private:
|
|||||||
Multiple
|
Multiple
|
||||||
};
|
};
|
||||||
|
|
||||||
void visitFunctionInputParams(FunctionCall const&, unsigned);
|
template <typename T>
|
||||||
|
void visitFunctionInputParams(T const&, unsigned);
|
||||||
void createFunctionDefAndCall(FunctionDef const&, unsigned, unsigned);
|
void createFunctionDefAndCall(FunctionDef const&, unsigned, unsigned);
|
||||||
|
void storeGlobals();
|
||||||
|
|
||||||
/// Convert function type to a string to be used while naming a
|
/// Convert function type to a string to be used while naming a
|
||||||
/// function that is created by a function declaration statement.
|
/// function that is created by a function declaration statement.
|
||||||
@ -169,46 +152,19 @@ private:
|
|||||||
/// in scope
|
/// in scope
|
||||||
bool varDeclAvailable();
|
bool varDeclAvailable();
|
||||||
|
|
||||||
/// Return true if a function call cannot be made, false otherwise.
|
/// Converts protobuf function call to a yul function call and appends
|
||||||
/// @param _type is an enum denoting the type of function call. It
|
|
||||||
/// can be one of NONE, SINGLE, MULTIDECL, MULTIASSIGN.
|
|
||||||
/// NONE -> Function call does not return a value
|
|
||||||
/// SINGLE -> Function call returns a single value
|
|
||||||
/// MULTIDECL -> Function call returns more than one value
|
|
||||||
/// and it is used to create a multi declaration
|
|
||||||
/// statement
|
|
||||||
/// MULTIASSIGN -> Function call returns more than one value
|
|
||||||
/// and it is used to create a multi assignment
|
|
||||||
/// statement
|
|
||||||
/// @return True if the function call cannot be created for one of the
|
|
||||||
/// following reasons
|
|
||||||
// - It is a SINGLE function call (we reserve SINGLE functions for
|
|
||||||
// expressions)
|
|
||||||
// - It is a MULTIASSIGN function call and we do not have any
|
|
||||||
// variables available for assignment.
|
|
||||||
bool functionCallNotPossible(FunctionCall_Returns _type);
|
|
||||||
|
|
||||||
/// Checks if function call of type @a _type returns the correct number
|
|
||||||
/// of values.
|
|
||||||
/// @param _type Function call type of the function being checked
|
|
||||||
/// @param _numOutParams Number of values returned by the function
|
|
||||||
/// being checked
|
|
||||||
/// @return true if the function returns the correct number of values,
|
|
||||||
/// false otherwise
|
|
||||||
bool functionValid(FunctionCall_Returns _type, unsigned _numOutParams);
|
|
||||||
|
|
||||||
/// Converts protobuf function call to a Yul function call and appends
|
|
||||||
/// it to output stream.
|
/// it to output stream.
|
||||||
/// @param _x Protobuf function call
|
/// @param _x Protobuf function call
|
||||||
/// @param _name Function name
|
/// @param _name Function name
|
||||||
/// @param _numInParams Number of input arguments accepted by function
|
/// @param _numInParams Number of input arguments accepted by function
|
||||||
/// @param _newLine Flag that prints a new line to the output stream if
|
/// @param _newline Boolean flag that is true if new line to be printed after
|
||||||
/// true. Default value for the flag is true.
|
/// function call, false otherwise
|
||||||
|
template <typename T>
|
||||||
void convertFunctionCall(
|
void convertFunctionCall(
|
||||||
FunctionCall const& _x,
|
T const& _x,
|
||||||
std::string _name,
|
std::string _name,
|
||||||
unsigned _numInParams,
|
unsigned _numInParams,
|
||||||
bool _newLine = true
|
bool _newline = false
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Prints a Yul formatted variable declaration statement to the output
|
/// Prints a Yul formatted variable declaration statement to the output
|
||||||
@ -270,11 +226,6 @@ private:
|
|||||||
/// Removes entry from m_functionMap and m_functionName
|
/// Removes entry from m_functionMap and m_functionName
|
||||||
void updateFunctionMaps(std::string const& _x);
|
void updateFunctionMaps(std::string const& _x);
|
||||||
|
|
||||||
/// Build a tree of objects that contains the object/data
|
|
||||||
/// identifiers that are in scope in a given object.
|
|
||||||
/// @param _x root object of the Yul protobuf specification.
|
|
||||||
void buildObjectScopeTree(Object const& _x);
|
|
||||||
|
|
||||||
/// Returns a pseudo-random dictionary token.
|
/// Returns a pseudo-random dictionary token.
|
||||||
/// @param _p Enum that decides if the returned token is hex prefixed ("0x") or not
|
/// @param _p Enum that decides if the returned token is hex prefixed ("0x") or not
|
||||||
/// @return Dictionary token at the index computed using a
|
/// @return Dictionary token at the index computed using a
|
||||||
@ -284,9 +235,9 @@ private:
|
|||||||
/// dictionarySize is the total number of entries in the dictionary.
|
/// dictionarySize is the total number of entries in the dictionary.
|
||||||
std::string dictionaryToken(util::HexPrefix _p = util::HexPrefix::Add);
|
std::string dictionaryToken(util::HexPrefix _p = util::HexPrefix::Add);
|
||||||
|
|
||||||
/// Returns an EVMVersion object corresponding to the protobuf
|
/// Return variable reference.
|
||||||
/// enum of type Program_Version
|
/// @param _index: Index of variable to be referenced
|
||||||
solidity::langutil::EVMVersion evmVersionMapping(Program_Version const& _x);
|
std::string varRef(unsigned _index);
|
||||||
|
|
||||||
/// Returns a monotonically increasing counter that starts from zero.
|
/// Returns a monotonically increasing counter that starts from zero.
|
||||||
unsigned counter()
|
unsigned counter()
|
||||||
@ -302,29 +253,7 @@ private:
|
|||||||
return "foo_" + functionTypeToString(_type) + "_" + std::to_string(counter());
|
return "foo_" + functionTypeToString(_type) + "_" + std::to_string(counter());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a pseudo-randomly chosen object identifier that is in the
|
std::string dummyExpression();
|
||||||
/// scope of the Yul object being visited.
|
|
||||||
std::string getObjectIdentifier(unsigned _x);
|
|
||||||
|
|
||||||
/// Return new object identifier as string. Identifier string
|
|
||||||
/// is a template of the form "\"object<n>\"" where <n> is
|
|
||||||
/// a monotonically increasing object ID counter.
|
|
||||||
/// @param _decorate If true (default value), object ID is
|
|
||||||
/// enclosed within double quotes.
|
|
||||||
std::string newObjectId(bool _decorate = true)
|
|
||||||
{
|
|
||||||
return util::Whiskers(R"(<?decorate>"</decorate>object<id><?decorate>"</decorate>)")
|
|
||||||
("decorate", _decorate)
|
|
||||||
("id", std::to_string(m_objectId++))
|
|
||||||
.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the object counter value corresponding to the object
|
|
||||||
/// being visited.
|
|
||||||
unsigned currentObjectId()
|
|
||||||
{
|
|
||||||
return m_objectId - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostringstream m_output;
|
std::ostringstream m_output;
|
||||||
/// Variables in all function definitions
|
/// Variables in all function definitions
|
||||||
@ -349,13 +278,9 @@ private:
|
|||||||
std::stack<std::set<u256>> m_switchLiteralSetPerScope;
|
std::stack<std::set<u256>> m_switchLiteralSetPerScope;
|
||||||
// Look-up table per function type that holds the number of input (output) function parameters
|
// Look-up table per function type that holds the number of input (output) function parameters
|
||||||
std::map<std::string, std::pair<unsigned, unsigned>> m_functionSigMap;
|
std::map<std::string, std::pair<unsigned, unsigned>> m_functionSigMap;
|
||||||
/// Tree of objects and their scopes
|
|
||||||
std::vector<std::vector<std::string>> m_objectScopeTree;
|
|
||||||
// mod input/output parameters impose an upper bound on the number of input/output parameters a function may have.
|
// mod input/output parameters impose an upper bound on the number of input/output parameters a function may have.
|
||||||
static unsigned constexpr s_modInputParams = 5;
|
static unsigned constexpr s_modInputParams = 5;
|
||||||
static unsigned constexpr s_modOutputParams = 5;
|
static unsigned constexpr s_modOutputParams = 5;
|
||||||
/// Hard-coded identifier for a Yul object's data block
|
|
||||||
static auto constexpr s_dataIdentifier = "datablock";
|
|
||||||
/// Predicate to keep track of for body scope. If false, break/continue
|
/// Predicate to keep track of for body scope. If false, break/continue
|
||||||
/// statements can not be created.
|
/// statements can not be created.
|
||||||
bool m_inForBodyScope;
|
bool m_inForBodyScope;
|
||||||
@ -368,24 +293,15 @@ private:
|
|||||||
/// Predicate to keep track of for loop init scope. If true, variable
|
/// Predicate to keep track of for loop init scope. If true, variable
|
||||||
/// or function declarations can not be created.
|
/// or function declarations can not be created.
|
||||||
bool m_inForInitScope;
|
bool m_inForInitScope;
|
||||||
/// Flag that is true while converting for loop condition,
|
|
||||||
/// false otherwise.
|
|
||||||
bool m_inForCond;
|
|
||||||
/// Monotonically increasing counter
|
/// Monotonically increasing counter
|
||||||
unsigned m_counter;
|
unsigned m_counter;
|
||||||
/// Size of protobuf input
|
/// Size of protobuf input
|
||||||
unsigned m_inputSize;
|
unsigned m_inputSize;
|
||||||
/// Predicate that is true if inside function definition, false otherwise
|
/// Predicate that is true if inside function definition, false otherwise
|
||||||
bool m_inFunctionDef;
|
bool m_inFunctionDef;
|
||||||
/// Index used for naming objects
|
|
||||||
unsigned m_objectId;
|
|
||||||
/// Flag to track whether program is an object (true) or a statement block
|
|
||||||
/// (false: default value)
|
|
||||||
bool m_isObject;
|
|
||||||
/// Flag to track whether scope extension of variables defined in for-init
|
/// Flag to track whether scope extension of variables defined in for-init
|
||||||
/// block is enabled.
|
/// block is enabled.
|
||||||
bool m_forInitScopeExtEnabled;
|
bool m_forInitScopeExtEnabled;
|
||||||
/// Object that holds the targeted evm version specified by protobuf input
|
bool m_inForCond;
|
||||||
solidity::langutil::EVMVersion m_evmVersion;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
49
test/tools/ossfuzz/solArith.proto
Normal file
49
test/tools/ossfuzz/solArith.proto
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
import "yulProto.proto";
|
||||||
|
|
||||||
|
message Type {
|
||||||
|
enum Sign {
|
||||||
|
SIGNED = 0;
|
||||||
|
UNSIGNED = 1;
|
||||||
|
}
|
||||||
|
required Sign s = 1;
|
||||||
|
required uint32 bytewidth = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message VarDecl {
|
||||||
|
required Type t = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Assembly {
|
||||||
|
required solidity.yul.test.yul_fuzzer.Program p = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Block {
|
||||||
|
repeated VarDecl v = 1;
|
||||||
|
required Assembly a = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Program {
|
||||||
|
required Block b = 1;
|
||||||
|
required uint64 seed = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
package solidity.test.solarithfuzzer;
|
268
test/tools/ossfuzz/solArithProtoFuzzer.cpp
Normal file
268
test/tools/ossfuzz/solArithProtoFuzzer.cpp
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
/*
|
||||||
|
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/solarithprotoToSol.h>
|
||||||
|
#include <test/tools/ossfuzz/solArith.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::test::abiv2fuzzer;
|
||||||
|
using namespace solidity::test::solarithfuzzer;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::test;
|
||||||
|
using namespace solidity::util;
|
||||||
|
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);
|
||||||
|
//}
|
||||||
|
|
||||||
|
bool compareRuns(evmc::result const& _run1, evmc::result const& _run2)
|
||||||
|
{
|
||||||
|
if (_run1.output_size != _run2.output_size)
|
||||||
|
return false;
|
||||||
|
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 (langutil::StackTooDeepError const&)
|
||||||
|
{
|
||||||
|
throw langutil::FuzzerError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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{};
|
||||||
|
try {
|
||||||
|
sol_source = converter.programToString(_input);
|
||||||
|
}
|
||||||
|
catch (langutil::FuzzerError const&)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto minimalResult = compileDeployAndExecute(
|
||||||
|
sol_source,
|
||||||
|
":C",
|
||||||
|
"test()",
|
||||||
|
frontend::OptimiserSettings::minimal(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
bool successState = minimalResult.status_code == EVMC_SUCCESS;
|
||||||
|
|
||||||
|
auto optResult = compileDeployAndExecute(
|
||||||
|
sol_source,
|
||||||
|
":C",
|
||||||
|
"test()",
|
||||||
|
frontend::OptimiserSettings::standard(),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
if (successState)
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
optResult.status_code == EVMC_SUCCESS,
|
||||||
|
"Sol arith fuzzer: Optimal code failed"
|
||||||
|
);
|
||||||
|
solAssert(
|
||||||
|
compareRuns(minimalResult, optResult),
|
||||||
|
"Sol arith fuzzer: Runs produced different result"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (langutil::FuzzerError const&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
180
test/tools/ossfuzz/solarithprotoToSol.cpp
Normal file
180
test/tools/ossfuzz/solarithprotoToSol.cpp
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
#include <test/tools/ossfuzz/solarithprotoToSol.h>
|
||||||
|
#include <test/tools/ossfuzz/protoToYul.h>
|
||||||
|
|
||||||
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
|
#include <libsolutil/Whiskers.h>
|
||||||
|
|
||||||
|
#include <libyul/AssemblyStack.h>
|
||||||
|
#include <libyul/backends/evm/EVMDialect.h>
|
||||||
|
#include <libyul/Exceptions.h>
|
||||||
|
|
||||||
|
using namespace solidity::test::solarithfuzzer;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::util;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
string ProtoConverter::programToString(Program const& _program)
|
||||||
|
{
|
||||||
|
m_rand = make_unique<SolRandomNumGenerator>(
|
||||||
|
SolRandomNumGenerator(_program.seed())
|
||||||
|
);
|
||||||
|
return visit(_program);
|
||||||
|
}
|
||||||
|
|
||||||
|
string ProtoConverter::visit(Program const& _program)
|
||||||
|
{
|
||||||
|
Whiskers p(R"(pragma solidity >= 0.0.0;)");
|
||||||
|
Whiskers c(R"(contract C {)");
|
||||||
|
Whiskers t(R"(function test() public returns (uint)<body>)");
|
||||||
|
t("body", visit(_program.b()));
|
||||||
|
return p.render()
|
||||||
|
+ '\n'
|
||||||
|
+ c.render()
|
||||||
|
+ '\n'
|
||||||
|
+ '\t'
|
||||||
|
+ t.render()
|
||||||
|
+ '\n'
|
||||||
|
+ '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
string ProtoConverter::visit(Block const& _block)
|
||||||
|
{
|
||||||
|
ostringstream blockStr;
|
||||||
|
blockStr << '\n'
|
||||||
|
<< '\t'
|
||||||
|
<< '{'
|
||||||
|
<< '\n';
|
||||||
|
for (auto const& v: _block.v())
|
||||||
|
blockStr << visit(v);
|
||||||
|
blockStr << visit(_block.a());
|
||||||
|
ostringstream trace;
|
||||||
|
if (m_yulAssembly.empty())
|
||||||
|
blockStr << "\t\treturn 0;";
|
||||||
|
else
|
||||||
|
blockStr << addChecks(m_yulProgram, langutil::EVMVersion::berlin(), trace);
|
||||||
|
blockStr << '\n'
|
||||||
|
<< '\t'
|
||||||
|
<< '}';
|
||||||
|
return blockStr.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
string ProtoConverter::addChecks(
|
||||||
|
string const& _yulSource,
|
||||||
|
langutil::EVMVersion _version,
|
||||||
|
ostringstream& _os
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ostringstream out;
|
||||||
|
unsigned error = 1;
|
||||||
|
auto memoryDump = interpretYul(_yulSource, _version, _os);
|
||||||
|
unsigned index = 0;
|
||||||
|
for (auto const& v: m_varTypeMap)
|
||||||
|
{
|
||||||
|
Whiskers check(R"(<ind>if (<var> != <type>(0x<value>)) return <error>;<endl>)");
|
||||||
|
check("ind", "\t\t");
|
||||||
|
check("var", v.first);
|
||||||
|
check("type", get<1>(v.second));
|
||||||
|
u256 memIdx = index * 0x20;
|
||||||
|
string val{};
|
||||||
|
if (memoryDump.count(memIdx))
|
||||||
|
{
|
||||||
|
unsigned byteWidth = get<2>(v.second);
|
||||||
|
val = extractBytes(memoryDump.at(memIdx), byteWidth);
|
||||||
|
// Avoid interpretation of 20 byte literals as address literals
|
||||||
|
// by prepending 00.
|
||||||
|
if (byteWidth == 20)
|
||||||
|
val = "00" + val;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
val = "0";
|
||||||
|
check("value", val);
|
||||||
|
check("error", to_string(error++));
|
||||||
|
check("endl", "\n");
|
||||||
|
out << check.render();
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
out << "\t\treturn 0;\n";
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
map<u256, string> ProtoConverter::interpretYul(
|
||||||
|
string const& _yulSource,
|
||||||
|
langutil::EVMVersion _version,
|
||||||
|
ostringstream& _os
|
||||||
|
)
|
||||||
|
{
|
||||||
|
using namespace yul;
|
||||||
|
using namespace solidity::yul::test::yul_fuzzer;
|
||||||
|
YulStringRepository::reset();
|
||||||
|
|
||||||
|
// AssemblyStack entry point
|
||||||
|
AssemblyStack stack(
|
||||||
|
_version,
|
||||||
|
AssemblyStack::Language::StrictAssembly,
|
||||||
|
solidity::frontend::OptimiserSettings::full()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Parse protobuf mutated YUL code
|
||||||
|
if (!stack.parseAndAnalyze("source", _yulSource) || !stack.parserResult()->code ||
|
||||||
|
!stack.parserResult()->analysisInfo)
|
||||||
|
{
|
||||||
|
std::cout << _yulSource << std::endl;
|
||||||
|
yulAssert(false, "Proto fuzzer generated malformed program");
|
||||||
|
}
|
||||||
|
stack.optimize();
|
||||||
|
yulFuzzerUtil i;
|
||||||
|
|
||||||
|
auto r = i.interpret(
|
||||||
|
_os,
|
||||||
|
stack.parserResult()->code,
|
||||||
|
EVMDialect::strictAssemblyForEVMObjects(_version)
|
||||||
|
);
|
||||||
|
if (r != yulFuzzerUtil::TerminationReason::StepLimitReached && r != yulFuzzerUtil::TerminationReason::TraceLimitReached)
|
||||||
|
return i.memoryDump();
|
||||||
|
else
|
||||||
|
throw langutil::FuzzerError();
|
||||||
|
}
|
||||||
|
|
||||||
|
string ProtoConverter::visit(Assembly const& _as)
|
||||||
|
{
|
||||||
|
using namespace solidity::yul::test;
|
||||||
|
ostringstream assemblyStr;
|
||||||
|
assemblyStr << "\t\t" << "assembly {\n";
|
||||||
|
// Yul converter
|
||||||
|
auto c = yul_fuzzer::ProtoConverter{m_varCounter};
|
||||||
|
m_yulAssembly = c.programToString(_as.p());
|
||||||
|
m_yulProgram = Whiskers("{<endl><init><assembly>}")
|
||||||
|
("endl", "\n")
|
||||||
|
("init", m_yulInitCode.str())
|
||||||
|
("assembly", m_yulAssembly)
|
||||||
|
.render();
|
||||||
|
assemblyStr << m_yulAssembly;
|
||||||
|
assemblyStr << "\t\t}\n";
|
||||||
|
return assemblyStr.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
string ProtoConverter::visit(VarDecl const& _vardecl)
|
||||||
|
{
|
||||||
|
Whiskers v(R"(<type> <varName> = <type>(<value>);)");
|
||||||
|
string type = visit(_vardecl.t());
|
||||||
|
string varName = newVarName();
|
||||||
|
unsigned byteWidth = widthUnsigned(_vardecl.t().bytewidth());
|
||||||
|
m_varTypeMap.emplace(varName, tuple(typeSign(_vardecl.t()), type, byteWidth));
|
||||||
|
v("type", type);
|
||||||
|
v("varName", varName);
|
||||||
|
string value = maskUnsignedToHex(64);
|
||||||
|
v("value", value);
|
||||||
|
Whiskers i(R"(let <varName> := <value><endl>)");
|
||||||
|
i("varName", varName);
|
||||||
|
i("value", "0x" + extractBytes(value, byteWidth));
|
||||||
|
i("endl", "\n");
|
||||||
|
m_yulInitCode << i.render();
|
||||||
|
incrementVarCounter();
|
||||||
|
return "\t\t" + v.render() + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
string ProtoConverter::visit(Type const& _type)
|
||||||
|
{
|
||||||
|
return signString(_type.s()) + widthString(_type.bytewidth());
|
||||||
|
}
|
121
test/tools/ossfuzz/solarithprotoToSol.h
Normal file
121
test/tools/ossfuzz/solarithprotoToSol.h
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
#include <test/tools/ossfuzz/solArith.pb.h>
|
||||||
|
|
||||||
|
#include "yulFuzzerCommon.h"
|
||||||
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
#include <libsolutil/Common.h>
|
||||||
|
#include <libsolutil/FixedHash.h>
|
||||||
|
#include <libsolutil/Keccak256.h>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
namespace solidity::test::solarithfuzzer
|
||||||
|
{
|
||||||
|
/// Random number generator that is seeded with a fuzzer
|
||||||
|
/// supplied unsigned integer.
|
||||||
|
struct SolRandomNumGenerator
|
||||||
|
{
|
||||||
|
using RandomEngine = std::mt19937_64;
|
||||||
|
|
||||||
|
explicit SolRandomNumGenerator(unsigned _seed): m_random(RandomEngine(_seed)) {}
|
||||||
|
|
||||||
|
/// @returns a pseudo random unsigned integer
|
||||||
|
unsigned operator()()
|
||||||
|
{
|
||||||
|
return m_random();
|
||||||
|
}
|
||||||
|
|
||||||
|
RandomEngine m_random;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Sign
|
||||||
|
{
|
||||||
|
Signed,
|
||||||
|
Unsigned
|
||||||
|
};
|
||||||
|
|
||||||
|
class ProtoConverter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ProtoConverter()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
ProtoConverter(ProtoConverter const&) = delete;
|
||||||
|
ProtoConverter(ProtoConverter&&) = delete;
|
||||||
|
std::string programToString(Program const& _input);
|
||||||
|
private:
|
||||||
|
std::string visit(Type const& _type);
|
||||||
|
std::string visit(Assembly const& _a);
|
||||||
|
std::string visit(VarDecl const& _decl);
|
||||||
|
std::string visit(Block const& _block);
|
||||||
|
std::string visit(Program const& _program);
|
||||||
|
|
||||||
|
std::map<u256, std::string> interpretYul(
|
||||||
|
std::string const& _yulSource,
|
||||||
|
langutil::EVMVersion _version,
|
||||||
|
std::ostringstream& _os
|
||||||
|
);
|
||||||
|
std::string addChecks(
|
||||||
|
std::string const& _yulSource,
|
||||||
|
langutil::EVMVersion _version,
|
||||||
|
std::ostringstream& _os
|
||||||
|
);
|
||||||
|
|
||||||
|
std::string newVarName()
|
||||||
|
{
|
||||||
|
return "v" + std::to_string(m_varCounter);
|
||||||
|
}
|
||||||
|
void incrementVarCounter()
|
||||||
|
{
|
||||||
|
m_varCounter++;
|
||||||
|
}
|
||||||
|
static std::string signString(Type::Sign _sign)
|
||||||
|
{
|
||||||
|
return _sign == Type::Sign::Type_Sign_SIGNED ? "int" : "uint";
|
||||||
|
}
|
||||||
|
static std::string signString(Sign _sign)
|
||||||
|
{
|
||||||
|
return _sign == Sign::Signed ? "int" : "uint";
|
||||||
|
}
|
||||||
|
static std::string widthString(unsigned _width)
|
||||||
|
{
|
||||||
|
return std::to_string((_width % 32 + 1) * 8);
|
||||||
|
}
|
||||||
|
static unsigned widthUnsigned(unsigned _width)
|
||||||
|
{
|
||||||
|
return _width % 32 + 1;
|
||||||
|
}
|
||||||
|
bool varAvailable()
|
||||||
|
{
|
||||||
|
return m_varCounter > 0;
|
||||||
|
}
|
||||||
|
Sign typeSign(Type const& _ts)
|
||||||
|
{
|
||||||
|
return _ts.s() == Type::SIGNED ? Sign::Signed : Sign::Unsigned;
|
||||||
|
}
|
||||||
|
std::string maskUnsignedToHex(unsigned _numMaskNibbles)
|
||||||
|
{
|
||||||
|
return toHex(maskUnsignedInt(_numMaskNibbles), util::HexPrefix::Add);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert _counter to string and return its keccak256 hash
|
||||||
|
solidity::u256 hashUnsignedInt()
|
||||||
|
{
|
||||||
|
return util::keccak256(util::h256((*m_rand)()));
|
||||||
|
}
|
||||||
|
u256 maskUnsignedInt(unsigned _numMaskNibbles)
|
||||||
|
{
|
||||||
|
return hashUnsignedInt() & u256("0x" + std::string(_numMaskNibbles, 'f'));
|
||||||
|
}
|
||||||
|
static std::string extractBytes(std::string _value, unsigned _numBytes)
|
||||||
|
{
|
||||||
|
return _value.substr(_value.size() - (_numBytes * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned m_varCounter = 0;
|
||||||
|
std::shared_ptr<SolRandomNumGenerator> m_rand;
|
||||||
|
std::map<std::string, std::tuple<Sign, std::string, unsigned>> m_varTypeMap;
|
||||||
|
std::string m_yulAssembly;
|
||||||
|
std::string m_yulProgram;
|
||||||
|
std::ostringstream m_yulInitCode;
|
||||||
|
};
|
||||||
|
}
|
@ -29,12 +29,11 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret(
|
|||||||
size_t _maxTraceSize
|
size_t _maxTraceSize
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
InterpreterState state;
|
m_state.maxTraceSize = _maxTraceSize;
|
||||||
state.maxTraceSize = _maxTraceSize;
|
m_state.maxSteps = _maxSteps;
|
||||||
state.maxSteps = _maxSteps;
|
|
||||||
// Add 64 bytes of pseudo-randomly generated calldata so that
|
// Add 64 bytes of pseudo-randomly generated calldata so that
|
||||||
// calldata opcodes perform non trivial work.
|
// calldata opcodes perform non trivial work.
|
||||||
state.calldata = {
|
m_state.calldata = {
|
||||||
0xe9, 0x96, 0x40, 0x7d, 0xa5, 0xda, 0xb0, 0x2d,
|
0xe9, 0x96, 0x40, 0x7d, 0xa5, 0xda, 0xb0, 0x2d,
|
||||||
0x97, 0xf5, 0xc3, 0x44, 0xd7, 0x65, 0x0a, 0xd8,
|
0x97, 0xf5, 0xc3, 0x44, 0xd7, 0x65, 0x0a, 0xd8,
|
||||||
0x2c, 0x14, 0x3a, 0xf3, 0xe7, 0x40, 0x0f, 0x1e,
|
0x2c, 0x14, 0x3a, 0xf3, 0xe7, 0x40, 0x0f, 0x1e,
|
||||||
@ -44,7 +43,24 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret(
|
|||||||
0xc7, 0x60, 0x5f, 0x7c, 0xcd, 0xfb, 0x92, 0xcd,
|
0xc7, 0x60, 0x5f, 0x7c, 0xcd, 0xfb, 0x92, 0xcd,
|
||||||
0x8e, 0xf3, 0x9b, 0xe4, 0x4f, 0x6c, 0x14, 0xde
|
0x8e, 0xf3, 0x9b, 0xe4, 0x4f, 0x6c, 0x14, 0xde
|
||||||
};
|
};
|
||||||
Interpreter interpreter(state, _dialect);
|
m_state.returndata = {
|
||||||
|
0xe9, 0x96, 0x40, 0x7d, 0xa5, 0xda, 0xb0, 0x2d,
|
||||||
|
0x97, 0xf5, 0xc3, 0x44, 0xd7, 0x65, 0x0a, 0xd8,
|
||||||
|
0x2c, 0x14, 0x3a, 0xf3, 0xe7, 0x40, 0x0f, 0x1e,
|
||||||
|
0x67, 0xce, 0x90, 0x44, 0x2e, 0x92, 0xdb, 0x88,
|
||||||
|
0xb8, 0x43, 0x9c, 0x41, 0x42, 0x08, 0xf1, 0xd7,
|
||||||
|
0x65, 0xe9, 0x7f, 0xeb, 0x7b, 0xb9, 0x56, 0x9f,
|
||||||
|
0xc7, 0x60, 0x5f, 0x7c, 0xcd, 0xfb, 0x92, 0xcd,
|
||||||
|
0x8e, 0xf3, 0x9b, 0xe4, 0x4f, 0x6c, 0x14, 0xde
|
||||||
|
};
|
||||||
|
// for (unsigned i = 0; i < 64; i++)
|
||||||
|
// {
|
||||||
|
// m_state.memory[i] = m_state.calldata[i];
|
||||||
|
// if (i % 32 == 0)
|
||||||
|
// m_state.storage[util::h256(i / 32)] = util::h256(m_state.calldata[i]);
|
||||||
|
// }
|
||||||
|
|
||||||
|
Interpreter interpreter(m_state, _dialect);
|
||||||
|
|
||||||
TerminationReason reason = TerminationReason::None;
|
TerminationReason reason = TerminationReason::None;
|
||||||
try
|
try
|
||||||
@ -64,6 +80,11 @@ yulFuzzerUtil::TerminationReason yulFuzzerUtil::interpret(
|
|||||||
reason = TerminationReason::ExplicitlyTerminated;
|
reason = TerminationReason::ExplicitlyTerminated;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.dumpTraceAndState(_os);
|
m_state.dumpTraceAndState(_os);
|
||||||
return reason;
|
return reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::map<u256, std::string> yulFuzzerUtil::memoryDump()
|
||||||
|
{
|
||||||
|
return m_state.dumpMemory();
|
||||||
|
}
|
||||||
|
@ -30,13 +30,15 @@ struct yulFuzzerUtil
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
static TerminationReason interpret(
|
TerminationReason interpret(
|
||||||
std::ostream& _os,
|
std::ostream& _os,
|
||||||
std::shared_ptr<yul::Block> _ast,
|
std::shared_ptr<yul::Block> _ast,
|
||||||
Dialect const& _dialect,
|
Dialect const& _dialect,
|
||||||
size_t _maxSteps = maxSteps,
|
size_t _maxSteps = maxSteps,
|
||||||
size_t _maxTraceSize = maxTraceSize
|
size_t _maxTraceSize = maxTraceSize
|
||||||
);
|
);
|
||||||
|
std::map<u256, std::string> memoryDump();
|
||||||
|
InterpreterState m_state;
|
||||||
static size_t constexpr maxSteps = 100;
|
static size_t constexpr maxSteps = 100;
|
||||||
static size_t constexpr maxTraceSize = 75;
|
static size_t constexpr maxTraceSize = 75;
|
||||||
};
|
};
|
||||||
|
@ -17,82 +17,17 @@
|
|||||||
|
|
||||||
syntax = "proto2";
|
syntax = "proto2";
|
||||||
|
|
||||||
message VarDecl {
|
|
||||||
required Expression expr = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message MultiVarDecl {
|
|
||||||
required uint32 num_vars = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message LowLevelCall {
|
|
||||||
enum Type {
|
|
||||||
CALL = 0;
|
|
||||||
CALLCODE = 1;
|
|
||||||
DELEGATECALL = 2;
|
|
||||||
STATICCALL = 3;
|
|
||||||
}
|
|
||||||
required Type callty = 1;
|
|
||||||
required Expression gas = 2;
|
|
||||||
required Expression addr = 3;
|
|
||||||
// Valid for call and callcode only
|
|
||||||
required Expression wei = 4;
|
|
||||||
required Expression in = 5;
|
|
||||||
required Expression insize = 6;
|
|
||||||
required Expression out = 7;
|
|
||||||
required Expression outsize = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Create {
|
|
||||||
enum Type {
|
|
||||||
CREATE = 0;
|
|
||||||
CREATE2 = 1;
|
|
||||||
}
|
|
||||||
required Type createty = 1;
|
|
||||||
required Expression wei = 2;
|
|
||||||
required Expression position = 3;
|
|
||||||
required Expression size = 4;
|
|
||||||
// Valid for create2 only
|
|
||||||
required Expression value = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
message FunctionCall {
|
message FunctionCall {
|
||||||
enum Returns {
|
|
||||||
ZERO = 1;
|
|
||||||
SINGLE = 2;
|
|
||||||
MULTIDECL = 3;
|
|
||||||
MULTIASSIGN = 4;
|
|
||||||
}
|
|
||||||
required Returns ret = 1;
|
|
||||||
// Indexes an existing function
|
// Indexes an existing function
|
||||||
required uint32 func_index = 2;
|
required uint32 func_index = 1;
|
||||||
required Expression in_param1 = 3;
|
required Expression in_param1 = 2;
|
||||||
required Expression in_param2 = 4;
|
required Expression in_param2 = 3;
|
||||||
required Expression in_param3 = 5;
|
required Expression in_param3 = 4;
|
||||||
required Expression in_param4 = 6;
|
required Expression in_param4 = 5;
|
||||||
required VarRef out_param1 = 7;
|
required VarRef out_param1 = 6;
|
||||||
required VarRef out_param2 = 8;
|
required VarRef out_param2 = 7;
|
||||||
required VarRef out_param3 = 9;
|
required VarRef out_param3 = 8;
|
||||||
required VarRef out_param4 = 10;
|
required VarRef out_param4 = 9;
|
||||||
}
|
|
||||||
|
|
||||||
message TypedVarDecl {
|
|
||||||
enum TypeName {
|
|
||||||
BOOL = 1;
|
|
||||||
U8 = 2;
|
|
||||||
U32 = 3;
|
|
||||||
U64 = 4;
|
|
||||||
U128 = 5;
|
|
||||||
U256 = 6;
|
|
||||||
S8 = 7;
|
|
||||||
S32 = 8;
|
|
||||||
S64 = 9;
|
|
||||||
S128 = 10;
|
|
||||||
S256 = 11;
|
|
||||||
};
|
|
||||||
required int32 id = 1;
|
|
||||||
required TypeName type = 2;
|
|
||||||
required Expression expr = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message VarRef {
|
message VarRef {
|
||||||
@ -108,24 +43,6 @@ message Literal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message TypedLiteral {
|
|
||||||
enum TypeName {
|
|
||||||
BOOL = 1;
|
|
||||||
U8 = 2;
|
|
||||||
U32 = 3;
|
|
||||||
U64 = 4;
|
|
||||||
U128 = 5;
|
|
||||||
U256 = 6;
|
|
||||||
S8 = 7;
|
|
||||||
S32 = 8;
|
|
||||||
S64 = 9;
|
|
||||||
S128 = 10;
|
|
||||||
S256 = 11;
|
|
||||||
};
|
|
||||||
required int32 val = 1;
|
|
||||||
required TypeName type = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message BinaryOp {
|
message BinaryOp {
|
||||||
enum BOp {
|
enum BOp {
|
||||||
ADD = 0;
|
ADD = 0;
|
||||||
@ -163,24 +80,11 @@ message UnaryOp {
|
|||||||
SLOAD = 2;
|
SLOAD = 2;
|
||||||
ISZERO = 3;
|
ISZERO = 3;
|
||||||
CALLDATALOAD = 4;
|
CALLDATALOAD = 4;
|
||||||
EXTCODESIZE = 5;
|
|
||||||
EXTCODEHASH = 6;
|
|
||||||
BALANCE = 7;
|
|
||||||
BLOCKHASH = 8;
|
|
||||||
}
|
}
|
||||||
required UOp op = 1;
|
required UOp op = 1;
|
||||||
required Expression operand = 2;
|
required Expression operand = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UnaryOpData {
|
|
||||||
enum UOpData {
|
|
||||||
SIZE = 1;
|
|
||||||
OFFSET = 2;
|
|
||||||
}
|
|
||||||
required UOpData op = 1;
|
|
||||||
required uint64 identifier = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message TernaryOp {
|
message TernaryOp {
|
||||||
enum TOp {
|
enum TOp {
|
||||||
ADDM = 0;
|
ADDM = 0;
|
||||||
@ -192,33 +96,9 @@ message TernaryOp {
|
|||||||
required Expression arg3 = 4;
|
required Expression arg3 = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CopyFunc {
|
|
||||||
enum CopyType {
|
|
||||||
CALLDATA = 0;
|
|
||||||
CODE = 1;
|
|
||||||
RETURNDATA = 2;
|
|
||||||
DATA = 3;
|
|
||||||
}
|
|
||||||
required CopyType ct = 1;
|
|
||||||
required Expression target = 2;
|
|
||||||
required Expression source = 3;
|
|
||||||
required Expression size = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message ExtCodeCopy {
|
|
||||||
required Expression addr = 1;
|
|
||||||
required Expression target = 2;
|
|
||||||
required Expression source = 3;
|
|
||||||
required Expression size = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
message NullaryOp {
|
message NullaryOp {
|
||||||
enum NOp {
|
enum NOp {
|
||||||
PC = 1;
|
|
||||||
MSIZE = 2;
|
|
||||||
GAS = 3;
|
|
||||||
CALLDATASIZE = 4;
|
CALLDATASIZE = 4;
|
||||||
CODESIZE = 5;
|
|
||||||
RETURNDATASIZE = 6;
|
RETURNDATASIZE = 6;
|
||||||
ADDRESS = 7;
|
ADDRESS = 7;
|
||||||
ORIGIN = 8;
|
ORIGIN = 8;
|
||||||
@ -226,7 +106,6 @@ message NullaryOp {
|
|||||||
CALLVALUE = 10;
|
CALLVALUE = 10;
|
||||||
GASPRICE = 11;
|
GASPRICE = 11;
|
||||||
COINBASE = 12;
|
COINBASE = 12;
|
||||||
TIMESTAMP = 13;
|
|
||||||
NUMBER = 14;
|
NUMBER = 14;
|
||||||
DIFFICULTY = 15;
|
DIFFICULTY = 15;
|
||||||
GASLIMIT = 16;
|
GASLIMIT = 16;
|
||||||
@ -247,21 +126,12 @@ message StoreFunc {
|
|||||||
required Storage st = 3;
|
required Storage st = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LogFunc {
|
message FunctionExpr {
|
||||||
enum NumTopics {
|
required uint64 index = 1;
|
||||||
ZERO = 0;
|
required Expression in_param1 = 2;
|
||||||
ONE = 1;
|
required Expression in_param2 = 3;
|
||||||
TWO = 2;
|
required Expression in_param3 = 4;
|
||||||
THREE = 3;
|
required Expression in_param4 = 5;
|
||||||
FOUR = 4;
|
|
||||||
}
|
|
||||||
required Expression pos = 1;
|
|
||||||
required Expression size = 2;
|
|
||||||
required NumTopics num_topics = 3;
|
|
||||||
required Expression t1 = 4;
|
|
||||||
required Expression t2 = 5;
|
|
||||||
required Expression t3 = 6;
|
|
||||||
required Expression t4 = 7;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message Expression {
|
message Expression {
|
||||||
@ -272,10 +142,7 @@ message Expression {
|
|||||||
UnaryOp unop = 4;
|
UnaryOp unop = 4;
|
||||||
TernaryOp top = 5;
|
TernaryOp top = 5;
|
||||||
NullaryOp nop = 6;
|
NullaryOp nop = 6;
|
||||||
FunctionCall func_expr = 7;
|
FunctionExpr funcexpr = 10;
|
||||||
LowLevelCall lowcall = 8;
|
|
||||||
Create create = 9;
|
|
||||||
UnaryOpData unopdata = 10;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,15 +153,15 @@ message AssignmentStatement {
|
|||||||
|
|
||||||
message IfStmt {
|
message IfStmt {
|
||||||
required Expression cond = 1;
|
required Expression cond = 1;
|
||||||
required Block if_body = 2;
|
required Block block = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BoundedForStmt {
|
message BoundedForStmt {
|
||||||
required Block for_body = 1;
|
required Block block = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ForStmt {
|
message ForStmt {
|
||||||
required Block for_body = 1;
|
required Block block = 1;
|
||||||
required Block for_init = 2;
|
required Block for_init = 2;
|
||||||
required Block for_post = 3;
|
required Block for_post = 3;
|
||||||
required Expression for_cond = 4;
|
required Expression for_cond = 4;
|
||||||
@ -302,48 +169,18 @@ message ForStmt {
|
|||||||
|
|
||||||
message CaseStmt {
|
message CaseStmt {
|
||||||
required Literal case_lit = 1;
|
required Literal case_lit = 1;
|
||||||
required Block case_block = 2;
|
required Block block = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SwitchStmt {
|
message SwitchStmt {
|
||||||
required Expression switch_expr = 1;
|
required Expression switch_expr = 1;
|
||||||
repeated CaseStmt case_stmt = 2;
|
repeated CaseStmt case_stmt = 2;
|
||||||
optional Block default_block = 3;
|
optional Block block = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BreakStmt {}
|
message BreakStmt {}
|
||||||
message ContinueStmt {}
|
message ContinueStmt {}
|
||||||
|
|
||||||
message StopInvalidStmt {
|
|
||||||
enum Type {
|
|
||||||
STOP = 0;
|
|
||||||
INVALID = 1;
|
|
||||||
}
|
|
||||||
required Type stmt = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message RetRevStmt {
|
|
||||||
enum Type {
|
|
||||||
RETURN = 0;
|
|
||||||
REVERT = 1;
|
|
||||||
}
|
|
||||||
required Type stmt = 1;
|
|
||||||
required Expression pos = 2;
|
|
||||||
required Expression size = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SelfDestructStmt {
|
|
||||||
required Expression addr = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message TerminatingStmt {
|
|
||||||
oneof term_oneof {
|
|
||||||
StopInvalidStmt stop_invalid = 1;
|
|
||||||
RetRevStmt ret_rev = 2;
|
|
||||||
SelfDestructStmt self_des = 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message FunctionDef {
|
message FunctionDef {
|
||||||
required uint32 num_input_params = 1;
|
required uint32 num_input_params = 1;
|
||||||
required uint32 num_output_params = 2;
|
required uint32 num_output_params = 2;
|
||||||
@ -359,7 +196,6 @@ message LeaveStmt {}
|
|||||||
|
|
||||||
message Statement {
|
message Statement {
|
||||||
oneof stmt_oneof {
|
oneof stmt_oneof {
|
||||||
VarDecl decl = 1;
|
|
||||||
AssignmentStatement assignment = 2;
|
AssignmentStatement assignment = 2;
|
||||||
IfStmt ifstmt = 3;
|
IfStmt ifstmt = 3;
|
||||||
StoreFunc storage_func = 4;
|
StoreFunc storage_func = 4;
|
||||||
@ -368,16 +204,11 @@ message Statement {
|
|||||||
SwitchStmt switchstmt = 7;
|
SwitchStmt switchstmt = 7;
|
||||||
BreakStmt breakstmt = 8;
|
BreakStmt breakstmt = 8;
|
||||||
ContinueStmt contstmt = 9;
|
ContinueStmt contstmt = 9;
|
||||||
LogFunc log_func = 10;
|
|
||||||
CopyFunc copy_func = 11;
|
|
||||||
ExtCodeCopy extcode_copy = 12;
|
|
||||||
TerminatingStmt terminatestmt = 13;
|
|
||||||
FunctionCall functioncall = 14;
|
FunctionCall functioncall = 14;
|
||||||
BoundedForStmt boundedforstmt = 15;
|
BoundedForStmt boundedforstmt = 15;
|
||||||
FunctionDef funcdef = 16;
|
FunctionDef funcdef = 16;
|
||||||
PopStmt pop = 17;
|
PopStmt pop = 17;
|
||||||
LeaveStmt leave = 18;
|
LeaveStmt leave = 18;
|
||||||
MultiVarDecl multidecl = 19;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,36 +216,8 @@ message Block {
|
|||||||
repeated Statement statements = 1;
|
repeated Statement statements = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Object {
|
message Program {
|
||||||
required Code code = 1;
|
|
||||||
optional Data data = 2;
|
|
||||||
optional Object sub_obj = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Code {
|
|
||||||
required Block block = 1;
|
required Block block = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Data {
|
package solidity.yul.test.yul_fuzzer;
|
||||||
required string hex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Program {
|
|
||||||
enum Version {
|
|
||||||
HOMESTEAD = 0;
|
|
||||||
TANGERINE = 1;
|
|
||||||
SPURIOUS = 2;
|
|
||||||
BYZANTIUM = 3;
|
|
||||||
CONSTANTINOPLE = 4;
|
|
||||||
PETERSBURG = 5;
|
|
||||||
ISTANBUL = 6;
|
|
||||||
BERLIN = 7;
|
|
||||||
}
|
|
||||||
oneof program_oneof {
|
|
||||||
Block block = 1;
|
|
||||||
Object obj = 2;
|
|
||||||
}
|
|
||||||
required Version ver = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
package solidity.yul.test.yul_fuzzer;
|
|
@ -96,7 +96,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
stack.optimize();
|
stack.optimize();
|
||||||
termReason = yulFuzzerUtil::interpret(
|
yulFuzzerUtil::interpret(
|
||||||
os2,
|
os2,
|
||||||
stack.parserResult()->code,
|
stack.parserResult()->code,
|
||||||
EVMDialect::strictAssemblyForEVMObjects(version),
|
EVMDialect::strictAssemblyForEVMObjects(version),
|
||||||
|
@ -46,6 +46,18 @@ using namespace solidity::yul::test;
|
|||||||
|
|
||||||
using solidity::util::h256;
|
using solidity::util::h256;
|
||||||
|
|
||||||
|
std::map<u256, std::string> InterpreterState::dumpMemory() const
|
||||||
|
{
|
||||||
|
std::map<u256, std::string> out;
|
||||||
|
map<u256, u256> words;
|
||||||
|
for (auto const& [offset, value]: memory)
|
||||||
|
words[(offset / 0x20) * 0x20] |= u256(uint32_t(value)) << (256 - 8 - 8 * size_t(offset % 0x20));
|
||||||
|
for (auto const& [offset, value]: words)
|
||||||
|
if (value != 0)
|
||||||
|
out.emplace(offset, h256(value).hex());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
void InterpreterState::dumpTraceAndState(ostream& _out) const
|
void InterpreterState::dumpTraceAndState(ostream& _out) const
|
||||||
{
|
{
|
||||||
_out << "Trace:" << endl;
|
_out << "Trace:" << endl;
|
||||||
|
@ -94,6 +94,7 @@ struct InterpreterState
|
|||||||
ControlFlowState controlFlowState = ControlFlowState::Default;
|
ControlFlowState controlFlowState = ControlFlowState::Default;
|
||||||
|
|
||||||
void dumpTraceAndState(std::ostream& _out) const;
|
void dumpTraceAndState(std::ostream& _out) const;
|
||||||
|
std::map<u256, std::string> dumpMemory() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user